diff --git a/.env b/.env index 6ffb10bc93..66bc54aac9 100644 --- a/.env +++ b/.env @@ -40,6 +40,7 @@ _APP_STORAGE_S3_ACCESS_KEY= _APP_STORAGE_S3_SECRET= _APP_STORAGE_S3_REGION=us-east-1 _APP_STORAGE_S3_BUCKET= +_APP_STORAGE_S3_ENDPOINT= _APP_STORAGE_DO_SPACES_ACCESS_KEY= _APP_STORAGE_DO_SPACES_SECRET= _APP_STORAGE_DO_SPACES_REGION=us-east-1 @@ -89,6 +90,7 @@ _APP_MAINTENANCE_RETENTION_EXECUTION=1209600 _APP_MAINTENANCE_RETENTION_ABUSE=86400 _APP_MAINTENANCE_RETENTION_AUDIT=1209600 _APP_USAGE_AGGREGATION_INTERVAL=30 +_APP_STATS_RESOURCES_INTERVAL=3600 _APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000 _APP_MAINTENANCE_RETENTION_SCHEDULES=86400 _APP_USAGE_STATS=enabled @@ -113,3 +115,4 @@ _APP_MESSAGE_PUSH_TEST_DSN= _APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10 _APP_PROJECT_REGIONS=default _APP_FUNCTIONS_CREATION_ABUSE_LIMIT=5000 +_APP_STATS_USAGE_DUAL_WRITING_DBS=database_db_main \ No newline at end of file diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 050f80b10a..0c0482ca8f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -13,8 +13,13 @@ on: schedule: - cron: '0 16 * * 0' +permissions: + contents: read + jobs: analyze: + permissions: + security-events: write name: Analyze runs-on: ubuntu-latest diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 9fe3e024d7..9419302dbc 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -8,9 +8,33 @@ env: IMAGE: appwrite-dev CACHE_KEY: appwrite-dev-${{ github.event.pull_request.head.sha }} -on: [pull_request] +on: [ pull_request ] jobs: + check_database_changes: + name: Check if utopia-php/database changed + runs-on: ubuntu-latest + outputs: + database_changed: ${{ steps.check.outputs.database_changed }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Fetch base branch + run: git fetch origin ${{ github.event.pull_request.base.ref }} + + - name: Check for utopia-php/database changes + id: check + run: | + if git diff origin/${{ github.event.pull_request.base.ref }} HEAD -- composer.lock | grep -q '"name": "utopia-php/database"'; then + echo "Database version changed, going to run all mode tests." + echo "database_changed=true" >> "$GITHUB_ENV" + echo "database_changed=true" >> "$GITHUB_OUTPUT" + else + echo "database_changed=false" >> "$GITHUB_ENV" + echo "database_changed=false" >> "$GITHUB_OUTPUT" + fi + setup: name: Setup & Build Appwrite Image runs-on: ubuntu-latest @@ -106,6 +130,62 @@ jobs: name: E2E Service Test runs-on: ubuntu-latest needs: setup + strategy: + fail-fast: false + matrix: + service: [ + Account, + Avatars, + Console, + Databases, + Functions, + FunctionsSchedule, + GraphQL, + Health, + Locale, + Projects, + Realtime, + Storage, + Teams, + Users, + Webhooks, + VCS, + Messaging, + Migrations + ] + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Load Cache + uses: actions/cache@v4 + with: + key: ${{ env.CACHE_KEY }} + path: /tmp/${{ env.IMAGE }}.tar + fail-on-cache-miss: true + + - name: Load and Start Appwrite + run: | + docker load --input /tmp/${{ env.IMAGE }}.tar + docker compose up -d + sleep 30 + + - name: Run ${{ matrix.service }} tests with Project table mode + run: | + echo "Using project tables" + export _APP_DATABASE_SHARED_TABLES= + export _APP_DATABASE_SHARED_TABLES_V1= + + docker compose exec -T \ + -e _APP_DATABASE_SHARED_TABLES \ + -e _APP_DATABASE_SHARED_TABLES_V1 \ + appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug + + e2e_shared_mode_test: + name: E2E Shared Mode Service Test + runs-on: ubuntu-latest + needs: [ setup, check_database_changes ] + if: needs.check_database_changes.outputs.database_changed == 'true' strategy: fail-fast: false matrix: @@ -132,7 +212,6 @@ jobs: Migrations ] tables-mode: [ - 'Project', 'Shared V1', 'Shared V2', ] @@ -164,10 +243,6 @@ jobs: echo "Using shared tables V2" export _APP_DATABASE_SHARED_TABLES=database_db_main export _APP_DATABASE_SHARED_TABLES_V1= - else - echo "Using project tables" - export _APP_DATABASE_SHARED_TABLES= - export _APP_DATABASE_SHARED_TABLES_V1= fi echo 'Sleep 1 minute, as temporary fix for v4rc executor startup (image pulling)' @@ -258,4 +333,4 @@ jobs: comment-id: ${{ steps.fc.outputs.comment-id }} issue-number: ${{ github.event.pull_request.number }} body-path: benchmark.txt - edit-mode: replace \ No newline at end of file + edit-mode: replace diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 19fd9e3d48..7746ef99af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,6 @@ # Contributing -We would ❤️ you to contribute to Appwrite and help make it better! We want contributing to Appwrite to be fun, enjoyable, and educational for anyone and everyone. All contributions are welcome, including issues, and new docs, as well as updates and tweaks, blog posts, workshops, and more. +We would :heart: you to contribute to Appwrite and help make it better! We want contributing to Appwrite to be fun, enjoyable, and educational for anyone and everyone. All contributions are welcome, including issues, and new docs, as well as updates and tweaks, blog posts, workshops, and more. ## Here for Hacktoberfest? If you're here to contribute during Hacktoberfest, we're so happy to see you here. Appwrite has been a long-time participant of Hacktoberfest and we welcome you, whatever your experience level. This year, we're **only taking contributions for issues tagged** `hacktoberfest`, so we can focus our resources to support your contributions. @@ -9,13 +9,13 @@ You can [find issues using this query](https://github.com/search?q=org%3Aappwrit ## How to Start? -If you are worried or don’t know where to start, check out the next section that explains what kind of help we could use and where you can get involved. You can send your questions to [@appwrite](https://twitter.com/appwrite) on Twitter or to anyone from the [Appwrite team on Discord](https://appwrite.io/discord). You can also submit an issue, and a maintainer can guide you! +If you are worried or don’t know where to start, check out the next section that explains what kind of help we could use and where you can get involved. You can send your questions to [@appwrite on Twitter](https://twitter.com/appwrite) or to anyone from the [Appwrite team on Discord](https://appwrite.io/discord). You can also submit an issue, and a maintainer can guide you! ## Code of Conduct Help us keep Appwrite open and inclusive. Please read and follow our [Code of Conduct](https://github.com/appwrite/.github/blob/main/CODE_OF_CONDUCT.md). -## Submit a Pull Request 🚀 +## Submit a Pull Request :rocket: Branch naming convention is as following @@ -65,13 +65,13 @@ Now, go a step further by running the linter using the following command to manu composer lint ``` -This will give you a list of errors to rectify. If you need more information on the errors, you can pass in additional command line arguments to get more verbose information. More lists of available arguments can be found [here](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage). A very useful command line argument is `--report=diff`. This will give you the expected changes by the linter for easy fixing of formatting issues. +This will give you a list of errors to rectify. If you need more information on the errors, you can pass in additional command line arguments to get more verbose information. More lists of available arguments can be found [on PHP_Codesniffer usage Wiki](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage). A very useful command line argument is `--report=diff`. This will give you the expected changes by the linter for easy fixing of formatting issues. ```bash composer lint --report=diff ``` -5. Push changes to GitHub. +5. Push changes to GitHub ``` $ git push origin [name_of_your_new_branch] @@ -323,7 +323,7 @@ Adding a new dependency should have vital value for the product with minimum pos ## Introducing New Features -We would 💖 you to contribute to Appwrite, but we also want to ensure Appwrite is loyal to its vision and mission statement 🙏. +We would :sparkling_heart: you to contribute to Appwrite, but we also want to ensure Appwrite is loyal to its vision and mission statement :pray:. For us to find the right balance, please open an issue explaining your ideas before introducing a new pull request. @@ -389,7 +389,7 @@ In file `app/controllers/shared/api.php` On the database listener, add to an exi ```php case $document->getCollection() === 'teams': - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_TEAMS, $value); // per project break; ``` @@ -401,10 +401,10 @@ In that case you need also to handle children removal using addReduce() method c ```php case $document->getCollection() === 'buckets': //buckets - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_BUCKETS, $value); // per project if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage + $queueForStatsUsage ->addReduce($document); } break; @@ -450,16 +450,16 @@ public function __construct() ->inject('dbForProject') ->inject('queueForFunctions') ->inject('queueForEvents') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('log') - ->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log)); + ->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForStatsUsage, $log)); } ``` and then trigger the queue with the new metric like so: ```php -$queueForUsage +$queueForStatsUsage ->addMetric(METRIC_BUILDS, 1) ->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0)) ->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000) @@ -662,7 +662,7 @@ Pull requests are great, but there are many other ways you can help Appwrite. ### Blogging & Speaking -Blogging, speaking about, or creating tutorials about one of Appwrite’s many features are great ways to get the word out about Appwrite. Mention [@appwrite](https://twitter.com/appwrite) on Twitter and/or [email team@appwrite.io](mailto:team@appwrite.io) so we can give pointers and tips and help you spread the word by promoting your content on the different Appwrite communication channels. Please add your blog posts and videos of talks to our [Awesome Appwrite](https://github.com/appwrite/awesome-appwrite) repo on GitHub. +Blogging, speaking about, or creating tutorials about one of Appwrite’s many features are great ways to get the word out about Appwrite. Mention [@appwrite on Twitter](https://twitter.com/appwrite) and/or [email team@appwrite.io](mailto:team@appwrite.io) so we can give pointers and tips and help you spread the word by promoting your content on the different Appwrite communication channels. Please add your blog posts and videos of talks to our [Awesome Appwrite](https://github.com/appwrite/awesome-appwrite) repo on GitHub. ### Presenting at Meetups diff --git a/Dockerfile b/Dockerfile index 41810f5dc4..2bb9f80d9e 100755 --- a/Dockerfile +++ b/Dockerfile @@ -85,6 +85,10 @@ RUN chmod +x /usr/local/bin/doctor && \ chmod +x /usr/local/bin/worker-messaging && \ chmod +x /usr/local/bin/worker-migrations && \ chmod +x /usr/local/bin/worker-webhooks && \ + chmod +x /usr/local/bin/worker-stats-usage && \ + chmod +x /usr/local/bin/worker-stats-usage-dump && \ + chmod +x /usr/local/bin/stats-resources && \ + chmod +x /usr/local/bin/worker-stats-resources && \ chmod +x /usr/local/bin/worker-usage && \ chmod +x /usr/local/bin/worker-usage-dump diff --git a/README-CN.md b/README-CN.md index 92a9bf9806..c024491907 100644 --- a/README-CN.md +++ b/README-CN.md @@ -1,8 +1,8 @@ -> 好消息!Appwrite 云现已进入公开测试版!立即访问 cloud.appwrite.io 注册,体验无忧的托管服务。今天就加入我们的云端吧!☁️🎉 +> 好消息!Appwrite 云现已进入公开测试版!立即访问 cloud.appwrite.io 注册,体验无忧的托管服务。今天就加入我们的云端吧!:cloud: :tada:

- Appwrite Logo + Appwrite banner with logo and slogan build like a team of hundreds

适用于[Flutter/Vue/Angular/React/iOS/Android/* 等等平台 *]的完整后端服务 @@ -36,6 +36,8 @@ Appwrite 可以提供给开发者用户验证,外部授权,用户数据读 内容: + +- [开始](#开始) - [安装](#安装) - [Unix](#unix) - [Windows](#windows) @@ -54,6 +56,9 @@ Appwrite 可以提供给开发者用户验证,外部授权,用户数据读 - [订阅我们](#订阅我们) - [版权说明](#版权说明) +## 开始 +要轻松开始使用Appwrite,您可以[**免费注册Appwrite Cloud**](https://cloud.appwrite.io/)。在Appwrite Cloud公开测试版期间,您可以完全免费使用Appwrite,而且我们不会收集您的信用卡信息。 + ## 安装 Appwrite 的容器化服务器只需要一行指令就可以运行。您可以使用 docker-compose 在本地主机上运行 Appwrite,也可以在任何其他容器化工具(如 [Kubernetes](https://kubernetes.io/docs/home/)、[Docker Swarm](https://docs.docker.com/engine/swarm/) 或 [Rancher](https://rancher.com/docs/))上运行 Appwrite。 @@ -98,7 +103,42 @@ docker run -it --rm ` ### 从旧版本升级 -如果您从旧版本升级 Appwrite 服务器,则应在设置完成后使用 Appwrite 迁移工具。有关这方面的更多信息,请查看 [安装文档](https://appwrite.io/docs/installation)。 +如果您从旧版本升级 Appwrite 服务器,则应在设置完成后使用 Appwrite 迁移工具。有关这方面的更多信息,请查看 [安装文档](https://appwrite.io/docs/self-hosting)。 + +## 一键配置 + +除了在本地运行 Appwrite,您还可以使用预配置的设置启动 Appwrite。这样可以让您快速启动并运行 Appwrite,而无需在本地计算机上安装 Docker。 + +请从以下提供商中选择一个: + + + + + + + + +
+ + DigitalOcean Logo +
DigitalOcean
+ +
+ + Gitpod Logo +
Gitpod
+ +
+ + Akamai Logo +
Akamai Compute
+ +
+ + AWS Logo +
AWS Marketplace
+ +
## 入门 @@ -146,29 +186,25 @@ docker run -it --rm ` 以下是当前支持的平台和语言列表。如果您想帮助我们为您选择的平台添加支持,您可以访问我们的 [SDK 生成器](https://github.com/appwrite/sdk-generator) 项目并查看我们的 [贡献指南](https://github.com/appwrite/sdk-generator/blob/master/CONTRIBUTING.md)。 #### 客户端 - -- ✅   [Web](https://github.com/appwrite/sdk-for-web) (由 Appwrite 团队维护) -- ✅   [Flutter](https://github.com/appwrite/sdk-for-flutter) (由 Appwrite 团队维护) -- ✅   [Apple](https://github.com/appwrite/sdk-for-apple) (由 Appwrite 团队维护) -- ✅   [Android](https://github.com/appwrite/sdk-for-android) (由 Appwrite 团队维护) -- ✅   [React Native](https://github.com/appwrite/sdk-for-react-native) - **公测** (由 Appwrite 团队维护) +* :white_check_mark:   [Web](https://github.com/appwrite/sdk-for-web) (由 Appwrite 团队维护) +* :white_check_mark:   [Flutter](https://github.com/appwrite/sdk-for-flutter) (由 Appwrite 团队维护) +* :white_check_mark:   [Apple](https://github.com/appwrite/sdk-for-apple) - **公测** (由 Appwrite 团队维护) +* :white_check_mark:   [Android](https://github.com/appwrite/sdk-for-android) (由 Appwrite 团队维护) #### 服务器 - -- ✅   [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护) -- ✅   [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwrite 团队维护) -- ✅   [Dart](https://github.com/appwrite/sdk-for-dart) (由 Appwrite 团队维护) -- ✅   [Deno](https://github.com/appwrite/sdk-for-deno) (由 Appwrite 团队维护) -- ✅   [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护) -- ✅   [Python](https://github.com/appwrite/sdk-for-python) (由 Appwrite 团队维护) -- ✅   [Kotlin](https://github.com/appwrite/sdk-for-kotlin) (由 Appwrite 团队维护) -- ✅   [Swift](https://github.com/appwrite/sdk-for-swift) (由 Appwrite 团队维护) -- ✅   [.NET](https://github.com/appwrite/sdk-for-dotnet) - **公测** (由 Appwrite 团队维护) +* :white_check_mark:   [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护) +* :white_check_mark:   [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwrite 团队维护) +* :white_check_mark:   [Dart](https://github.com/appwrite/sdk-for-dart) - (由 Appwrite 团队维护) +* :white_check_mark:   [Deno](https://github.com/appwrite/sdk-for-deno) - **公测** (由 Appwrite 团队维护) +* :white_check_mark:   [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护) +* :white_check_mark:   [Python](https://github.com/appwrite/sdk-for-python) (由 Appwrite 团队维护) +* :white_check_mark:   [Kotlin](https://github.com/appwrite/sdk-for-kotlin) - **公测** (由 Appwrite 团队维护) +* :white_check_mark:   [Apple](https://github.com/appwrite/sdk-for-apple) - **公测** (由 Appwrite 团队维护) +* :white_check_mark:   [.NET](https://github.com/appwrite/sdk-for-dotnet) - **公测** (由 Appwrite 团队维护) #### 开发者社区 - -- ✅   [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (维护者 [Michael Gangolf](https://github.com/m1ga/)) -- ✅   [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (维护者 [fenix-hub @GodotNuts](https://github.com/fenix-hub)) +* :white_check_mark:   [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (维护者 [Michael Gangolf](https://github.com/m1ga/)) +* :white_check_mark:   [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (维护者 [fenix-hub @GodotNuts](https://github.com/fenix-hub)) 找不到需要的的 SDK? - 欢迎通过发起 PR 来帮助我们完善 Appwrite 的软件生态环境 [SDK 生成器](https://github.com/appwrite/sdk-generator)! diff --git a/README.md b/README.md index ab57e65c4c..5bccf4739f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ -> Appwrite Init has concluded! You can check out all the latest announcements [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) :rocket:

- Appwrite Logo + Appwrite banner, with logo and text saying

Appwrite is a backend platform for developing Web, Mobile, and Flutter applications. Built with the open source community and optimized for developer experience in the coding languages you love. @@ -12,11 +12,11 @@ -[![We're Hiring](https://img.shields.io/static/v1?label=We're&message=Hiring&color=blue&style=flat-square)](https://appwrite.io/company/careers) -[![Hacktoberfest](https://img.shields.io/static/v1?label=hacktoberfest&message=ready&color=191120&style=flat-square)](https://hacktoberfest.appwrite.io) -[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github) -[![Build Status](https://img.shields.io/github/actions/workflow/status/appwrite/appwrite/tests.yml?branch=master&label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions) -[![X Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) +[![We're Hiring label](https://img.shields.io/static/v1?label=We're&message=Hiring&color=blue&style=flat-square)](https://appwrite.io/company/careers) +[![Hacktoberfest label](https://img.shields.io/static/v1?label=hacktoberfest&message=ready&color=191120&style=flat-square)](https://hacktoberfest.appwrite.io) +[![Discord label](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github) +[![Build Status label](https://img.shields.io/github/actions/workflow/status/appwrite/appwrite/tests.yml?branch=master&label=tests&style=flat-square)](https://github.com/appwrite/appwrite/actions) +[![X Account label](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) @@ -37,13 +37,14 @@ Using Appwrite, you can easily integrate your app with user authentication and m

-![Appwrite](public/images/github.png) +![Appwrite project dashboard showing various Appwrite features](public/images/github.png) -Find out more at: [https://appwrite.io](https://appwrite.io) +Find out more at: [https://appwrite.io](https://appwrite.io). Table of Contents: -- [Installation](#installation) +- [Getting Started](#getting-started) +- [Self-Hosting](#self-hosting) - [Unix](#unix) - [Windows](#windows) - [CMD](#cmd) @@ -62,11 +63,14 @@ Table of Contents: - [Follow Us](#follow-us) - [License](#license) -## Installation +## Getting Started +The easiest way to get started with Appwrite is by [signing up for Appwrite Cloud](https://cloud.appwrite.io/). While Appwrite Cloud is in public beta, you can build with Appwrite completely free, and we won't collect you credit card information. + +## Self-Hosting Appwrite is designed to run in a containerized environment. Running your server is as easy as running one command from your terminal. You can either run Appwrite on your localhost using docker-compose or on any other container orchestration tool, such as [Kubernetes](https://kubernetes.io/docs/home/), [Docker Swarm](https://docs.docker.com/engine/swarm/), or [Rancher](https://rancher.com/docs/). -The easiest way to start running your Appwrite server is by running our docker-compose file. Before running the installation command, make sure you have [Docker](https://www.docker.com/products/docker-desktop) installed on your machine: +Before running the installation command, make sure you have [Docker](https://www.docker.com/products/docker-desktop) installed on your machine: ### Unix @@ -106,7 +110,7 @@ For advanced production and custom installation, check out our Docker [environme ### Upgrade from an Older Version -If you are upgrading your Appwrite server from an older version, you should use the Appwrite migration tool once your setup is completed. For more information regarding this, check out the [Installation Docs](https://appwrite.io/docs/installation). +If you are upgrading your Appwrite server from an older version, you should use the Appwrite migration tool once your setup is completed. For more information regarding this, check out the [Installation Docs](https://appwrite.io/docs/self-hosting). ## One-Click Setups @@ -192,34 +196,34 @@ Below is a list of currently supported platforms and languages. If you would lik #### Client -- ✅   [Web](https://github.com/appwrite/sdk-for-web) (Maintained by the Appwrite Team) -- ✅   [Flutter](https://github.com/appwrite/sdk-for-flutter) (Maintained by the Appwrite Team) -- ✅   [Apple](https://github.com/appwrite/sdk-for-apple) (Maintained by the Appwrite Team) -- ✅   [Android](https://github.com/appwrite/sdk-for-android) (Maintained by the Appwrite Team) -- ✅   [React Native](https://github.com/appwrite/sdk-for-react-native) - **Beta** (Maintained by the Appwrite Team) +- :white_check_mark:   [Web](https://github.com/appwrite/sdk-for-web) (Maintained by the Appwrite Team) +- :white_check_mark:   [Flutter](https://github.com/appwrite/sdk-for-flutter) (Maintained by the Appwrite Team) +- :white_check_mark:   [Apple](https://github.com/appwrite/sdk-for-apple) (Maintained by the Appwrite Team) +- :white_check_mark:   [Android](https://github.com/appwrite/sdk-for-android) (Maintained by the Appwrite Team) +- :white_check_mark:   [React Native](https://github.com/appwrite/sdk-for-react-native) - **Beta** (Maintained by the Appwrite Team) #### Server -- ✅   [NodeJS](https://github.com/appwrite/sdk-for-node) (Maintained by the Appwrite Team) -- ✅   [PHP](https://github.com/appwrite/sdk-for-php) (Maintained by the Appwrite Team) -- ✅   [Dart](https://github.com/appwrite/sdk-for-dart) (Maintained by the Appwrite Team) -- ✅   [Deno](https://github.com/appwrite/sdk-for-deno) (Maintained by the Appwrite Team) -- ✅   [Ruby](https://github.com/appwrite/sdk-for-ruby) (Maintained by the Appwrite Team) -- ✅   [Python](https://github.com/appwrite/sdk-for-python) (Maintained by the Appwrite Team) -- ✅   [Kotlin](https://github.com/appwrite/sdk-for-kotlin) (Maintained by the Appwrite Team) -- ✅   [Swift](https://github.com/appwrite/sdk-for-swift) (Maintained by the Appwrite Team) -- ✅   [.NET](https://github.com/appwrite/sdk-for-dotnet) - **Beta** (Maintained by the Appwrite Team) +- :white_check_mark:   [NodeJS](https://github.com/appwrite/sdk-for-node) (Maintained by the Appwrite Team) +- :white_check_mark:   [PHP](https://github.com/appwrite/sdk-for-php) (Maintained by the Appwrite Team) +- :white_check_mark:   [Dart](https://github.com/appwrite/sdk-for-dart) (Maintained by the Appwrite Team) +- :white_check_mark:   [Deno](https://github.com/appwrite/sdk-for-deno) (Maintained by the Appwrite Team) +- :white_check_mark:   [Ruby](https://github.com/appwrite/sdk-for-ruby) (Maintained by the Appwrite Team) +- :white_check_mark:   [Python](https://github.com/appwrite/sdk-for-python) (Maintained by the Appwrite Team) +- :white_check_mark:   [Kotlin](https://github.com/appwrite/sdk-for-kotlin) (Maintained by the Appwrite Team) +- :white_check_mark:   [Swift](https://github.com/appwrite/sdk-for-swift) (Maintained by the Appwrite Team) +- :white_check_mark:   [.NET](https://github.com/appwrite/sdk-for-dotnet) - **Beta** (Maintained by the Appwrite Team) #### Community -- ✅   [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (Maintained by [Michael Gangolf](https://github.com/m1ga/)) -- ✅   [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (Maintained by [fenix-hub @GodotNuts](https://github.com/fenix-hub)) +- :white_check_mark:   [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (Maintained by [Michael Gangolf](https://github.com/m1ga/)) +- :white_check_mark:   [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (Maintained by [fenix-hub @GodotNuts](https://github.com/fenix-hub)) Looking for more SDKs? - Help us by contributing a pull request to our [SDK Generator](https://github.com/appwrite/sdk-generator)! ## Architecture -![Appwrite Architecture](docs/specs/overview.drawio.svg) +![Appwrite Architecture showing how Appwrite is built and the services and tools it uses](docs/specs/overview.drawio.svg) Appwrite uses a microservices architecture that was designed for easy scaling and delegation of responsibilities. In addition, Appwrite supports multiple APIs, such as REST, WebSocket, and GraphQL to allow you to interact with your resources by leveraging your existing knowledge and protocols of choice. @@ -229,7 +233,7 @@ The Appwrite API layer was designed to be extremely fast by leveraging in-memory All code contributions, including those of people having commit access, must go through a pull request and be approved by a core developer before being merged. This is to ensure a proper review of all the code. -We truly ❤️ pull requests! If you wish to help, you can learn more about how you can contribute to this project in the [contribution guide](CONTRIBUTING.md). +We truly :heart: pull requests! If you wish to help, you can learn more about how you can contribute to this project in the [contribution guide](CONTRIBUTING.md). ## Security diff --git a/app/cli.php b/app/cli.php index 22f09c7c23..360b5478c5 100644 --- a/app/cli.php +++ b/app/cli.php @@ -5,6 +5,8 @@ require_once __DIR__ . '/init.php'; use Appwrite\Event\Certificate; use Appwrite\Event\Delete; use Appwrite\Event\Func; +use Appwrite\Event\StatsResources; +use Appwrite\Event\StatsUsage; use Appwrite\Platform\Appwrite; use Appwrite\Runtimes\Runtimes; use Utopia\Cache\Adapter\Sharding; @@ -160,6 +162,45 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform }; }, ['pools', 'dbForPlatform', 'cache']); +CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) { + $database = null; + return function (?Document $project = null) use ($pools, $cache, $database) { + if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { + $database->setTenant($project->getInternalId()); + return $database; + } + + $dbAdapter = $pools + ->get('logs') + ->pop() + ->getResource(); + + $database = new Database( + $dbAdapter, + $cache + ); + + $database + ->setSharedTables(true) + ->setNamespace('logsV1') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS) + ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); + + // set tenant + if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') { + $database->setTenant($project->getInternalId()); + } + + return $database; + }; +}, ['pools', 'cache']); + +CLI::setResource('queueForStatsUsage', function (Connection $publisher) { + return new StatsUsage($publisher); +}, ['publisher']); +CLI::setResource('queueForStatsResources', function (Publisher $publisher) { + return new StatsResources($publisher); +}, ['publisher']); CLI::setResource('publisher', function (Group $pools) { return $pools->get('publisher')->pop()->getResource(); }, ['pools']); diff --git a/app/config/avatars/credit-cards.php b/app/config/avatars/credit-cards.php index eb76c576cf..52760bf9dc 100644 --- a/app/config/avatars/credit-cards.php +++ b/app/config/avatars/credit-cards.php @@ -16,5 +16,6 @@ return [ 'union-china-pay' => ['name' => 'Union China Pay', 'path' => __DIR__ . '/credit-cards/union-china-pay.png'], 'visa' => ['name' => 'Visa', 'path' => __DIR__ . '/credit-cards/visa.png'], 'mir' => ['name' => 'MIR', 'path' => __DIR__ . '/credit-cards/mir.png'], - 'maestro' => ['name' => 'Maestro', 'path' => __DIR__ . '/credit-cards/maestro.png'] + 'maestro' => ['name' => 'Maestro', 'path' => __DIR__ . '/credit-cards/maestro.png'], + 'rupay' => ['name' => 'Rupay', 'path' => __DIR__ . '/credit-cards/rupay.png'] ]; diff --git a/app/config/avatars/credit-cards/rupay.png b/app/config/avatars/credit-cards/rupay.png new file mode 100644 index 0000000000..dfb95e9844 Binary files /dev/null and b/app/config/avatars/credit-cards/rupay.png differ diff --git a/app/config/collections.php b/app/config/collections.php index 8c8356aafd..533dee57a8 100644 --- a/app/config/collections.php +++ b/app/config/collections.php @@ -5,6 +5,7 @@ $common = include __DIR__ . '/collections/common.php'; $projects = include __DIR__ . '/collections/projects.php'; $databases = include __DIR__ . '/collections/databases.php'; $platform = include __DIR__ . '/collections/platform.php'; +$logs = include __DIR__ . '/collections/logs.php'; // see - http.php#245 // $collections['buckets']['files']; @@ -27,6 +28,7 @@ $collections = [ 'databases' => $databases, 'projects' => array_merge($projects, $common), 'console' => array_merge($platform, $common), + 'logs' => $logs, ]; return $collections; diff --git a/app/config/collections/logs.php b/app/config/collections/logs.php new file mode 100644 index 0000000000..069dcf5a4b --- /dev/null +++ b/app/config/collections/logs.php @@ -0,0 +1,94 @@ + ID::custom(Database::METADATA), + '$id' => ID::custom('stats'), + 'name' => 'stats', + 'attributes' => [ + [ + '$id' => ID::custom('metric'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('region'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 255, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('value'), + 'type' => Database::VAR_INTEGER, + 'format' => '', + 'size' => 8, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + [ + '$id' => ID::custom('time'), + 'type' => Database::VAR_DATETIME, + 'format' => '', + 'size' => 0, + 'signed' => false, + 'required' => false, + 'default' => null, + 'array' => false, + 'filters' => ['datetime'], + ], + [ + '$id' => ID::custom('period'), + 'type' => Database::VAR_STRING, + 'format' => '', + 'size' => 4, + 'signed' => true, + 'required' => true, + 'default' => null, + 'array' => false, + 'filters' => [], + ], + ], + 'indexes' => [ + [ + '$id' => ID::custom('_key_time'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['time'], + 'lengths' => [], + 'orders' => [Database::ORDER_DESC], + ], + [ + '$id' => ID::custom('_key_period_time'), + 'type' => Database::INDEX_KEY, + 'attributes' => ['period', 'time'], + 'lengths' => [], + 'orders' => [Database::ORDER_ASC], + ], + [ + '$id' => ID::custom('_key_metric_period_time'), + 'type' => Database::INDEX_UNIQUE, + 'attributes' => ['metric', 'period', 'time'], + 'lengths' => [], + 'orders' => [Database::ORDER_DESC], + ], + ], +]; + +return $logsCollection; diff --git a/app/config/errors.php b/app/config/errors.php index 6e9f06d33a..e41f663168 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -356,7 +356,7 @@ return [ ], Exception::TEAM_INVALID_SECRET => [ 'name' => Exception::TEAM_INVALID_SECRET, - 'description' => 'The team invitation secret is invalid. Please request a new invitation and try again.', + 'description' => 'The team invitation secret is invalid. Please request a new invitation and try again.', 'code' => 401, ], Exception::TEAM_MEMBERSHIP_MISMATCH => [ diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 21a66361e5..05b38d5cbf 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -3383,7 +3383,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3404,7 +3404,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3423,7 +3424,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -4365,7 +4367,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -4761,7 +4763,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 300, + "weight": 301, "cookies": false, "type": "", "deprecated": false, @@ -4846,7 +4848,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 299, + "weight": 300, "cookies": false, "type": "", "deprecated": false, @@ -4960,7 +4962,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 301, + "weight": 302, "cookies": false, "type": "", "deprecated": false, @@ -5033,7 +5035,7 @@ }, "x-appwrite": { "method": "query", - "weight": 324, + "weight": 325, "cookies": false, "type": "graphql", "deprecated": false, @@ -5084,7 +5086,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 323, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -5543,7 +5545,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 369, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -5625,7 +5627,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 373, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -5699,7 +5701,7 @@ }, "x-appwrite": { "method": "listFiles", - "weight": 207, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -5784,7 +5786,7 @@ }, "x-appwrite": { "method": "createFile", - "weight": 206, + "weight": 207, "cookies": false, "type": "upload", "deprecated": false, @@ -5881,7 +5883,7 @@ }, "x-appwrite": { "method": "getFile", - "weight": 208, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -5952,7 +5954,7 @@ }, "x-appwrite": { "method": "updateFile", - "weight": 213, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -6040,7 +6042,7 @@ }, "x-appwrite": { "method": "deleteFile", - "weight": 214, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -6106,7 +6108,7 @@ }, "x-appwrite": { "method": "getFileDownload", - "weight": 210, + "weight": 211, "cookies": false, "type": "location", "deprecated": false, @@ -6172,7 +6174,7 @@ }, "x-appwrite": { "method": "getFilePreview", - "weight": 209, + "weight": 210, "cookies": false, "type": "location", "deprecated": false, @@ -6388,7 +6390,7 @@ }, "x-appwrite": { "method": "getFileView", - "weight": 211, + "weight": 212, "cookies": false, "type": "location", "deprecated": false, @@ -6461,7 +6463,7 @@ }, "x-appwrite": { "method": "list", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -6536,7 +6538,7 @@ }, "x-appwrite": { "method": "create", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -6620,7 +6622,7 @@ }, "x-appwrite": { "method": "get", - "weight": 219, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -6681,7 +6683,7 @@ }, "x-appwrite": { "method": "updateName", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -6754,7 +6756,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -6817,7 +6819,7 @@ }, "x-appwrite": { "method": "listMemberships", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -6902,7 +6904,7 @@ }, "x-appwrite": { "method": "createMembership", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -7012,7 +7014,7 @@ }, "x-appwrite": { "method": "getMembership", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -7083,7 +7085,7 @@ }, "x-appwrite": { "method": "updateMembership", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -7169,7 +7171,7 @@ }, "x-appwrite": { "method": "deleteMembership", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -7242,7 +7244,7 @@ }, "x-appwrite": { "method": "updateMembershipStatus", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -7339,7 +7341,7 @@ }, "x-appwrite": { "method": "getPrefs", - "weight": 220, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -7399,7 +7401,7 @@ }, "x-appwrite": { "method": "updatePrefs", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 60795ac276..d51a36910c 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -3387,7 +3387,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3408,7 +3408,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3427,7 +3428,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -4293,7 +4295,7 @@ }, "x-appwrite": { "method": "chat", - "weight": 326, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -4352,7 +4354,7 @@ }, "x-appwrite": { "method": "getResource", - "weight": 419, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -4426,7 +4428,7 @@ }, "x-appwrite": { "method": "variables", - "weight": 325, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -7890,7 +7892,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -9053,7 +9055,7 @@ }, "x-appwrite": { "method": "list", - "weight": 387, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -9125,7 +9127,7 @@ }, "x-appwrite": { "method": "create", - "weight": 385, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -9354,7 +9356,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 388, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -9402,7 +9404,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 287, + "weight": 288, "cookies": false, "type": "", "deprecated": false, @@ -9451,7 +9453,7 @@ }, "x-appwrite": { "method": "listTemplates", - "weight": 308, + "weight": 309, "cookies": false, "type": "", "deprecated": false, @@ -9550,7 +9552,7 @@ }, "x-appwrite": { "method": "getTemplate", - "weight": 309, + "weight": 310, "cookies": false, "type": "", "deprecated": false, @@ -9609,7 +9611,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 290, + "weight": 291, "cookies": false, "type": "", "deprecated": false, @@ -9680,7 +9682,7 @@ }, "x-appwrite": { "method": "get", - "weight": 288, + "weight": 289, "cookies": false, "type": "", "deprecated": false, @@ -9738,7 +9740,7 @@ }, "x-appwrite": { "method": "update", - "weight": 386, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -9964,7 +9966,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 293, + "weight": 294, "cookies": false, "type": "", "deprecated": false, @@ -10024,7 +10026,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 294, + "weight": 295, "cookies": false, "type": "", "deprecated": false, @@ -10106,7 +10108,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 389, + "weight": 390, "cookies": false, "type": "upload", "deprecated": false, @@ -10201,7 +10203,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 390, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -10281,6 +10283,101 @@ } } }, + "\/functions\/{functionId}\/deployments\/vcs": { + "post": { + "summary": "Create VCS deployment", + "operationId": "functionsCreateVcsDeployment", + "tags": [ + "functions" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/deployment" + } + } + } + } + }, + "x-appwrite": { + "method": "createVcsDeployment", + "weight": 392, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "functions\/create-vcs-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment when a function is connected to VCS.\n\nThis endpoint lets you create deployment from a branch, commit, or a tag.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "functions.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "functionId", + "description": "Function ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of reference passed. Allowed values are: branch", + "x-example": "branch", + "enum": [ + "branch" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "VCS reference to create deployment from. Depending on type this can be: branch name", + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "x-example": false + } + }, + "required": [ + "type", + "reference" + ] + } + } + } + } + } + }, "\/functions\/{functionId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -10303,7 +10400,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 295, + "weight": 296, "cookies": false, "type": "", "deprecated": false, @@ -10364,7 +10461,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 296, + "weight": 297, "cookies": false, "type": "", "deprecated": false, @@ -10427,7 +10524,7 @@ }, "x-appwrite": { "method": "createBuild", - "weight": 297, + "weight": 298, "cookies": false, "type": "", "deprecated": false, @@ -10506,7 +10603,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 291, + "weight": 292, "cookies": false, "type": "location", "deprecated": false, @@ -10578,7 +10675,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 300, + "weight": 301, "cookies": false, "type": "", "deprecated": false, @@ -10663,7 +10760,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 299, + "weight": 300, "cookies": false, "type": "", "deprecated": false, @@ -10777,7 +10874,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 301, + "weight": 302, "cookies": false, "type": "", "deprecated": false, @@ -10841,7 +10938,7 @@ }, "x-appwrite": { "method": "deleteExecution", - "weight": 302, + "weight": 303, "cookies": false, "type": "", "deprecated": false, @@ -10911,7 +11008,7 @@ }, "x-appwrite": { "method": "getFunctionUsage", - "weight": 289, + "weight": 290, "cookies": false, "type": "", "deprecated": false, @@ -10992,7 +11089,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 304, + "weight": 305, "cookies": false, "type": "", "deprecated": false, @@ -11050,7 +11147,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 303, + "weight": 304, "cookies": false, "type": "", "deprecated": false, @@ -11140,7 +11237,7 @@ }, "x-appwrite": { "method": "query", - "weight": 324, + "weight": 325, "cookies": false, "type": "graphql", "deprecated": false, @@ -11191,7 +11288,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 323, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -11290,7 +11387,7 @@ }, "x-appwrite": { "method": "getAntivirus", - "weight": 146, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -11796,7 +11893,7 @@ }, "x-appwrite": { "method": "getFailedJobs", - "weight": 147, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -11834,8 +11931,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -12166,14 +12264,14 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -12187,13 +12285,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", + "method": "getQueueStatsResources", "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12227,10 +12325,71 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/healthQueue" + } + } + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 5000 + }, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "tags": [ "health" ], @@ -12248,13 +12407,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", - "weight": 143, + "method": "getQueueStatsUsageDump", + "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12371,7 +12530,7 @@ }, "x-appwrite": { "method": "getStorage", - "weight": 145, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -12419,7 +12578,7 @@ }, "x-appwrite": { "method": "getStorageLocal", - "weight": 144, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -12923,7 +13082,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 377, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -12998,7 +13157,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 374, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -13141,7 +13300,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 381, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -13286,7 +13445,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 376, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -13459,7 +13618,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 383, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -13636,7 +13795,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 375, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -13744,7 +13903,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 382, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -13855,7 +14014,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 380, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -13907,7 +14066,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 384, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -13968,7 +14127,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 378, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -14042,7 +14201,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 379, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -14116,7 +14275,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 349, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -14191,7 +14350,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 348, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -14295,7 +14454,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 361, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -14402,7 +14561,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 347, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -14486,7 +14645,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 360, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -14573,7 +14732,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 339, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -14687,7 +14846,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 352, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -14804,7 +14963,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 342, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -14898,7 +15057,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 355, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -14995,7 +15154,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 340, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -15099,7 +15258,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 353, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -15206,7 +15365,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 341, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -15348,7 +15507,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 354, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -15492,7 +15651,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 343, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -15586,7 +15745,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 356, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -15683,7 +15842,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 344, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -15777,7 +15936,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 357, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -15874,7 +16033,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 345, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -15968,7 +16127,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 358, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -16065,7 +16224,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 346, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -16159,7 +16318,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 359, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -16256,7 +16415,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 351, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -16308,7 +16467,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 362, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -16369,7 +16528,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 350, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -16443,7 +16602,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 371, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -16517,7 +16676,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 364, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -16590,7 +16749,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 363, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -16672,7 +16831,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 366, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -16731,7 +16890,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 367, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -16807,7 +16966,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 368, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -16868,7 +17027,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 365, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -16942,7 +17101,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 370, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -17025,7 +17184,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 369, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -17114,7 +17273,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 372, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -17176,7 +17335,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 373, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -17250,7 +17409,7 @@ }, "x-appwrite": { "method": "list", - "weight": 331, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -17323,7 +17482,7 @@ }, "x-appwrite": { "method": "createAppwriteMigration", - "weight": 327, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -17362,17 +17521,17 @@ }, "endpoint": { "type": "string", - "description": "Source's Appwrite Endpoint", + "description": "Source Appwrite endpoint", "x-example": "https:\/\/example.com" }, "projectId": { "type": "string", - "description": "Source's Project ID", + "description": "Source Project ID", "x-example": "" }, "apiKey": { "type": "string", - "description": "Source's API Key", + "description": "Source API Key", "x-example": "" } }, @@ -17410,7 +17569,7 @@ }, "x-appwrite": { "method": "getAppwriteReport", - "weight": 333, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -17502,7 +17661,7 @@ }, "x-appwrite": { "method": "createFirebaseMigration", - "weight": 328, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -17577,7 +17736,7 @@ }, "x-appwrite": { "method": "getFirebaseReport", - "weight": 334, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -17648,7 +17807,7 @@ }, "x-appwrite": { "method": "createNHostMigration", - "weight": 330, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -17758,7 +17917,7 @@ }, "x-appwrite": { "method": "getNHostReport", - "weight": 336, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -17890,7 +18049,7 @@ }, "x-appwrite": { "method": "createSupabaseMigration", - "weight": 329, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -17994,7 +18153,7 @@ }, "x-appwrite": { "method": "getSupabaseReport", - "weight": 335, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -18117,7 +18276,7 @@ }, "x-appwrite": { "method": "get", - "weight": 332, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -18174,7 +18333,7 @@ }, "x-appwrite": { "method": "retry", - "weight": 337, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -18224,7 +18383,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 338, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -18283,7 +18442,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 195, + "weight": 196, "cookies": false, "type": "", "deprecated": false, @@ -18370,7 +18529,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 197, + "weight": 198, "cookies": false, "type": "", "deprecated": false, @@ -18415,7 +18574,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 196, + "weight": 197, "cookies": false, "type": "", "deprecated": false, @@ -18492,7 +18651,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 198, + "weight": 199, "cookies": false, "type": "", "deprecated": false, @@ -18549,7 +18708,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 199, + "weight": 200, "cookies": false, "type": "", "deprecated": false, @@ -18628,7 +18787,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 200, + "weight": 201, "cookies": false, "type": "", "deprecated": false, @@ -18687,7 +18846,7 @@ }, "x-appwrite": { "method": "list", - "weight": 150, + "weight": 151, "cookies": false, "type": "", "deprecated": false, @@ -18758,7 +18917,7 @@ }, "x-appwrite": { "method": "create", - "weight": 149, + "weight": 150, "cookies": false, "type": "", "deprecated": false, @@ -18892,7 +19051,7 @@ }, "x-appwrite": { "method": "get", - "weight": 151, + "weight": 152, "cookies": false, "type": "", "deprecated": false, @@ -18949,7 +19108,7 @@ }, "x-appwrite": { "method": "update", - "weight": 152, + "weight": 153, "cookies": false, "type": "", "deprecated": false, @@ -19063,7 +19222,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 169, + "weight": 170, "cookies": false, "type": "", "deprecated": false, @@ -19122,7 +19281,7 @@ }, "x-appwrite": { "method": "updateApiStatus", - "weight": 156, + "weight": 157, "cookies": false, "type": "", "deprecated": false, @@ -19213,7 +19372,7 @@ }, "x-appwrite": { "method": "updateApiStatusAll", - "weight": 157, + "weight": 158, "cookies": false, "type": "", "deprecated": false, @@ -19291,7 +19450,7 @@ }, "x-appwrite": { "method": "updateAuthDuration", - "weight": 162, + "weight": 163, "cookies": false, "type": "", "deprecated": false, @@ -19369,7 +19528,7 @@ }, "x-appwrite": { "method": "updateAuthLimit", - "weight": 161, + "weight": 162, "cookies": false, "type": "", "deprecated": false, @@ -19447,7 +19606,7 @@ }, "x-appwrite": { "method": "updateAuthSessionsLimit", - "weight": 167, + "weight": 168, "cookies": false, "type": "", "deprecated": false, @@ -19525,7 +19684,7 @@ }, "x-appwrite": { "method": "updateMembershipsPrivacy", - "weight": 160, + "weight": 161, "cookies": false, "type": "", "deprecated": false, @@ -19615,7 +19774,7 @@ }, "x-appwrite": { "method": "updateMockNumbers", - "weight": 168, + "weight": 169, "cookies": false, "type": "", "deprecated": false, @@ -19696,7 +19855,7 @@ }, "x-appwrite": { "method": "updateAuthPasswordDictionary", - "weight": 165, + "weight": 166, "cookies": false, "type": "", "deprecated": false, @@ -19774,7 +19933,7 @@ }, "x-appwrite": { "method": "updateAuthPasswordHistory", - "weight": 164, + "weight": 165, "cookies": false, "type": "", "deprecated": false, @@ -19852,7 +20011,7 @@ }, "x-appwrite": { "method": "updatePersonalDataCheck", - "weight": 166, + "weight": 167, "cookies": false, "type": "", "deprecated": false, @@ -19930,7 +20089,7 @@ }, "x-appwrite": { "method": "updateSessionAlerts", - "weight": 159, + "weight": 160, "cookies": false, "type": "", "deprecated": false, @@ -20008,7 +20167,7 @@ }, "x-appwrite": { "method": "updateAuthStatus", - "weight": 163, + "weight": 164, "cookies": false, "type": "", "deprecated": false, @@ -20107,7 +20266,7 @@ }, "x-appwrite": { "method": "createJWT", - "weight": 181, + "weight": 182, "cookies": false, "type": "", "deprecated": false, @@ -20193,7 +20352,7 @@ }, "x-appwrite": { "method": "listKeys", - "weight": 177, + "weight": 178, "cookies": false, "type": "", "deprecated": false, @@ -20250,7 +20409,7 @@ }, "x-appwrite": { "method": "createKey", - "weight": 176, + "weight": 177, "cookies": false, "type": "", "deprecated": false, @@ -20342,7 +20501,7 @@ }, "x-appwrite": { "method": "getKey", - "weight": 178, + "weight": 179, "cookies": false, "type": "", "deprecated": false, @@ -20409,7 +20568,7 @@ }, "x-appwrite": { "method": "updateKey", - "weight": 179, + "weight": 180, "cookies": false, "type": "", "deprecated": false, @@ -20502,7 +20661,7 @@ }, "x-appwrite": { "method": "deleteKey", - "weight": 180, + "weight": 181, "cookies": false, "type": "", "deprecated": false, @@ -20571,7 +20730,7 @@ }, "x-appwrite": { "method": "updateOAuth2", - "weight": 158, + "weight": 159, "cookies": false, "type": "", "deprecated": false, @@ -20707,7 +20866,7 @@ }, "x-appwrite": { "method": "listPlatforms", - "weight": 183, + "weight": 184, "cookies": false, "type": "", "deprecated": false, @@ -20764,7 +20923,7 @@ }, "x-appwrite": { "method": "createPlatform", - "weight": 182, + "weight": 183, "cookies": false, "type": "", "deprecated": false, @@ -20882,7 +21041,7 @@ }, "x-appwrite": { "method": "getPlatform", - "weight": 184, + "weight": 185, "cookies": false, "type": "", "deprecated": false, @@ -20949,7 +21108,7 @@ }, "x-appwrite": { "method": "updatePlatform", - "weight": 185, + "weight": 186, "cookies": false, "type": "", "deprecated": false, @@ -21043,7 +21202,7 @@ }, "x-appwrite": { "method": "deletePlatform", - "weight": 186, + "weight": 187, "cookies": false, "type": "", "deprecated": false, @@ -21112,7 +21271,7 @@ }, "x-appwrite": { "method": "updateServiceStatus", - "weight": 154, + "weight": 155, "cookies": false, "type": "", "deprecated": false, @@ -21212,7 +21371,7 @@ }, "x-appwrite": { "method": "updateServiceStatusAll", - "weight": 155, + "weight": 156, "cookies": false, "type": "", "deprecated": false, @@ -21290,7 +21449,7 @@ }, "x-appwrite": { "method": "updateSmtp", - "weight": 187, + "weight": 188, "cookies": false, "type": "", "deprecated": false, @@ -21407,7 +21566,7 @@ }, "x-appwrite": { "method": "createSmtpTest", - "weight": 188, + "weight": 189, "cookies": false, "type": "", "deprecated": false, @@ -21537,7 +21696,7 @@ }, "x-appwrite": { "method": "updateTeam", - "weight": 153, + "weight": 154, "cookies": false, "type": "", "deprecated": false, @@ -21615,7 +21774,7 @@ }, "x-appwrite": { "method": "getEmailTemplate", - "weight": 190, + "weight": 191, "cookies": false, "type": "", "deprecated": false, @@ -21838,7 +21997,7 @@ }, "x-appwrite": { "method": "updateEmailTemplate", - "weight": 192, + "weight": 193, "cookies": false, "type": "", "deprecated": false, @@ -22101,7 +22260,7 @@ }, "x-appwrite": { "method": "deleteEmailTemplate", - "weight": 194, + "weight": 195, "cookies": false, "type": "", "deprecated": false, @@ -22326,7 +22485,7 @@ }, "x-appwrite": { "method": "getSmsTemplate", - "weight": 189, + "weight": 190, "cookies": false, "type": "", "deprecated": false, @@ -22546,7 +22705,7 @@ }, "x-appwrite": { "method": "updateSmsTemplate", - "weight": 191, + "weight": 192, "cookies": false, "type": "", "deprecated": false, @@ -22785,7 +22944,7 @@ }, "x-appwrite": { "method": "deleteSmsTemplate", - "weight": 193, + "weight": 194, "cookies": false, "type": "", "deprecated": false, @@ -23007,7 +23166,7 @@ }, "x-appwrite": { "method": "listWebhooks", - "weight": 171, + "weight": 172, "cookies": false, "type": "", "deprecated": false, @@ -23064,7 +23223,7 @@ }, "x-appwrite": { "method": "createWebhook", - "weight": 170, + "weight": 171, "cookies": false, "type": "", "deprecated": false, @@ -23178,7 +23337,7 @@ }, "x-appwrite": { "method": "getWebhook", - "weight": 172, + "weight": 173, "cookies": false, "type": "", "deprecated": false, @@ -23245,7 +23404,7 @@ }, "x-appwrite": { "method": "updateWebhook", - "weight": 173, + "weight": 174, "cookies": false, "type": "", "deprecated": false, @@ -23360,7 +23519,7 @@ }, "x-appwrite": { "method": "deleteWebhook", - "weight": 175, + "weight": 176, "cookies": false, "type": "", "deprecated": false, @@ -23429,7 +23588,7 @@ }, "x-appwrite": { "method": "updateWebhookSignature", - "weight": 174, + "weight": 175, "cookies": false, "type": "", "deprecated": false, @@ -23498,7 +23657,7 @@ }, "x-appwrite": { "method": "listRules", - "weight": 310, + "weight": 311, "cookies": false, "type": "", "deprecated": false, @@ -23569,7 +23728,7 @@ }, "x-appwrite": { "method": "createRule", - "weight": 420, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -23653,7 +23812,7 @@ }, "x-appwrite": { "method": "getRule", - "weight": 311, + "weight": 312, "cookies": false, "type": "", "deprecated": false, @@ -23703,7 +23862,7 @@ }, "x-appwrite": { "method": "deleteRule", - "weight": 312, + "weight": 313, "cookies": false, "type": "", "deprecated": false, @@ -23762,7 +23921,7 @@ }, "x-appwrite": { "method": "updateRuleVerification", - "weight": 313, + "weight": 314, "cookies": false, "type": "", "deprecated": false, @@ -23821,7 +23980,7 @@ }, "x-appwrite": { "method": "list", - "weight": 393, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -23890,7 +24049,7 @@ }, "x-appwrite": { "method": "create", - "weight": 391, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -24117,7 +24276,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 396, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -24165,7 +24324,7 @@ }, "x-appwrite": { "method": "listTemplates", - "weight": 415, + "weight": 418, "cookies": false, "type": "", "deprecated": false, @@ -24264,7 +24423,7 @@ }, "x-appwrite": { "method": "getTemplate", - "weight": 416, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -24323,7 +24482,7 @@ }, "x-appwrite": { "method": "listUsage", - "weight": 417, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -24390,7 +24549,7 @@ }, "x-appwrite": { "method": "get", - "weight": 392, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -24448,7 +24607,7 @@ }, "x-appwrite": { "method": "update", - "weight": 394, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -24671,7 +24830,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 395, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -24731,7 +24890,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 400, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -24813,7 +24972,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 397, + "weight": 399, "cookies": false, "type": "upload", "deprecated": false, @@ -24913,7 +25072,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 398, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -24993,6 +25152,101 @@ } } }, + "\/sites\/{siteId}\/deployments\/vcs": { + "post": { + "summary": "Create VCS deployment", + "operationId": "sitesCreateVcsDeployment", + "tags": [ + "sites" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/deployment" + } + } + } + } + }, + "x-appwrite": { + "method": "createVcsDeployment", + "weight": 401, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "sites\/create-vcs-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment when a site is connected to VCS.\n\nThis endpoint lets you create deployment from a branch, commit, or a tag.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "sites.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "siteId", + "description": "Site ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of reference passed. Allowed values are: branch", + "x-example": "branch", + "enum": [ + "branch" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "VCS reference to create deployment from. Depending on type this can be: branch name", + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "x-example": false + } + }, + "required": [ + "type", + "reference" + ] + } + } + } + } + } + }, "\/sites\/{siteId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -25015,7 +25269,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 399, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -25083,7 +25337,7 @@ }, "x-appwrite": { "method": "updateDeployment", - "weight": 401, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -25144,7 +25398,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 402, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -25207,7 +25461,7 @@ }, "x-appwrite": { "method": "createDeploymentBuild", - "weight": 405, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -25275,7 +25529,7 @@ }, "x-appwrite": { "method": "updateDeploymentBuild", - "weight": 406, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -25338,7 +25592,7 @@ }, "x-appwrite": { "method": "getDeploymentBuildDownload", - "weight": 404, + "weight": 407, "cookies": false, "type": "location", "deprecated": false, @@ -25403,7 +25657,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 403, + "weight": 406, "cookies": false, "type": "location", "deprecated": false, @@ -25475,7 +25729,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 408, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -25556,7 +25810,7 @@ }, "x-appwrite": { "method": "getLog", - "weight": 407, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -25617,7 +25871,7 @@ }, "x-appwrite": { "method": "deleteLog", - "weight": 409, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -25687,7 +25941,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 418, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -25768,7 +26022,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 412, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -25826,7 +26080,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 410, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -25916,7 +26170,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 411, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -25984,7 +26238,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 413, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -26074,7 +26328,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 414, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -26144,7 +26398,7 @@ }, "x-appwrite": { "method": "listBuckets", - "weight": 202, + "weight": 203, "cookies": false, "type": "", "deprecated": false, @@ -26216,7 +26470,7 @@ }, "x-appwrite": { "method": "createBucket", - "weight": 201, + "weight": 202, "cookies": false, "type": "", "deprecated": false, @@ -26342,7 +26596,7 @@ }, "x-appwrite": { "method": "getBucket", - "weight": 203, + "weight": 204, "cookies": false, "type": "", "deprecated": false, @@ -26400,7 +26654,7 @@ }, "x-appwrite": { "method": "updateBucket", - "weight": 204, + "weight": 205, "cookies": false, "type": "", "deprecated": false, @@ -26523,7 +26777,7 @@ }, "x-appwrite": { "method": "deleteBucket", - "weight": 205, + "weight": 206, "cookies": false, "type": "", "deprecated": false, @@ -26583,7 +26837,7 @@ }, "x-appwrite": { "method": "listFiles", - "weight": 207, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -26668,7 +26922,7 @@ }, "x-appwrite": { "method": "createFile", - "weight": 206, + "weight": 207, "cookies": false, "type": "upload", "deprecated": false, @@ -26765,7 +27019,7 @@ }, "x-appwrite": { "method": "getFile", - "weight": 208, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -26836,7 +27090,7 @@ }, "x-appwrite": { "method": "updateFile", - "weight": 213, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -26924,7 +27178,7 @@ }, "x-appwrite": { "method": "deleteFile", - "weight": 214, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -26990,7 +27244,7 @@ }, "x-appwrite": { "method": "getFileDownload", - "weight": 210, + "weight": 211, "cookies": false, "type": "location", "deprecated": false, @@ -27056,7 +27310,7 @@ }, "x-appwrite": { "method": "getFilePreview", - "weight": 209, + "weight": 210, "cookies": false, "type": "location", "deprecated": false, @@ -27272,7 +27526,7 @@ }, "x-appwrite": { "method": "getFileView", - "weight": 211, + "weight": 212, "cookies": false, "type": "location", "deprecated": false, @@ -27345,7 +27599,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 215, + "weight": 216, "cookies": false, "type": "", "deprecated": false, @@ -27416,7 +27670,7 @@ }, "x-appwrite": { "method": "getBucketUsage", - "weight": 216, + "weight": 217, "cookies": false, "type": "", "deprecated": false, @@ -27497,7 +27751,7 @@ }, "x-appwrite": { "method": "list", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -27572,7 +27826,7 @@ }, "x-appwrite": { "method": "create", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -27656,7 +27910,7 @@ }, "x-appwrite": { "method": "get", - "weight": 219, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -27717,7 +27971,7 @@ }, "x-appwrite": { "method": "updateName", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -27790,7 +28044,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -27853,7 +28107,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 230, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -27925,7 +28179,7 @@ }, "x-appwrite": { "method": "listMemberships", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -28010,7 +28264,7 @@ }, "x-appwrite": { "method": "createMembership", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -28120,7 +28374,7 @@ }, "x-appwrite": { "method": "getMembership", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -28191,7 +28445,7 @@ }, "x-appwrite": { "method": "updateMembership", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -28277,7 +28531,7 @@ }, "x-appwrite": { "method": "deleteMembership", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -28350,7 +28604,7 @@ }, "x-appwrite": { "method": "updateMembershipStatus", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -28446,7 +28700,7 @@ }, "x-appwrite": { "method": "getPrefs", - "weight": 220, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -28505,7 +28759,7 @@ }, "x-appwrite": { "method": "updatePrefs", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -28585,7 +28839,7 @@ }, "x-appwrite": { "method": "list", - "weight": 240, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -28657,7 +28911,7 @@ }, "x-appwrite": { "method": "create", - "weight": 231, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -28744,7 +28998,7 @@ }, "x-appwrite": { "method": "createArgon2User", - "weight": 234, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -28828,7 +29082,7 @@ }, "x-appwrite": { "method": "createBcryptUser", - "weight": 232, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -28912,7 +29166,7 @@ }, "x-appwrite": { "method": "listIdentities", - "weight": 248, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -28979,7 +29233,7 @@ }, "x-appwrite": { "method": "deleteIdentity", - "weight": 271, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -29039,7 +29293,7 @@ }, "x-appwrite": { "method": "createMD5User", - "weight": 233, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -29123,7 +29377,7 @@ }, "x-appwrite": { "method": "createPHPassUser", - "weight": 236, + "weight": 237, "cookies": false, "type": "", "deprecated": false, @@ -29207,7 +29461,7 @@ }, "x-appwrite": { "method": "createScryptUser", - "weight": 237, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -29321,7 +29575,7 @@ }, "x-appwrite": { "method": "createScryptModifiedUser", - "weight": 238, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -29423,7 +29677,7 @@ }, "x-appwrite": { "method": "createSHAUser", - "weight": 235, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -29527,7 +29781,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 273, + "weight": 274, "cookies": false, "type": "", "deprecated": false, @@ -29598,7 +29852,7 @@ }, "x-appwrite": { "method": "get", - "weight": 241, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -29649,7 +29903,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 269, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -29709,7 +29963,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 254, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -29788,7 +30042,7 @@ }, "x-appwrite": { "method": "createJWT", - "weight": 272, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -29869,7 +30123,7 @@ }, "x-appwrite": { "method": "updateLabels", - "weight": 250, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -29951,7 +30205,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 246, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -30024,7 +30278,7 @@ }, "x-appwrite": { "method": "listMemberships", - "weight": 245, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -30084,7 +30338,7 @@ }, "x-appwrite": { "method": "updateMfa", - "weight": 259, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -30156,7 +30410,7 @@ }, "x-appwrite": { "method": "deleteMfaAuthenticator", - "weight": 264, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -30231,7 +30485,7 @@ }, "x-appwrite": { "method": "listMfaFactors", - "weight": 260, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -30291,7 +30545,7 @@ }, "x-appwrite": { "method": "getMfaRecoveryCodes", - "weight": 261, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -30349,7 +30603,7 @@ }, "x-appwrite": { "method": "updateMfaRecoveryCodes", - "weight": 263, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -30407,7 +30661,7 @@ }, "x-appwrite": { "method": "createMfaRecoveryCodes", - "weight": 262, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -30467,7 +30721,7 @@ }, "x-appwrite": { "method": "updateName", - "weight": 252, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -30546,7 +30800,7 @@ }, "x-appwrite": { "method": "updatePassword", - "weight": 253, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -30625,7 +30879,7 @@ }, "x-appwrite": { "method": "updatePhone", - "weight": 255, + "weight": 256, "cookies": false, "type": "", "deprecated": false, @@ -30704,7 +30958,7 @@ }, "x-appwrite": { "method": "getPrefs", - "weight": 242, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -30762,7 +31016,7 @@ }, "x-appwrite": { "method": "updatePrefs", - "weight": 257, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -30841,7 +31095,7 @@ }, "x-appwrite": { "method": "listSessions", - "weight": 244, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -30899,7 +31153,7 @@ }, "x-appwrite": { "method": "createSession", - "weight": 265, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -30950,7 +31204,7 @@ }, "x-appwrite": { "method": "deleteSessions", - "weight": 268, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -31003,7 +31257,7 @@ }, "x-appwrite": { "method": "deleteSession", - "weight": 267, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -31073,7 +31327,7 @@ }, "x-appwrite": { "method": "updateStatus", - "weight": 249, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -31152,7 +31406,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 247, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -31224,7 +31478,7 @@ }, "x-appwrite": { "method": "createTarget", - "weight": 239, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -31333,7 +31587,7 @@ }, "x-appwrite": { "method": "getTarget", - "weight": 243, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -31402,7 +31656,7 @@ }, "x-appwrite": { "method": "updateTarget", - "weight": 258, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -31490,7 +31744,7 @@ }, "x-appwrite": { "method": "deleteTarget", - "weight": 270, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -31561,7 +31815,7 @@ }, "x-appwrite": { "method": "createToken", - "weight": 266, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -31642,7 +31896,7 @@ }, "x-appwrite": { "method": "updateEmailVerification", - "weight": 256, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -31721,7 +31975,7 @@ }, "x-appwrite": { "method": "updatePhoneVerification", - "weight": 251, + "weight": 252, "cookies": false, "type": "", "deprecated": false, @@ -31800,7 +32054,7 @@ }, "x-appwrite": { "method": "listRepositories", - "weight": 278, + "weight": 279, "cookies": false, "type": "", "deprecated": false, @@ -31868,7 +32122,7 @@ }, "x-appwrite": { "method": "createRepository", - "weight": 279, + "weight": 280, "cookies": false, "type": "", "deprecated": false, @@ -31952,7 +32206,7 @@ }, "x-appwrite": { "method": "getRepository", - "weight": 280, + "weight": 281, "cookies": false, "type": "", "deprecated": false, @@ -32021,7 +32275,7 @@ }, "x-appwrite": { "method": "listRepositoryBranches", - "weight": 281, + "weight": 282, "cookies": false, "type": "", "deprecated": false, @@ -32090,7 +32344,7 @@ }, "x-appwrite": { "method": "getRepositoryContents", - "weight": 276, + "weight": 277, "cookies": false, "type": "", "deprecated": false, @@ -32170,7 +32424,7 @@ }, "x-appwrite": { "method": "createRepositoryDetection", - "weight": 277, + "weight": 278, "cookies": false, "type": "", "deprecated": false, @@ -32248,7 +32502,7 @@ }, "x-appwrite": { "method": "updateExternalDeployments", - "weight": 286, + "weight": 287, "cookies": false, "type": "", "deprecated": false, @@ -32336,7 +32590,7 @@ }, "x-appwrite": { "method": "listInstallations", - "weight": 283, + "weight": 284, "cookies": false, "type": "", "deprecated": false, @@ -32409,7 +32663,7 @@ }, "x-appwrite": { "method": "getInstallation", - "weight": 284, + "weight": 285, "cookies": false, "type": "", "deprecated": false, @@ -32459,7 +32713,7 @@ }, "x-appwrite": { "method": "deleteInstallation", - "weight": 285, + "weight": 286, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index f6a62e21b5..5a3cf63fa1 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -3077,7 +3077,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "schema": { "type": "string", @@ -3098,7 +3098,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3117,7 +3118,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ] }, "in": "path" @@ -7381,7 +7383,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -8138,7 +8140,7 @@ }, "x-appwrite": { "method": "list", - "weight": 387, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -8211,7 +8213,7 @@ }, "x-appwrite": { "method": "create", - "weight": 385, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -8441,7 +8443,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 388, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -8490,7 +8492,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 287, + "weight": 288, "cookies": false, "type": "", "deprecated": false, @@ -8540,7 +8542,7 @@ }, "x-appwrite": { "method": "get", - "weight": 288, + "weight": 289, "cookies": false, "type": "", "deprecated": false, @@ -8599,7 +8601,7 @@ }, "x-appwrite": { "method": "update", - "weight": 386, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -8826,7 +8828,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 293, + "weight": 294, "cookies": false, "type": "", "deprecated": false, @@ -8887,7 +8889,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 294, + "weight": 295, "cookies": false, "type": "", "deprecated": false, @@ -8970,7 +8972,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 389, + "weight": 390, "cookies": false, "type": "upload", "deprecated": false, @@ -9066,7 +9068,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 390, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -9147,6 +9149,102 @@ } } }, + "\/functions\/{functionId}\/deployments\/vcs": { + "post": { + "summary": "Create VCS deployment", + "operationId": "functionsCreateVcsDeployment", + "tags": [ + "functions" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/deployment" + } + } + } + } + }, + "x-appwrite": { + "method": "createVcsDeployment", + "weight": 392, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "functions\/create-vcs-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment when a function is connected to VCS.\n\nThis endpoint lets you create deployment from a branch, commit, or a tag.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "functions.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "functionId", + "description": "Function ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of reference passed. Allowed values are: branch", + "x-example": "branch", + "enum": [ + "branch" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "VCS reference to create deployment from. Depending on type this can be: branch name", + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "x-example": false + } + }, + "required": [ + "type", + "reference" + ] + } + } + } + } + } + }, "\/functions\/{functionId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -9169,7 +9267,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 295, + "weight": 296, "cookies": false, "type": "", "deprecated": false, @@ -9231,7 +9329,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 296, + "weight": 297, "cookies": false, "type": "", "deprecated": false, @@ -9295,7 +9393,7 @@ }, "x-appwrite": { "method": "createBuild", - "weight": 297, + "weight": 298, "cookies": false, "type": "", "deprecated": false, @@ -9375,7 +9473,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 291, + "weight": 292, "cookies": false, "type": "location", "deprecated": false, @@ -9448,7 +9546,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 300, + "weight": 301, "cookies": false, "type": "", "deprecated": false, @@ -9535,7 +9633,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 299, + "weight": 300, "cookies": false, "type": "", "deprecated": false, @@ -9651,7 +9749,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 301, + "weight": 302, "cookies": false, "type": "", "deprecated": false, @@ -9717,7 +9815,7 @@ }, "x-appwrite": { "method": "deleteExecution", - "weight": 302, + "weight": 303, "cookies": false, "type": "", "deprecated": false, @@ -9788,7 +9886,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 304, + "weight": 305, "cookies": false, "type": "", "deprecated": false, @@ -9847,7 +9945,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 303, + "weight": 304, "cookies": false, "type": "", "deprecated": false, @@ -9938,7 +10036,7 @@ }, "x-appwrite": { "method": "query", - "weight": 324, + "weight": 325, "cookies": false, "type": "graphql", "deprecated": false, @@ -9991,7 +10089,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 323, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -10093,7 +10191,7 @@ }, "x-appwrite": { "method": "getAntivirus", - "weight": 146, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -10608,7 +10706,7 @@ }, "x-appwrite": { "method": "getFailedJobs", - "weight": 147, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -10647,8 +10745,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -10984,14 +11083,14 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -11005,13 +11104,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", + "method": "getQueueStatsResources", "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11046,10 +11145,72 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/healthQueue" + } + } + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "schema": { + "type": "integer", + "format": "int32", + "default": 5000 + }, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "tags": [ "health" ], @@ -11067,13 +11228,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", - "weight": 143, + "method": "getQueueStatsUsageDump", + "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11192,7 +11353,7 @@ }, "x-appwrite": { "method": "getStorage", - "weight": 145, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -11241,7 +11402,7 @@ }, "x-appwrite": { "method": "getStorageLocal", - "weight": 144, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -11763,7 +11924,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 377, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -11839,7 +12000,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 374, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -11983,7 +12144,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 381, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -12129,7 +12290,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 376, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -12303,7 +12464,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 383, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -12481,7 +12642,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 375, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -12590,7 +12751,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 382, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -12702,7 +12863,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 380, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -12755,7 +12916,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 384, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -12817,7 +12978,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 378, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -12892,7 +13053,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 379, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -12967,7 +13128,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 349, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -13043,7 +13204,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 348, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -13148,7 +13309,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 361, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -13256,7 +13417,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 347, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -13341,7 +13502,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 360, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -13429,7 +13590,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 339, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -13544,7 +13705,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 352, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -13662,7 +13823,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 342, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -13757,7 +13918,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 355, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -13855,7 +14016,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 340, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -13960,7 +14121,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 353, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -14068,7 +14229,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 341, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -14211,7 +14372,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 354, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -14356,7 +14517,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 343, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -14451,7 +14612,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 356, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -14549,7 +14710,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 344, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -14644,7 +14805,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 357, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -14742,7 +14903,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 345, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -14837,7 +14998,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 358, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -14935,7 +15096,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 346, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -15030,7 +15191,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 359, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -15128,7 +15289,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 351, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -15181,7 +15342,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 362, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -15243,7 +15404,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 350, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -15318,7 +15479,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 371, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -15393,7 +15554,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 364, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -15467,7 +15628,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 363, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -15550,7 +15711,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 366, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -15610,7 +15771,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 367, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -15687,7 +15848,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 368, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -15749,7 +15910,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 365, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -15824,7 +15985,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 370, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -15908,7 +16069,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 369, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -15999,7 +16160,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 372, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -16062,7 +16223,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 373, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -16138,7 +16299,7 @@ }, "x-appwrite": { "method": "list", - "weight": 393, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -16208,7 +16369,7 @@ }, "x-appwrite": { "method": "create", - "weight": 391, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -16436,7 +16597,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 396, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -16485,7 +16646,7 @@ }, "x-appwrite": { "method": "get", - "weight": 392, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -16544,7 +16705,7 @@ }, "x-appwrite": { "method": "update", - "weight": 394, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -16768,7 +16929,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 395, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -16829,7 +16990,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 400, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -16912,7 +17073,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 397, + "weight": 399, "cookies": false, "type": "upload", "deprecated": false, @@ -17013,7 +17174,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 398, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -17094,6 +17255,102 @@ } } }, + "\/sites\/{siteId}\/deployments\/vcs": { + "post": { + "summary": "Create VCS deployment", + "operationId": "sitesCreateVcsDeployment", + "tags": [ + "sites" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/deployment" + } + } + } + } + }, + "x-appwrite": { + "method": "createVcsDeployment", + "weight": 401, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "sites\/create-vcs-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment when a site is connected to VCS.\n\nThis endpoint lets you create deployment from a branch, commit, or a tag.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "sites.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "siteId", + "description": "Site ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of reference passed. Allowed values are: branch", + "x-example": "branch", + "enum": [ + "branch" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "VCS reference to create deployment from. Depending on type this can be: branch name", + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "x-example": false + } + }, + "required": [ + "type", + "reference" + ] + } + } + } + } + } + }, "\/sites\/{siteId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -17116,7 +17373,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 399, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -17185,7 +17442,7 @@ }, "x-appwrite": { "method": "updateDeployment", - "weight": 401, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -17247,7 +17504,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 402, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -17311,7 +17568,7 @@ }, "x-appwrite": { "method": "createDeploymentBuild", - "weight": 405, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -17380,7 +17637,7 @@ }, "x-appwrite": { "method": "updateDeploymentBuild", - "weight": 406, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -17444,7 +17701,7 @@ }, "x-appwrite": { "method": "getDeploymentBuildDownload", - "weight": 404, + "weight": 407, "cookies": false, "type": "location", "deprecated": false, @@ -17510,7 +17767,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 403, + "weight": 406, "cookies": false, "type": "location", "deprecated": false, @@ -17583,7 +17840,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 408, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -17665,7 +17922,7 @@ }, "x-appwrite": { "method": "getLog", - "weight": 407, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -17727,7 +17984,7 @@ }, "x-appwrite": { "method": "deleteLog", - "weight": 409, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -17798,7 +18055,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 412, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -17857,7 +18114,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 410, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -17948,7 +18205,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 411, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -18017,7 +18274,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 413, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -18108,7 +18365,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 414, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -18179,7 +18436,7 @@ }, "x-appwrite": { "method": "listBuckets", - "weight": 202, + "weight": 203, "cookies": false, "type": "", "deprecated": false, @@ -18252,7 +18509,7 @@ }, "x-appwrite": { "method": "createBucket", - "weight": 201, + "weight": 202, "cookies": false, "type": "", "deprecated": false, @@ -18379,7 +18636,7 @@ }, "x-appwrite": { "method": "getBucket", - "weight": 203, + "weight": 204, "cookies": false, "type": "", "deprecated": false, @@ -18438,7 +18695,7 @@ }, "x-appwrite": { "method": "updateBucket", - "weight": 204, + "weight": 205, "cookies": false, "type": "", "deprecated": false, @@ -18562,7 +18819,7 @@ }, "x-appwrite": { "method": "deleteBucket", - "weight": 205, + "weight": 206, "cookies": false, "type": "", "deprecated": false, @@ -18623,7 +18880,7 @@ }, "x-appwrite": { "method": "listFiles", - "weight": 207, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -18710,7 +18967,7 @@ }, "x-appwrite": { "method": "createFile", - "weight": 206, + "weight": 207, "cookies": false, "type": "upload", "deprecated": false, @@ -18809,7 +19066,7 @@ }, "x-appwrite": { "method": "getFile", - "weight": 208, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -18882,7 +19139,7 @@ }, "x-appwrite": { "method": "updateFile", - "weight": 213, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -18972,7 +19229,7 @@ }, "x-appwrite": { "method": "deleteFile", - "weight": 214, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -19040,7 +19297,7 @@ }, "x-appwrite": { "method": "getFileDownload", - "weight": 210, + "weight": 211, "cookies": false, "type": "location", "deprecated": false, @@ -19108,7 +19365,7 @@ }, "x-appwrite": { "method": "getFilePreview", - "weight": 209, + "weight": 210, "cookies": false, "type": "location", "deprecated": false, @@ -19326,7 +19583,7 @@ }, "x-appwrite": { "method": "getFileView", - "weight": 211, + "weight": 212, "cookies": false, "type": "location", "deprecated": false, @@ -19401,7 +19658,7 @@ }, "x-appwrite": { "method": "list", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -19478,7 +19735,7 @@ }, "x-appwrite": { "method": "create", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -19564,7 +19821,7 @@ }, "x-appwrite": { "method": "get", - "weight": 219, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -19627,7 +19884,7 @@ }, "x-appwrite": { "method": "updateName", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -19702,7 +19959,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -19767,7 +20024,7 @@ }, "x-appwrite": { "method": "listMemberships", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -19854,7 +20111,7 @@ }, "x-appwrite": { "method": "createMembership", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -19966,7 +20223,7 @@ }, "x-appwrite": { "method": "getMembership", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -20039,7 +20296,7 @@ }, "x-appwrite": { "method": "updateMembership", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -20127,7 +20384,7 @@ }, "x-appwrite": { "method": "deleteMembership", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -20202,7 +20459,7 @@ }, "x-appwrite": { "method": "updateMembershipStatus", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -20300,7 +20557,7 @@ }, "x-appwrite": { "method": "getPrefs", - "weight": 220, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -20361,7 +20618,7 @@ }, "x-appwrite": { "method": "updatePrefs", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -20443,7 +20700,7 @@ }, "x-appwrite": { "method": "list", - "weight": 240, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -20516,7 +20773,7 @@ }, "x-appwrite": { "method": "create", - "weight": 231, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -20604,7 +20861,7 @@ }, "x-appwrite": { "method": "createArgon2User", - "weight": 234, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -20689,7 +20946,7 @@ }, "x-appwrite": { "method": "createBcryptUser", - "weight": 232, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -20774,7 +21031,7 @@ }, "x-appwrite": { "method": "listIdentities", - "weight": 248, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -20842,7 +21099,7 @@ }, "x-appwrite": { "method": "deleteIdentity", - "weight": 271, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -20903,7 +21160,7 @@ }, "x-appwrite": { "method": "createMD5User", - "weight": 233, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -20988,7 +21245,7 @@ }, "x-appwrite": { "method": "createPHPassUser", - "weight": 236, + "weight": 237, "cookies": false, "type": "", "deprecated": false, @@ -21073,7 +21330,7 @@ }, "x-appwrite": { "method": "createScryptUser", - "weight": 237, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -21188,7 +21445,7 @@ }, "x-appwrite": { "method": "createScryptModifiedUser", - "weight": 238, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -21291,7 +21548,7 @@ }, "x-appwrite": { "method": "createSHAUser", - "weight": 235, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -21396,7 +21653,7 @@ }, "x-appwrite": { "method": "get", - "weight": 241, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -21448,7 +21705,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 269, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -21509,7 +21766,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 254, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -21589,7 +21846,7 @@ }, "x-appwrite": { "method": "createJWT", - "weight": 272, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -21671,7 +21928,7 @@ }, "x-appwrite": { "method": "updateLabels", - "weight": 250, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -21754,7 +22011,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 246, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -21828,7 +22085,7 @@ }, "x-appwrite": { "method": "listMemberships", - "weight": 245, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -21889,7 +22146,7 @@ }, "x-appwrite": { "method": "updateMfa", - "weight": 259, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -21962,7 +22219,7 @@ }, "x-appwrite": { "method": "deleteMfaAuthenticator", - "weight": 264, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -22038,7 +22295,7 @@ }, "x-appwrite": { "method": "listMfaFactors", - "weight": 260, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -22099,7 +22356,7 @@ }, "x-appwrite": { "method": "getMfaRecoveryCodes", - "weight": 261, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -22158,7 +22415,7 @@ }, "x-appwrite": { "method": "updateMfaRecoveryCodes", - "weight": 263, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -22217,7 +22474,7 @@ }, "x-appwrite": { "method": "createMfaRecoveryCodes", - "weight": 262, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -22278,7 +22535,7 @@ }, "x-appwrite": { "method": "updateName", - "weight": 252, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -22358,7 +22615,7 @@ }, "x-appwrite": { "method": "updatePassword", - "weight": 253, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -22438,7 +22695,7 @@ }, "x-appwrite": { "method": "updatePhone", - "weight": 255, + "weight": 256, "cookies": false, "type": "", "deprecated": false, @@ -22518,7 +22775,7 @@ }, "x-appwrite": { "method": "getPrefs", - "weight": 242, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -22577,7 +22834,7 @@ }, "x-appwrite": { "method": "updatePrefs", - "weight": 257, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -22657,7 +22914,7 @@ }, "x-appwrite": { "method": "listSessions", - "weight": 244, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -22716,7 +22973,7 @@ }, "x-appwrite": { "method": "createSession", - "weight": 265, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -22768,7 +23025,7 @@ }, "x-appwrite": { "method": "deleteSessions", - "weight": 268, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -22822,7 +23079,7 @@ }, "x-appwrite": { "method": "deleteSession", - "weight": 267, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -22893,7 +23150,7 @@ }, "x-appwrite": { "method": "updateStatus", - "weight": 249, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -22973,7 +23230,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 247, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -23046,7 +23303,7 @@ }, "x-appwrite": { "method": "createTarget", - "weight": 239, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -23156,7 +23413,7 @@ }, "x-appwrite": { "method": "getTarget", - "weight": 243, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -23226,7 +23483,7 @@ }, "x-appwrite": { "method": "updateTarget", - "weight": 258, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -23315,7 +23572,7 @@ }, "x-appwrite": { "method": "deleteTarget", - "weight": 270, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -23387,7 +23644,7 @@ }, "x-appwrite": { "method": "createToken", - "weight": 266, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -23469,7 +23726,7 @@ }, "x-appwrite": { "method": "updateEmailVerification", - "weight": 256, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -23549,7 +23806,7 @@ }, "x-appwrite": { "method": "updatePhoneVerification", - "weight": 251, + "weight": 252, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 923594c51d..f646dbd81a 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -3557,7 +3557,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3577,7 +3577,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3596,7 +3597,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -4547,7 +4549,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -4927,7 +4929,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 300, + "weight": 301, "cookies": false, "type": "", "deprecated": false, @@ -5009,7 +5011,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 299, + "weight": 300, "cookies": false, "type": "", "deprecated": false, @@ -5127,7 +5129,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 301, + "weight": 302, "cookies": false, "type": "", "deprecated": false, @@ -5198,7 +5200,7 @@ }, "x-appwrite": { "method": "query", - "weight": 324, + "weight": 325, "cookies": false, "type": "graphql", "deprecated": false, @@ -5271,7 +5273,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 323, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -5768,7 +5770,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 369, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -5852,7 +5854,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 373, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -5924,7 +5926,7 @@ }, "x-appwrite": { "method": "listFiles", - "weight": 207, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -6006,7 +6008,7 @@ }, "x-appwrite": { "method": "createFile", - "weight": 206, + "weight": 207, "cookies": false, "type": "upload", "deprecated": false, @@ -6097,7 +6099,7 @@ }, "x-appwrite": { "method": "getFile", - "weight": 208, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -6166,7 +6168,7 @@ }, "x-appwrite": { "method": "updateFile", - "weight": 213, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -6254,7 +6256,7 @@ }, "x-appwrite": { "method": "deleteFile", - "weight": 214, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -6325,7 +6327,7 @@ }, "x-appwrite": { "method": "getFileDownload", - "weight": 210, + "weight": 211, "cookies": false, "type": "location", "deprecated": false, @@ -6396,7 +6398,7 @@ }, "x-appwrite": { "method": "getFilePreview", - "weight": 209, + "weight": 210, "cookies": false, "type": "location", "deprecated": false, @@ -6595,7 +6597,7 @@ }, "x-appwrite": { "method": "getFileView", - "weight": 211, + "weight": 212, "cookies": false, "type": "location", "deprecated": false, @@ -6666,7 +6668,7 @@ }, "x-appwrite": { "method": "list", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -6740,7 +6742,7 @@ }, "x-appwrite": { "method": "create", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -6831,7 +6833,7 @@ }, "x-appwrite": { "method": "get", - "weight": 219, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -6892,7 +6894,7 @@ }, "x-appwrite": { "method": "updateName", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -6966,7 +6968,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -7029,7 +7031,7 @@ }, "x-appwrite": { "method": "listMemberships", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -7111,7 +7113,7 @@ }, "x-appwrite": { "method": "createMembership", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -7225,7 +7227,7 @@ }, "x-appwrite": { "method": "getMembership", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -7294,7 +7296,7 @@ }, "x-appwrite": { "method": "updateMembership", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -7379,7 +7381,7 @@ }, "x-appwrite": { "method": "deleteMembership", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -7450,7 +7452,7 @@ }, "x-appwrite": { "method": "updateMembershipStatus", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -7545,7 +7547,7 @@ }, "x-appwrite": { "method": "getPrefs", - "weight": 220, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -7605,7 +7607,7 @@ }, "x-appwrite": { "method": "updatePrefs", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 94da70da74..bc5850aa78 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -3577,7 +3577,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3597,7 +3597,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3616,7 +3617,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -4497,7 +4499,7 @@ }, "x-appwrite": { "method": "chat", - "weight": 326, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -4561,7 +4563,7 @@ }, "x-appwrite": { "method": "getResource", - "weight": 419, + "weight": 422, "cookies": false, "type": "", "deprecated": false, @@ -4633,7 +4635,7 @@ }, "x-appwrite": { "method": "variables", - "weight": 325, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -8079,7 +8081,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -9202,7 +9204,7 @@ }, "x-appwrite": { "method": "list", - "weight": 387, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -9273,7 +9275,7 @@ }, "x-appwrite": { "method": "create", - "weight": 385, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -9522,7 +9524,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 388, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -9572,7 +9574,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 287, + "weight": 288, "cookies": false, "type": "", "deprecated": false, @@ -9623,7 +9625,7 @@ }, "x-appwrite": { "method": "listTemplates", - "weight": 308, + "weight": 309, "cookies": false, "type": "", "deprecated": false, @@ -9718,7 +9720,7 @@ }, "x-appwrite": { "method": "getTemplate", - "weight": 309, + "weight": 310, "cookies": false, "type": "", "deprecated": false, @@ -9777,7 +9779,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 290, + "weight": 291, "cookies": false, "type": "", "deprecated": false, @@ -9848,7 +9850,7 @@ }, "x-appwrite": { "method": "get", - "weight": 288, + "weight": 289, "cookies": false, "type": "", "deprecated": false, @@ -9906,7 +9908,7 @@ }, "x-appwrite": { "method": "update", - "weight": 386, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -10149,7 +10151,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 293, + "weight": 294, "cookies": false, "type": "", "deprecated": false, @@ -10209,7 +10211,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 294, + "weight": 295, "cookies": false, "type": "", "deprecated": false, @@ -10288,7 +10290,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 389, + "weight": 390, "cookies": false, "type": "upload", "deprecated": false, @@ -10379,7 +10381,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 390, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -10460,6 +10462,102 @@ ] } }, + "\/functions\/{functionId}\/deployments\/vcs": { + "post": { + "summary": "Create VCS deployment", + "operationId": "functionsCreateVcsDeployment", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "functions" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "schema": { + "$ref": "#\/definitions\/deployment" + } + } + }, + "x-appwrite": { + "method": "createVcsDeployment", + "weight": 392, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "functions\/create-vcs-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment when a function is connected to VCS.\n\nThis endpoint lets you create deployment from a branch, commit, or a tag.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "functions.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "functionId", + "description": "Function ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of reference passed. Allowed values are: branch", + "default": null, + "x-example": "branch", + "enum": [ + "branch" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "VCS reference to create deployment from. Depending on type this can be: branch name", + "default": null, + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "default": false, + "x-example": false + } + }, + "required": [ + "type", + "reference" + ] + } + } + ] + } + }, "\/functions\/{functionId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -10484,7 +10582,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 295, + "weight": 296, "cookies": false, "type": "", "deprecated": false, @@ -10545,7 +10643,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 296, + "weight": 297, "cookies": false, "type": "", "deprecated": false, @@ -10610,7 +10708,7 @@ }, "x-appwrite": { "method": "createBuild", - "weight": 297, + "weight": 298, "cookies": false, "type": "", "deprecated": false, @@ -10693,7 +10791,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 291, + "weight": 292, "cookies": false, "type": "location", "deprecated": false, @@ -10763,7 +10861,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 300, + "weight": 301, "cookies": false, "type": "", "deprecated": false, @@ -10845,7 +10943,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 299, + "weight": 300, "cookies": false, "type": "", "deprecated": false, @@ -10963,7 +11061,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 301, + "weight": 302, "cookies": false, "type": "", "deprecated": false, @@ -11027,7 +11125,7 @@ }, "x-appwrite": { "method": "deleteExecution", - "weight": 302, + "weight": 303, "cookies": false, "type": "", "deprecated": false, @@ -11095,7 +11193,7 @@ }, "x-appwrite": { "method": "getFunctionUsage", - "weight": 289, + "weight": 290, "cookies": false, "type": "", "deprecated": false, @@ -11174,7 +11272,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 304, + "weight": 305, "cookies": false, "type": "", "deprecated": false, @@ -11232,7 +11330,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 303, + "weight": 304, "cookies": false, "type": "", "deprecated": false, @@ -11323,7 +11421,7 @@ }, "x-appwrite": { "method": "query", - "weight": 324, + "weight": 325, "cookies": false, "type": "graphql", "deprecated": false, @@ -11396,7 +11494,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 323, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -11519,7 +11617,7 @@ }, "x-appwrite": { "method": "getAntivirus", - "weight": 146, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -12031,7 +12129,7 @@ }, "x-appwrite": { "method": "getFailedJobs", - "weight": 147, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -12068,8 +12166,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -12397,10 +12496,10 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "consumes": [ "application\/json" ], @@ -12410,7 +12509,7 @@ "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -12420,13 +12519,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", + "method": "getQueueStatsResources", "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12458,10 +12557,71 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "schema": { + "$ref": "#\/definitions\/healthQueue" + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "type": "integer", + "format": "int32", + "default": 5000, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "consumes": [ "application\/json" ], @@ -12481,13 +12641,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", - "weight": 143, + "method": "getQueueStatsUsageDump", + "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -12604,7 +12764,7 @@ }, "x-appwrite": { "method": "getStorage", - "weight": 145, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -12654,7 +12814,7 @@ }, "x-appwrite": { "method": "getStorageLocal", - "weight": 144, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -13178,7 +13338,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 377, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -13252,7 +13412,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 374, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -13409,7 +13569,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 381, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -13563,7 +13723,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 376, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -13757,7 +13917,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 383, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -13950,7 +14110,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 375, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -14067,7 +14227,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 382, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -14182,7 +14342,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 380, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -14236,7 +14396,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 384, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -14297,7 +14457,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 378, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -14370,7 +14530,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 379, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -14443,7 +14603,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 349, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -14517,7 +14677,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 348, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -14631,7 +14791,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 361, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -14743,7 +14903,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 347, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -14833,7 +14993,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 360, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -14921,7 +15081,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 339, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -15047,7 +15207,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 352, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -15171,7 +15331,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 342, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -15273,7 +15433,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 355, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -15373,7 +15533,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 340, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -15487,7 +15647,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 353, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -15599,7 +15759,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 341, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -15757,7 +15917,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 354, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -15912,7 +16072,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 343, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -16014,7 +16174,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 356, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -16114,7 +16274,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 344, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -16216,7 +16376,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 357, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -16316,7 +16476,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 345, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -16418,7 +16578,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 358, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -16518,7 +16678,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 346, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -16620,7 +16780,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 359, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -16720,7 +16880,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 351, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -16774,7 +16934,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 362, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -16835,7 +16995,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 350, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -16908,7 +17068,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 371, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -16981,7 +17141,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 364, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -17053,7 +17213,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 363, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -17142,7 +17302,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 366, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -17201,7 +17361,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 367, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -17279,7 +17439,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 368, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -17340,7 +17500,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 365, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -17413,7 +17573,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 370, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -17493,7 +17653,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 369, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -17582,7 +17742,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 372, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -17644,7 +17804,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 373, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -17716,7 +17876,7 @@ }, "x-appwrite": { "method": "list", - "weight": 331, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -17788,7 +17948,7 @@ }, "x-appwrite": { "method": "createAppwriteMigration", - "weight": 327, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -17829,19 +17989,19 @@ }, "endpoint": { "type": "string", - "description": "Source's Appwrite Endpoint", + "description": "Source Appwrite endpoint", "default": null, "x-example": "https:\/\/example.com" }, "projectId": { "type": "string", - "description": "Source's Project ID", + "description": "Source Project ID", "default": null, "x-example": "" }, "apiKey": { "type": "string", - "description": "Source's API Key", + "description": "Source API Key", "default": null, "x-example": "" } @@ -17881,7 +18041,7 @@ }, "x-appwrite": { "method": "getAppwriteReport", - "weight": 333, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -17968,7 +18128,7 @@ }, "x-appwrite": { "method": "createFirebaseMigration", - "weight": 328, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -18047,7 +18207,7 @@ }, "x-appwrite": { "method": "getFirebaseReport", - "weight": 334, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -18117,7 +18277,7 @@ }, "x-appwrite": { "method": "createNHostMigration", - "weight": 330, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -18237,7 +18397,7 @@ }, "x-appwrite": { "method": "getNHostReport", - "weight": 336, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -18356,7 +18516,7 @@ }, "x-appwrite": { "method": "createSupabaseMigration", - "weight": 329, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -18469,7 +18629,7 @@ }, "x-appwrite": { "method": "getSupabaseReport", - "weight": 335, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -18581,7 +18741,7 @@ }, "x-appwrite": { "method": "get", - "weight": 332, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -18638,7 +18798,7 @@ }, "x-appwrite": { "method": "retry", - "weight": 337, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -18690,7 +18850,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 338, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -18749,7 +18909,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 195, + "weight": 196, "cookies": false, "type": "", "deprecated": false, @@ -18832,7 +18992,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 197, + "weight": 198, "cookies": false, "type": "", "deprecated": false, @@ -18879,7 +19039,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 196, + "weight": 197, "cookies": false, "type": "", "deprecated": false, @@ -18961,7 +19121,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 198, + "weight": 199, "cookies": false, "type": "", "deprecated": false, @@ -19018,7 +19178,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 199, + "weight": 200, "cookies": false, "type": "", "deprecated": false, @@ -19100,7 +19260,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 200, + "weight": 201, "cookies": false, "type": "", "deprecated": false, @@ -19159,7 +19319,7 @@ }, "x-appwrite": { "method": "list", - "weight": 150, + "weight": 151, "cookies": false, "type": "", "deprecated": false, @@ -19229,7 +19389,7 @@ }, "x-appwrite": { "method": "create", - "weight": 149, + "weight": 150, "cookies": false, "type": "", "deprecated": false, @@ -19378,7 +19538,7 @@ }, "x-appwrite": { "method": "get", - "weight": 151, + "weight": 152, "cookies": false, "type": "", "deprecated": false, @@ -19435,7 +19595,7 @@ }, "x-appwrite": { "method": "update", - "weight": 152, + "weight": 153, "cookies": false, "type": "", "deprecated": false, @@ -19559,7 +19719,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 169, + "weight": 170, "cookies": false, "type": "", "deprecated": false, @@ -19618,7 +19778,7 @@ }, "x-appwrite": { "method": "updateApiStatus", - "weight": 156, + "weight": 157, "cookies": false, "type": "", "deprecated": false, @@ -19709,7 +19869,7 @@ }, "x-appwrite": { "method": "updateApiStatusAll", - "weight": 157, + "weight": 158, "cookies": false, "type": "", "deprecated": false, @@ -19786,7 +19946,7 @@ }, "x-appwrite": { "method": "updateAuthDuration", - "weight": 162, + "weight": 163, "cookies": false, "type": "", "deprecated": false, @@ -19863,7 +20023,7 @@ }, "x-appwrite": { "method": "updateAuthLimit", - "weight": 161, + "weight": 162, "cookies": false, "type": "", "deprecated": false, @@ -19940,7 +20100,7 @@ }, "x-appwrite": { "method": "updateAuthSessionsLimit", - "weight": 167, + "weight": 168, "cookies": false, "type": "", "deprecated": false, @@ -20017,7 +20177,7 @@ }, "x-appwrite": { "method": "updateMembershipsPrivacy", - "weight": 160, + "weight": 161, "cookies": false, "type": "", "deprecated": false, @@ -20108,7 +20268,7 @@ }, "x-appwrite": { "method": "updateMockNumbers", - "weight": 168, + "weight": 169, "cookies": false, "type": "", "deprecated": false, @@ -20188,7 +20348,7 @@ }, "x-appwrite": { "method": "updateAuthPasswordDictionary", - "weight": 165, + "weight": 166, "cookies": false, "type": "", "deprecated": false, @@ -20265,7 +20425,7 @@ }, "x-appwrite": { "method": "updateAuthPasswordHistory", - "weight": 164, + "weight": 165, "cookies": false, "type": "", "deprecated": false, @@ -20342,7 +20502,7 @@ }, "x-appwrite": { "method": "updatePersonalDataCheck", - "weight": 166, + "weight": 167, "cookies": false, "type": "", "deprecated": false, @@ -20419,7 +20579,7 @@ }, "x-appwrite": { "method": "updateSessionAlerts", - "weight": 159, + "weight": 160, "cookies": false, "type": "", "deprecated": false, @@ -20496,7 +20656,7 @@ }, "x-appwrite": { "method": "updateAuthStatus", - "weight": 163, + "weight": 164, "cookies": false, "type": "", "deprecated": false, @@ -20592,7 +20752,7 @@ }, "x-appwrite": { "method": "createJWT", - "weight": 181, + "weight": 182, "cookies": false, "type": "", "deprecated": false, @@ -20678,7 +20838,7 @@ }, "x-appwrite": { "method": "listKeys", - "weight": 177, + "weight": 178, "cookies": false, "type": "", "deprecated": false, @@ -20735,7 +20895,7 @@ }, "x-appwrite": { "method": "createKey", - "weight": 176, + "weight": 177, "cookies": false, "type": "", "deprecated": false, @@ -20828,7 +20988,7 @@ }, "x-appwrite": { "method": "getKey", - "weight": 178, + "weight": 179, "cookies": false, "type": "", "deprecated": false, @@ -20893,7 +21053,7 @@ }, "x-appwrite": { "method": "updateKey", - "weight": 179, + "weight": 180, "cookies": false, "type": "", "deprecated": false, @@ -20987,7 +21147,7 @@ }, "x-appwrite": { "method": "deleteKey", - "weight": 180, + "weight": 181, "cookies": false, "type": "", "deprecated": false, @@ -21054,7 +21214,7 @@ }, "x-appwrite": { "method": "updateOAuth2", - "weight": 158, + "weight": 159, "cookies": false, "type": "", "deprecated": false, @@ -21192,7 +21352,7 @@ }, "x-appwrite": { "method": "listPlatforms", - "weight": 183, + "weight": 184, "cookies": false, "type": "", "deprecated": false, @@ -21249,7 +21409,7 @@ }, "x-appwrite": { "method": "createPlatform", - "weight": 182, + "weight": 183, "cookies": false, "type": "", "deprecated": false, @@ -21370,7 +21530,7 @@ }, "x-appwrite": { "method": "getPlatform", - "weight": 184, + "weight": 185, "cookies": false, "type": "", "deprecated": false, @@ -21435,7 +21595,7 @@ }, "x-appwrite": { "method": "updatePlatform", - "weight": 185, + "weight": 186, "cookies": false, "type": "", "deprecated": false, @@ -21531,7 +21691,7 @@ }, "x-appwrite": { "method": "deletePlatform", - "weight": 186, + "weight": 187, "cookies": false, "type": "", "deprecated": false, @@ -21598,7 +21758,7 @@ }, "x-appwrite": { "method": "updateServiceStatus", - "weight": 154, + "weight": 155, "cookies": false, "type": "", "deprecated": false, @@ -21698,7 +21858,7 @@ }, "x-appwrite": { "method": "updateServiceStatusAll", - "weight": 155, + "weight": 156, "cookies": false, "type": "", "deprecated": false, @@ -21775,7 +21935,7 @@ }, "x-appwrite": { "method": "updateSmtp", - "weight": 187, + "weight": 188, "cookies": false, "type": "", "deprecated": false, @@ -21903,7 +22063,7 @@ }, "x-appwrite": { "method": "createSmtpTest", - "weight": 188, + "weight": 189, "cookies": false, "type": "", "deprecated": false, @@ -22040,7 +22200,7 @@ }, "x-appwrite": { "method": "updateTeam", - "weight": 153, + "weight": 154, "cookies": false, "type": "", "deprecated": false, @@ -22117,7 +22277,7 @@ }, "x-appwrite": { "method": "getEmailTemplate", - "weight": 190, + "weight": 191, "cookies": false, "type": "", "deprecated": false, @@ -22336,7 +22496,7 @@ }, "x-appwrite": { "method": "updateEmailTemplate", - "weight": 192, + "weight": 193, "cookies": false, "type": "", "deprecated": false, @@ -22598,7 +22758,7 @@ }, "x-appwrite": { "method": "deleteEmailTemplate", - "weight": 194, + "weight": 195, "cookies": false, "type": "", "deprecated": false, @@ -22819,7 +22979,7 @@ }, "x-appwrite": { "method": "getSmsTemplate", - "weight": 189, + "weight": 190, "cookies": false, "type": "", "deprecated": false, @@ -23035,7 +23195,7 @@ }, "x-appwrite": { "method": "updateSmsTemplate", - "weight": 191, + "weight": 192, "cookies": false, "type": "", "deprecated": false, @@ -23269,7 +23429,7 @@ }, "x-appwrite": { "method": "deleteSmsTemplate", - "weight": 193, + "weight": 194, "cookies": false, "type": "", "deprecated": false, @@ -23487,7 +23647,7 @@ }, "x-appwrite": { "method": "listWebhooks", - "weight": 171, + "weight": 172, "cookies": false, "type": "", "deprecated": false, @@ -23544,7 +23704,7 @@ }, "x-appwrite": { "method": "createWebhook", - "weight": 170, + "weight": 171, "cookies": false, "type": "", "deprecated": false, @@ -23663,7 +23823,7 @@ }, "x-appwrite": { "method": "getWebhook", - "weight": 172, + "weight": 173, "cookies": false, "type": "", "deprecated": false, @@ -23728,7 +23888,7 @@ }, "x-appwrite": { "method": "updateWebhook", - "weight": 173, + "weight": 174, "cookies": false, "type": "", "deprecated": false, @@ -23848,7 +24008,7 @@ }, "x-appwrite": { "method": "deleteWebhook", - "weight": 175, + "weight": 176, "cookies": false, "type": "", "deprecated": false, @@ -23915,7 +24075,7 @@ }, "x-appwrite": { "method": "updateWebhookSignature", - "weight": 174, + "weight": 175, "cookies": false, "type": "", "deprecated": false, @@ -23982,7 +24142,7 @@ }, "x-appwrite": { "method": "listRules", - "weight": 310, + "weight": 311, "cookies": false, "type": "", "deprecated": false, @@ -24052,7 +24212,7 @@ }, "x-appwrite": { "method": "createRule", - "weight": 420, + "weight": 423, "cookies": false, "type": "", "deprecated": false, @@ -24141,7 +24301,7 @@ }, "x-appwrite": { "method": "getRule", - "weight": 311, + "weight": 312, "cookies": false, "type": "", "deprecated": false, @@ -24193,7 +24353,7 @@ }, "x-appwrite": { "method": "deleteRule", - "weight": 312, + "weight": 313, "cookies": false, "type": "", "deprecated": false, @@ -24252,7 +24412,7 @@ }, "x-appwrite": { "method": "updateRuleVerification", - "weight": 313, + "weight": 314, "cookies": false, "type": "", "deprecated": false, @@ -24311,7 +24471,7 @@ }, "x-appwrite": { "method": "list", - "weight": 393, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -24382,7 +24542,7 @@ }, "x-appwrite": { "method": "create", - "weight": 391, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -24628,7 +24788,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 396, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -24678,7 +24838,7 @@ }, "x-appwrite": { "method": "listTemplates", - "weight": 415, + "weight": 418, "cookies": false, "type": "", "deprecated": false, @@ -24773,7 +24933,7 @@ }, "x-appwrite": { "method": "getTemplate", - "weight": 416, + "weight": 419, "cookies": false, "type": "", "deprecated": false, @@ -24832,7 +24992,7 @@ }, "x-appwrite": { "method": "listUsage", - "weight": 417, + "weight": 420, "cookies": false, "type": "", "deprecated": false, @@ -24899,7 +25059,7 @@ }, "x-appwrite": { "method": "get", - "weight": 392, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -24957,7 +25117,7 @@ }, "x-appwrite": { "method": "update", - "weight": 394, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -25196,7 +25356,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 395, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -25256,7 +25416,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 400, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -25335,7 +25495,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 397, + "weight": 399, "cookies": false, "type": "upload", "deprecated": false, @@ -25434,7 +25594,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 398, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -25515,6 +25675,102 @@ ] } }, + "\/sites\/{siteId}\/deployments\/vcs": { + "post": { + "summary": "Create VCS deployment", + "operationId": "sitesCreateVcsDeployment", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "sites" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "schema": { + "$ref": "#\/definitions\/deployment" + } + } + }, + "x-appwrite": { + "method": "createVcsDeployment", + "weight": 401, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "sites\/create-vcs-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment when a site is connected to VCS.\n\nThis endpoint lets you create deployment from a branch, commit, or a tag.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "sites.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "siteId", + "description": "Site ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of reference passed. Allowed values are: branch", + "default": null, + "x-example": "branch", + "enum": [ + "branch" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "VCS reference to create deployment from. Depending on type this can be: branch name", + "default": null, + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "default": false, + "x-example": false + } + }, + "required": [ + "type", + "reference" + ] + } + } + ] + } + }, "\/sites\/{siteId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -25539,7 +25795,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 399, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -25605,7 +25861,7 @@ }, "x-appwrite": { "method": "updateDeployment", - "weight": 401, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -25666,7 +25922,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 402, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -25731,7 +25987,7 @@ }, "x-appwrite": { "method": "createDeploymentBuild", - "weight": 405, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -25797,7 +26053,7 @@ }, "x-appwrite": { "method": "updateDeploymentBuild", - "weight": 406, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -25865,7 +26121,7 @@ }, "x-appwrite": { "method": "getDeploymentBuildDownload", - "weight": 404, + "weight": 407, "cookies": false, "type": "location", "deprecated": false, @@ -25935,7 +26191,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 403, + "weight": 406, "cookies": false, "type": "location", "deprecated": false, @@ -26005,7 +26261,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 408, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -26086,7 +26342,7 @@ }, "x-appwrite": { "method": "getLog", - "weight": 407, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -26149,7 +26405,7 @@ }, "x-appwrite": { "method": "deleteLog", - "weight": 409, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -26217,7 +26473,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 418, + "weight": 421, "cookies": false, "type": "", "deprecated": false, @@ -26296,7 +26552,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 412, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -26354,7 +26610,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 410, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -26445,7 +26701,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 411, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -26511,7 +26767,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 413, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -26602,7 +26858,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 414, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -26670,7 +26926,7 @@ }, "x-appwrite": { "method": "listBuckets", - "weight": 202, + "weight": 203, "cookies": false, "type": "", "deprecated": false, @@ -26741,7 +26997,7 @@ }, "x-appwrite": { "method": "createBucket", - "weight": 201, + "weight": 202, "cookies": false, "type": "", "deprecated": false, @@ -26879,7 +27135,7 @@ }, "x-appwrite": { "method": "getBucket", - "weight": 203, + "weight": 204, "cookies": false, "type": "", "deprecated": false, @@ -26937,7 +27193,7 @@ }, "x-appwrite": { "method": "updateBucket", - "weight": 204, + "weight": 205, "cookies": false, "type": "", "deprecated": false, @@ -27069,7 +27325,7 @@ }, "x-appwrite": { "method": "deleteBucket", - "weight": 205, + "weight": 206, "cookies": false, "type": "", "deprecated": false, @@ -27129,7 +27385,7 @@ }, "x-appwrite": { "method": "listFiles", - "weight": 207, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -27211,7 +27467,7 @@ }, "x-appwrite": { "method": "createFile", - "weight": 206, + "weight": 207, "cookies": false, "type": "upload", "deprecated": false, @@ -27302,7 +27558,7 @@ }, "x-appwrite": { "method": "getFile", - "weight": 208, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -27371,7 +27627,7 @@ }, "x-appwrite": { "method": "updateFile", - "weight": 213, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -27459,7 +27715,7 @@ }, "x-appwrite": { "method": "deleteFile", - "weight": 214, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -27530,7 +27786,7 @@ }, "x-appwrite": { "method": "getFileDownload", - "weight": 210, + "weight": 211, "cookies": false, "type": "location", "deprecated": false, @@ -27601,7 +27857,7 @@ }, "x-appwrite": { "method": "getFilePreview", - "weight": 209, + "weight": 210, "cookies": false, "type": "location", "deprecated": false, @@ -27800,7 +28056,7 @@ }, "x-appwrite": { "method": "getFileView", - "weight": 211, + "weight": 212, "cookies": false, "type": "location", "deprecated": false, @@ -27871,7 +28127,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 215, + "weight": 216, "cookies": false, "type": "", "deprecated": false, @@ -27942,7 +28198,7 @@ }, "x-appwrite": { "method": "getBucketUsage", - "weight": 216, + "weight": 217, "cookies": false, "type": "", "deprecated": false, @@ -28021,7 +28277,7 @@ }, "x-appwrite": { "method": "list", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -28095,7 +28351,7 @@ }, "x-appwrite": { "method": "create", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -28186,7 +28442,7 @@ }, "x-appwrite": { "method": "get", - "weight": 219, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -28247,7 +28503,7 @@ }, "x-appwrite": { "method": "updateName", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -28321,7 +28577,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -28384,7 +28640,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 230, + "weight": 231, "cookies": false, "type": "", "deprecated": false, @@ -28455,7 +28711,7 @@ }, "x-appwrite": { "method": "listMemberships", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -28537,7 +28793,7 @@ }, "x-appwrite": { "method": "createMembership", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -28651,7 +28907,7 @@ }, "x-appwrite": { "method": "getMembership", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -28720,7 +28976,7 @@ }, "x-appwrite": { "method": "updateMembership", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -28805,7 +29061,7 @@ }, "x-appwrite": { "method": "deleteMembership", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -28876,7 +29132,7 @@ }, "x-appwrite": { "method": "updateMembershipStatus", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -28970,7 +29226,7 @@ }, "x-appwrite": { "method": "getPrefs", - "weight": 220, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -29029,7 +29285,7 @@ }, "x-appwrite": { "method": "updatePrefs", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -29108,7 +29364,7 @@ }, "x-appwrite": { "method": "list", - "weight": 240, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -29179,7 +29435,7 @@ }, "x-appwrite": { "method": "create", - "weight": 231, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -29273,7 +29529,7 @@ }, "x-appwrite": { "method": "createArgon2User", - "weight": 234, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -29363,7 +29619,7 @@ }, "x-appwrite": { "method": "createBcryptUser", - "weight": 232, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -29453,7 +29709,7 @@ }, "x-appwrite": { "method": "listIdentities", - "weight": 248, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -29521,7 +29777,7 @@ }, "x-appwrite": { "method": "deleteIdentity", - "weight": 271, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -29581,7 +29837,7 @@ }, "x-appwrite": { "method": "createMD5User", - "weight": 233, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -29671,7 +29927,7 @@ }, "x-appwrite": { "method": "createPHPassUser", - "weight": 236, + "weight": 237, "cookies": false, "type": "", "deprecated": false, @@ -29761,7 +30017,7 @@ }, "x-appwrite": { "method": "createScryptUser", - "weight": 237, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -29886,7 +30142,7 @@ }, "x-appwrite": { "method": "createScryptModifiedUser", - "weight": 238, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -29997,7 +30253,7 @@ }, "x-appwrite": { "method": "createSHAUser", - "weight": 235, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -30108,7 +30364,7 @@ }, "x-appwrite": { "method": "getUsage", - "weight": 273, + "weight": 274, "cookies": false, "type": "", "deprecated": false, @@ -30179,7 +30435,7 @@ }, "x-appwrite": { "method": "get", - "weight": 241, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -30232,7 +30488,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 269, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -30292,7 +30548,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 254, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -30370,7 +30626,7 @@ }, "x-appwrite": { "method": "createJWT", - "weight": 272, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -30451,7 +30707,7 @@ }, "x-appwrite": { "method": "updateLabels", - "weight": 250, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -30532,7 +30788,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 246, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -30604,7 +30860,7 @@ }, "x-appwrite": { "method": "listMemberships", - "weight": 245, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -30664,7 +30920,7 @@ }, "x-appwrite": { "method": "updateMfa", - "weight": 259, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -30737,7 +30993,7 @@ }, "x-appwrite": { "method": "deleteMfaAuthenticator", - "weight": 264, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -30810,7 +31066,7 @@ }, "x-appwrite": { "method": "listMfaFactors", - "weight": 260, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -30870,7 +31126,7 @@ }, "x-appwrite": { "method": "getMfaRecoveryCodes", - "weight": 261, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -30928,7 +31184,7 @@ }, "x-appwrite": { "method": "updateMfaRecoveryCodes", - "weight": 263, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -30986,7 +31242,7 @@ }, "x-appwrite": { "method": "createMfaRecoveryCodes", - "weight": 262, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -31046,7 +31302,7 @@ }, "x-appwrite": { "method": "updateName", - "weight": 252, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -31124,7 +31380,7 @@ }, "x-appwrite": { "method": "updatePassword", - "weight": 253, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -31202,7 +31458,7 @@ }, "x-appwrite": { "method": "updatePhone", - "weight": 255, + "weight": 256, "cookies": false, "type": "", "deprecated": false, @@ -31280,7 +31536,7 @@ }, "x-appwrite": { "method": "getPrefs", - "weight": 242, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -31338,7 +31594,7 @@ }, "x-appwrite": { "method": "updatePrefs", - "weight": 257, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -31416,7 +31672,7 @@ }, "x-appwrite": { "method": "listSessions", - "weight": 244, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -31474,7 +31730,7 @@ }, "x-appwrite": { "method": "createSession", - "weight": 265, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -31527,7 +31783,7 @@ }, "x-appwrite": { "method": "deleteSessions", - "weight": 268, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -31582,7 +31838,7 @@ }, "x-appwrite": { "method": "deleteSession", - "weight": 267, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -31650,7 +31906,7 @@ }, "x-appwrite": { "method": "updateStatus", - "weight": 249, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -31728,7 +31984,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 247, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -31799,7 +32055,7 @@ }, "x-appwrite": { "method": "createTarget", - "weight": 239, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -31911,7 +32167,7 @@ }, "x-appwrite": { "method": "getTarget", - "weight": 243, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -31978,7 +32234,7 @@ }, "x-appwrite": { "method": "updateTarget", - "weight": 258, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -32067,7 +32323,7 @@ }, "x-appwrite": { "method": "deleteTarget", - "weight": 270, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -32136,7 +32392,7 @@ }, "x-appwrite": { "method": "createToken", - "weight": 266, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -32217,7 +32473,7 @@ }, "x-appwrite": { "method": "updateEmailVerification", - "weight": 256, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -32295,7 +32551,7 @@ }, "x-appwrite": { "method": "updatePhoneVerification", - "weight": 251, + "weight": 252, "cookies": false, "type": "", "deprecated": false, @@ -32373,7 +32629,7 @@ }, "x-appwrite": { "method": "listRepositories", - "weight": 278, + "weight": 279, "cookies": false, "type": "", "deprecated": false, @@ -32439,7 +32695,7 @@ }, "x-appwrite": { "method": "createRepository", - "weight": 279, + "weight": 280, "cookies": false, "type": "", "deprecated": false, @@ -32523,7 +32779,7 @@ }, "x-appwrite": { "method": "getRepository", - "weight": 280, + "weight": 281, "cookies": false, "type": "", "deprecated": false, @@ -32590,7 +32846,7 @@ }, "x-appwrite": { "method": "listRepositoryBranches", - "weight": 281, + "weight": 282, "cookies": false, "type": "", "deprecated": false, @@ -32657,7 +32913,7 @@ }, "x-appwrite": { "method": "getRepositoryContents", - "weight": 276, + "weight": 277, "cookies": false, "type": "", "deprecated": false, @@ -32733,7 +32989,7 @@ }, "x-appwrite": { "method": "createRepositoryDetection", - "weight": 277, + "weight": 278, "cookies": false, "type": "", "deprecated": false, @@ -32812,7 +33068,7 @@ }, "x-appwrite": { "method": "updateExternalDeployments", - "weight": 286, + "weight": 287, "cookies": false, "type": "", "deprecated": false, @@ -32897,7 +33153,7 @@ }, "x-appwrite": { "method": "listInstallations", - "weight": 283, + "weight": 284, "cookies": false, "type": "", "deprecated": false, @@ -32969,7 +33225,7 @@ }, "x-appwrite": { "method": "getInstallation", - "weight": 284, + "weight": 285, "cookies": false, "type": "", "deprecated": false, @@ -33021,7 +33277,7 @@ }, "x-appwrite": { "method": "deleteInstallation", - "weight": 285, + "weight": 286, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 5b50b0ec33..c5ea60e44a 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -3261,7 +3261,7 @@ "parameters": [ { "name": "code", - "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro.", + "description": "Credit Card Code. Possible values: amex, argencard, cabal, cencosud, diners, discover, elo, hipercard, jcb, mastercard, naranja, targeta-shopping, union-china-pay, visa, mir, maestro, rupay.", "required": true, "type": "string", "x-example": "amex", @@ -3281,7 +3281,8 @@ "union-china-pay", "visa", "mir", - "maestro" + "maestro", + "rupay" ], "x-enum-name": "CreditCard", "x-enum-keys": [ @@ -3300,7 +3301,8 @@ "Union China Pay", "Visa", "MIR", - "Maestro" + "Maestro", + "Rupay" ], "in": "path" }, @@ -7552,7 +7554,7 @@ "tags": [ "databases" ], - "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.", + "description": "Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https:\/\/appwrite.io\/docs\/server\/databases#databasesCreateCollection) API or directly from your database console.\n", "responses": { "201": { "description": "Document", @@ -8284,7 +8286,7 @@ }, "x-appwrite": { "method": "list", - "weight": 387, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -8356,7 +8358,7 @@ }, "x-appwrite": { "method": "create", - "weight": 385, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -8606,7 +8608,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 388, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -8657,7 +8659,7 @@ }, "x-appwrite": { "method": "listSpecifications", - "weight": 287, + "weight": 288, "cookies": false, "type": "", "deprecated": false, @@ -8709,7 +8711,7 @@ }, "x-appwrite": { "method": "get", - "weight": 288, + "weight": 289, "cookies": false, "type": "", "deprecated": false, @@ -8768,7 +8770,7 @@ }, "x-appwrite": { "method": "update", - "weight": 386, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -9012,7 +9014,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 293, + "weight": 294, "cookies": false, "type": "", "deprecated": false, @@ -9073,7 +9075,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 294, + "weight": 295, "cookies": false, "type": "", "deprecated": false, @@ -9153,7 +9155,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 389, + "weight": 390, "cookies": false, "type": "upload", "deprecated": false, @@ -9245,7 +9247,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 390, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -9327,6 +9329,103 @@ ] } }, + "\/functions\/{functionId}\/deployments\/vcs": { + "post": { + "summary": "Create VCS deployment", + "operationId": "functionsCreateVcsDeployment", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "functions" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "schema": { + "$ref": "#\/definitions\/deployment" + } + } + }, + "x-appwrite": { + "method": "createVcsDeployment", + "weight": 392, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "functions\/create-vcs-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment when a function is connected to VCS.\n\nThis endpoint lets you create deployment from a branch, commit, or a tag.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "functions.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "functionId", + "description": "Function ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of reference passed. Allowed values are: branch", + "default": null, + "x-example": "branch", + "enum": [ + "branch" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "VCS reference to create deployment from. Depending on type this can be: branch name", + "default": null, + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "default": false, + "x-example": false + } + }, + "required": [ + "type", + "reference" + ] + } + } + ] + } + }, "\/functions\/{functionId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -9351,7 +9450,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 295, + "weight": 296, "cookies": false, "type": "", "deprecated": false, @@ -9413,7 +9512,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 296, + "weight": 297, "cookies": false, "type": "", "deprecated": false, @@ -9479,7 +9578,7 @@ }, "x-appwrite": { "method": "createBuild", - "weight": 297, + "weight": 298, "cookies": false, "type": "", "deprecated": false, @@ -9563,7 +9662,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 291, + "weight": 292, "cookies": false, "type": "location", "deprecated": false, @@ -9634,7 +9733,7 @@ }, "x-appwrite": { "method": "listExecutions", - "weight": 300, + "weight": 301, "cookies": false, "type": "", "deprecated": false, @@ -9718,7 +9817,7 @@ }, "x-appwrite": { "method": "createExecution", - "weight": 299, + "weight": 300, "cookies": false, "type": "", "deprecated": false, @@ -9838,7 +9937,7 @@ }, "x-appwrite": { "method": "getExecution", - "weight": 301, + "weight": 302, "cookies": false, "type": "", "deprecated": false, @@ -9904,7 +10003,7 @@ }, "x-appwrite": { "method": "deleteExecution", - "weight": 302, + "weight": 303, "cookies": false, "type": "", "deprecated": false, @@ -9973,7 +10072,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 304, + "weight": 305, "cookies": false, "type": "", "deprecated": false, @@ -10032,7 +10131,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 303, + "weight": 304, "cookies": false, "type": "", "deprecated": false, @@ -10124,7 +10223,7 @@ }, "x-appwrite": { "method": "query", - "weight": 324, + "weight": 325, "cookies": false, "type": "graphql", "deprecated": false, @@ -10199,7 +10298,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 323, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -10325,7 +10424,7 @@ }, "x-appwrite": { "method": "getAntivirus", - "weight": 146, + "weight": 147, "cookies": false, "type": "", "deprecated": false, @@ -10846,7 +10945,7 @@ }, "x-appwrite": { "method": "getFailedJobs", - "weight": 147, + "weight": 148, "cookies": false, "type": "", "deprecated": false, @@ -10884,8 +10983,9 @@ "v1-audits", "v1-mails", "v1-functions", - "v1-usage", - "v1-usage-dump", + "v1-stats-resources", + "v1-stats-usage", + "v1-stats-usage-dump", "v1-webhooks", "v1-certificates", "v1-builds", @@ -11218,10 +11318,10 @@ ] } }, - "\/health\/queue\/usage": { + "\/health\/queue\/stats-resources": { "get": { - "summary": "Get usage queue", - "operationId": "healthGetQueueUsage", + "summary": "Get stats resources queue", + "operationId": "healthGetQueueStatsResources", "consumes": [ "application\/json" ], @@ -11231,7 +11331,7 @@ "tags": [ "health" ], - "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "description": "Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue.", "responses": { "200": { "description": "Health Queue", @@ -11241,13 +11341,13 @@ } }, "x-appwrite": { - "method": "getQueueUsage", + "method": "getQueueStatsResources", "weight": 142, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage.md", + "demo": "health\/get-queue-stats-resources.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-resources.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11280,10 +11380,72 @@ ] } }, - "\/health\/queue\/usage-dump": { + "\/health\/queue\/stats-usage": { + "get": { + "summary": "Get stats usage queue", + "operationId": "healthGetQueueUsage", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "health" + ], + "description": "Get the number of metrics that are waiting to be processed in the Appwrite internal queue server.", + "responses": { + "200": { + "description": "Health Queue", + "schema": { + "$ref": "#\/definitions\/healthQueue" + } + } + }, + "x-appwrite": { + "method": "getQueueUsage", + "weight": 143, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "health\/get-queue-usage.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage.md", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "health.read", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "threshold", + "description": "Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.", + "required": false, + "type": "integer", + "format": "int32", + "default": 5000, + "in": "query" + } + ] + } + }, + "\/health\/queue\/stats-usage-dump": { "get": { "summary": "Get usage dump queue", - "operationId": "healthGetQueueUsageDump", + "operationId": "healthGetQueueStatsUsageDump", "consumes": [ "application\/json" ], @@ -11303,13 +11465,13 @@ } }, "x-appwrite": { - "method": "getQueueUsageDump", - "weight": 143, + "method": "getQueueStatsUsageDump", + "weight": 144, "cookies": false, "type": "", "deprecated": false, - "demo": "health\/get-queue-usage-dump.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-usage-dump.md", + "demo": "health\/get-queue-stats-usage-dump.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/health\/get-queue-stats-usage-dump.md", "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", @@ -11428,7 +11590,7 @@ }, "x-appwrite": { "method": "getStorage", - "weight": 145, + "weight": 146, "cookies": false, "type": "", "deprecated": false, @@ -11479,7 +11641,7 @@ }, "x-appwrite": { "method": "getStorageLocal", - "weight": 144, + "weight": 145, "cookies": false, "type": "", "deprecated": false, @@ -12021,7 +12183,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 377, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -12096,7 +12258,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 374, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -12254,7 +12416,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 381, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -12409,7 +12571,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 376, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -12604,7 +12766,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 383, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -12798,7 +12960,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 375, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -12916,7 +13078,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 382, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -13032,7 +13194,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 380, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -13087,7 +13249,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 384, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -13149,7 +13311,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 378, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -13223,7 +13385,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 379, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -13297,7 +13459,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 349, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -13372,7 +13534,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 348, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -13487,7 +13649,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 361, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -13600,7 +13762,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 347, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -13691,7 +13853,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 360, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -13780,7 +13942,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 339, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -13907,7 +14069,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 352, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -14032,7 +14194,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 342, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -14135,7 +14297,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 355, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -14236,7 +14398,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 340, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -14351,7 +14513,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 353, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -14464,7 +14626,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 341, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -14623,7 +14785,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 354, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -14779,7 +14941,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 343, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -14882,7 +15044,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 356, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -14983,7 +15145,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 344, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -15086,7 +15248,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 357, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -15187,7 +15349,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 345, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -15290,7 +15452,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 358, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -15391,7 +15553,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 346, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -15494,7 +15656,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 359, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -15595,7 +15757,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 351, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -15650,7 +15812,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 362, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -15712,7 +15874,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 350, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -15786,7 +15948,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 371, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -15860,7 +16022,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 364, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -15933,7 +16095,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 363, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -16023,7 +16185,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 366, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -16083,7 +16245,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 367, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -16162,7 +16324,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 368, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -16224,7 +16386,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 365, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -16298,7 +16460,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 370, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -16379,7 +16541,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 369, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -16470,7 +16632,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 372, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -16533,7 +16695,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 373, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -16607,7 +16769,7 @@ }, "x-appwrite": { "method": "list", - "weight": 393, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -16679,7 +16841,7 @@ }, "x-appwrite": { "method": "create", - "weight": 391, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -16926,7 +17088,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 396, + "weight": 398, "cookies": false, "type": "", "deprecated": false, @@ -16977,7 +17139,7 @@ }, "x-appwrite": { "method": "get", - "weight": 392, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -17036,7 +17198,7 @@ }, "x-appwrite": { "method": "update", - "weight": 394, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -17276,7 +17438,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 395, + "weight": 397, "cookies": false, "type": "", "deprecated": false, @@ -17337,7 +17499,7 @@ }, "x-appwrite": { "method": "listDeployments", - "weight": 400, + "weight": 403, "cookies": false, "type": "", "deprecated": false, @@ -17417,7 +17579,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 397, + "weight": 399, "cookies": false, "type": "upload", "deprecated": false, @@ -17517,7 +17679,7 @@ }, "x-appwrite": { "method": "createTemplateDeployment", - "weight": 398, + "weight": 400, "cookies": false, "type": "", "deprecated": false, @@ -17599,6 +17761,103 @@ ] } }, + "\/sites\/{siteId}\/deployments\/vcs": { + "post": { + "summary": "Create VCS deployment", + "operationId": "sitesCreateVcsDeployment", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "sites" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "schema": { + "$ref": "#\/definitions\/deployment" + } + } + }, + "x-appwrite": { + "method": "createVcsDeployment", + "weight": 401, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "sites\/create-vcs-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment when a site is connected to VCS.\n\nThis endpoint lets you create deployment from a branch, commit, or a tag.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "sites.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "siteId", + "description": "Site ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "type": { + "type": "string", + "description": "Type of reference passed. Allowed values are: branch", + "default": null, + "x-example": "branch", + "enum": [ + "branch" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "reference": { + "type": "string", + "description": "VCS reference to create deployment from. Depending on type this can be: branch name", + "default": null, + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "default": false, + "x-example": false + } + }, + "required": [ + "type", + "reference" + ] + } + } + ] + } + }, "\/sites\/{siteId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -17623,7 +17882,7 @@ }, "x-appwrite": { "method": "getDeployment", - "weight": 399, + "weight": 402, "cookies": false, "type": "", "deprecated": false, @@ -17690,7 +17949,7 @@ }, "x-appwrite": { "method": "updateDeployment", - "weight": 401, + "weight": 404, "cookies": false, "type": "", "deprecated": false, @@ -17752,7 +18011,7 @@ }, "x-appwrite": { "method": "deleteDeployment", - "weight": 402, + "weight": 405, "cookies": false, "type": "", "deprecated": false, @@ -17818,7 +18077,7 @@ }, "x-appwrite": { "method": "createDeploymentBuild", - "weight": 405, + "weight": 408, "cookies": false, "type": "", "deprecated": false, @@ -17885,7 +18144,7 @@ }, "x-appwrite": { "method": "updateDeploymentBuild", - "weight": 406, + "weight": 409, "cookies": false, "type": "", "deprecated": false, @@ -17954,7 +18213,7 @@ }, "x-appwrite": { "method": "getDeploymentBuildDownload", - "weight": 404, + "weight": 407, "cookies": false, "type": "location", "deprecated": false, @@ -18025,7 +18284,7 @@ }, "x-appwrite": { "method": "getDeploymentDownload", - "weight": 403, + "weight": 406, "cookies": false, "type": "location", "deprecated": false, @@ -18096,7 +18355,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 408, + "weight": 411, "cookies": false, "type": "", "deprecated": false, @@ -18178,7 +18437,7 @@ }, "x-appwrite": { "method": "getLog", - "weight": 407, + "weight": 410, "cookies": false, "type": "", "deprecated": false, @@ -18242,7 +18501,7 @@ }, "x-appwrite": { "method": "deleteLog", - "weight": 409, + "weight": 412, "cookies": false, "type": "", "deprecated": false, @@ -18311,7 +18570,7 @@ }, "x-appwrite": { "method": "listVariables", - "weight": 412, + "weight": 415, "cookies": false, "type": "", "deprecated": false, @@ -18370,7 +18629,7 @@ }, "x-appwrite": { "method": "createVariable", - "weight": 410, + "weight": 413, "cookies": false, "type": "", "deprecated": false, @@ -18462,7 +18721,7 @@ }, "x-appwrite": { "method": "getVariable", - "weight": 411, + "weight": 414, "cookies": false, "type": "", "deprecated": false, @@ -18529,7 +18788,7 @@ }, "x-appwrite": { "method": "updateVariable", - "weight": 413, + "weight": 416, "cookies": false, "type": "", "deprecated": false, @@ -18621,7 +18880,7 @@ }, "x-appwrite": { "method": "deleteVariable", - "weight": 414, + "weight": 417, "cookies": false, "type": "", "deprecated": false, @@ -18690,7 +18949,7 @@ }, "x-appwrite": { "method": "listBuckets", - "weight": 202, + "weight": 203, "cookies": false, "type": "", "deprecated": false, @@ -18762,7 +19021,7 @@ }, "x-appwrite": { "method": "createBucket", - "weight": 201, + "weight": 202, "cookies": false, "type": "", "deprecated": false, @@ -18901,7 +19160,7 @@ }, "x-appwrite": { "method": "getBucket", - "weight": 203, + "weight": 204, "cookies": false, "type": "", "deprecated": false, @@ -18960,7 +19219,7 @@ }, "x-appwrite": { "method": "updateBucket", - "weight": 204, + "weight": 205, "cookies": false, "type": "", "deprecated": false, @@ -19093,7 +19352,7 @@ }, "x-appwrite": { "method": "deleteBucket", - "weight": 205, + "weight": 206, "cookies": false, "type": "", "deprecated": false, @@ -19154,7 +19413,7 @@ }, "x-appwrite": { "method": "listFiles", - "weight": 207, + "weight": 208, "cookies": false, "type": "", "deprecated": false, @@ -19238,7 +19497,7 @@ }, "x-appwrite": { "method": "createFile", - "weight": 206, + "weight": 207, "cookies": false, "type": "upload", "deprecated": false, @@ -19331,7 +19590,7 @@ }, "x-appwrite": { "method": "getFile", - "weight": 208, + "weight": 209, "cookies": false, "type": "", "deprecated": false, @@ -19402,7 +19661,7 @@ }, "x-appwrite": { "method": "updateFile", - "weight": 213, + "weight": 214, "cookies": false, "type": "", "deprecated": false, @@ -19492,7 +19751,7 @@ }, "x-appwrite": { "method": "deleteFile", - "weight": 214, + "weight": 215, "cookies": false, "type": "", "deprecated": false, @@ -19565,7 +19824,7 @@ }, "x-appwrite": { "method": "getFileDownload", - "weight": 210, + "weight": 211, "cookies": false, "type": "location", "deprecated": false, @@ -19638,7 +19897,7 @@ }, "x-appwrite": { "method": "getFilePreview", - "weight": 209, + "weight": 210, "cookies": false, "type": "location", "deprecated": false, @@ -19839,7 +20098,7 @@ }, "x-appwrite": { "method": "getFileView", - "weight": 211, + "weight": 212, "cookies": false, "type": "location", "deprecated": false, @@ -19912,7 +20171,7 @@ }, "x-appwrite": { "method": "list", - "weight": 218, + "weight": 219, "cookies": false, "type": "", "deprecated": false, @@ -19988,7 +20247,7 @@ }, "x-appwrite": { "method": "create", - "weight": 217, + "weight": 218, "cookies": false, "type": "", "deprecated": false, @@ -20081,7 +20340,7 @@ }, "x-appwrite": { "method": "get", - "weight": 219, + "weight": 220, "cookies": false, "type": "", "deprecated": false, @@ -20144,7 +20403,7 @@ }, "x-appwrite": { "method": "updateName", - "weight": 221, + "weight": 222, "cookies": false, "type": "", "deprecated": false, @@ -20220,7 +20479,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 223, + "weight": 224, "cookies": false, "type": "", "deprecated": false, @@ -20285,7 +20544,7 @@ }, "x-appwrite": { "method": "listMemberships", - "weight": 225, + "weight": 226, "cookies": false, "type": "", "deprecated": false, @@ -20369,7 +20628,7 @@ }, "x-appwrite": { "method": "createMembership", - "weight": 224, + "weight": 225, "cookies": false, "type": "", "deprecated": false, @@ -20485,7 +20744,7 @@ }, "x-appwrite": { "method": "getMembership", - "weight": 226, + "weight": 227, "cookies": false, "type": "", "deprecated": false, @@ -20556,7 +20815,7 @@ }, "x-appwrite": { "method": "updateMembership", - "weight": 227, + "weight": 228, "cookies": false, "type": "", "deprecated": false, @@ -20643,7 +20902,7 @@ }, "x-appwrite": { "method": "deleteMembership", - "weight": 229, + "weight": 230, "cookies": false, "type": "", "deprecated": false, @@ -20716,7 +20975,7 @@ }, "x-appwrite": { "method": "updateMembershipStatus", - "weight": 228, + "weight": 229, "cookies": false, "type": "", "deprecated": false, @@ -20812,7 +21071,7 @@ }, "x-appwrite": { "method": "getPrefs", - "weight": 220, + "weight": 221, "cookies": false, "type": "", "deprecated": false, @@ -20873,7 +21132,7 @@ }, "x-appwrite": { "method": "updatePrefs", - "weight": 222, + "weight": 223, "cookies": false, "type": "", "deprecated": false, @@ -20954,7 +21213,7 @@ }, "x-appwrite": { "method": "list", - "weight": 240, + "weight": 241, "cookies": false, "type": "", "deprecated": false, @@ -21026,7 +21285,7 @@ }, "x-appwrite": { "method": "create", - "weight": 231, + "weight": 232, "cookies": false, "type": "", "deprecated": false, @@ -21121,7 +21380,7 @@ }, "x-appwrite": { "method": "createArgon2User", - "weight": 234, + "weight": 235, "cookies": false, "type": "", "deprecated": false, @@ -21212,7 +21471,7 @@ }, "x-appwrite": { "method": "createBcryptUser", - "weight": 232, + "weight": 233, "cookies": false, "type": "", "deprecated": false, @@ -21303,7 +21562,7 @@ }, "x-appwrite": { "method": "listIdentities", - "weight": 248, + "weight": 249, "cookies": false, "type": "", "deprecated": false, @@ -21372,7 +21631,7 @@ }, "x-appwrite": { "method": "deleteIdentity", - "weight": 271, + "weight": 272, "cookies": false, "type": "", "deprecated": false, @@ -21433,7 +21692,7 @@ }, "x-appwrite": { "method": "createMD5User", - "weight": 233, + "weight": 234, "cookies": false, "type": "", "deprecated": false, @@ -21524,7 +21783,7 @@ }, "x-appwrite": { "method": "createPHPassUser", - "weight": 236, + "weight": 237, "cookies": false, "type": "", "deprecated": false, @@ -21615,7 +21874,7 @@ }, "x-appwrite": { "method": "createScryptUser", - "weight": 237, + "weight": 238, "cookies": false, "type": "", "deprecated": false, @@ -21741,7 +22000,7 @@ }, "x-appwrite": { "method": "createScryptModifiedUser", - "weight": 238, + "weight": 239, "cookies": false, "type": "", "deprecated": false, @@ -21853,7 +22112,7 @@ }, "x-appwrite": { "method": "createSHAUser", - "weight": 235, + "weight": 236, "cookies": false, "type": "", "deprecated": false, @@ -21965,7 +22224,7 @@ }, "x-appwrite": { "method": "get", - "weight": 241, + "weight": 242, "cookies": false, "type": "", "deprecated": false, @@ -22019,7 +22278,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 269, + "weight": 270, "cookies": false, "type": "", "deprecated": false, @@ -22080,7 +22339,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 254, + "weight": 255, "cookies": false, "type": "", "deprecated": false, @@ -22159,7 +22418,7 @@ }, "x-appwrite": { "method": "createJWT", - "weight": 272, + "weight": 273, "cookies": false, "type": "", "deprecated": false, @@ -22241,7 +22500,7 @@ }, "x-appwrite": { "method": "updateLabels", - "weight": 250, + "weight": 251, "cookies": false, "type": "", "deprecated": false, @@ -22323,7 +22582,7 @@ }, "x-appwrite": { "method": "listLogs", - "weight": 246, + "weight": 247, "cookies": false, "type": "", "deprecated": false, @@ -22396,7 +22655,7 @@ }, "x-appwrite": { "method": "listMemberships", - "weight": 245, + "weight": 246, "cookies": false, "type": "", "deprecated": false, @@ -22457,7 +22716,7 @@ }, "x-appwrite": { "method": "updateMfa", - "weight": 259, + "weight": 260, "cookies": false, "type": "", "deprecated": false, @@ -22531,7 +22790,7 @@ }, "x-appwrite": { "method": "deleteMfaAuthenticator", - "weight": 264, + "weight": 265, "cookies": false, "type": "", "deprecated": false, @@ -22605,7 +22864,7 @@ }, "x-appwrite": { "method": "listMfaFactors", - "weight": 260, + "weight": 261, "cookies": false, "type": "", "deprecated": false, @@ -22666,7 +22925,7 @@ }, "x-appwrite": { "method": "getMfaRecoveryCodes", - "weight": 261, + "weight": 262, "cookies": false, "type": "", "deprecated": false, @@ -22725,7 +22984,7 @@ }, "x-appwrite": { "method": "updateMfaRecoveryCodes", - "weight": 263, + "weight": 264, "cookies": false, "type": "", "deprecated": false, @@ -22784,7 +23043,7 @@ }, "x-appwrite": { "method": "createMfaRecoveryCodes", - "weight": 262, + "weight": 263, "cookies": false, "type": "", "deprecated": false, @@ -22845,7 +23104,7 @@ }, "x-appwrite": { "method": "updateName", - "weight": 252, + "weight": 253, "cookies": false, "type": "", "deprecated": false, @@ -22924,7 +23183,7 @@ }, "x-appwrite": { "method": "updatePassword", - "weight": 253, + "weight": 254, "cookies": false, "type": "", "deprecated": false, @@ -23003,7 +23262,7 @@ }, "x-appwrite": { "method": "updatePhone", - "weight": 255, + "weight": 256, "cookies": false, "type": "", "deprecated": false, @@ -23082,7 +23341,7 @@ }, "x-appwrite": { "method": "getPrefs", - "weight": 242, + "weight": 243, "cookies": false, "type": "", "deprecated": false, @@ -23141,7 +23400,7 @@ }, "x-appwrite": { "method": "updatePrefs", - "weight": 257, + "weight": 258, "cookies": false, "type": "", "deprecated": false, @@ -23220,7 +23479,7 @@ }, "x-appwrite": { "method": "listSessions", - "weight": 244, + "weight": 245, "cookies": false, "type": "", "deprecated": false, @@ -23279,7 +23538,7 @@ }, "x-appwrite": { "method": "createSession", - "weight": 265, + "weight": 266, "cookies": false, "type": "", "deprecated": false, @@ -23333,7 +23592,7 @@ }, "x-appwrite": { "method": "deleteSessions", - "weight": 268, + "weight": 269, "cookies": false, "type": "", "deprecated": false, @@ -23389,7 +23648,7 @@ }, "x-appwrite": { "method": "deleteSession", - "weight": 267, + "weight": 268, "cookies": false, "type": "", "deprecated": false, @@ -23458,7 +23717,7 @@ }, "x-appwrite": { "method": "updateStatus", - "weight": 249, + "weight": 250, "cookies": false, "type": "", "deprecated": false, @@ -23537,7 +23796,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 247, + "weight": 248, "cookies": false, "type": "", "deprecated": false, @@ -23609,7 +23868,7 @@ }, "x-appwrite": { "method": "createTarget", - "weight": 239, + "weight": 240, "cookies": false, "type": "", "deprecated": false, @@ -23722,7 +23981,7 @@ }, "x-appwrite": { "method": "getTarget", - "weight": 243, + "weight": 244, "cookies": false, "type": "", "deprecated": false, @@ -23790,7 +24049,7 @@ }, "x-appwrite": { "method": "updateTarget", - "weight": 258, + "weight": 259, "cookies": false, "type": "", "deprecated": false, @@ -23880,7 +24139,7 @@ }, "x-appwrite": { "method": "deleteTarget", - "weight": 270, + "weight": 271, "cookies": false, "type": "", "deprecated": false, @@ -23950,7 +24209,7 @@ }, "x-appwrite": { "method": "createToken", - "weight": 266, + "weight": 267, "cookies": false, "type": "", "deprecated": false, @@ -24032,7 +24291,7 @@ }, "x-appwrite": { "method": "updateEmailVerification", - "weight": 256, + "weight": 257, "cookies": false, "type": "", "deprecated": false, @@ -24111,7 +24370,7 @@ }, "x-appwrite": { "method": "updatePhoneVerification", - "weight": 251, + "weight": 252, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/variables.php b/app/config/variables.php index 57810525c7..98dd9ffec1 100644 --- a/app/config/variables.php +++ b/app/config/variables.php @@ -572,7 +572,7 @@ return [ ], [ 'name' => '_APP_STORAGE_S3_ACCESS_KEY', - 'description' => 'AWS S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your AWS console', + 'description' => 'S3 storage access key. Required when the storage adapter is set to S3. You can get your access key from your S3 storage provider', 'introduction' => '0.13.0', 'default' => '', 'required' => false, @@ -580,7 +580,7 @@ return [ ], [ 'name' => '_APP_STORAGE_S3_SECRET', - 'description' => 'AWS S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your AWS console.', + 'description' => 'S3 storage secret key. Required when the storage adapter is set to S3. You can get your secret key from your S3 storage provider.', 'introduction' => '0.13.0', 'default' => '', 'required' => false, @@ -588,7 +588,7 @@ return [ ], [ 'name' => '_APP_STORAGE_S3_REGION', - 'description' => 'AWS S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from AWS console.', + 'description' => 'S3 storage region. Required when storage adapter is set to S3. You can find your region info for your bucket from your S3 storage provider.', 'introduction' => '0.13.0', 'default' => 'us-east-1', 'required' => false, @@ -596,12 +596,20 @@ return [ ], [ 'name' => '_APP_STORAGE_S3_BUCKET', - 'description' => 'AWS S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your AWS console.', + 'description' => 'S3 storage bucket. Required when storage adapter is set to S3. You can create buckets in your S3 storage provider.', 'introduction' => '0.13.0', 'default' => '', 'required' => false, 'question' => '', ], + [ + 'name' => '_APP_STORAGE_S3_ENDPOINT', + 'description' => 'S3 storage endpoint. Required when using S3 storage providers other than AWS.', + 'introduction' => '0.16.2', + 'default' => '', + 'required' => false, + 'question' => '', + ], [ 'name' => '_APP_STORAGE_DO_SPACES_ACCESS_KEY', 'description' => 'DigitalOcean spaces access key. Required when the storage adapter is set to DOSpaces. You can get your access key from your DigitalOcean console.', diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php index bd9562110f..21a04800b5 100644 --- a/app/controllers/api/account.php +++ b/app/controllers/api/account.php @@ -17,7 +17,7 @@ use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Event\Mail; use Appwrite\Event\Messaging; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\Hooks\Hooks; use Appwrite\Network\Validator\Email; @@ -2432,9 +2432,9 @@ App::post('/v1/account/tokens/phone') ->inject('queueForMessaging') ->inject('locale') ->inject('timelimit') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, Usage $queueForUsage, array $plan) { + ->action(function (string $userId, string $phone, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -2583,11 +2583,11 @@ App::post('/v1/account/tokens/phone') $countryCode = $helper->parse($phone)->getCountryCode(); if (!empty($countryCode)) { - $queueForUsage + $queueForStatsUsage ->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1); } } - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) ->setProject($project) ->trigger(); @@ -3678,9 +3678,9 @@ App::post('/v1/account/verification/phone') ->inject('project') ->inject('locale') ->inject('timelimit') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, Usage $queueForUsage, array $plan) { + ->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) { if (empty(System::getEnv('_APP_SMS_PROVIDER'))) { throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured'); } @@ -3775,11 +3775,11 @@ App::post('/v1/account/verification/phone') $countryCode = $helper->parse($phone)->getCountryCode(); if (!empty($countryCode)) { - $queueForUsage + $queueForStatsUsage ->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1); } } - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) ->setProject($project) ->trigger(); @@ -4310,9 +4310,9 @@ App::post('/v1/account/mfa/challenge') ->inject('queueForMessaging') ->inject('queueForMails') ->inject('timelimit') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, callable $timelimit, Usage $queueForUsage, array $plan) { + ->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) { $expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM); $code = Auth::codeGenerator(); @@ -4383,11 +4383,11 @@ App::post('/v1/account/mfa/challenge') $countryCode = $helper->parse($phone)->getCountryCode(); if (!empty($countryCode)) { - $queueForUsage + $queueForStatsUsage ->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1); } } - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) ->setProject($project) ->trigger(); diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index df46c1890b..c563d1e8e0 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -4,7 +4,7 @@ use Appwrite\Auth\Auth; use Appwrite\Detector\Detector; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Event; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\Network\Validator\Email; use Appwrite\SDK\AuthType; @@ -27,6 +27,7 @@ use Utopia\Database\Document; use Utopia\Database\Exception\Authorization as AuthorizationException; use Utopia\Database\Exception\Conflict as ConflictException; use Utopia\Database\Exception\Duplicate as DuplicateException; +use Utopia\Database\Exception\Index as IndexException; use Utopia\Database\Exception\Limit as LimitException; use Utopia\Database\Exception\NotFound as NotFoundException; use Utopia\Database\Exception\Query as QueryException; @@ -393,6 +394,8 @@ function updateAttribute( throw new Exception(Exception::ATTRIBUTE_NOT_FOUND); } catch (LimitException) { throw new Exception(Exception::ATTRIBUTE_LIMIT_EXCEEDED); + } catch (IndexException $e) { + throw new Exception(Exception::INDEX_INVALID, $e->getMessage()); } } @@ -476,8 +479,8 @@ App::post('/v1/databases') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('queueForUsage') - ->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage) { + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId; @@ -527,7 +530,7 @@ App::post('/v1/databases') } $queueForEvents->setParam('databaseId', $database->getId()); - $queueForUsage->addMetric(str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE), 1); // per database + $queueForStatsUsage->addMetric(str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE), 1); // per database $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -797,8 +800,8 @@ App::delete('/v1/databases/:databaseId') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('queueForUsage') - ->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) { + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -821,7 +824,7 @@ App::delete('/v1/databases/:databaseId') ->setParam('databaseId', $database->getId()) ->setPayload($response->output($database, Response::MODEL_DATABASE)); - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_DATABASES_STORAGE, 1); // Global, deletion forces full recalculation $response->noContent(); @@ -2618,8 +2621,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->inject('queueForUsage') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) { + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2716,7 +2719,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->setContext('database', $db) ->setPayload($response->output($attribute, $model)); - $queueForUsage + $queueForStatsUsage ->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$db->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection $response->noContent(); @@ -2822,7 +2825,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/indexes') $attributeIndex = \array_search($attribute, array_column($oldAttributes, 'key')); if ($attributeIndex === false) { - throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown attribute: ' . $attribute); + throw new Exception(Exception::ATTRIBUTE_UNKNOWN, 'Unknown attribute: ' . $attribute . '. Verify the attribute name or create the attribute.'); } $attributeStatus = $oldAttributes[$attributeIndex]['status']; @@ -3134,9 +3137,8 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('dbForProject') ->inject('user') ->inject('queueForEvents') - ->inject('queueForUsage') - ->inject('mode') - ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode) { + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -3339,7 +3341,7 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') $processDocument($collection, $document); - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, $operations) ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations) ->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection @@ -3391,9 +3393,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), '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.', true) ->inject('response') ->inject('dbForProject') - ->inject('mode') - ->inject('queueForUsage') - ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, string $mode, Usage $queueForUsage) { + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, string $collectionId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -3505,10 +3506,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents') $processDocument($collection, $document); } - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, $operations) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations) - ; + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); $response->addHeader('X-Debug-Operations', $operations); @@ -3570,9 +3570,8 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen ->param('queries', [], new ArrayList(new Text(APP_LIMIT_ARRAY_ELEMENT_SIZE), APP_LIMIT_ARRAY_PARAMS_SIZE), '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.', true) ->inject('response') ->inject('dbForProject') - ->inject('mode') - ->inject('queueForUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, string $mode, Usage $queueForUsage) { + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, string $collectionId, string $documentId, array $queries, Response $response, Database $dbForProject, StatsUsage $queueForStatsUsage) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -3648,10 +3647,9 @@ App::get('/v1/databases/:databaseId/collections/:collectionId/documents/:documen $processDocument($collection, $document); - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_READS, $operations) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations) - ; + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_READS), $operations); $response->addHeader('X-Debug-Operations', $operations); @@ -3804,9 +3802,8 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('mode') - ->inject('queueForUsage') - ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, string $mode, Usage $queueForUsage) { + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -3946,10 +3943,9 @@ App::patch('/v1/databases/:databaseId/collections/:collectionId/documents/:docum $setCollection($collection, $newDocument); - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, $operations) - ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations) - ; + ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), $operations); $response->addHeader('X-Debug-Operations', $operations); @@ -4058,9 +4054,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->inject('queueForUsage') - ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage, string $mode) { + ->inject('queueForStatsUsage') + ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); @@ -4129,7 +4124,7 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu $processDocument($collection, $document); - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_DATABASES_OPERATIONS_WRITES, 1) ->addMetric(str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_OPERATIONS_WRITES), 1) ->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection diff --git a/app/controllers/api/functions.php b/app/controllers/api/functions.php index f6c9d472cf..c2909eabba 100644 --- a/app/controllers/api/functions.php +++ b/app/controllers/api/functions.php @@ -6,7 +6,7 @@ use Appwrite\Event\Build; use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Event\Func; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Functions\Validator\Headers; @@ -962,10 +962,10 @@ App::post('/v1/functions/:functionId/executions') ->inject('dbForPlatform') ->inject('user') ->inject('queueForEvents') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('queueForFunctions') ->inject('geodb') - ->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForPlatform, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) { + ->action(function (string $functionId, string $body, mixed $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForPlatform, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb) { $async = \strval($async) === 'true' || \strval($async) === '1'; if (!$async && !is_null($scheduledAt)) { @@ -1219,8 +1219,8 @@ App::post('/v1/functions/:functionId/executions') 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', - 'APPWRITE_COMPUTE_CPUS' => $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT, - 'APPWRITE_COMPUTE_MEMORY' => $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT, + 'APPWRITE_FUNCTION_CPUS' => $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT, + 'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT, 'APPWRITE_VERSION' => APP_VERSION_STABLE, 'APPWRITE_REGION' => $project->getAttribute('region'), 'APPWRITE_DEPLOYMENT_TYPE' => $deployment->getAttribute('type', ''), @@ -1293,7 +1293,7 @@ App::post('/v1/functions/:functionId/executions') throw $th; } } finally { - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_EXECUTIONS, 1) ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) ->addMetric(METRIC_EXECUTIONS_COMPUTE, (int)($execution->getAttribute('duration') * 1000)) // per project diff --git a/app/controllers/api/health.php b/app/controllers/api/health.php index 305444abc5..e5336067c8 100644 --- a/app/controllers/api/health.php +++ b/app/controllers/api/health.php @@ -692,15 +692,15 @@ App::get('/v1/health/queue/functions') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }, ['response']); -App::get('/v1/health/queue/usage') - ->desc('Get usage queue') +App::get('/v1/health/queue/stats-resources') + ->desc('Get stats resources queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( auth: [AuthType::KEY], namespace: 'health', - name: 'getQueueUsage', - description: '/docs/references/health/get-queue-usage.md', + name: 'getQueueStatsResources', + description: '/docs/references/health/get-queue-stats-resources.md', responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -715,7 +715,7 @@ App::get('/v1/health/queue/usage') ->action(function (int|string $threshold, Publisher $publisher, Response $response) { $threshold = \intval($threshold); - $size = $publisher->getQueueSize(new Queue(Event::USAGE_QUEUE_NAME)); + $size = $publisher->getQueueSize(new Queue(Event::STATS_RESOURCES_QUEUE_NAME)); if ($size >= $threshold) { throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); @@ -724,15 +724,15 @@ App::get('/v1/health/queue/usage') $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); }); -App::get('/v1/health/queue/usage-dump') - ->desc('Get usage dump queue') +App::get('/v1/health/queue/stats-usage') + ->desc('Get stats usage queue') ->groups(['api', 'health']) ->label('scope', 'health.read') ->label('sdk', new Method( auth: [AuthType::KEY], namespace: 'health', - name: 'getQueueUsageDump', - description: '/docs/references/health/get-queue-usage-dump.md', + name: 'getQueueUsage', + description: '/docs/references/health/get-queue-stats-usage.md', responses: [ new SDKResponse( code: Response::STATUS_CODE_OK, @@ -747,7 +747,39 @@ App::get('/v1/health/queue/usage-dump') ->action(function (int|string $threshold, Publisher $publisher, Response $response) { $threshold = \intval($threshold); - $size = $publisher->getQueueSize(new Queue(Event::USAGE_DUMP_QUEUE_NAME)); + $size = $publisher->getQueueSize(new Queue(Event::STATS_USAGE_QUEUE_NAME)); + + if ($size >= $threshold) { + throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); + } + + $response->dynamic(new Document([ 'size' => $size ]), Response::MODEL_HEALTH_QUEUE); + }); + +App::get('/v1/health/queue/stats-usage-dump') + ->desc('Get usage dump queue') + ->groups(['api', 'health']) + ->label('scope', 'health.read') + ->label('sdk', new Method( + auth: [AuthType::KEY], + namespace: 'health', + name: 'getQueueStatsUsageDump', + description: '/docs/references/health/get-queue-stats-usage-dump.md', + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_HEALTH_QUEUE, + ) + ], + 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('publisher') + ->inject('response') + ->action(function (int|string $threshold, Publisher $publisher, Response $response) { + $threshold = \intval($threshold); + + $size = $publisher->getQueueSize(new Queue(Event::STATS_USAGE_DUMP_QUEUE_NAME)); if ($size >= $threshold) { throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}."); @@ -920,8 +952,9 @@ App::get('/v1/health/queue/failed/:name') Event::AUDITS_QUEUE_NAME, Event::MAILS_QUEUE_NAME, Event::FUNCTIONS_QUEUE_NAME, - Event::USAGE_QUEUE_NAME, - Event::USAGE_DUMP_QUEUE_NAME, + Event::STATS_RESOURCES_QUEUE_NAME, + Event::STATS_USAGE_QUEUE_NAME, + Event::STATS_USAGE_DUMP_QUEUE_NAME, Event::WEBHOOK_QUEUE_NAME, Event::CERTIFICATES_QUEUE_NAME, Event::BUILDS_QUEUE_NAME, diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index ac149ac8eb..75afc7ed2c 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -48,9 +48,9 @@ App::post('/v1/migrations/appwrite') ] )) ->param('resources', [], new ArrayList(new WhiteList(Appwrite::getSupportedResources())), 'List of resources to migrate') - ->param('endpoint', '', new URL(), "Source's Appwrite Endpoint") - ->param('projectId', '', new UID(), "Source's Project ID") - ->param('apiKey', '', new Text(512), "Source's API Key") + ->param('endpoint', '', new URL(), 'Source Appwrite endpoint') + ->param('projectId', '', new UID(), 'Source Project ID') + ->param('apiKey', '', new Text(512), 'Source API Key') ->inject('response') ->inject('dbForProject') ->inject('project') diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php index b5ddc94c9d..f180c22acf 100644 --- a/app/controllers/api/storage.php +++ b/app/controllers/api/storage.php @@ -6,7 +6,7 @@ use Appwrite\Auth\Auth; use Appwrite\ClamAV\Network; use Appwrite\Event\Delete; use Appwrite\Event\Event; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\OpenSSL\OpenSSL; use Appwrite\SDK\AuthType; @@ -942,8 +942,8 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') ->inject('mode') ->inject('deviceForFiles') ->inject('deviceForLocal') - ->inject('queueForUsage') - ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, string $mode, Device $deviceForFiles, Device $deviceForLocal, Usage $queueForUsage) { + ->inject('queueForStatsUsage') + ->action(function (string $bucketId, string $fileId, int $width, int $height, string $gravity, int $quality, int $borderWidth, string $borderColor, int $borderRadius, float $opacity, int $rotation, string $background, string $output, Request $request, Response $response, Document $project, Database $dbForProject, string $mode, Device $deviceForFiles, Device $deviceForLocal, StatsUsage $queueForStatsUsage) { if (!\extension_loaded('imagick')) { throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing'); @@ -1071,7 +1071,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview') $contentType = (\array_key_exists($output, $outputs)) ? $outputs[$output] : $outputs['jpg']; - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_FILES_TRANSFORMATIONS, 1) ->addMetric(str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_TRANSFORMATIONS), 1) ; diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index 7f988d726f..06e653c105 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -8,7 +8,7 @@ use Appwrite\Event\Delete; use Appwrite\Event\Event; use Appwrite\Event\Mail; use Appwrite\Event\Messaging; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\Network\Validator\Email; use Appwrite\Platform\Workers\Deletes; @@ -466,9 +466,9 @@ App::post('/v1/teams/:teamId/memberships') ->inject('queueForMessaging') ->inject('queueForEvents') ->inject('timelimit') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('plan') - ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, Usage $queueForUsage, array $plan) { + ->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) { $isAppUser = Auth::isAppUser(Authorization::getRoles()); $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); @@ -588,9 +588,8 @@ App::post('/v1/teams/:teamId/memberships') Query::equal('teamInternalId', [$team->getInternalId()]), ]); + $secret = Auth::tokenGenerator(); if ($membership->isEmpty()) { - $secret = Auth::tokenGenerator(); - $membershipId = ID::unique(); $membership = new Document([ '$id' => $membershipId, @@ -618,7 +617,8 @@ App::post('/v1/teams/:teamId/memberships') $dbForProject->createDocument('memberships', $membership); Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1)); - } else { + } elseif ($membership->getAttribute('confirm') === false) { + $membership->setAttribute('secret', Auth::hash($secret)); $membership->setAttribute('invited', DateTime::now()); if ($isPrivilegedUser || $isAppUser) { @@ -629,9 +629,10 @@ App::post('/v1/teams/:teamId/memberships') $membership = ($isPrivilegedUser || $isAppUser) ? Authorization::skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) : $dbForProject->updateDocument('memberships', $membership->getId(), $membership); + } else { + throw new Exception(Exception::MEMBERSHIP_ALREADY_CONFIRMED); } - if ($isPrivilegedUser || $isAppUser) { $dbForProject->purgeCachedDocument('users', $invitee->getId()); } else { @@ -757,11 +758,11 @@ App::post('/v1/teams/:teamId/memberships') $countryCode = $helper->parse($phone)->getCountryCode(); if (!empty($countryCode)) { - $queueForUsage + $queueForStatsUsage ->addMetric(str_replace('{countryCode}', $countryCode, METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE), 1); } } - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_AUTH_METHOD_PHONE, 1) ->setProject($project) ->trigger(); diff --git a/app/controllers/general.php b/app/controllers/general.php index a1a5dac3d1..87af29c02f 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -7,7 +7,7 @@ use Appwrite\Auth\Auth; use Appwrite\Event\Certificate; use Appwrite\Event\Event; use Appwrite\Event\Func; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception as AppwriteException; use Appwrite\Network\Validator\Origin; use Appwrite\Platform\Appwrite; @@ -54,7 +54,7 @@ Config::setParam('domainVerification', false); Config::setParam('cookieDomain', 'localhost'); Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE); -function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) +function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) { $utopia->getRoute()?->label('error', __DIR__ . '/../views/general/error.phtml'); @@ -368,8 +368,8 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', - 'APPWRITE_COMPUTE_CPUS' => $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT, - 'APPWRITE_COMPUTE_MEMORY' => $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT, + 'APPWRITE_FUNCTION_CPUS' => $spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT, + 'APPWRITE_FUNCTION_MEMORY' => $spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT, 'APPWRITE_VERSION' => APP_VERSION_STABLE, 'APPWRITE_REGION' => $project->getAttribute('region'), 'APPWRITE_DEPLOYMENT_TYPE' => $deployment->getAttribute('type', ''), @@ -547,7 +547,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; } - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_NETWORK_REQUESTS, 1) ->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize) ->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize()) @@ -611,13 +611,13 @@ App::init() ->inject('localeCodes') ->inject('clients') ->inject('geodb') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('queueForEvents') ->inject('queueForCertificates') ->inject('queueForFunctions') ->inject('isResourceBlocked') ->inject('previewHostname') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, Usage $queueForUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked, string $previewHostname) { + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Document $console, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, array $clients, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Certificate $queueForCertificates, Func $queueForFunctions, callable $isResourceBlocked, string $previewHostname) { /* * Appwrite Router */ @@ -625,9 +625,8 @@ App::init() $mainDomain = System::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain if ($host !== $mainDomain || !empty($previewHostname)) { - if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) { + if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) { $utopia->getRoute()?->label('router', true); - return; } } @@ -861,12 +860,12 @@ App::options() ->inject('dbForPlatform') ->inject('getProjectDB') ->inject('queueForEvents') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('queueForFunctions') ->inject('geodb') ->inject('isResourceBlocked') ->inject('previewHostname') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) { + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) { /* * Appwrite Router */ @@ -874,9 +873,8 @@ App::options() $mainDomain = System::getEnv('_APP_DOMAIN', ''); // Only run Router when external domain if ($host !== $mainDomain || !empty($previewHostname)) { - if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) { + if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) { $utopia->getRoute()?->label('router', true); - return; } } @@ -900,8 +898,8 @@ App::error() ->inject('project') ->inject('logger') ->inject('log') - ->inject('queueForUsage') - ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, Usage $queueForUsage) { + ->inject('queueForStatsUsage') + ->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage) { $version = System::getEnv('_APP_VERSION', 'UNKNOWN'); $route = $utopia->getRoute(); $class = \get_class($error); @@ -1012,18 +1010,17 @@ App::error() $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; } - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_NETWORK_REQUESTS, 1) ->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize) ->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize()); } - $queueForUsage + $queueForStatsUsage ->setProject($project) ->trigger(); } - if ($logger && $publish) { try { /** @var Utopia\Database\Document $user */ @@ -1171,12 +1168,12 @@ App::get('/robots.txt') ->inject('dbForPlatform') ->inject('getProjectDB') ->inject('queueForEvents') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('queueForFunctions') ->inject('geodb') ->inject('isResourceBlocked') ->inject('previewHostname') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) { + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) { $host = $request->getHostname() ?? ''; $mainDomain = System::getEnv('_APP_DOMAIN', ''); @@ -1184,7 +1181,7 @@ App::get('/robots.txt') $template = new View(__DIR__ . '/../views/general/robots.phtml'); $response->text($template->render(false)); } else { - if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) { + if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) { $utopia->getRoute()?->label('router', true); } } @@ -1201,12 +1198,12 @@ App::get('/humans.txt') ->inject('dbForPlatform') ->inject('getProjectDB') ->inject('queueForEvents') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('queueForFunctions') ->inject('geodb') ->inject('isResourceBlocked') ->inject('previewHostname') - ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) { + ->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Reader $geodb, callable $isResourceBlocked, string $previewHostname) { $host = $request->getHostname() ?? ''; $mainDomain = System::getEnv('_APP_DOMAIN', ''); @@ -1214,7 +1211,7 @@ App::get('/humans.txt') $template = new View(__DIR__ . '/../views/general/humans.phtml'); $response->text($template->render(false)); } else { - if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) { + if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $geodb, $isResourceBlocked, $previewHostname)) { $utopia->getRoute()?->label('router', true); } } @@ -1283,7 +1280,7 @@ App::get('/v1/ping') ->inject('dbForPlatform') ->inject('queueForEvents') ->action(function (Response $response, Document $project, Database $dbForPlatform, Event $queueForEvents) { - if ($project->isEmpty()) { + if ($project->isEmpty() || $project->getId() === 'console') { throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND); } diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 58a77f243a..f8371ed8e6 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -1,8 +1,7 @@ getCollection() === 'teams': - $queueForUsage - ->addMetric(METRIC_TEAMS, $value); // per project + $queueForStatsUsage->addMetric(METRIC_TEAMS, $value); // per project break; case $document->getCollection() === 'users': - $queueForUsage - ->addMetric(METRIC_USERS, $value); // per project + $queueForStatsUsage->addMetric(METRIC_USERS, $value); // per project if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage - ->addReduce($document); + $queueForStatsUsage->addReduce($document); } break; case $document->getCollection() === 'sessions': // sessions - $queueForUsage - ->addMetric(METRIC_SESSIONS, $value); //per project + $queueForStatsUsage->addMetric(METRIC_SESSIONS, $value); //per project break; case $document->getCollection() === 'databases': // databases - $queueForUsage - ->addMetric(METRIC_DATABASES, $value); // per project + $queueForStatsUsage->addMetric(METRIC_DATABASES, $value); // per project if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage - ->addReduce($document); + $queueForStatsUsage->addReduce($document); } break; case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections $parts = explode('_', $document->getCollection()); $databaseInternalId = $parts[1] ?? 0; - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_COLLECTIONS, $value) // per project - ->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value) - ; + ->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value); if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage - ->addReduce($document); + $queueForStatsUsage->addReduce($document); } break; case str_starts_with($document->getCollection(), 'database_') && str_contains($document->getCollection(), '_collection_'): //documents $parts = explode('_', $document->getCollection()); $databaseInternalId = $parts[1] ?? 0; $collectionInternalId = $parts[3] ?? 0; - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_DOCUMENTS, $value) // per project ->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS), $value) // per database ->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $collectionInternalId], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS), $value); // per collection break; case $document->getCollection() === 'buckets': //buckets - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_BUCKETS, $value); // per project if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage + $queueForStatsUsage ->addReduce($document); } break; case str_starts_with($document->getCollection(), 'bucket_'): // files $parts = explode('_', $document->getCollection()); $bucketInternalId = $parts[1]; - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_FILES, $value) // per project ->addMetric(METRIC_FILES_STORAGE, $document->getAttribute('sizeOriginal') * $value) // per project ->addMetric(str_replace('{bucketInternalId}', $bucketInternalId, METRIC_BUCKET_ID_FILES), $value) // per bucket ->addMetric(str_replace('{bucketInternalId}', $bucketInternalId, METRIC_BUCKET_ID_FILES_STORAGE), $document->getAttribute('sizeOriginal') * $value); // per bucket break; case $document->getCollection() === 'functions': - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_FUNCTIONS, $value); // per project if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage + $queueForStatsUsage ->addReduce($document); } break; case $document->getCollection() === 'sites': - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_SITES, $value); // per project if ($event === Database::EVENT_DOCUMENT_DELETE) { - $queueForUsage + $queueForStatsUsage ->addReduce($document); } break; case $document->getCollection() === 'deployments': - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_DEPLOYMENTS, $value) // per project ->addMetric(METRIC_DEPLOYMENTS_STORAGE, $document->getAttribute('size') * $value) // per project ->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [$document->getAttribute('resourceType'), $document->getAttribute('resourceInternalId')], METRIC_FUNCTION_ID_DEPLOYMENTS), $value) // per function @@ -204,117 +196,79 @@ App::init() ->inject('servers') ->inject('mode') ->inject('team') - ->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team) { + ->inject('apiKey') + ->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey) { $route = $utopia->getRoute(); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - /** Default role */ $roles = Config::getParam('roles', []); - $role = ($user->isEmpty()) + + $role = $user->isEmpty() ? Role::guests()->toString() : Role::users()->toString(); - /** Allowed Scopes for the role */ $scopes = $roles[$role]['scopes']; - $apiKey = $request->getHeader('x-appwrite-key', ''); - // API Key authentication if (!empty($apiKey)) { - // Do not allow API key and session to be set at the same time if (!$user->isEmpty()) { throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET); } - - // Remove after migration - if (!\str_contains($apiKey, '_')) { - $keyType = API_KEY_STANDARD; - $authKey = $apiKey; - } else { - [ $keyType, $authKey ] = \explode('_', $apiKey, 2); + if ($apiKey->isExpired()) { + throw new Exception(Exception::PROJECT_KEY_EXPIRED); } - if ($keyType === API_KEY_DYNAMIC) { - // Dynamic key + $role = $apiKey->getRole(); + $scopes = $apiKey->getScopes(); - $jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400, 0); + // Disable authorization checks for API keys + Authorization::setDefaultStatus(false); - try { - $payload = $jwtObj->decode($authKey); - } catch (JWTException $error) { - throw new Exception(Exception::API_KEY_EXPIRED); - } + if ($apiKey->getRole() === Auth::USER_ROLE_APPS) { + $user = new Document([ + '$id' => '', + 'status' => true, + 'type' => Auth::ACTIVITY_TYPE_APP, + 'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(), + 'password' => '', + 'name' => $apiKey->getName(), + ]); - $projectId = $payload['projectId'] ?? ''; - $tokenScopes = $payload['scopes'] ?? []; + $queueForAudits->setUser($user); + } - // JWT includes project ID for better security - if ($projectId === $project->getId()) { - $user = new Document([ - '$id' => '', - 'status' => true, - 'type' => Auth::ACTIVITY_TYPE_APP, - 'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(), - 'password' => '', - 'name' => 'Dynamic Key', - ]); + if ($apiKey->getType() === API_KEY_STANDARD) { + $dbKey = $project->find( + key: 'secret', + find: $request->getHeader('x-appwrite-key', ''), + subject: 'keys' + ); - $role = Auth::USER_ROLE_APPS; - $scopes = \array_merge($roles[$role]['scopes'], $tokenScopes); + if ($dbKey) { + $accessedAt = $dbKey->getAttribute('accessedAt', ''); - Authorization::setRole(Auth::USER_ROLE_APPS); - Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. - - $queueForAudits->setUser($user); - } - } elseif ($keyType === API_KEY_STANDARD) { - // No underline means no prefix. Backwards compatibility. - // Regular key - - // Check if given key match project API keys - $key = $project->find('secret', $apiKey, 'keys'); - if ($key) { - $user = new Document([ - '$id' => '', - 'status' => true, - 'type' => Auth::ACTIVITY_TYPE_APP, - 'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(), - 'password' => '', - 'name' => $key->getAttribute('name', 'UNKNOWN'), - ]); - - $role = Auth::USER_ROLE_APPS; - $scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', [])); - - $expire = $key->getAttribute('expire'); - if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) { - throw new Exception(Exception::PROJECT_KEY_EXPIRED); - } - - Authorization::setRole(Auth::USER_ROLE_APPS); - Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys. - - $accessedAt = $key->getAttribute('accessedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) { - $key->setAttribute('accessedAt', DateTime::now()); - $dbForPlatform->updateDocument('keys', $key->getId(), $key); + $dbKey->setAttribute('accessedAt', DateTime::now()); + $dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey); $dbForPlatform->purgeCachedDocument('projects', $project->getId()); } $sdkValidator = new WhiteList($servers, true); $sdk = $request->getHeader('x-sdk-name', 'UNKNOWN'); + if ($sdkValidator->isValid($sdk)) { - $sdks = $key->getAttribute('sdks', []); + $sdks = $dbKey->getAttribute('sdks', []); + if (!in_array($sdk, $sdks)) { - array_push($sdks, $sdk); - $key->setAttribute('sdks', $sdks); + $sdks[] = $sdk; + $dbKey->setAttribute('sdks', $sdks); /** Update access time as well */ - $key->setAttribute('accessedAt', Datetime::now()); - $dbForPlatform->updateDocument('keys', $key->getId(), $key); + $dbKey->setAttribute('accessedAt', Datetime::now()); + $dbForPlatform->updateDocument('keys', $dbKey->getId(), $dbKey); $dbForPlatform->purgeCachedDocument('projects', $project->getId()); } } @@ -322,8 +276,7 @@ App::init() $queueForAudits->setUser($user); } } - } - // Admin User Authentication + } // Admin User Authentication elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) { $teamId = $team->getId(); $adminRoles = []; @@ -339,7 +292,7 @@ App::init() throw new Exception(Exception::USER_UNAUTHORIZED); } - $scopes = []; // reset scope if admin + $scopes = []; // Reset scope if admin foreach ($adminRoles as $role) { $scopes = \array_merge($scopes, $roles[$role]['scopes']); } @@ -354,9 +307,7 @@ App::init() Authorization::setRole($authRole); } - /** - * Update project last activity - */ + // Update project last activity if (!$project->isEmpty() && $project->getId() !== 'console') { $accessedAt = $project->getAttribute('accessedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) { @@ -365,9 +316,7 @@ App::init() } } - /** - * Update user last activity - */ + // Update user last activity if (!empty($user->getId())) { $accessedAt = $user->getAttribute('accessedAt', ''); if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCESS)) > $accessedAt) { @@ -381,18 +330,18 @@ App::init() } } - /** Do not allow access to disabled services */ /** - * @var ?\Appwrite\SDK\Method $method + * @var ?Method $method */ $method = $route->getLabel('sdk', false); - if (is_array($method)) { + if (\is_array($method)) { $method = $method[0]; } if (!empty($method)) { $namespace = $method->getNamespace(); + if ( array_key_exists($namespace, $project->getAttribute('services', [])) && !$project->getAttribute('services', [])[$namespace] @@ -402,13 +351,13 @@ App::init() } } - /** Do now allow access if scope is not allowed */ + // Do now allow access if scope is not allowed $scope = $route->getLabel('scope', 'none'); if (!\in_array($scope, $scopes)) { throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')'); } - /** Do not allow access to blocked accounts */ + // Do not allow access to blocked accounts if (false === $user->getAttribute('status')) { // Account is blocked throw new Exception(Exception::USER_BLOCKED); } @@ -445,11 +394,12 @@ App::init() ->inject('queueForDeletes') ->inject('queueForDatabase') ->inject('queueForBuilds') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('dbForProject') ->inject('timelimit') ->inject('mode') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Usage $queueForUsage, Database $dbForProject, callable $timelimit, string $mode) use ($usageDatabaseListener, $eventDatabaseListener) { + ->inject('apiKey') + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, string $mode, ?Key $apiKey) use ($usageDatabaseListener, $eventDatabaseListener) { $route = $utopia->getRoute(); @@ -480,7 +430,7 @@ App::init() ->setParam('{ip}', $request->getIP()) ->setParam('{url}', $request->getHostname() . $route->getPath()) ->setParam('{method}', $request->getMethod()) - ->setParam('{chunkId}', (int) ($start / ($end + 1 - $start))); + ->setParam('{chunkId}', (int)($start / ($end + 1 - $start))); $timeLimitArray[] = $timeLimit; } @@ -546,6 +496,12 @@ App::init() $queueForAudits->setUser($userClone); } + if (!empty($apiKey) && !empty($apiKey->getDisabledMetrics())) { + foreach ($apiKey->getDisabledMetrics() as $key) { + $queueForStatsUsage->disableMetric($key); + } + } + $queueForDeletes->setProject($project); $queueForDatabase->setProject($project); $queueForBuilds->setProject($project); @@ -559,8 +515,8 @@ App::init() $queueForRealtime = new Realtime(); $dbForProject - ->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForUsage)) - ->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForUsage)) + ->on(Database::EVENT_DOCUMENT_CREATE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage)) + ->on(Database::EVENT_DOCUMENT_DELETE, 'calculate-usage', fn ($event, $document) => $usageDatabaseListener($event, $document, $queueForStatsUsage)) ->on(Database::EVENT_DOCUMENT_CREATE, 'create-trigger-events', fn ($event, $document) => $eventDatabaseListener( $project, $document, @@ -589,10 +545,7 @@ App::init() $bucketId = $parts[1] ?? null; $bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId)); - $isAPIKey = Auth::isAppUser(Authorization::getRoles()); - $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles()); - - if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAPIKey && !$isPrivilegedUser)) { + if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) { throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND); } @@ -634,8 +587,7 @@ App::init() ->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate') ->addHeader('Pragma', 'no-cache') ->addHeader('Expires', '0') - ->addHeader('X-Appwrite-Cache', 'miss') - ; + ->addHeader('X-Appwrite-Cache', 'miss'); } } }); @@ -703,7 +655,7 @@ App::shutdown() ->inject('user') ->inject('queueForEvents') ->inject('queueForAudits') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('queueForDeletes') ->inject('queueForDatabase') ->inject('queueForBuilds') @@ -712,7 +664,7 @@ App::shutdown() ->inject('queueForWebhooks') ->inject('queueForRealtime') ->inject('dbForProject') - ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, Usage $queueForUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject) use ($parseLabel) { + ->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject) use ($parseLabel) { $responsePayload = $response->getPayload(); @@ -796,6 +748,7 @@ App::shutdown() foreach ($queueForEvents->getParams() as $key => $value) { $queueForAudits->setParam($key, $value); } + $queueForAudits->trigger(); } @@ -815,9 +768,7 @@ App::shutdown() $queueForMessaging->trigger(); } - /** - * Cache label - */ + // Cache label $useCache = $route->getLabel('cache', false); if ($useCache) { $resource = $resourceType = null; @@ -869,13 +820,13 @@ App::shutdown() $fileSize = (\is_array($file['size']) && isset($file['size'][0])) ? $file['size'][0] : $file['size']; } - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_NETWORK_REQUESTS, 1) ->addMetric(METRIC_NETWORK_INBOUND, $request->getSize() + $fileSize) ->addMetric(METRIC_NETWORK_OUTBOUND, $response->getSize()); } - $queueForUsage + $queueForStatsUsage ->setProject($project) ->trigger(); } diff --git a/app/http.php b/app/http.php index 608ac2ec12..b9aa69a7cc 100644 --- a/app/http.php +++ b/app/http.php @@ -17,7 +17,6 @@ use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; -use Utopia\Database\Exception\Duplicate; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; @@ -156,6 +155,79 @@ function dispatch(Server $server, int $fd, int $type, $data = null): int include __DIR__ . '/controllers/general.php'; +function createDatabase(App $app, string $resourceKey, string $dbName, array $collections, mixed $pools, callable $extraSetup = null): void +{ + $max = 10; + $sleep = 1; + $attempts = 0; + + do { + try { + $attempts++; + $resource = $app->getResource($resourceKey); + /* @var $database Database */ + $database = is_callable($resource) ? $resource() : $resource; + break; // exit loop on success + } catch (\Exception $e) { + Console::warning(" └── Database not ready. Retrying connection ({$attempts})..."); + $pools->reclaim(); + if ($attempts >= $max) { + throw new \Exception(' └── Failed to connect to database: ' . $e->getMessage()); + } + sleep($sleep); + } + } while ($attempts < $max); + + Console::success("[Setup] - $dbName database init started..."); + + // Attempt to create the database + try { + Console::info(" └── Creating database: $dbName..."); + $database->create(); + } catch (\Exception $e) { + Console::info(" └── Skip: metadata table already exists"); + } + + // Process collections + foreach ($collections as $key => $collection) { + if (($collection['$collection'] ?? '') !== Database::METADATA) { + continue; + } + + if (!$database->getCollection($key)->isEmpty()) { + continue; + } + + Console::info(" └── Creating collection: {$collection['$id']}..."); + + $attributes = array_map(fn ($attr) => new Document([ + '$id' => ID::custom($attr['$id']), + 'type' => $attr['type'], + 'size' => $attr['size'], + 'required' => $attr['required'], + 'signed' => $attr['signed'], + 'array' => $attr['array'], + 'filters' => $attr['filters'], + 'default' => $attr['default'] ?? null, + 'format' => $attr['format'] ?? '' + ]), $collection['attributes']); + + $indexes = array_map(fn ($index) => new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]), $collection['indexes']); + + $database->createCollection($key, $attributes, $indexes); + } + + if ($extraSetup) { + $extraSetup($database); + } +} + $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) { $app = new App('UTC'); @@ -164,140 +236,75 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg /** @var Group $pools */ App::setResource('pools', fn () => $pools); - // wait for database to be ready - $attempts = 0; - $max = 10; - $sleep = 1; - - do { - try { - $attempts++; - $dbForPlatform = $app->getResource('dbForPlatform'); - /** @var Utopia\Database\Database $dbForPlatform */ - break; // leave the do-while if successful - } catch (\Throwable $e) { - Console::warning("Database not ready. Retrying connection ({$attempts})..."); - if ($attempts >= $max) { - throw new \Exception('Failed to connect to database: ' . $e->getMessage()); - } - sleep($sleep); - } - } while ($attempts < $max); - - Console::success('[Setup] - Server database init started...'); - - try { - Console::success('[Setup] - Creating console database...'); - $dbForPlatform->create(); - } catch (Duplicate) { - Console::success('[Setup] - Skip: metadata table already exists'); - } - - if ($dbForPlatform->getCollection(Audit::COLLECTION)->isEmpty()) { - $audit = new Audit($dbForPlatform); - $audit->setup(); - } - /** @var array $collections */ $collections = Config::getParam('collections', []); - $consoleCollections = $collections['console']; - foreach ($consoleCollections as $key => $collection) { - if (($collection['$collection'] ?? '') !== Database::METADATA) { - continue; - } - if (!$dbForPlatform->getCollection($key)->isEmpty()) { - continue; + + // create logs database first, `getLogsDB` is a callable. + createDatabase($app, 'getLogsDB', 'logs', $collections['logs'], $pools); + + // create appwrite database, `dbForPlatform` is a direct access call. + createDatabase($app, 'dbForPlatform', 'appwrite', $collections['console'], $pools, function (Database $dbForPlatform) use ($collections) { + if ($dbForPlatform->getCollection(Audit::COLLECTION)->isEmpty()) { + $audit = new Audit($dbForPlatform); + $audit->setup(); } - Console::success('[Setup] - Creating console collection: ' . $collection['$id'] . '...'); + if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty() && + !$dbForPlatform->exists($dbForPlatform->getDatabase(), 'bucket_1')) { + Console::info(" └── Creating default bucket..."); + $dbForPlatform->createDocument('buckets', new Document([ + '$id' => ID::custom('default'), + '$collection' => ID::custom('buckets'), + 'name' => 'Default', + 'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), + 'allowedFileExtensions' => [], + 'enabled' => true, + 'compression' => 'gzip', + 'encryption' => true, + 'antivirus' => true, + 'fileSecurity' => true, + '$permissions' => [ + Permission::create(Role::any()), + Permission::read(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + 'search' => 'buckets Default', + ])); - $attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']); - $indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']); + $bucket = $dbForPlatform->getDocument('buckets', 'default'); - $dbForPlatform->createCollection($key, $attributes, $indexes); - } - - if ($dbForPlatform->getDocument('buckets', 'default')->isEmpty() && !$dbForPlatform->exists($dbForPlatform->getDatabase(), 'bucket_1')) { - Console::success('[Setup] - Creating default bucket...'); - $dbForPlatform->createDocument('buckets', new Document([ - '$id' => ID::custom('default'), - '$collection' => ID::custom('buckets'), - 'name' => 'Default', - 'maximumFileSize' => (int) System::getEnv('_APP_STORAGE_LIMIT', 0), // 10MB - 'allowedFileExtensions' => [], - 'enabled' => true, - 'compression' => 'gzip', - 'encryption' => true, - 'antivirus' => true, - 'fileSecurity' => true, - '$permissions' => [ - Permission::create(Role::any()), - Permission::read(Role::any()), - Permission::update(Role::any()), - Permission::delete(Role::any()), - ], - 'search' => 'buckets Default', - ])); - - $bucket = $dbForPlatform->getDocument('buckets', 'default'); - - Console::success('[Setup] - Creating files collection for default bucket...'); - $files = $collections['buckets']['files'] ?? []; - if (empty($files)) { - throw new Exception('Files collection is not configured.'); - } - - $attributes = \array_map(fn ($attribute) => new Document($attribute), $files['attributes']); - $indexes = \array_map(fn (array $index) => new Document($index), $files['indexes']); - - $dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); - } - - $projectCollections = $collections['projects']; - $sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', '')); - $sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', '')); - $sharedTablesV2 = \array_diff($sharedTables, $sharedTablesV1); - - $cache = $app->getResource('cache'); - - foreach ($sharedTablesV2 as $hostname) { - $adapter = $pools - ->get($hostname) - ->pop() - ->getResource(); - - $dbForProject = (new Database($adapter, $cache)) - ->setDatabase('appwrite') - ->setSharedTables(true) - ->setTenant(null) - ->setNamespace(System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '')); - - try { - Console::success('[Setup] - Creating project database: ' . $hostname . '...'); - $dbForProject->create(); - } catch (Duplicate) { - Console::success('[Setup] - Skip: metadata table already exists'); - } - - foreach ($projectCollections as $key => $collection) { - if (($collection['$collection'] ?? '') !== Database::METADATA) { - continue; - } - if (!$dbForProject->getCollection($key)->isEmpty()) { - continue; + Console::info(" └── Creating files collection for default bucket..."); + $files = $collections['buckets']['files'] ?? []; + if (empty($files)) { + throw new Exception('Files collection is not configured.'); } - $attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']); - $indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']); + $attributes = array_map(fn ($attr) => new Document([ + '$id' => ID::custom($attr['$id']), + 'type' => $attr['type'], + 'size' => $attr['size'], + 'required' => $attr['required'], + 'signed' => $attr['signed'], + 'array' => $attr['array'], + 'filters' => $attr['filters'], + 'default' => $attr['default'] ?? null, + 'format' => $attr['format'] ?? '' + ]), $files['attributes']); - Console::success('[Setup] - Creating project collection: ' . $collection['$id'] . '...'); + $indexes = array_map(fn ($index) => new Document([ + '$id' => ID::custom($index['$id']), + 'type' => $index['type'], + 'attributes' => $index['attributes'], + 'lengths' => $index['lengths'], + 'orders' => $index['orders'], + ]), $files['indexes']); - $dbForProject->createCollection($key, $attributes, $indexes); + $dbForPlatform->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes); } - } + }); $pools->reclaim(); - Console::success('[Setup] - Server database init completed...'); }); diff --git a/app/init.php b/app/init.php index fbffc4b3ad..8609109e90 100644 --- a/app/init.php +++ b/app/init.php @@ -21,6 +21,7 @@ if (\file_exists(__DIR__ . '/../vendor/autoload.php')) { use Ahc\Jwt\JWT; use Ahc\Jwt\JWTException; use Appwrite\Auth\Auth; +use Appwrite\Auth\Key; use Appwrite\Event\Audit; use Appwrite\Event\Build; use Appwrite\Event\Certificate; @@ -32,7 +33,7 @@ use Appwrite\Event\Mail; use Appwrite\Event\Messaging; use Appwrite\Event\Migration; use Appwrite\Event\Realtime; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Event\Webhook; use Appwrite\Extend\Exception; use Appwrite\Functions\Specification; @@ -239,8 +240,6 @@ const METRIC_WEBHOOKS_SENT = 'webhooks.events.sent'; const METRIC_WEBHOOKS_FAILED = 'webhooks.events.failed'; const METRIC_WEBHOOK_ID_SENT = '{webhookInternalId}.webhooks.events.sent'; const METRIC_WEBHOOK_ID_FAILED = '{webhookInternalId}.webhooks.events.failed'; - - const METRIC_AUTH_METHOD_PHONE = 'auth.method.phone'; const METRIC_AUTH_METHOD_PHONE_COUNTRY_CODE = METRIC_AUTH_METHOD_PHONE . '.{countryCode}'; const METRIC_MESSAGES = 'messages'; @@ -271,6 +270,8 @@ const METRIC_FILES = 'files'; const METRIC_FILES_STORAGE = 'files.storage'; const METRIC_FILES_TRANSFORMATIONS = 'files.transformations'; const METRIC_BUCKET_ID_FILES_TRANSFORMATIONS = '{bucketInternalId}.files.transformations'; +const METRIC_FILES_IMAGES_TRANSFORMED = 'files.imagesTransformed'; +const METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED = '{bucketInternalId}.files.imagesTransformed'; const METRIC_BUCKET_ID_FILES = '{bucketInternalId}.files'; const METRIC_BUCKET_ID_FILES_STORAGE = '{bucketInternalId}.files.storage'; const METRIC_SITES = 'sites'; @@ -318,6 +319,18 @@ const METRIC_SITE_ID_BUILDS_MB_SECONDS = '{siteInternalId}.builds.mbSeconds'; const METRIC_NETWORK_REQUESTS = 'network.requests'; const METRIC_NETWORK_INBOUND = 'network.inbound'; const METRIC_NETWORK_OUTBOUND = 'network.outbound'; +const METRIC_MAU = 'users.mau'; +const METRIC_DAU = 'users.dau'; +const METRIC_WAU = 'users.wau'; +const METRIC_WEBHOOKS = 'webhooks'; +const METRIC_PLATFORMS = 'platforms'; +const METRIC_PROVIDERS = 'providers'; +const METRIC_TOPICS = 'topics'; +const METRIC_KEYS = 'keys'; +const METRIC_RESOURCE_TYPE_ID_BUILDS = '{resourceType}.{resourceInternalId}.builds'; +const METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE = '{resourceType}.{resourceInternalId}.builds.storage'; +const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments'; +const METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage'; // Resource types @@ -1198,6 +1211,9 @@ App::setResource('queueForWebhooks', function (Queue\Publisher $publisher) { App::setResource('queueForRealtime', function () { return new Realtime(); }, []); +App::setResource('queueForStatsUsage', function (Queue\Publisher $publisher) { + return new StatsUsage($publisher); +}, ['publisher']); App::setResource('queueForAudits', function (Queue\Publisher $publisher) { return new Audit($publisher); }, ['publisher']); @@ -1568,6 +1584,39 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform }; }, ['pools', 'dbForPlatform', 'cache']); +App::setResource('getLogsDB', function (Group $pools, Cache $cache) { + $database = null; + return function (?Document $project = null) use ($pools, $cache, $database) { + if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { + $database->setTenant($project->getInternalId()); + return $database; + } + + $dbAdapter = $pools + ->get('logs') + ->pop() + ->getResource(); + + $database = new Database( + $dbAdapter, + $cache + ); + + $database + ->setSharedTables(true) + ->setNamespace('logsV1') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS) + ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); + + // set tenant + if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') { + $database->setTenant($project->getInternalId()); + } + + return $database; + }; +}, ['pools', 'cache']); + App::setResource('cache', function (Group $pools) { $list = Config::getParam('pools-cache', []); $adapters = []; @@ -1635,6 +1684,7 @@ function getDevice(string $root, string $connection = ''): Device $accessSecret = ''; $bucket = ''; $region = ''; + $url = App::getEnv('_APP_STORAGE_S3_ENDPOINT', ''); try { $dsn = new DSN($connection); @@ -1649,7 +1699,7 @@ function getDevice(string $root, string $connection = ''): Device switch ($device) { case Storage::DEVICE_S3: - return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl); + return new S3($root, $accessKey, $accessSecret, $bucket, $region, $acl, $url); case STORAGE::DEVICE_DO_SPACES: $device = new DOSpaces($root, $accessKey, $accessSecret, $bucket, $region, $acl); $device->setHttpVersion(S3::HTTP_VERSION_1_1); @@ -1675,7 +1725,8 @@ function getDevice(string $root, string $connection = ''): Device $s3Region = System::getEnv('_APP_STORAGE_S3_REGION', ''); $s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', ''); $s3Acl = 'private'; - return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl); + $s3EndpointUrl = App::getEnv('_APP_STORAGE_S3_ENDPOINT', ''); + return new S3($root, $s3AccessKey, $s3SecretKey, $s3Bucket, $s3Region, $s3Acl, $s3EndpointUrl); case Storage::DEVICE_DO_SPACES: $doSpacesAccessKey = System::getEnv('_APP_STORAGE_DO_SPACES_ACCESS_KEY', ''); $doSpacesSecretKey = System::getEnv('_APP_STORAGE_DO_SPACES_SECRET', ''); @@ -1920,3 +1971,13 @@ App::setResource('previewHostname', function (Request $request) { return ''; }, ['request']); + +App::setResource('apiKey', function (Request $request, Document $project): ?Key { + $key = $request->getHeader('x-appwrite-key'); + + if (empty($key)) { + return null; + } + + return Key::decode($project, $key); +}, ['request', 'project']); diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index aa06406f14..41384e1f02 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -116,6 +116,7 @@ $image = $this->getParam('image', ''); - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -320,6 +321,7 @@ $image = $this->getParam('image', ''); - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -415,6 +417,7 @@ $image = $this->getParam('image', ''); - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -570,6 +573,7 @@ $image = $this->getParam('image', ''); - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -651,10 +655,69 @@ $image = $this->getParam('image', ''); - _APP_MAINTENANCE_RETENTION_USAGE_HOURLY - _APP_MAINTENANCE_RETENTION_SCHEDULES - appwrite-worker-usage: + appwrite-task-stats-resources: image: /: - entrypoint: worker-usage - container_name: appwrite-worker-usage + container_name: appwrite-task-stats-resources + entrypoint: stats-resources + <<: *x-logging + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES + - _APP_STATS_RESOURCES_INTERVAL + + appwrite-worker-stats-resources: + image: /: + entrypoint: worker-stats-resources + container_name: appwrite-worker-stats-resources + <<: *x-logging + restart: unless-stopped + networks: + - appwrite + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_STATS_RESOURCES_INTERVAL + + appwrite-worker-stats-usage: + image: /: + entrypoint: worker-stats-usage + container_name: appwrite-worker-stats-usage <<: *x-logging restart: unless-stopped networks: @@ -679,11 +742,11 @@ $image = $this->getParam('image', ''); - _APP_LOGGING_CONFIG - _APP_USAGE_AGGREGATION_INTERVAL - appwrite-worker-usage-dump: + appwrite-worker-stats-usage-dump: image: /: - entrypoint: worker-usage-dump + entrypoint: worker-stats-usage-dump <<: *x-logging - container_name: appwrite-worker-usage-dump + container_name: appwrite-worker-stats-usage-dump restart: unless-stopped networks: - appwrite @@ -824,6 +887,7 @@ $image = $this->getParam('image', ''); - OPR_EXECUTOR_STORAGE_S3_SECRET=$_APP_STORAGE_S3_SECRET - OPR_EXECUTOR_STORAGE_S3_REGION=$_APP_STORAGE_S3_REGION - OPR_EXECUTOR_STORAGE_S3_BUCKET=$_APP_STORAGE_S3_BUCKET + - OPR_EXECUTOR_STORAGE_S3_ENDPOINT=$_APP_STORAGE_S3_ENDPOINT - OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=$_APP_STORAGE_DO_SPACES_ACCESS_KEY - OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=$_APP_STORAGE_DO_SPACES_SECRET - OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=$_APP_STORAGE_DO_SPACES_REGION diff --git a/app/worker.php b/app/worker.php index 8c7e24b748..28ee4057cf 100644 --- a/app/worker.php +++ b/app/worker.php @@ -13,8 +13,12 @@ use Appwrite\Event\Func; use Appwrite\Event\Mail; use Appwrite\Event\Messaging; use Appwrite\Event\Migration; +use Appwrite\Event\StatsUsage; +use Appwrite\Event\StatsUsageDump; +/** remove */ use Appwrite\Event\Usage; use Appwrite\Event\UsageDump; +/** /remove */ use Appwrite\Platform\Appwrite; use Swoole\Runtime; use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis; @@ -173,6 +177,39 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf }; }, ['pools', 'dbForPlatform', 'cache']); +Server::setResource('getLogsDB', function (Group $pools, Cache $cache) { + $database = null; + return function (?Document $project = null) use ($pools, $cache, $database) { + if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') { + $database->setTenant($project->getInternalId()); + return $database; + } + + $dbAdapter = $pools + ->get('logs') + ->pop() + ->getResource(); + + $database = new Database( + $dbAdapter, + $cache + ); + + $database + ->setSharedTables(true) + ->setNamespace('logsV1') + ->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS) + ->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES); + + // set tenant + if ($project !== null && !$project->isEmpty() && $project->getId() !== 'console') { + $database->setTenant($project->getInternalId()); + } + + return $database; + }; +}, ['pools', 'cache']); + Server::setResource('abuseRetention', function () { return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400); }); @@ -240,6 +277,14 @@ Server::setResource('queueForUsageDump', function (Publisher $publisher) { return new UsageDump($publisher); }, ['publisher']); +Server::setResource('queueForStatsUsage', function (Publisher $publisher) { + return new StatsUsage($publisher); +}, ['publisher']); + +Server::setResource('queueForStatsUsageDump', function (Publisher $publisher) { + return new StatsUsageDump($publisher); +}, ['publisher']); + Server::setResource('queueForDatabase', function (Publisher $publisher) { return new EventDatabase($publisher); }, ['publisher']); diff --git a/bin/stats-resources b/bin/stats-resources new file mode 100644 index 0000000000..3104bab896 --- /dev/null +++ b/bin/stats-resources @@ -0,0 +1,3 @@ +#!/bin/sh + +php /usr/src/code/app/cli.php stats-resources $@ \ No newline at end of file diff --git a/bin/worker-stats-resources b/bin/worker-stats-resources new file mode 100644 index 0000000000..9c5d2bebff --- /dev/null +++ b/bin/worker-stats-resources @@ -0,0 +1,3 @@ +#!/bin/sh + +php /usr/src/code/app/worker.php stats-resources $@ \ No newline at end of file diff --git a/bin/worker-stats-usage b/bin/worker-stats-usage new file mode 100644 index 0000000000..2c267d805e --- /dev/null +++ b/bin/worker-stats-usage @@ -0,0 +1,3 @@ +#!/bin/sh + +php /usr/src/code/app/worker.php stats-usage $@ \ No newline at end of file diff --git a/bin/worker-stats-usage-dump b/bin/worker-stats-usage-dump new file mode 100644 index 0000000000..98e3c2cac7 --- /dev/null +++ b/bin/worker-stats-usage-dump @@ -0,0 +1,3 @@ +#!/bin/sh + +php /usr/src/code/app/worker.php stats-usage-dump $@ \ No newline at end of file diff --git a/composer.json b/composer.json index 3c1a2951d9..d5a10bf962 100644 --- a/composer.json +++ b/composer.json @@ -46,13 +46,13 @@ "ext-sockets": "*", "appwrite/php-runtimes": "0.17.*", "appwrite/php-clamav": "2.0.*", - "utopia-php/abuse": "0.49.*", + "utopia-php/abuse": "0.50.*", "utopia-php/analytics": "0.10.*", - "utopia-php/audit": "0.49.*", + "utopia-php/audit": "0.51.*", "utopia-php/cache": "0.11.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.58.4", + "utopia-php/database": "0.59.0", "utopia-php/detector": "dev-feat-pseudocode-draft2 as 0.1.99", "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.2.1", @@ -86,7 +86,7 @@ }, "require-dev": { "ext-fileinfo": "*", - "appwrite/sdk-generator": "0.39.32", + "appwrite/sdk-generator": "0.40.*", "phpunit/phpunit": "9.5.20", "swoole/ide-helper": "5.1.2", "textalk/websocket": "1.5.7", diff --git a/composer.lock b/composer.lock index 00f755f400..ea939c0aed 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "44bd3c613083a66e4f446a27a94d6977", + "content-hash": "81769c65caf560ea69f2c13fc8b04e7d", "packages": [ { "name": "adhocore/jwt", @@ -1757,16 +1757,16 @@ }, { "name": "php-amqplib/php-amqplib", - "version": "v3.7.2", + "version": "v3.7.3", "source": { "type": "git", "url": "https://github.com/php-amqplib/php-amqplib.git", - "reference": "738a73eb0019b6c99d9bc25d7a0c0dd8f56a5199" + "reference": "9f50fe69a9f1a19e2cb25596a354d705de36fe59" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/738a73eb0019b6c99d9bc25d7a0c0dd8f56a5199", - "reference": "738a73eb0019b6c99d9bc25d7a0c0dd8f56a5199", + "url": "https://api.github.com/repos/php-amqplib/php-amqplib/zipball/9f50fe69a9f1a19e2cb25596a354d705de36fe59", + "reference": "9f50fe69a9f1a19e2cb25596a354d705de36fe59", "shasum": "" }, "require": { @@ -1832,9 +1832,9 @@ ], "support": { "issues": "https://github.com/php-amqplib/php-amqplib/issues", - "source": "https://github.com/php-amqplib/php-amqplib/tree/v3.7.2" + "source": "https://github.com/php-amqplib/php-amqplib/tree/v3.7.3" }, - "time": "2024-11-21T09:21:41+00:00" + "time": "2025-02-18T20:11:13+00:00" }, { "name": "php-http/discovery", @@ -3377,16 +3377,16 @@ }, { "name": "utopia-php/abuse", - "version": "0.49.0", + "version": "0.50.0", "source": { "type": "git", "url": "https://github.com/utopia-php/abuse.git", - "reference": "76612c274b895aa3d4d1fa27557a6402463eea99" + "reference": "3ff67819e9de61506c5ca070a70552f7ebe99f80" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/abuse/zipball/76612c274b895aa3d4d1fa27557a6402463eea99", - "reference": "76612c274b895aa3d4d1fa27557a6402463eea99", + "url": "https://api.github.com/repos/utopia-php/abuse/zipball/3ff67819e9de61506c5ca070a70552f7ebe99f80", + "reference": "3ff67819e9de61506c5ca070a70552f7ebe99f80", "shasum": "" }, "require": { @@ -3394,7 +3394,7 @@ "ext-pdo": "*", "ext-redis": "*", "php": ">=8.0", - "utopia-php/database": "0.58.*" + "utopia-php/database": "0.59.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3422,9 +3422,9 @@ ], "support": { "issues": "https://github.com/utopia-php/abuse/issues", - "source": "https://github.com/utopia-php/abuse/tree/0.49.0" + "source": "https://github.com/utopia-php/abuse/tree/0.50.0" }, - "time": "2025-02-04T07:33:59+00:00" + "time": "2025-02-12T09:13:59+00:00" }, { "name": "utopia-php/analytics", @@ -3474,21 +3474,21 @@ }, { "name": "utopia-php/audit", - "version": "0.49.0", + "version": "0.51.0", "source": { "type": "git", "url": "https://github.com/utopia-php/audit.git", - "reference": "9d5c5e0cf0f6d9157b911fc3971da4331d71c96d" + "reference": "a5a4b73a57e27a0fac8025b1d6038e145a1ca04e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/audit/zipball/9d5c5e0cf0f6d9157b911fc3971da4331d71c96d", - "reference": "9d5c5e0cf0f6d9157b911fc3971da4331d71c96d", + "url": "https://api.github.com/repos/utopia-php/audit/zipball/a5a4b73a57e27a0fac8025b1d6038e145a1ca04e", + "reference": "a5a4b73a57e27a0fac8025b1d6038e145a1ca04e", "shasum": "" }, "require": { "php": ">=8.0", - "utopia-php/database": "0.58.*" + "utopia-php/database": "0.59.*" }, "require-dev": { "laravel/pint": "1.*", @@ -3515,9 +3515,9 @@ ], "support": { "issues": "https://github.com/utopia-php/audit/issues", - "source": "https://github.com/utopia-php/audit/tree/0.49.0" + "source": "https://github.com/utopia-php/audit/tree/0.51.0" }, - "time": "2025-02-04T07:27:18+00:00" + "time": "2025-02-12T09:12:44+00:00" }, { "name": "utopia-php/cache", @@ -3717,16 +3717,16 @@ }, { "name": "utopia-php/database", - "version": "0.58.4", + "version": "0.59.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "ff3fd22e4fe757cc2a78f17169f6dcc45c96d0fe" + "reference": "0eed7f1ad3eb66ff4a7d73b68dd9d3e05089eb18" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/ff3fd22e4fe757cc2a78f17169f6dcc45c96d0fe", - "reference": "ff3fd22e4fe757cc2a78f17169f6dcc45c96d0fe", + "url": "https://api.github.com/repos/utopia-php/database/zipball/0eed7f1ad3eb66ff4a7d73b68dd9d3e05089eb18", + "reference": "0eed7f1ad3eb66ff4a7d73b68dd9d3e05089eb18", "shasum": "" }, "require": { @@ -3767,9 +3767,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.58.4" + "source": "https://github.com/utopia-php/database/tree/0.59.0" }, - "time": "2025-02-05T02:51:02+00:00" + "time": "2025-02-12T08:08:29+00:00" }, { "name": "utopia-php/detector", @@ -4233,16 +4233,16 @@ }, { "name": "utopia-php/migration", - "version": "0.6.17", + "version": "0.6.19", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "677a5c4688d7f54d1631a91f76a35d51346cf96b" + "reference": "3c9497f7a54ef88b1077c48d8326893133ad78eb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/677a5c4688d7f54d1631a91f76a35d51346cf96b", - "reference": "677a5c4688d7f54d1631a91f76a35d51346cf96b", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/3c9497f7a54ef88b1077c48d8326893133ad78eb", + "reference": "3c9497f7a54ef88b1077c48d8326893133ad78eb", "shasum": "" }, "require": { @@ -4250,7 +4250,7 @@ "ext-curl": "*", "ext-openssl": "*", "php": ">=8.1", - "utopia-php/database": "0.58.*", + "utopia-php/database": "0.59.*", "utopia-php/dsn": "0.2.*", "utopia-php/framework": "0.33.*", "utopia-php/storage": "0.18.*" @@ -4283,9 +4283,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.6.17" + "source": "https://github.com/utopia-php/migration/tree/0.6.19" }, - "time": "2025-02-05T05:27:29+00:00" + "time": "2025-02-13T07:50:21+00:00" }, { "name": "utopia-php/mongo", @@ -5114,16 +5114,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.39.32", + "version": "0.40.0", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "2d02e1305ea5004fb0aec6b2618d6c597659b75c" + "reference": "d2880132c900f64108d3e4484a6c1ed1bed2303c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/2d02e1305ea5004fb0aec6b2618d6c597659b75c", - "reference": "2d02e1305ea5004fb0aec6b2618d6c597659b75c", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/d2880132c900f64108d3e4484a6c1ed1bed2303c", + "reference": "d2880132c900f64108d3e4484a6c1ed1bed2303c", "shasum": "" }, "require": { @@ -5159,9 +5159,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/0.39.32" + "source": "https://github.com/appwrite/sdk-generator/tree/0.40.0" }, - "time": "2025-01-29T04:04:19+00:00" + "time": "2025-02-04T12:47:33+00:00" }, { "name": "doctrine/annotations", @@ -6253,16 +6253,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "2.0.2", + "version": "2.1.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "51087f87dcce2663e1fed4dfd4e56eccd580297e" + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/51087f87dcce2663e1fed4dfd4e56eccd580297e", - "reference": "51087f87dcce2663e1fed4dfd4e56eccd580297e", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", "shasum": "" }, "require": { @@ -6294,9 +6294,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.0.2" + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.1.0" }, - "time": "2025-02-17T20:25:51+00:00" + "time": "2025-02-19T13:28:12+00:00" }, { "name": "phpstan/phpstan", diff --git a/docker-compose.yml b/docker-compose.yml index c827ec2108..6dec6e3918 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -141,6 +141,7 @@ services: - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -369,6 +370,7 @@ services: - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -470,6 +472,7 @@ services: - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -636,6 +639,7 @@ services: - _APP_STORAGE_S3_SECRET - _APP_STORAGE_S3_REGION - _APP_STORAGE_S3_BUCKET + - _APP_STORAGE_S3_ENDPOINT - _APP_STORAGE_DO_SPACES_ACCESS_KEY - _APP_STORAGE_DO_SPACES_SECRET - _APP_STORAGE_DO_SPACES_REGION @@ -727,10 +731,72 @@ services: - _APP_MAINTENANCE_DELAY - _APP_DATABASE_SHARED_TABLES - appwrite-worker-usage: - entrypoint: worker-usage + appwrite-task-stats-resources: + container_name: appwrite-task-stats-resources + entrypoint: stats-resources <<: *x-logging - container_name: appwrite-worker-usage + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_DATABASE_SHARED_TABLES + - _APP_STATS_RESOURCES_INTERVAL + + appwrite-worker-stats-resources: + entrypoint: worker-stats-resources + <<: *x-logging + container_name: appwrite-worker-stats-resources + image: appwrite-dev + networks: + - appwrite + volumes: + - ./app:/usr/src/code/app + - ./src:/usr/src/code/src + depends_on: + - redis + - mariadb + environment: + - _APP_ENV + - _APP_WORKER_PER_CORE + - _APP_OPENSSL_KEY_V1 + - _APP_DB_HOST + - _APP_DB_PORT + - _APP_DB_SCHEMA + - _APP_DB_USER + - _APP_DB_PASS + - _APP_REDIS_HOST + - _APP_REDIS_PORT + - _APP_REDIS_USER + - _APP_REDIS_PASS + - _APP_USAGE_STATS + - _APP_LOGGING_CONFIG + - _APP_USAGE_AGGREGATION_INTERVAL + - _APP_DATABASE_SHARED_TABLES + + appwrite-worker-stats-usage: + entrypoint: worker-stats-usage + <<: *x-logging + container_name: appwrite-worker-stats-usage image: appwrite-dev networks: - appwrite @@ -758,10 +824,10 @@ services: - _APP_USAGE_AGGREGATION_INTERVAL - _APP_DATABASE_SHARED_TABLES - appwrite-worker-usage-dump: - entrypoint: worker-usage-dump + appwrite-worker-stats-usage-dump: + entrypoint: worker-stats-usage-dump <<: *x-logging - container_name: appwrite-worker-usage-dump + container_name: appwrite-worker-stats-usage-dump image: appwrite-dev networks: - appwrite @@ -788,7 +854,8 @@ services: - _APP_LOGGING_CONFIG - _APP_USAGE_AGGREGATION_INTERVAL - _APP_DATABASE_SHARED_TABLES - + - _APP_STATS_USAGE_DUAL_WRITING_DBS + appwrite-task-scheduler-functions: entrypoint: schedule-functions <<: *x-logging @@ -914,6 +981,7 @@ services: - OPR_EXECUTOR_STORAGE_S3_SECRET=$_APP_STORAGE_S3_SECRET - OPR_EXECUTOR_STORAGE_S3_REGION=$_APP_STORAGE_S3_REGION - OPR_EXECUTOR_STORAGE_S3_BUCKET=$_APP_STORAGE_S3_BUCKET + - OPR_EXECUTOR_STORAGE_S3_ENDPOINT=$_APP_STORAGE_S3_ENDPOINT - OPR_EXECUTOR_STORAGE_DO_SPACES_ACCESS_KEY=$_APP_STORAGE_DO_SPACES_ACCESS_KEY - OPR_EXECUTOR_STORAGE_DO_SPACES_SECRET=$_APP_STORAGE_DO_SPACES_SECRET - OPR_EXECUTOR_STORAGE_DO_SPACES_REGION=$_APP_STORAGE_DO_SPACES_REGION diff --git a/docs/references/databases/create-document.md b/docs/references/databases/create-document.md index a2444d58a4..643518df51 100644 --- a/docs/references/databases/create-document.md +++ b/docs/references/databases/create-document.md @@ -1 +1 @@ -Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection) API or directly from your database console. \ No newline at end of file +Create a new Document. Before using this route, you should create a new collection resource using either a [server integration](https://appwrite.io/docs/server/databases#databasesCreateCollection) API or directly from your database console. diff --git a/docs/references/health/get-queue-stats-resources.md b/docs/references/health/get-queue-stats-resources.md new file mode 100644 index 0000000000..5221327467 --- /dev/null +++ b/docs/references/health/get-queue-stats-resources.md @@ -0,0 +1 @@ +Get the number of metrics that are waiting to be processed in the Appwrite stats resources queue. \ No newline at end of file diff --git a/docs/references/health/get-queue-usage-dump.md b/docs/references/health/get-queue-stats-usage-dump.md similarity index 100% rename from docs/references/health/get-queue-usage-dump.md rename to docs/references/health/get-queue-stats-usage-dump.md diff --git a/docs/references/health/get-queue-usage.md b/docs/references/health/get-queue-stats-usage.md similarity index 100% rename from docs/references/health/get-queue-usage.md rename to docs/references/health/get-queue-stats-usage.md diff --git a/docs/tutorials/add-oauth2-provider.md b/docs/tutorials/add-oauth2-provider.md index ab33f70cb2..da5f514919 100644 --- a/docs/tutorials/add-oauth2-provider.md +++ b/docs/tutorials/add-oauth2-provider.md @@ -175,9 +175,9 @@ Please mention in your documentation what resources or API docs you used to impl ## 3. Test your provider -After you finished adding your new provider to Appwrite, you should be able to see it in your Appwrite console. Navigate to 'Project > Users > Providers' and check your new provider's settings form. +After you finish adding your new provider to Appwrite, you should be able to see it in your Appwrite console. Navigate to 'Project > Users > Providers' and check your new provider's settings form. -> To start Appwrite console from the source code, you can simply run `docker compose up -d'. +> To start the Appwrite console from the source code, you can simply run `docker compose up -d'. Add credentials and check both a successful and a failed login (where the user denies integration on the provider page). diff --git a/src/Appwrite/Auth/Key.php b/src/Appwrite/Auth/Key.php new file mode 100644 index 0000000000..1c40b35f54 --- /dev/null +++ b/src/Appwrite/Auth/Key.php @@ -0,0 +1,158 @@ +projectId; + } + + public function getType(): string + { + return $this->type; + } + + public function getRole(): string + { + return $this->role; + } + + public function getScopes(): array + { + return $this->scopes; + } + + public function getName(): string + { + return $this->name; + } + + public function isExpired(): bool + { + return $this->expired; + } + + public function getDisabledMetrics(): array + { + return $this->disabledMetrics; + } + + /** + * Decode the given secret key into a Key object, containing the project ID, type, role, scopes, and name. + * Can be a stored API key or a dynamic key (JWT). + * + * @param Document $project + * @param string $key + * @return Key + * @throws Exception + */ + public static function decode( + Document $project, + string $key + ): Key { + if (\str_contains($key, '_')) { + [$type, $secret] = \explode('_', $key, 2); + } else { + $type = API_KEY_STANDARD; + $secret = $key; + } + + $role = Auth::USER_ROLE_APPS; + $roles = Config::getParam('roles', []); + $scopes = $roles[Auth::USER_ROLE_APPS]['scopes'] ?? []; + $expired = false; + + $guestKey = new Key( + $project->getId(), + $type, + Auth::USER_ROLE_GUESTS, + $roles[Auth::USER_ROLE_GUESTS]['scopes'] ?? [], + 'UNKNOWN' + ); + + switch ($type) { + case API_KEY_DYNAMIC: + $jwtObj = new JWT( + key: System::getEnv('_APP_OPENSSL_KEY_V1'), + algo: 'HS256', + maxAge: 86400, + leeway: 0 + ); + + try { + $payload = $jwtObj->decode($secret); + } catch (JWTException) { + $expired = true; + } + + $name = $payload['name'] ?? 'Dynamic Key'; + $projectId = $payload['projectId'] ?? ''; + $disabledMetrics = $payload['disabledMetrics'] ?? []; + $scopes = \array_merge($payload['scopes'] ?? [], $scopes); + + if ($projectId !== $project->getId()) { + return $guestKey; + } + + return new Key( + $projectId, + $type, + $role, + $scopes, + $name, + $expired, + $disabledMetrics + ); + case API_KEY_STANDARD: + $key = $project->find( + key: 'secret', + find: $key, + subject: 'keys' + ); + + if (!$key) { + return $guestKey; + } + + $expire = $key->getAttribute('expire'); + if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) { + $expired = true; + } + + $name = $key->getAttribute('name', 'UNKNOWN'); + $scopes = \array_merge($key->getAttribute('scopes', []), $scopes); + + return new Key( + $project->getId(), + $type, + $role, + $scopes, + $name, + $expired + ); + default: + return $guestKey; + } + } +} diff --git a/src/Appwrite/Event/Event.php b/src/Appwrite/Event/Event.php index ff55ad877a..1e19072138 100644 --- a/src/Appwrite/Event/Event.php +++ b/src/Appwrite/Event/Event.php @@ -27,11 +27,22 @@ class Event public const SITES_QUEUE_NAME = 'v1-sites'; public const SITES_CLASS_NAME = 'SitesV1'; + /** remove */ public const USAGE_QUEUE_NAME = 'v1-usage'; public const USAGE_CLASS_NAME = 'UsageV1'; public const USAGE_DUMP_QUEUE_NAME = 'v1-usage-dump'; public const USAGE_DUMP_CLASS_NAME = 'UsageDumpV1'; + /** /remove */ + + public const STATS_RESOURCES_QUEUE_NAME = 'v1-stats-resources'; + public const STATS_RESOURCES_CLASS_NAME = 'StatsResourcesV1'; + + public const STATS_USAGE_QUEUE_NAME = 'v1-stats-usage'; + public const STATS_USAGE_CLASS_NAME = 'StatsUsageV1'; + + public const STATS_USAGE_DUMP_QUEUE_NAME = 'v1-stats-usage-dump'; + public const STATS_USAGE_DUMP_CLASS_NAME = 'StatsUsageDumpV1'; public const WEBHOOK_QUEUE_NAME = 'v1-webhooks'; public const WEBHOOK_CLASS_NAME = 'WebhooksV1'; diff --git a/src/Appwrite/Event/StatsResources.php b/src/Appwrite/Event/StatsResources.php new file mode 100644 index 0000000000..e7a3df97e0 --- /dev/null +++ b/src/Appwrite/Event/StatsResources.php @@ -0,0 +1,29 @@ +setQueue(Event::STATS_RESOURCES_QUEUE_NAME) + ->setClass(Event::STATS_RESOURCES_CLASS_NAME); + } + + /** + * Prepare the payload for the usage event. + * + * @return array + */ + protected function preparePayload(): array + { + return [ + 'project' => $this->project + ]; + } +} diff --git a/src/Appwrite/Event/StatsUsage.php b/src/Appwrite/Event/StatsUsage.php new file mode 100644 index 0000000000..e259ba5e04 --- /dev/null +++ b/src/Appwrite/Event/StatsUsage.php @@ -0,0 +1,86 @@ +setQueue(Event::STATS_USAGE_QUEUE_NAME) + ->setClass(Event::STATS_USAGE_CLASS_NAME); + } + + /** + * Add reduce. + * + * @param Document $document + * @return self + */ + public function addReduce(Document $document): self + { + $this->reduce[] = $document; + + return $this; + } + + /** + * Add metric. + * + * @param string $key + * @param int $value + * @return self + */ + public function addMetric(string $key, int $value): self + { + $this->metrics[] = [ + 'key' => $key, + 'value' => $value, + ]; + + return $this; + } + + /** + * Set disabled metrics. + * + * @param string $key + * @return self + */ + public function disableMetric(string $key): self + { + $this->disabled[] = $key; + + return $this; + } + + /** + * Prepare the payload for the event + * + * @return array + */ + protected function preparePayload(): array + { + return [ + 'project' => $this->getProject(), + 'reduce' => $this->reduce, + 'metrics' => \array_filter($this->metrics, function ($metric) { + foreach ($this->disabled as $disabledMetric) { + if (\str_ends_with($metric['key'], $disabledMetric)) { + return false; + } + } + return true; + }), + ]; + } +} diff --git a/src/Appwrite/Event/StatsUsageDump.php b/src/Appwrite/Event/StatsUsageDump.php new file mode 100644 index 0000000000..0573a88040 --- /dev/null +++ b/src/Appwrite/Event/StatsUsageDump.php @@ -0,0 +1,44 @@ +setQueue(Event::STATS_USAGE_DUMP_QUEUE_NAME) + ->setClass(Event::STATS_USAGE_DUMP_CLASS_NAME); + } + + /** + * Add Stats. + * + * @param array $stats + * @return self + */ + public function setStats(array $stats): self + { + $this->stats = $stats; + + return $this; + } + + /** + * Prepare the payload for the usage dump event. + * + * @return array + */ + protected function preparePayload(): array + { + return [ + 'stats' => $this->stats, + ]; + } +} diff --git a/src/Appwrite/Migration/Migration.php b/src/Appwrite/Migration/Migration.php index 19f69b1a4f..56016f1057 100644 --- a/src/Appwrite/Migration/Migration.php +++ b/src/Appwrite/Migration/Migration.php @@ -92,6 +92,7 @@ abstract class Migration '1.5.11' => 'V20', '1.6.0' => 'V21', '1.6.1' => 'V21', + '1.6.2' => 'V22', ]; /** diff --git a/src/Appwrite/Migration/Version/V22.php b/src/Appwrite/Migration/Version/V22.php new file mode 100644 index 0000000000..4d15662112 --- /dev/null +++ b/src/Appwrite/Migration/Version/V22.php @@ -0,0 +1,83 @@ + null, + fn () => [] + ); + } + + Console::info('Migrating Collections'); + $this->migrateCollections(); + } + + /** + * Migrate Collections. + * + * @return void + * @throws Exception|Throwable + */ + private function migrateCollections(): void + { + $internalProjectId = $this->project->getInternalId(); + $collectionType = match ($internalProjectId) { + 'console' => 'console', + default => 'projects', + }; + + $collections = $this->collections[$collectionType]; + foreach ($collections as $collection) { + $id = $collection['$id']; + + Console::log("Migrating Collection \"{$id}\""); + + $this->projectDB->setNamespace("_$internalProjectId"); + + switch ($id) { + case 'installations': + // Create personalAccessToken attribute + try { + $this->createAttributeFromCollection($this->projectDB, $id, 'personalAccessToken'); + } catch (Throwable $th) { + Console::warning("'personalAccessToken' from {$id}: {$th->getMessage()}"); + } + + // Create personalAccessTokenExpiry attribute + try { + $this->createAttributeFromCollection($this->projectDB, $id, 'personalAccessTokenExpiry'); + } catch (Throwable $th) { + Console::warning("'personalAccessTokenExpiry' from {$id}: {$th->getMessage()}"); + } + + // Create personalRefreshToken attribute + try { + $this->createAttributeFromCollection($this->projectDB, $id, 'personalRefreshToken'); + } catch (Throwable $th) { + Console::warning("'personalRefreshToken' from {$id}: {$th->getMessage()}"); + } + break; + } + + usleep(50000); + } + } +} diff --git a/src/Appwrite/Platform/Action.php b/src/Appwrite/Platform/Action.php new file mode 100644 index 0000000000..72c41582ea --- /dev/null +++ b/src/Appwrite/Platform/Action.php @@ -0,0 +1,90 @@ +disableValidation(); + $results = $database->find($collection, $newQueries); + $database->enableValidation(); + } catch (\Exception $e) { + if (!empty($this->logError)) { + call_user_func_array($this->logError, [$e, "CLI", "fetch_documents_namespace_{$database->getNamespace()}_collection{$collection}"]); + } + } + + if (empty($results)) { + return; + } + + $sum = count($results); + + if ($concurrent) { + $callables = []; + $errors = []; + + foreach ($results as $document) { + if (is_callable($callback)) { + $callables[] = Co\go(function () use ($document, $callback, &$errors) { + try { + $callback($document); + } catch (\Throwable $error) { + $errors[] = $error; + } + }); + } + } + + Co::join($callables); + + if (!empty($errors)) { + throw new \Error("Errors found in concurrent foreachDocument: " . \json_encode($errors)); + } + } else { + foreach ($results as $document) { + if (is_callable($callback)) { + $callback($document); + } + } + } + + $latestDocument = $results[array_key_last($results)]; + } + } +} diff --git a/src/Appwrite/Platform/Modules/Compute/Base.php b/src/Appwrite/Platform/Modules/Compute/Base.php index e8f8ab764d..d31b731407 100644 --- a/src/Appwrite/Platform/Modules/Compute/Base.php +++ b/src/Appwrite/Platform/Modules/Compute/Base.php @@ -19,7 +19,7 @@ use Utopia\VCS\Exception\RepositoryNotFound; class Base extends Action { - public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github, string $referenceType = 'branch', string $reference = ''): Document + 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 { $deploymentId = ID::unique(); $entrypoint = $function->getAttribute('entrypoint', ''); @@ -88,7 +88,7 @@ class Base extends Action 'providerBranch' => $providerBranch, 'providerRootDirectory' => $function->getAttribute('providerRootDirectory', ''), 'search' => implode(' ', [$deploymentId, $entrypoint]), - 'activate' => true, + 'activate' => $activate, ])); $queueForBuilds @@ -100,7 +100,7 @@ class Base extends Action return $deployment; } - public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github, string $referenceType = 'branch', string $reference = ''): Document + public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, string $referenceType = 'branch', string $reference = ''): Document { $deploymentId = ID::unique(); $providerInstallationId = $installation->getAttribute('providerInstallationId', ''); @@ -169,7 +169,7 @@ class Base extends Action 'providerBranch' => $providerBranch, 'providerRootDirectory' => $site->getAttribute('providerRootDirectory', ''), 'search' => implode(' ', [$deploymentId]), - 'activate' => true, + 'activate' => $activate, ])); // Preview deployments for sites diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php index 5082c23db2..9a61693e15 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Template/Create.php @@ -95,7 +95,17 @@ class Create extends Base if (!empty($function->getAttribute('providerRepositoryId'))) { $installation = $dbForPlatform->getDocument('installations', $function->getAttribute('installationId')); - $deployment = $this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github); + $deployment = $this->redeployVcsFunction( + request: $request, + function: $function, + project: $project, + installation: $installation, + dbForProject: $dbForProject, + queueForBuilds: $queueForBuilds, + template: $template, + github: $github, + activate: $activate + ); $queueForEvents ->setParam('functionId', $function->getId()) diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php index 6efa86a2f5..9c9e894eb0 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Deployments/Vcs/Create.php @@ -16,6 +16,7 @@ use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Request; +use Utopia\Validator\Boolean; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub; @@ -60,6 +61,7 @@ class Create extends Base // TODO: Support tag and commit in future ->param('type', '', new WhiteList(['branch']), 'Type of reference passed. Allowed values are: branch') ->param('reference', '', new Text(255), 'VCS reference to create deployment from. Depending on type this can be: branch name') + ->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true) ->inject('request') ->inject('response') ->inject('dbForProject') @@ -71,7 +73,7 @@ class Create extends Base ->callback([$this, 'action']); } - public function action(string $functionId, string $type, string $reference, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Document $project, Event $queueForEvents, Build $queueForBuilds, GitHub $github) + public function action(string $functionId, string $type, string $reference, bool $activate, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Document $project, Event $queueForEvents, Build $queueForBuilds, GitHub $github) { $function = $dbForProject->getDocument('functions', $functionId); @@ -92,6 +94,7 @@ class Create extends Base queueForBuilds: $queueForBuilds, template: $template, github: $github, + activate: $activate, reference: $reference, referenceType: $type ); diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php index 1c8731655d..9244b10c8c 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Update.php @@ -248,7 +248,7 @@ class Update extends Base // Redeploy logic if (!$isConnected && !empty($providerRepositoryId)) { - $this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github); + $this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github, true); } // Inform scheduler if function is still active diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php index 809cb96cc2..0a4907fdb7 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Builds.php @@ -5,7 +5,7 @@ namespace Appwrite\Platform\Modules\Functions\Workers; use Ahc\Jwt\JWT; use Appwrite\Event\Event; use Appwrite\Event\Func; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Utopia\Response\Model\Deployment; use Appwrite\Vcs\Comment; @@ -51,12 +51,14 @@ class Builds extends Action ->inject('dbForPlatform') ->inject('queueForEvents') ->inject('queueForFunctions') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('cache') ->inject('dbForProject') ->inject('deviceForFunctions') + ->inject('isResourceBlocked') ->inject('log') - ->callback(fn ($message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, Usage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log) => $this->action($message, $project, $dbForPlatform, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $log)); + ->callback(fn ($message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, StatsUsage $usage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, callable $isResourceBlocked, Log $log) => + $this->action($message, $project, $dbForPlatform, $queueForEvents, $queueForFunctions, $usage, $cache, $dbForProject, $deviceForFunctions, $isResourceBlocked, $log)); } /** @@ -65,7 +67,7 @@ class Builds extends Action * @param Database $dbForPlatform * @param Event $queueForEvents * @param Func $queueForFunctions - * @param Usage $queueForUsage + * @param StatsUsage $queueForStatsUsage * @param Cache $cache * @param Database $dbForProject * @param Device $deviceForFunctions @@ -73,7 +75,7 @@ class Builds extends Action * @return void * @throws \Utopia\Database\Exception */ - public function action(Message $message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, Usage $queueForUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, Log $log): void + public function action(Message $message, Document $project, Database $dbForPlatform, Event $queueForEvents, Func $queueForFunctions, StatsUsage $queueForStatsUsage, Cache $cache, Database $dbForProject, Device $deviceForFunctions, callable $isResourceBlocked, Log $log): void { $payload = $message->getPayload() ?? []; @@ -94,7 +96,7 @@ class Builds extends Action case BUILD_TYPE_RETRY: Console::info('Creating build for deployment: ' . $deployment->getId()); $github = new GitHub($cache); - $this->buildDeployment($deviceForFunctions, $queueForFunctions, $queueForEvents, $queueForUsage, $dbForPlatform, $dbForProject, $github, $project, $resource, $deployment, $template, $log); + $this->buildDeployment($deviceForFunctions, $queueForFunctions, $queueForEvents, $queueForStatsUsage, $dbForPlatform, $dbForProject, $github, $project, $resource, $deployment, $template, $isResourceBlocked, $log); break; default: @@ -106,7 +108,7 @@ class Builds extends Action * @param Device $deviceForFunctions * @param Func $queueForFunctions * @param Event $queueForEvents - * @param Usage $queueForUsage + * @param StatsUsage $queueForStatsUsage * @param Database $dbForPlatform * @param Database $dbForProject * @param GitHub $github @@ -120,7 +122,7 @@ class Builds extends Action * * @throws Exception */ - protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Database $dbForPlatform, Database $dbForProject, GitHub $github, Document $project, Document $resource, Document $deployment, Document $template, Log $log): void + protected function buildDeployment(Device $deviceForFunctions, Func $queueForFunctions, Event $queueForEvents, StatsUsage $queueForStatsUsage, Database $dbForPlatform, Database $dbForProject, GitHub $github, Document $project, Document $resource, Document $deployment, Document $template, callable $isResourceBlocked, Log $log): void { $resourceKey = match($resource->getCollection()) { 'functions' => 'functionId', @@ -134,24 +136,33 @@ class Builds extends Action $resource = $dbForProject->getDocument($resource->getCollection(), $resource->getId()); if ($resource->isEmpty()) { - throw new \Exception('Function not found', 404); + throw new \Exception('Resource not found'); + } + + // TODO: Sites support + if ($isResourceBlocked($project, RESOURCE_TYPE_FUNCTIONS, $resource->getId())) { + throw new \Exception('Resource is blocked'); } $log->addTag('deploymentId', $deployment->getId()); $deployment = $dbForProject->getDocument('deployments', $deployment->getId()); if ($deployment->isEmpty()) { - throw new \Exception('Deployment not found', 404); + throw new \Exception('Deployment not found'); } - // todo: figure out a better way, entrypoint is not required for sites if ($resource->getCollection() === 'functions' && empty($deployment->getAttribute('entrypoint', ''))) { - throw new \Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".', 500); + throw new \Exception('Entrypoint for your Appwrite Function is missing. Please specify it when making deployment or update the entrypoint under your function\'s "Settings" > "Configuration" > "Entrypoint".'); } $version = $this->getVersion($resource); $runtime = $this->getRuntime($resource, $version); - $spec = Config::getParam('runtime-specifications')[$resource->getAttribute('specifications', APP_COMPUTE_SPECIFICATION_DEFAULT)]; + + $spec = Config::getParam('runtime-specifications')[$resource->getAttribute('specification', APP_COMPUTE_SPECIFICATION_DEFAULT)]; + + if ($resource->getCollection() === 'functions' && \is_null($runtime)) { + throw new \Exception('Runtime "' . $resource->getAttribute('runtime', '') . '" is not supported'); + } // Realtime preparation $allEvents = Event::generateEvents("{$resource->getCollection()}.[{$resourceKey}].deployments.[deploymentId].update", [ @@ -502,8 +513,6 @@ class Builds extends Action 'APPWRITE_VERSION' => APP_VERSION_STABLE, 'APPWRITE_REGION' => $project->getAttribute('region'), 'APPWRITE_DEPLOYMENT_TYPE' => $deployment->getAttribute('type', ''), - 'APPWRITE_COMPUTE_CPUS' => $cpus, - 'APPWRITE_COMPUTE_MEMORY' => $memory, 'APPWRITE_VCS_REPOSITORY_ID' => $deployment->getAttribute('providerRepositoryId', ''), 'APPWRITE_VCS_REPOSITORY_NAME' => $deployment->getAttribute('providerRepositoryName', ''), 'APPWRITE_VCS_REPOSITORY_OWNER' => $deployment->getAttribute('providerRepositoryOwner', ''), @@ -530,6 +539,8 @@ class Builds extends Action 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', + 'APPWRITE_FUNCTION_CPUS' => $cpus, + 'APPWRITE_FUNCTION_MEMORY' => $memory, ]; break; case 'sites': @@ -543,6 +554,8 @@ class Builds extends Action 'APPWRITE_SITE_PROJECT_ID' => $project->getId(), 'APPWRITE_SITE_RUNTIME_NAME' => $runtime['name'] ?? '', 'APPWRITE_SITE_RUNTIME_VERSION' => $runtime['version'] ?? '', + 'APPWRITE_SITE_CPUS' => $cpus, + 'APPWRITE_SITE_MEMORY' => $memory, ]; break; } @@ -604,7 +617,7 @@ class Builds extends Action $build = $dbForProject->getDocument('builds', $build->getId()); if ($build->isEmpty()) { - throw new \Exception('Build not found', 404); + throw new \Exception('Build not found'); } if ($build->getAttribute('status') === 'canceled') { @@ -772,12 +785,12 @@ class Builds extends Action resource:$resource, build: $build, project: $project, - queue: $queueForUsage + queue: $queueForStatsUsage ); } } - protected function sendUsage(Document $resource, Document $build, Document $project, Usage $queue): void + protected function sendUsage(Document $resource, Document $build, Document $project, StatsUsage $queue): void { $key = match($resource->getCollection()) { 'functions' => 'functionInternalId', @@ -813,8 +826,8 @@ class Builds extends Action $queue ->addMetric(METRIC_BUILDS_SUCCESS, 1) // per project ->addMetric(METRIC_BUILDS_COMPUTE_SUCCESS, (int)$build->getAttribute('duration', 0) * 1000) - ->addMetric(str_replace($key, $resource->getInternalId(), $metrics['buildsSuccess']), 1) // per function - ->addMetric(str_replace($key, $resource->getInternalId(), $metrics['buildsComputeSuccess']), (int)$build->getAttribute('duration', 0) * 1000); + ->addMetric(str_replace($key, $resource->getInternalId(), METRIC_FUNCTION_ID_BUILDS_SUCCESS), 1) // per function + ->addMetric(str_replace($key, $resource->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS), (int)$build->getAttribute('duration', 0) * 1000); break; case 'failed': $queue @@ -830,10 +843,10 @@ class Builds extends Action ->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0)) ->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000) ->addMetric(METRIC_BUILDS_MB_SECONDS, (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $build->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) - ->addMetric(str_replace($key, $resource->getInternalId(), $metrics['builds']), 1) // per function - ->addMetric(str_replace($key, $resource->getInternalId(), $metrics['buildsStorage']), $build->getAttribute('size', 0)) - ->addMetric(str_replace($key, $resource->getInternalId(), $metrics['buildsCompute']), (int)$build->getAttribute('duration', 0) * 1000) - ->addMetric(str_replace($key, $resource->getInternalId(), $metrics['buildsMbSeconds']), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $build->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) + ->addMetric(str_replace($key, $resource->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1) // per function + ->addMetric(str_replace($key, $resource->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0)) + ->addMetric(str_replace($key, $resource->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000) + ->addMetric(str_replace($key, $resource->getInternalId(), METRIC_FUNCTION_ID_BUILDS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $build->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT))) ->setProject($project) ->trigger(); } diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php index e1658d2493..ec75740759 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Template/Create.php @@ -96,7 +96,18 @@ class Create extends Base if (!empty($site->getAttribute('providerRepositoryId'))) { $installation = $dbForPlatform->getDocument('installations', $site->getAttribute('installationId')); - $deployment = $this->redeployVcsSite($request, $site, $project, $installation, $dbForProject, $dbForPlatform, $queueForBuilds, $template, $github); + $deployment = $this->redeployVcsSite( + request: $request, + site: $site, + project: $project, + installation: $installation, + dbForProject: $dbForProject, + dbForPlatform: $dbForPlatform, + queueForBuilds: $queueForBuilds, + template: $template, + github: $github, + activate: $activate, + ); $queueForEvents ->setParam('siteId', $site->getId()) diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Vcs/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Vcs/Create.php index 03590c6a7d..f920f4a42f 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Vcs/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Deployments/Vcs/Create.php @@ -16,6 +16,7 @@ use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\Swoole\Request; +use Utopia\Validator\Boolean; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; use Utopia\VCS\Adapter\Git\GitHub; @@ -60,6 +61,7 @@ class Create extends Base // TODO: Support tag and commit in future ->param('type', '', new WhiteList(['branch']), 'Type of reference passed. Allowed values are: branch') ->param('reference', '', new Text(255), 'VCS reference to create deployment from. Depending on type this can be: branch name') + ->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true) ->inject('request') ->inject('response') ->inject('dbForProject') @@ -71,7 +73,7 @@ class Create extends Base ->callback([$this, 'action']); } - public function action(string $siteId, string $type, string $reference, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Document $project, Event $queueForEvents, Build $queueForBuilds, GitHub $github) + public function action(string $siteId, string $type, string $reference, bool $activate, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Document $project, Event $queueForEvents, Build $queueForBuilds, GitHub $github) { $site = $dbForProject->getDocument('sites', $siteId); @@ -93,6 +95,7 @@ class Create extends Base queueForBuilds: $queueForBuilds, template: $template, github: $github, + activate: $activate, reference: $reference, referenceType: $type ); diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php index 578e0cf164..c9b72983c2 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Update.php @@ -244,7 +244,7 @@ class Update extends Base // Redeploy logic if (!$isConnected && !empty($providerRepositoryId)) { - $this->redeployVcsFunction($request, $site, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github); + $this->redeployVcsFunction($request, $site, $project, $installation, $dbForProject, $queueForBuilds, new Document(), $github, true); } $queueForEvents->setParam('siteId', $site->getId()); diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php index c09f961fc0..6a6cb3237a 100644 --- a/src/Appwrite/Platform/Services/Tasks.php +++ b/src/Appwrite/Platform/Services/Tasks.php @@ -13,6 +13,7 @@ use Appwrite\Platform\Tasks\ScheduleMessages; use Appwrite\Platform\Tasks\SDKs; use Appwrite\Platform\Tasks\Specs; use Appwrite\Platform\Tasks\SSL; +use Appwrite\Platform\Tasks\StatsResources; use Appwrite\Platform\Tasks\Upgrade; use Appwrite\Platform\Tasks\Vars; use Appwrite\Platform\Tasks\Version; @@ -38,6 +39,7 @@ class Tasks extends Service ->addAction(Upgrade::getName(), new Upgrade()) ->addAction(Vars::getName(), new Vars()) ->addAction(Version::getName(), new Version()) + ->addAction(StatsResources::getName(), new StatsResources()) ; } } diff --git a/src/Appwrite/Platform/Services/Workers.php b/src/Appwrite/Platform/Services/Workers.php index bdbb99be75..c37bc80d6e 100644 --- a/src/Appwrite/Platform/Services/Workers.php +++ b/src/Appwrite/Platform/Services/Workers.php @@ -10,8 +10,13 @@ use Appwrite\Platform\Workers\Functions; use Appwrite\Platform\Workers\Mails; use Appwrite\Platform\Workers\Messaging; use Appwrite\Platform\Workers\Migrations; +use Appwrite\Platform\Workers\StatsResources; +use Appwrite\Platform\Workers\StatsUsage; +use Appwrite\Platform\Workers\StatsUsageDump; +/** remove */ use Appwrite\Platform\Workers\Usage; use Appwrite\Platform\Workers\UsageDump; +/** /remove */ use Appwrite\Platform\Workers\Webhooks; use Utopia\Platform\Service; @@ -29,10 +34,14 @@ class Workers extends Service ->addAction(Mails::getName(), new Mails()) ->addAction(Messaging::getName(), new Messaging()) ->addAction(Webhooks::getName(), new Webhooks()) + ->addAction(StatsUsageDump::getName(), new StatsUsageDump()) + ->addAction(StatsUsage::getName(), new StatsUsage()) + ->addAction(Migrations::getName(), new Migrations()) + ->addAction(StatsResources::getName(), new StatsResources()) + /** Remove */ ->addAction(UsageDump::getName(), new UsageDump()) ->addAction(Usage::getName(), new Usage()) - ->addAction(Migrations::getName(), new Migrations()) - + /** /remove */ ; } } diff --git a/src/Appwrite/Platform/Tasks/StatsResources.php b/src/Appwrite/Platform/Tasks/StatsResources.php new file mode 100644 index 0000000000..ac3b9ead73 --- /dev/null +++ b/src/Appwrite/Platform/Tasks/StatsResources.php @@ -0,0 +1,81 @@ +desc('Schedules projects for usage count') + ->inject('dbForPlatform') + ->inject('logError') + ->inject('queueForStatsResources') + ->callback([$this, 'action']); + } + + public function action(Database $dbForPlatform, callable $logError, EventStatsResources $queue): void + { + $this->logError = $logError; + $this->dbForPlatform = $dbForPlatform; + + Console::title("Stats resources V1"); + + Console::success('Stats resources: started'); + + $interval = (int) System::getEnv('_APP_STATS_RESOURCES_INTERVAL', '3600'); + Console::loop(function () use ($queue) { + Authorization::disable(); + Authorization::setDefaultStatus(false); + + $last24Hours = (new \DateTime())->sub(\DateInterval::createFromDateString('24 hours')); + /** + * For each project that were accessed in last 24 hours + */ + $this->foreachDocument($this->dbForPlatform, 'projects', [ + Query::greaterThanEqual('accessedAt', DateTime::format($last24Hours)) + ], function ($project) use ($queue) { + $queue + ->setProject($project) + ->trigger(); + Console::success('project: ' . $project->getId() . '(' . $project->getInternalId() . ')' . ' queued'); + }); + }, $interval); + + Console::log("Stats resources: exited"); + } +} diff --git a/src/Appwrite/Platform/Workers/Audits.php b/src/Appwrite/Platform/Workers/Audits.php index c0bcab1c3a..f1ae46eea7 100644 --- a/src/Appwrite/Platform/Workers/Audits.php +++ b/src/Appwrite/Platform/Workers/Audits.php @@ -6,15 +6,33 @@ use Appwrite\Auth\Auth; use Exception; use Throwable; use Utopia\Audit\Audit; +use Utopia\CLI\Console; use Utopia\Database\Database; +use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Authorization; use Utopia\Database\Exception\Structure; use Utopia\Platform\Action; use Utopia\Queue\Message; +use Utopia\System\System; class Audits extends Action { + private const BATCH_SIZE_DEVELOPMENT = 1; // smaller batch size for development + private const BATCH_SIZE_PRODUCTION = 5_000; + private const BATCH_AGGREGATION_INTERVAL = 60; // in seconds + + private int $lastTriggeredTime = 0; + + private array $logs = []; + + private function getBatchSize(): int + { + return System::getEnv('_APP_ENV', 'development') === 'development' + ? self::BATCH_SIZE_DEVELOPMENT + : self::BATCH_SIZE_PRODUCTION; + } + public static function getName(): string { return 'audits'; @@ -30,6 +48,8 @@ class Audits extends Action ->inject('message') ->inject('dbForProject') ->callback(fn ($message, $dbForProject) => $this->action($message, $dbForProject)); + + $this->lastTriggeredTime = time(); } @@ -44,13 +64,14 @@ class Audits extends Action */ public function action(Message $message, Database $dbForProject): void { - $payload = $message->getPayload() ?? []; if (empty($payload)) { throw new Exception('Missing payload'); } + Console::info('Aggregating audit logs'); + $event = $payload['event'] ?? ''; $auditPayload = $payload['payload'] ?? ''; $mode = $payload['mode'] ?? ''; @@ -63,23 +84,48 @@ class Audits extends Action $userEmail = $user->getAttribute('email', ''); $userType = $user->getAttribute('type', Auth::ACTIVITY_TYPE_USER); - $audit = new Audit($dbForProject); - $audit->log( - userId: $user->getInternalId(), - // Pass first, most verbose event pattern - event: $event, - resource: $resource, - userAgent: $userAgent, - ip: $ip, - location: '', - data: [ + // Create event data + $eventData = [ + 'userId' => $user->getInternalId(), + 'event' => $event, + 'resource' => $resource, + 'userAgent' => $userAgent, + 'ip' => $ip, + 'location' => '', + 'data' => [ 'userId' => $user->getId(), 'userName' => $userName, 'userEmail' => $userEmail, 'userType' => $userType, 'mode' => $mode, 'data' => $auditPayload, - ] - ); + ], + 'timestamp' => DateTime::formatTz(DateTime::now()) + ]; + + $this->logs[] = $eventData; + + // Check if we should process the batch by checking both for the batch size and the elapsed time + $batchSize = $this->getBatchSize(); + $shouldProcessBatch = count($this->logs) >= $batchSize; + if (!$shouldProcessBatch && count($this->logs) > 0) { + $shouldProcessBatch = (time() - $this->lastTriggeredTime) >= self::BATCH_AGGREGATION_INTERVAL; + } + + if ($shouldProcessBatch) { + Console::log('Processing batch with ' . count($this->logs) . ' events'); + $audit = new Audit($dbForProject); + + try { + $audit->logBatch($this->logs); + Console::success('Audit logs processed successfully'); + } catch (Throwable $e) { + Console::error('Error processing audit logs: ' . $e->getMessage()); + } finally { + // Clear the pending events after successful batch processing + $this->logs = []; + $this->lastTriggeredTime = time(); + } + } } } diff --git a/src/Appwrite/Platform/Workers/Databases.php b/src/Appwrite/Platform/Workers/Databases.php index 441b09b4cc..0f400e0107 100644 --- a/src/Appwrite/Platform/Workers/Databases.php +++ b/src/Appwrite/Platform/Workers/Databases.php @@ -577,39 +577,25 @@ class Databases extends Action */ protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void { - $count = 0; - $chunk = 0; - $limit = 50; - $sum = $limit; + $start = \microtime(true); - $executionStart = \microtime(true); + try { + $documents = $database->deleteDocuments($collection, $queries); + } catch (\Throwable $th) { + Console::error('Failed to delete documents for collection ' . $collection . ': ' . $th->getMessage()); + return; + } - while ($sum === $limit) { - $chunk++; - - $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries)); - - $sum = count($results); - - Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents'); - - foreach ($results as $document) { - if ($database->deleteDocument($document->getCollection(), $document->getId())) { - Console::success('Deleted document "' . $document->getId() . '" successfully'); - - if (\is_callable($callback)) { - $callback($document); - } - } else { - Console::warning('Failed to delete document: ' . $document->getId()); - } - $count++; + if (\is_callable($callback)) { + foreach ($documents as $document) { + $callback($document); } } - $executionEnd = \microtime(true); + $end = \microtime(true); + $count = \count($documents); - Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); + Console::info("Deleted {$count} documents by group in " . ($end - $start) . " seconds"); } protected function trigger( diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index 09c71dc004..5e894341c7 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -7,6 +7,7 @@ use Appwrite\Certificates\Adapter as CertificatesAdapter; use Appwrite\Extend\Exception; use Executor\Executor; use Throwable; +use Utopia\Abuse\Adapters\TimeLimit\Database as AbuseDatabase; use Utopia\Audit\Audit; use Utopia\Cache\Adapter\Filesystem; use Utopia\Cache\Cache; @@ -510,7 +511,8 @@ class Deletes extends Action $projectCollectionIds = [ ...\array_keys(Config::getParam('collections', [])['projects']), - Audit::COLLECTION + Audit::COLLECTION, + AbuseDatabase::COLLECTION, ]; $limit = \count($projectCollectionIds) + 25; @@ -1039,65 +1041,39 @@ class Deletes extends Action $this->deleteRuntimes($getProjectDB, $document, $project); } - /** - * @param Document $document to be deleted - * @param Database $database to delete it from - * @param callable|null $callback to perform after document is deleted - * @return void - */ - private function deleteById(Document $document, Database $database, callable $callback = null): void - { - if ($database->deleteDocument($document->getCollection(), $document->getId())) { - Console::success('Deleted document "' . $document->getId() . '" successfully'); - - if (is_callable($callback)) { - $callback($document); - } - } else { - Console::error('Failed to delete document: ' . $document->getId()); - } - } - /** * @param string $collection collectionID * @param array $queries * @param Database $database - * @param callable|null $callback + * @param ?callable $callback * @return void * @throws Exception */ - protected function deleteByGroup(string $collection, array $queries, Database $database, callable $callback = null): void - { - $count = 0; - $chunk = 0; - $limit = 50; - $sum = $limit; + protected function deleteByGroup( + string $collection, + array $queries, + Database $database, + ?callable $callback = null + ): void { + $start = \microtime(true); - $executionStart = \microtime(true); + try { + $documents = $database->deleteDocuments($collection, $queries); + } catch (\Throwable $th) { + Console::error('Failed to delete documents for collection ' . $collection . ': ' . $th->getMessage()); + return; + } - while ($sum === $limit) { - $chunk++; - - try { - $results = $database->find($collection, [Query::limit($limit), ...$queries]); - } catch (DatabaseException $e) { - Console::error('Failed to find documents for collection ' . $collection . ': ' . $e->getMessage()); - return; - } - - $sum = count($results); - - Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents'); - - foreach ($results as $document) { - $this->deleteById($document, $database, $callback); - $count++; + if (\is_callable($callback)) { + foreach ($documents as $document) { + $callback($document); } } - $executionEnd = \microtime(true); + $end = \microtime(true); + $count = \count($documents); - Console::info("Deleted {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); + Console::info("Deleted {$count} documents by group in " . ($end - $start) . " seconds"); } /** @@ -1111,25 +1087,23 @@ class Deletes extends Action protected function listByGroup(string $collection, array $queries, Database $database, callable $callback = null): void { $count = 0; - $chunk = 0; - $limit = 50; - $results = []; + $limit = 1000; $sum = $limit; $cursor = null; - $executionStart = \microtime(true); + $start = \microtime(true); while ($sum === $limit) { - $chunk++; - $mergedQueries = \array_merge([Query::limit($limit)], $queries); - if ($cursor instanceof Document) { - $mergedQueries[] = Query::cursorAfter($cursor); + $queries = \array_merge([Query::limit($limit)], $queries); + + if ($cursor !== null) { + $queries[] = Query::cursorAfter($cursor); } - $results = $database->find($collection, $mergedQueries); + $results = $database->find($collection, $queries); - $sum = count($results); + $sum = \count($results); if ($sum > 0) { $cursor = $results[$sum - 1]; @@ -1144,9 +1118,9 @@ class Deletes extends Action } } - $executionEnd = \microtime(true); + $end = \microtime(true); - Console::info("Listed {$count} document by group in " . ($executionEnd - $executionStart) . " seconds"); + Console::info("Listed {$count} documents by group in " . ($end - $start) . " seconds"); } /** diff --git a/src/Appwrite/Platform/Workers/Functions.php b/src/Appwrite/Platform/Workers/Functions.php index 33f0a1e827..f6d441ef3c 100644 --- a/src/Appwrite/Platform/Workers/Functions.php +++ b/src/Appwrite/Platform/Workers/Functions.php @@ -5,7 +5,7 @@ namespace Appwrite\Platform\Workers; use Ahc\Jwt\JWT; use Appwrite\Event\Event; use Appwrite\Event\Func; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Utopia\Response\Model\Execution; use Exception; @@ -46,13 +46,13 @@ class Functions extends Action ->inject('dbForProject') ->inject('queueForFunctions') ->inject('queueForEvents') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('log') ->inject('isResourceBlocked') - ->callback(fn (Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log, callable $isResourceBlocked) => $this->action($project, $message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log, $isResourceBlocked)); + ->callback(fn (Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked) => $this->action($project, $message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForStatsUsage, $log, $isResourceBlocked)); } - public function action(Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log, callable $isResourceBlocked): void + public function action(Document $project, Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, StatsUsage $queueForStatsUsage, Log $log, callable $isResourceBlocked): void { $payload = $message->getPayload() ?? []; @@ -137,7 +137,7 @@ class Functions extends Action log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, - queueForUsage: $queueForUsage, + queueForStatsUsage: $queueForStatsUsage, queueForEvents: $queueForEvents, project: $project, function: $function, @@ -177,7 +177,7 @@ class Functions extends Action log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, - queueForUsage: $queueForUsage, + queueForStatsUsage: $queueForStatsUsage, queueForEvents: $queueForEvents, project: $project, function: $function, @@ -199,7 +199,7 @@ class Functions extends Action log: $log, dbForProject: $dbForProject, queueForFunctions: $queueForFunctions, - queueForUsage: $queueForUsage, + queueForStatsUsage: $queueForStatsUsage, queueForEvents: $queueForEvents, project: $project, function: $function, @@ -285,7 +285,7 @@ class Functions extends Action * @param Log $log * @param Database $dbForProject * @param Func $queueForFunctions - * @param Usage $queueForUsage + * @param StatsUsage $queueForStatsUsage * @param Event $queueForEvents * @param Document $project * @param Document $function @@ -309,7 +309,7 @@ class Functions extends Action Log $log, Database $dbForProject, Func $queueForFunctions, - Usage $queueForUsage, + StatsUsage $queueForStatsUsage, Event $queueForEvents, Document $project, Document $function, @@ -481,8 +481,8 @@ class Functions extends Action 'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(), 'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '', 'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '', - 'APPWRITE_COMPUTE_CPUS' => ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT), - 'APPWRITE_COMPUTE_MEMORY' => ($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT), + 'APPWRITE_FUNCTION_CPUS' => ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT), + 'APPWRITE_FUNCTION_MEMORY' => ($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT), 'APPWRITE_VERSION' => APP_VERSION_STABLE, 'APPWRITE_REGION' => $project->getAttribute('region'), 'APPWRITE_DEPLOYMENT_TYPE' => $deployment->getAttribute('type', ''), @@ -554,7 +554,7 @@ class Functions extends Action $errorCode = $th->getCode(); } finally { /** Trigger usage queue */ - $queueForUsage + $queueForStatsUsage ->setProject($project) ->addMetric(METRIC_EXECUTIONS, 1) ->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS), 1) diff --git a/src/Appwrite/Platform/Workers/Messaging.php b/src/Appwrite/Platform/Workers/Messaging.php index aee60a2bb5..cf2b8bfc84 100644 --- a/src/Appwrite/Platform/Workers/Messaging.php +++ b/src/Appwrite/Platform/Workers/Messaging.php @@ -2,7 +2,7 @@ namespace Appwrite\Platform\Workers; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Messaging\Status as MessageStatus; use Swoole\Runtime; use Utopia\CLI\Console; @@ -70,8 +70,8 @@ class Messaging extends Action ->inject('log') ->inject('dbForProject') ->inject('deviceForFiles') - ->inject('queueForUsage') - ->callback(fn (Message $message, Document $project, Log $log, Database $dbForProject, Device $deviceForFiles, Usage $queueForUsage) => $this->action($message, $project, $log, $dbForProject, $deviceForFiles, $queueForUsage)); + ->inject('queueForStatsUsage') + ->callback(fn (Message $message, Document $project, Log $log, Database $dbForProject, Device $deviceForFiles, StatsUsage $queueForStatsUsage) => $this->action($message, $project, $log, $dbForProject, $deviceForFiles, $queueForStatsUsage)); } /** @@ -80,7 +80,7 @@ class Messaging extends Action * @param Log $log * @param Database $dbForProject * @param Device $deviceForFiles - * @param Usage $queueForUsage + * @param StatsUsage $queueForStatsUsage * @return void * @throws \Exception */ @@ -90,7 +90,7 @@ class Messaging extends Action Log $log, Database $dbForProject, Device $deviceForFiles, - Usage $queueForUsage + StatsUsage $queueForStatsUsage ): void { Runtime::setHookFlags(SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_TCP); $payload = $message->getPayload() ?? []; @@ -111,7 +111,7 @@ class Messaging extends Action case MESSAGE_SEND_TYPE_EXTERNAL: $message = $dbForProject->getDocument('messages', $payload['messageId']); - $this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $project, $queueForUsage); + $this->sendExternalMessage($dbForProject, $message, $deviceForFiles, $project, $queueForStatsUsage); break; default: throw new \Exception('Unknown message type: ' . $type); @@ -123,7 +123,7 @@ class Messaging extends Action Document $message, Device $deviceForFiles, Document $project, - Usage $queueForUsage + StatsUsage $queueForStatsUsage ): void { $topicIds = $message->getAttribute('topics', []); $targetIds = $message->getAttribute('targets', []); @@ -229,8 +229,8 @@ class Messaging extends Action /** * @var array $results */ - $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) { + $results = batch(\array_map(function ($providerId) use ($identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project, $queueForStatsUsage) { + return function () use ($providerId, $identifiers, &$providers, $default, $message, $dbForProject, $deviceForFiles, $project, $queueForStatsUsage) { if (\array_key_exists($providerId, $providers)) { $provider = $providers[$providerId]; } else { @@ -257,8 +257,8 @@ class Messaging extends Action $adapter->getMaxMessagesPerRequest() ); - 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) { + return batch(\array_map(function ($batch) use ($message, $provider, $adapter, $dbForProject, $deviceForFiles, $project, $queueForStatsUsage) { + return function () use ($batch, $message, $provider, $adapter, $dbForProject, $deviceForFiles, $project, $queueForStatsUsage) { $deliveredTotal = 0; $deliveryErrors = []; $messageData = clone $message; @@ -298,7 +298,7 @@ class Messaging extends Action $deliveryErrors[] = 'Failed sending to targets with error: ' . $e->getMessage(); } finally { $errorTotal = \count($deliveryErrors); - $queueForUsage + $queueForStatsUsage ->setProject($project) ->addMetric(METRIC_MESSAGES, ($deliveredTotal + $errorTotal)) ->addMetric(METRIC_MESSAGES_SENT, $deliveredTotal) diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index cd567f6fa3..3bf8e06356 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -188,28 +188,21 @@ class Migrations extends Action } /** - * @throws \Utopia\Database\Exception - * @throws Authorization - * @throws Conflict - * @throws Restricted - * @throws Structure - */ - protected function removeAPIKey(Document $apiKey): void - { - $this->dbForPlatform->deleteDocument('keys', $apiKey->getId()); - } - - /** - * @throws Authorization - * @throws Structure - * @throws \Utopia\Database\Exception * @throws Exception */ protected function generateAPIKey(Document $project): string { $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400, 0); + $apiKey = $jwt->encode([ 'projectId' => $project->getId(), + 'disabledMetrics' => [ + METRIC_DATABASES_OPERATIONS_READS, + METRIC_DATABASES_OPERATIONS_WRITES, + METRIC_NETWORK_REQUESTS, + METRIC_NETWORK_INBOUND, + METRIC_NETWORK_OUTBOUND, + ], 'scopes' => [ 'users.read', 'users.write', @@ -222,12 +215,9 @@ class Migrations extends Action 'functions.read', 'functions.write', 'databases.read', - 'databases.write', 'collections.read', - 'collections.write', 'documents.read', - 'documents.write' - ] + ], ]); return API_KEY_DYNAMIC . '_' . $apiKey; diff --git a/src/Appwrite/Platform/Workers/StatsResources.php b/src/Appwrite/Platform/Workers/StatsResources.php new file mode 100644 index 0000000000..a6101522fb --- /dev/null +++ b/src/Appwrite/Platform/Workers/StatsResources.php @@ -0,0 +1,340 @@ + 'Y-m-d H:00', + '1d' => 'Y-m-d 00:00', + 'inf' => '0000-00-00 00:00' + ]; + + /** + * @var array $documents + * + * Array of documents to batch write + * + */ + protected array $documents = []; + + public static function getName(): string + { + return 'stats-resources'; + } + + + /** + * @throws Exception + */ + public function __construct() + { + $this + ->desc('Stats resources worker') + ->inject('message') + ->inject('project') + ->inject('getProjectDB') + ->inject('getLogsDB') + ->inject('dbForPlatform') + ->inject('logError') + ->callback([$this, 'action']); + } + + /** + * @param Message $message + * @param Document $project + * @param callable $getProjectDB + * @return void + * @throws \Utopia\Database\Exception + * @throws Exception + */ + public function action(Message $message, Document $project, callable $getProjectDB, callable $getLogsDB, Database $dbForPlatform, callable $logError): void + { + $this->logError = $logError; + + $payload = $message->getPayload() ?? []; + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + if (empty($project->getAttribute('database'))) { + var_dump($payload); + return; + } + + // Reset documents for each job + $this->documents = []; + + $this->countForProject($dbForPlatform, $getLogsDB, $getProjectDB, $project); + } + + + protected function countForProject(Database $dbForPlatform, callable $getLogsDB, callable $getProjectDB, Document $project): void + { + Console::info('Begining count for: ' . $project->getId()); + + $dbForLogs = null; + $dbForProject = null; + try { + /** @var \Utopia\Database\Database $dbForLogs */ + $dbForLogs = call_user_func($getLogsDB, $project); + /** @var \Utopia\Database\Database $dbForProject */ + $dbForProject = call_user_func($getProjectDB, $project); + } catch (Throwable $th) { + Console::error('Unable to get database'); + Console::error($th->getMessage()); + return; + } + + try { + + $region = $project->getAttribute('region'); + + $platforms = $dbForPlatform->count('platforms', [ + Query::equal('projectInternalId', [$project->getInternalId()]) + ]); + $webhooks = $dbForPlatform->count('webhooks', [ + Query::equal('projectInternalId', [$project->getInternalId()]) + ]); + $keys = $dbForPlatform->count('keys', [ + Query::equal('projectInternalId', [$project->getInternalId()]) + ]); + $databases = $dbForProject->count('databases'); + $buckets = $dbForProject->count('buckets'); + $users = $dbForProject->count('users'); + + $last30Days = (new \DateTime())->sub(\DateInterval::createFromDateString('30 days'))->format('Y-m-d 00:00:00'); + $usersMAU = $dbForProject->count('users', [ + Query::greaterThanEqual('accessedAt', $last30Days) + ]); + $last24Hours = (new \DateTime())->sub(\DateInterval::createFromDateString('24 hours'))->format('Y-m-d h:m:00'); + $usersDAU = $dbForProject->count('users', [ + Query::greaterThanEqual('accessedAt', $last24Hours) + ]); + $last7Days = (new \DateTime())->sub(\DateInterval::createFromDateString('7 days'))->format('Y-m-d 00:00:00'); + $usersWAU = $dbForProject->count('users', [ + Query::greaterThanEqual('accessedAt', $last7Days) + ]); + $teams = $dbForProject->count('teams'); + $functions = $dbForProject->count('functions'); + $messages = $dbForProject->count('messages'); + $providers = $dbForProject->count('providers'); + $topics = $dbForProject->count('topics'); + + $metrics = [ + METRIC_DATABASES => $databases, + METRIC_BUCKETS => $buckets, + METRIC_USERS => $users, + METRIC_FUNCTIONS => $functions, + METRIC_TEAMS => $teams, + METRIC_MESSAGES => $messages, + METRIC_MAU => $usersMAU, + METRIC_DAU => $usersDAU, + METRIC_WAU => $usersWAU, + METRIC_WEBHOOKS => $webhooks, + METRIC_PLATFORMS => $platforms, + METRIC_PROVIDERS => $providers, + METRIC_TOPICS => $topics, + METRIC_KEYS => $keys, + ]; + + foreach ($metrics as $metric => $value) { + $this->createStatsDocuments($region, $metric, $value); + } + + try { + $this->countForBuckets($dbForProject, $dbForLogs, $region); + } catch (Throwable $th) { + call_user_func_array($this->logError, [$th, "StatsResources", "count_for_buckets_{$project->getId()}"]); + } + + try { + $this->countImageTransformations($dbForProject, $dbForLogs, $region); + } catch (Throwable $th) { + call_user_func_array($this->logError, [$th, "StatsResources", "count_for_buckets_{$project->getId()}"]); + } + + try { + $this->countForDatabase($dbForProject, $dbForLogs, $region); + } catch (Throwable $th) { + call_user_func_array($this->logError, [$th, "StatsResources", "count_for_database_{$project->getId()}"]); + } + + try { + $this->countForFunctions($dbForProject, $dbForLogs, $region); + } catch (Throwable $th) { + call_user_func_array($this->logError, [$th, "StatsResources", "count_for_functions_{$project->getId()}"]); + } + + $this->writeDocuments($dbForLogs, $project); + } catch (Throwable $th) { + call_user_func_array($this->logError, [$th, "StatsResources", "count_for_project_{$project->getId()}"]); + } + + Console::info('End of count for: ' . $project->getId()); + } + + protected function countForBuckets(Database $dbForProject, Database $dbForLogs, string $region) + { + $totalFiles = 0; + $totalStorage = 0; + $this->foreachDocument($dbForProject, 'buckets', [], function ($bucket) use ($dbForProject, $dbForLogs, $region, &$totalFiles, &$totalStorage) { + $files = $dbForProject->count('bucket_' . $bucket->getInternalId()); + + $metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES); + $this->createStatsDocuments($region, $metric, $files); + + $storage = $dbForProject->sum('bucket_' . $bucket->getInternalId(), 'sizeActual'); + $metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE); + $this->createStatsDocuments($region, $metric, $storage); + + $totalStorage += $storage; + $totalFiles += $files; + }); + + $this->createStatsDocuments($region, METRIC_FILES, $totalFiles); + $this->createStatsDocuments($region, METRIC_FILES_STORAGE, $totalStorage); + } + + /** + * Need separate function to count per period data + */ + protected function countImageTransformations(Database $dbForProject, Database $dbForLogs, string $region) + { + $totalImageTransformations = 0; + $last30Days = (new \DateTime())->sub(\DateInterval::createFromDateString('30 days'))->format('Y-m-d 00:00:00'); + $this->foreachDocument($dbForProject, 'buckets', [], function ($bucket) use ($dbForProject, $last30Days, $region, &$totalImageTransformations) { + $imageTransformations = $dbForProject->count('bucket_' . $bucket->getInternalId(), [ + Query::greaterThanEqual('transformedAt', $last30Days), + ]); + $metric = str_replace('{bucketInternalId}', $bucket->getInternalId(), METRIC_BUCKET_ID_FILES_IMAGES_TRANSFORMED); + $this->createStatsDocuments($region, $metric, $imageTransformations); + $totalImageTransformations += $imageTransformations; + }); + + $this->createStatsDocuments($region, METRIC_FILES_IMAGES_TRANSFORMED, $totalImageTransformations); + } + + protected function countForDatabase(Database $dbForProject, Database $dbForLogs, string $region) + { + $totalCollections = 0; + $totalDocuments = 0; + + $this->foreachDocument($dbForProject, 'databases', [], function ($database) use ($dbForProject, $dbForLogs, $region, &$totalCollections, &$totalDocuments) { + $collections = $dbForProject->count('database_' . $database->getInternalId()); + + $metric = str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS); + $this->createStatsDocuments($region, $metric, $collections); + + $documents = $this->countForCollections($dbForProject, $dbForLogs, $database, $region); + + $totalDocuments += $documents; + $totalCollections += $collections; + }); + + $this->createStatsDocuments($region, METRIC_COLLECTIONS, $totalCollections); + $this->createStatsDocuments($region, METRIC_DOCUMENTS, $totalDocuments); + } + protected function countForCollections(Database $dbForProject, Database $dbForLogs, Document $database, string $region): int + { + $databaseDocuments = 0; + $this->foreachDocument($dbForProject, 'database_' . $database->getInternalId(), [], function ($collection) use ($dbForProject, $dbForLogs, $database, $region, &$totalCollections, &$databaseDocuments) { + $documents = $dbForProject->count('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + + $metric = str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS); + $this->createStatsDocuments($region, $metric, $documents); + + $databaseDocuments += $documents; + }); + + $metric = str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_DOCUMENTS); + $this->createStatsDocuments($region, $metric, $databaseDocuments); + + return $databaseDocuments; + } + + protected function countForFunctions(Database $dbForProject, Database $dbForLogs, string $region) + { + $deploymentsStorage = $dbForProject->sum('deployments', 'size'); + $buildsStorage = $dbForProject->sum('builds', 'size'); + $this->createStatsDocuments($region, METRIC_DEPLOYMENTS_STORAGE, $deploymentsStorage); + $this->createStatsDocuments($region, METRIC_BUILDS_STORAGE, $buildsStorage); + + $deployments = $dbForProject->count('deployments'); + $builds = $dbForProject->count('builds'); + $this->createStatsDocuments($region, METRIC_DEPLOYMENTS, $deployments); + $this->createStatsDocuments($region, METRIC_BUILDS, $builds); + + + $this->foreachDocument($dbForProject, 'functions', [], function (Document $function) use ($dbForProject, $dbForLogs, $region) { + $functionDeploymentsStorage = $dbForProject->sum('deployments', 'size', [ + Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]), + ]); + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS_STORAGE), $functionDeploymentsStorage); + + $functionDeployments = $dbForProject->count('deployments', [ + Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]), + ]); + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_DEPLOYMENTS), $functionDeployments); + + /** + * As deployments and builds have 1-1 relationship, + * the count for one should match the other + */ + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS), $functionDeployments); + + $functionBuildsStorage = 0; + + $this->foreachDocument($dbForProject, 'deployments', [ + Query::equal('resourceInternalId', [$function->getInternalId()]), + Query::equal('resourceType', [RESOURCE_TYPE_FUNCTIONS]), + ], function (Document $deployment) use ($dbForProject, &$functionBuildsStorage): void { + $build = $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')); + $functionBuildsStorage += $build->getAttribute('size', 0); + }); + + $this->createStatsDocuments($region, str_replace(['{resourceType}','{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS,$function->getInternalId()], METRIC_RESOURCE_TYPE_ID_BUILDS_STORAGE), $functionBuildsStorage); + }); + } + + protected function createStatsDocuments(string $region, string $metric, int $value) + { + foreach ($this->periods as $period => $format) { + $time = 'inf' === $period ? null : \date($format, \time()); + $id = \md5("{$time}_{$period}_{$metric}"); + + $this->documents[] = new Document([ + '$id' => $id, + 'metric' => $metric, + 'period' => $period, + 'region' => $region, + 'value' => $value, + 'time' => $time, + ]); + } + } + + protected function writeDocuments(Database $dbForLogs, Document $project): void + { + $dbForLogs->createOrUpdateDocuments( + 'stats', + $this->documents + ); + $this->documents = []; + Console::success('Stats written to logs db for project: ' . $project->getId() . '(' . $project->getInternalId() . ')'); + } +} diff --git a/src/Appwrite/Platform/Workers/StatsUsage.php b/src/Appwrite/Platform/Workers/StatsUsage.php new file mode 100644 index 0000000000..c4d8b0e8d2 --- /dev/null +++ b/src/Appwrite/Platform/Workers/StatsUsage.php @@ -0,0 +1,246 @@ +desc('Stats usage worker') + ->inject('message') + ->inject('getProjectDB') + ->inject('queueForStatsUsageDump') + ->callback([$this, 'action']); + + $this->lastTriggeredTime = time(); + } + + /** + * @param Message $message + * @param callable $getProjectDB + * @param StatsUsageDump $queueForStatsUsageDump + * @return void + * @throws \Utopia\Database\Exception + * @throws Exception + */ + public function action(Message $message, callable $getProjectDB, StatsUsageDump $queueForStatsUsageDump): void + { + $payload = $message->getPayload() ?? []; + if (empty($payload)) { + throw new Exception('Missing payload'); + } + //Todo Figure out way to preserve keys when the container is being recreated @shimonewman + + $aggregationInterval = (int) System::getEnv('_APP_USAGE_AGGREGATION_INTERVAL', '20'); + $project = new Document($payload['project'] ?? []); + $projectId = $project->getInternalId(); + foreach ($payload['reduce'] ?? [] as $document) { + if (empty($document)) { + continue; + } + + $this->reduce( + project: $project, + document: new Document($document), + metrics: $payload['metrics'], + getProjectDB: $getProjectDB + ); + } + + $this->stats[$projectId]['project'] = $project; + $this->stats[$projectId]['receivedAt'] = DateTime::now(); + foreach ($payload['metrics'] ?? [] as $metric) { + $this->keys++; + if (!isset($this->stats[$projectId]['keys'][$metric['key']])) { + $this->stats[$projectId]['keys'][$metric['key']] = $metric['value']; + continue; + } + + $this->stats[$projectId]['keys'][$metric['key']] += $metric['value']; + } + + // If keys crossed threshold or X time passed since the last send and there are some keys in the array ($this->stats) + if ( + $this->keys >= self::KEYS_THRESHOLD || + (time() - $this->lastTriggeredTime > $aggregationInterval && $this->keys > 0) + ) { + Console::warning('[' . DateTime::now() . '] Aggregated ' . $this->keys . ' keys'); + + $queueForStatsUsageDump + ->setStats($this->stats) + ->trigger(); + + $this->stats = []; + $this->keys = 0; + $this->lastTriggeredTime = time(); + } + } + + /** + * On Documents that tied by relations like functions>deployments>build || documents>collection>database || buckets>files. + * When we remove a parent document we need to deduct his children aggregation from the project scope. + * @param Document $project + * @param Document $document + * @param array $metrics + * @param callable $getProjectDB + * @return void + */ + private function reduce(Document $project, Document $document, array &$metrics, callable $getProjectDB): void + { + $dbForProject = $getProjectDB($project); + + try { + switch (true) { + case $document->getCollection() === 'users': // users + $sessions = count($document->getAttribute(METRIC_SESSIONS, 0)); + if (!empty($sessions)) { + $metrics[] = [ + 'key' => METRIC_SESSIONS, + 'value' => ($sessions * -1), + ]; + } + break; + case $document->getCollection() === 'databases': // databases + $collections = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS))); + $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{databaseInternalId}', $document->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS))); + if (!empty($collections['value'])) { + $metrics[] = [ + 'key' => METRIC_COLLECTIONS, + 'value' => ($collections['value'] * -1), + ]; + } + + if (!empty($documents['value'])) { + $metrics[] = [ + 'key' => METRIC_DOCUMENTS, + 'value' => ($documents['value'] * -1), + ]; + } + break; + case str_starts_with($document->getCollection(), 'database_') && !str_contains($document->getCollection(), 'collection'): //collections + $parts = explode('_', $document->getCollection()); + $databaseInternalId = $parts[1] ?? 0; + $documents = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$databaseInternalId, $document->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS))); + + if (!empty($documents['value'])) { + $metrics[] = [ + 'key' => METRIC_DOCUMENTS, + 'value' => ($documents['value'] * -1), + ]; + $metrics[] = [ + 'key' => str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_DOCUMENTS), + 'value' => ($documents['value'] * -1), + ]; + } + break; + + case $document->getCollection() === 'buckets': + $files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES))); + $storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE))); + + if (!empty($files['value'])) { + $metrics[] = [ + 'key' => METRIC_FILES, + 'value' => ($files['value'] * -1), + ]; + } + + if (!empty($storage['value'])) { + $metrics[] = [ + 'key' => METRIC_FILES_STORAGE, + 'value' => ($storage['value'] * -1), + ]; + } + break; + + case $document->getCollection() === 'functions': + $deployments = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS))); + $deploymentsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE))); + $builds = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS))); + $buildsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE))); + $buildsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE))); + $executions = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS))); + $executionsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE))); + + if (!empty($deployments['value'])) { + $metrics[] = [ + 'key' => METRIC_DEPLOYMENTS, + 'value' => ($deployments['value'] * -1), + ]; + } + + if (!empty($deploymentsStorage['value'])) { + $metrics[] = [ + 'key' => METRIC_DEPLOYMENTS_STORAGE, + 'value' => ($deploymentsStorage['value'] * -1), + ]; + } + + if (!empty($builds['value'])) { + $metrics[] = [ + 'key' => METRIC_BUILDS, + 'value' => ($builds['value'] * -1), + ]; + } + + if (!empty($buildsStorage['value'])) { + $metrics[] = [ + 'key' => METRIC_BUILDS_STORAGE, + 'value' => ($buildsStorage['value'] * -1), + ]; + } + + if (!empty($buildsCompute['value'])) { + $metrics[] = [ + 'key' => METRIC_BUILDS_COMPUTE, + 'value' => ($buildsCompute['value'] * -1), + ]; + } + + if (!empty($executions['value'])) { + $metrics[] = [ + 'key' => METRIC_EXECUTIONS, + 'value' => ($executions['value'] * -1), + ]; + } + + if (!empty($executionsCompute['value'])) { + $metrics[] = [ + 'key' => METRIC_EXECUTIONS_COMPUTE, + 'value' => ($executionsCompute['value'] * -1), + ]; + } + break; + default: + break; + } + } catch (\Throwable $e) { + console::error("[reducer] " . " {DateTime::now()} " . " {$project->getInternalId()} " . " {$e->getMessage()}"); + } + } +} diff --git a/src/Appwrite/Platform/Workers/StatsUsageDump.php b/src/Appwrite/Platform/Workers/StatsUsageDump.php new file mode 100644 index 0000000000..5d7240f4b5 --- /dev/null +++ b/src/Appwrite/Platform/Workers/StatsUsageDump.php @@ -0,0 +1,353 @@ + true, + METRIC_BUCKETS => true, + METRIC_USERS => true, + METRIC_FUNCTIONS => true, + METRIC_TEAMS => true, + METRIC_MESSAGES => true, + METRIC_MAU => true, + METRIC_WEBHOOKS => true, + METRIC_PLATFORMS => true, + METRIC_PROVIDERS => true, + METRIC_TOPICS => true, + METRIC_KEYS => true, + METRIC_FILES => true, + METRIC_FILES_STORAGE => true, + METRIC_DEPLOYMENTS_STORAGE => true, + METRIC_BUILDS_STORAGE => true, + METRIC_DEPLOYMENTS => true, + METRIC_BUILDS => true, + METRIC_COLLECTIONS => true, + METRIC_DOCUMENTS => true, + ]; + + /** + * Skip metrics associated with parent IDs + * these need to be checked individually with `str_ends_with` + */ + protected array $skipParentIdMetrics = [ + '.files', + '.files.storage', + '.collections', + '.documents', + '.deployments', + '.deployments.storage', + '.builds', + '.builds.storage', + ]; + + /** + * @var callable + */ + protected mixed $getLogsDB; + + protected array $periods = [ + '1h' => 'Y-m-d H:00', + '1d' => 'Y-m-d 00:00', + 'inf' => '0000-00-00 00:00' + ]; + + public static function getName(): string + { + return 'stats-usage-dump'; + } + + /** + * @throws \Exception + */ + public function __construct() + { + $this + ->inject('message') + ->inject('getProjectDB') + ->inject('getLogsDB') + ->inject('register') + ->callback([$this, 'action']); + } + + /** + * @param Message $message + * @param callable $getProjectDB + * @param callable $getLogsDB + * @param Registry $register + * @return void + * @throws Exception + * @throws \Throwable + * @throws \Utopia\Database\Exception + */ + public function action(Message $message, callable $getProjectDB, callable $getLogsDB, Registry $register): void + { + $this->getLogsDB = $getLogsDB; + $this->register = $register; + $payload = $message->getPayload() ?? []; + if (empty($payload)) { + throw new Exception('Missing payload'); + } + + foreach ($payload['stats'] ?? [] as $stats) { + $project = new Document($stats['project'] ?? []); + + $numberOfKeys = !empty($stats['keys']) ? count($stats['keys']) : 0; + $receivedAt = $stats['receivedAt'] ?? 'NONE'; + if ($numberOfKeys === 0) { + continue; + } + + console::log('['.DateTime::now().'] Id: '.$project->getId(). ' InternalId: '.$project->getInternalId(). ' Db: '.$project->getAttribute('database').' ReceivedAt: '.$receivedAt. ' Keys: '.$numberOfKeys); + + try { + /** @var \Utopia\Database\Database $dbForProject */ + $dbForProject = $getProjectDB($project); + foreach ($stats['keys'] ?? [] as $key => $value) { + if ($value == 0) { + continue; + } + + if (str_contains($key, METRIC_DATABASES_STORAGE)) { + try { + $this->handleDatabaseStorage($key, $dbForProject, $project); + } catch (\Exception $e) { + console::error('[' . DateTime::now() . '] failed to calculate database storage for key [' . $key . '] ' . $e->getMessage()); + } + continue; + } + + foreach ($this->periods as $period => $format) { + $time = 'inf' === $period ? null : date($format, time()); + $id = \md5("{$time}_{$period}_{$key}"); + + $document = new Document([ + '$id' => $id, + 'period' => $period, + 'time' => $time, + 'metric' => $key, + 'value' => $value, + 'region' => System::getEnv('_APP_REGION', 'default'), + ]); + + $documentClone = new Document($document->getArrayCopy()); + + $dbForProject->createOrUpdateDocumentsWithIncrease( + 'stats', + 'value', + [$document] + ); + + $this->writeToLogsDB($project, $documentClone); + } + } + } catch (\Exception $e) { + console::error('[' . DateTime::now() . '] project [' . $project->getInternalId() . '] database [' . $project['database'] . '] ' . ' ' . $e->getMessage()); + } + } + } + + private function handleDatabaseStorage(string $key, Database $dbForProject, Document $project): void + { + $data = explode('.', $key); + $start = microtime(true); + + $updateMetric = function (Database $dbForProject, Document $project, int $value, string $key, string $period, string|null $time) { + $id = \md5("{$time}_{$period}_{$key}"); + + $document = new Document([ + '$id' => $id, + 'period' => $period, + 'time' => $time, + 'metric' => $key, + 'value' => $value, + 'region' => System::getEnv('_APP_REGION', 'default'), + ]); + $documentClone = new Document($document->getArrayCopy()); + $dbForProject->createOrUpdateDocumentsWithIncrease( + 'stats', + 'value', + [$document] + ); + $this->writeToLogsDB($project, $documentClone); + }; + + foreach ($this->periods as $period => $format) { + $time = 'inf' === $period ? null : date($format, time()); + $id = \md5("{$time}_{$period}_{$key}"); + + $value = 0; + $previousValue = 0; + try { + $previousValue = ($dbForProject->getDocument('stats', $id))->getAttribute('value', 0); + } catch (\Exception $e) { + // No previous value + } + + switch (count($data)) { + // Collection Level + case self::METRIC_COLLECTION_LEVEL_STORAGE: + Console::log('[' . DateTime::now() . '] Collection Level Storage Calculation [' . $key . ']'); + $databaseInternalId = $data[0]; + $collectionInternalId = $data[1]; + + try { + $value = $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collectionInternalId); + } catch (\Exception $e) { + // Collection not found + if ($e->getMessage() !== 'Collection not found') { + throw $e; + } + } + + // Compare with previous value + $diff = $value - $previousValue; + + if ($diff === 0) { + break; + } + + // Update Collection + $updateMetric($dbForProject, $project, $diff, $key, $period, $time); + + // Update Database + $databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE); + $updateMetric($dbForProject, $project, $diff, $databaseKey, $period, $time); + + // Update Project + $projectKey = METRIC_DATABASES_STORAGE; + $updateMetric($dbForProject, $project, $diff, $projectKey, $period, $time); + break; + // Database Level + case self::METRIC_DATABASE_LEVEL_STORAGE: + Console::log('[' . DateTime::now() . '] Database Level Storage Calculation [' . $key . ']'); + $databaseInternalId = $data[0]; + + $collections = []; + try { + $collections = $dbForProject->find('database_' . $databaseInternalId); + } catch (\Exception $e) { + // Database not found + if ($e->getMessage() !== 'Collection not found') { + throw $e; + } + } + + foreach ($collections as $collection) { + try { + $value += $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId()); + } catch (\Exception $e) { + // Collection not found + if ($e->getMessage() !== 'Collection not found') { + throw $e; + } + } + } + + $diff = $value - $previousValue; + + if ($diff === 0) { + break; + } + + // Update Database + $databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE); + $updateMetric($dbForProject, $project, $diff, $databaseKey, $period, $time); + + // Update Project + $projectKey = METRIC_DATABASES_STORAGE; + $updateMetric($dbForProject, $project, $diff, $projectKey, $period, $time); + break; + // Project Level + case self::METRIC_PROJECT_LEVEL_STORAGE: + Console::log('[' . DateTime::now() . '] Project Level Storage Calculation [' . $key . ']'); + // Get all project databases + $databases = $dbForProject->find('database'); + + // Recalculate all databases + foreach ($databases as $database) { + $collections = $dbForProject->find('database_' . $database->getInternalId()); + + foreach ($collections as $collection) { + try { + $value += $dbForProject->getSizeOfCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + } catch (\Exception $e) { + // Collection not found + if ($e->getMessage() !== 'Collection not found') { + throw $e; + } + } + } + } + + $diff = $value - $previousValue; + + // Update Project + $projectKey = METRIC_DATABASES_STORAGE; + $updateMetric($dbForProject, $project, $diff, $projectKey, $period, $time); + break; + } + } + + $end = microtime(true); + + console::log('[' . DateTime::now() . '] DB Storage Calculation [' . $key . '] took ' . (($end - $start) * 1000) . ' milliseconds'); + } + + protected function writeToLogsDB(Document $project, Document $document): void + { + if (!System::getEnv('_APP_STATS_USAGE_DUAL_WRITING', false)) { + Console::log('Dual Writing is disabled. Skipping...'); + return; + } + + if (array_key_exists($document->getAttribute('metric'), $this->skipBaseMetrics)) { + return; + } + foreach ($this->skipParentIdMetrics as $skipMetric) { + if (str_ends_with($document->getAttribute('metric'), $skipMetric)) { + return; + } + } + + /** @var \Utopia\Database\Database $dbForLogs*/ + $dbForLogs = call_user_func($this->getLogsDB, $project); + + try { + $dbForLogs->createOrUpdateDocumentsWithIncrease( + 'stats', + 'value', + [$document] + ); + Console::success('Usage logs pushed to Logs DB'); + } catch (\Throwable $th) { + Console::error($th->getMessage()); + } + + $this->register->get('pools')->get('logs')->reclaim(); + } +} diff --git a/src/Appwrite/Platform/Workers/Webhooks.php b/src/Appwrite/Platform/Workers/Webhooks.php index a76e4f17b0..c903dafdae 100644 --- a/src/Appwrite/Platform/Workers/Webhooks.php +++ b/src/Appwrite/Platform/Workers/Webhooks.php @@ -3,7 +3,7 @@ namespace Appwrite\Platform\Workers; use Appwrite\Event\Mail; -use Appwrite\Event\Usage; +use Appwrite\Event\StatsUsage; use Appwrite\Template\Template; use Exception; use Utopia\Database\Database; @@ -35,9 +35,9 @@ class Webhooks extends Action ->inject('project') ->inject('dbForPlatform') ->inject('queueForMails') - ->inject('queueForUsage') + ->inject('queueForStatsUsage') ->inject('log') - ->callback(fn (Message $message, Document $project, Database $dbForPlatform, Mail $queueForMails, Usage $queueForUsage, Log $log) => $this->action($message, $project, $dbForPlatform, $queueForMails, $queueForUsage, $log)); + ->callback(fn (Message $message, Document $project, Database $dbForPlatform, Mail $queueForMails, StatsUsage $queueForStatsUsage, Log $log) => $this->action($message, $project, $dbForPlatform, $queueForMails, $queueForStatsUsage, $log)); } /** @@ -49,7 +49,7 @@ class Webhooks extends Action * @return void * @throws Exception */ - public function action(Message $message, Document $project, Database $dbForPlatform, Mail $queueForMails, Usage $queueForUsage, Log $log): void + public function action(Message $message, Document $project, Database $dbForPlatform, Mail $queueForMails, StatsUsage $queueForStatsUsage, Log $log): void { $this->errors = []; $payload = $message->getPayload() ?? []; @@ -66,7 +66,7 @@ class Webhooks extends Action foreach ($project->getAttribute('webhooks', []) as $webhook) { if (array_intersect($webhook->getAttribute('events', []), $events)) { - $this->execute($events, $webhookPayload, $webhook, $user, $project, $dbForPlatform, $queueForMails, $queueForUsage); + $this->execute($events, $webhookPayload, $webhook, $user, $project, $dbForPlatform, $queueForMails, $queueForStatsUsage); } } @@ -85,7 +85,7 @@ class Webhooks extends Action * @param Mail $queueForMails * @return void */ - private function execute(array $events, string $payload, Document $webhook, Document $user, Document $project, Database $dbForPlatform, Mail $queueForMails, Usage $queueForUsage): void + private function execute(array $events, string $payload, Document $webhook, Document $user, Document $project, Database $dbForPlatform, Mail $queueForMails, StatsUsage $queueForStatsUsage): void { if ($webhook->getAttribute('enabled') !== true) { return; @@ -168,7 +168,7 @@ class Webhooks extends Action $dbForPlatform->purgeCachedDocument('projects', $project->getId()); $this->errors[] = $logs; - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_WEBHOOKS_FAILED, 1) ->addMetric(str_replace('{webhookInternalId}', $webhook->getInternalId(), METRIC_WEBHOOK_ID_FAILED), 1) ; @@ -178,13 +178,13 @@ class Webhooks extends Action $webhook->setAttribute('attempts', 0); // Reset attempts on success $dbForPlatform->updateDocument('webhooks', $webhook->getId(), $webhook); $dbForPlatform->purgeCachedDocument('projects', $project->getId()); - $queueForUsage + $queueForStatsUsage ->addMetric(METRIC_WEBHOOKS_SENT, 1) ->addMetric(str_replace('{webhookInternalId}', $webhook->getInternalId(), METRIC_WEBHOOK_ID_SENT), 1) ; } - $queueForUsage + $queueForStatsUsage ->setProject($project) ->trigger(); } diff --git a/src/Appwrite/Utopia/Response/Model/Project.php b/src/Appwrite/Utopia/Response/Model/Project.php index 6e01baee84..fbbe062531 100644 --- a/src/Appwrite/Utopia/Response/Model/Project.php +++ b/src/Appwrite/Utopia/Response/Model/Project.php @@ -323,9 +323,9 @@ class Project extends Model } /** - * Get Collection + * Filter document structure * - * @return string + * @return Document */ public function filter(Document $document): Document { diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index b053e4b9cd..504f0a696d 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -54,6 +54,13 @@ class UsageTest extends Scope } } + public static function getYesterday(): string + { + $date = new DateTime(); + $date->modify('-1 day'); + return $date->format(self::$formatTz); + } + public static function getToday(): string { $date = new DateTime(); diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php index d079cb313c..0f57f94515 100644 --- a/tests/e2e/Services/Databases/DatabasesBase.php +++ b/tests/e2e/Services/Databases/DatabasesBase.php @@ -399,6 +399,73 @@ trait DatabasesBase $this->assertEquals(400, $response['headers']['status-code']); } + /** + * @depends testCreateDatabase + */ + public function testPatchAttribute(array $data): void + { + $databaseId = $data['databaseId']; + + $collection = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'collectionId' => ID::unique(), + 'name' => 'patch', + 'documentSecurity' => true, + 'permissions' => [ + Permission::create(Role::user($this->getUser()['$id'])), + ], + ]); + + $this->assertEquals(201, $collection['headers']['status-code']); + $this->assertEquals($collection['body']['name'], 'patch'); + + $attribute = $this->client->call(Client::METHOD_POST, '/databases/'.$databaseId.'/collections/'.$collection['body']['$id'].'/attributes/string', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'title', + 'required' => true, + 'size' => 100, + ]); + $this->assertEquals(202, $attribute['headers']['status-code']); + $this->assertEquals($attribute['body']['size'], 100); + + sleep(1); + + $index = $this->client->call(Client::METHOD_POST, '/databases/'.$databaseId.'/collections/'.$collection['body']['$id'].'/indexes', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'] + ]), [ + 'key' => 'titleIndex', + 'type' => 'key', + 'attributes' => ['title'], + ]); + $this->assertEquals(202, $index['headers']['status-code']); + + sleep(1); + + /** + * Update attribute size to exceed Index maximum length + */ + $attribute = $this->client->call(Client::METHOD_PATCH, '/databases/'.$databaseId.'/collections/'.$collection['body']['$id'].'/attributes/string/'.$attribute['body']['key'], array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'x-appwrite-key' => $this->getProject()['apiKey'], + ]), [ + 'size' => 1000, + 'required' => true, + 'default' => null, + ]); + + $this->assertEquals(400, $attribute['headers']['status-code']); + $this->assertStringContainsString('Index length is longer than the maximum: 76', $attribute['body']['message']); + } + public function testUpdateAttributeEnum(): void { $database = $this->client->call(Client::METHOD_POST, '/databases', [ @@ -1300,7 +1367,7 @@ trait DatabasesBase ]); $this->assertEquals(400, $unknown['headers']['status-code']); - $this->assertEquals('Unknown attribute: Unknown', $unknown['body']['message']); + $this->assertEquals('Unknown attribute: Unknown. Verify the attribute name or create the attribute.', $unknown['body']['message']); $index1 = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/indexes', array_merge([ 'content-type' => 'application/json', diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index f941c0658a..2daab87ffa 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1105,8 +1105,8 @@ class FunctionsCustomServerTest extends Scope $output = json_decode($execution['body']['responseBody'], true); - $this->assertEquals(1, $output['APPWRITE_COMPUTE_CPUS']); - $this->assertEquals(1024, $output['APPWRITE_COMPUTE_MEMORY']); + $this->assertEquals(1, $output['APPWRITE_FUNCTION_CPUS']); + $this->assertEquals(1024, $output['APPWRITE_FUNCTION_MEMORY']); // Change the specs to 1vcpu 512mb $function = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'], array_merge([ @@ -1133,8 +1133,8 @@ class FunctionsCustomServerTest extends Scope $output = json_decode($execution['body']['responseBody'], true); - $this->assertEquals(1, $output['APPWRITE_COMPUTE_CPUS']); - $this->assertEquals(512, $output['APPWRITE_COMPUTE_MEMORY']); + $this->assertEquals(1, $output['APPWRITE_FUNCTION_CPUS']); + $this->assertEquals(512, $output['APPWRITE_FUNCTION_MEMORY']); /** * Test for FAILURE @@ -1944,4 +1944,47 @@ class FunctionsCustomServerTest extends Scope $this->cleanupFunction($functionId); } + + public function testFunctionSpecifications() + { + // Check if the function specifications are correctly set in builds + $function = $this->createFunction([ + 'functionId' => ID::unique(), + 'runtime' => 'node-18.0', + 'name' => 'Specification Test', + 'entrypoint' => 'index.js', + 'logging' => false, + 'execute' => ['any'], + 'specification' => Specification::S_2VCPU_2GB, + 'commands' => 'echo $APPWRITE_FUNCTION_MEMORY:$APPWRITE_FUNCTION_CPUS', + ]); + + $this->assertEquals(201, $function['headers']['status-code']); + $this->assertEquals(Specification::S_2VCPU_2GB, $function['body']['specification']); + $this->assertNotEmpty($function['body']['$id']); + + $functionId = $functionId = $function['body']['$id'] ?? ''; + + $deploymentId = $this->setupDeployment($functionId, [ + 'code' => $this->packageFunction('node'), + 'activate' => true + ]); + + $this->assertEventually(function () use ($functionId, $deploymentId) { + $deployment = $this->getDeployment($functionId, $deploymentId); + $this->assertTrue(str_contains($deployment['body']['buildLogs'], '2048:2')); + }, 10000, 500); + + // Check if the function specifications are correctly set in executions + $execution = $this->createExecution($functionId); + + $this->assertEquals(201, $execution['headers']['status-code']); + $this->assertNotEmpty($execution['body']['$id']); + + $executionResponse = json_decode($execution['body']['responseBody'], true); + $this->assertEquals('2048', $executionResponse['APPWRITE_FUNCTION_MEMORY']); + $this->assertEquals('2', $executionResponse['APPWRITE_FUNCTION_CPUS']); + + $this->cleanupFunction($functionId); + } } diff --git a/tests/e2e/Services/Health/HealthCustomServerTest.php b/tests/e2e/Services/Health/HealthCustomServerTest.php index f2c6a2e5c2..04b1408cd0 100644 --- a/tests/e2e/Services/Health/HealthCustomServerTest.php +++ b/tests/e2e/Services/Health/HealthCustomServerTest.php @@ -494,12 +494,12 @@ class HealthCustomServerTest extends Scope return []; } - public function testUsageSuccess() + public function testStatsResources() { /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/usage', array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/stats-resources', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); @@ -511,19 +511,19 @@ class HealthCustomServerTest extends Scope /** * Test for FAILURE */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/usage?threshold=0', array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/stats-resources?threshold=0', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); $this->assertEquals(503, $response['headers']['status-code']); } - public function testUsageDumpSuccess() + public function testUsageSuccess() { /** * Test for SUCCESS */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/usage-dump', array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/stats-usage', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); @@ -535,7 +535,31 @@ class HealthCustomServerTest extends Scope /** * Test for FAILURE */ - $response = $this->client->call(Client::METHOD_GET, '/health/queue/usage-dump?threshold=0', array_merge([ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/stats-usage?threshold=0', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + $this->assertEquals(503, $response['headers']['status-code']); + } + + public function testStatsUsageDumpSuccess() + { + /** + * Test for SUCCESS + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/stats-usage-dump', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), []); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertIsInt($response['body']['size']); + $this->assertLessThan(100, $response['body']['size']); + + /** + * Test for FAILURE + */ + $response = $this->client->call(Client::METHOD_GET, '/health/queue/stats-usage-dump?threshold=0', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), []); diff --git a/tests/e2e/Services/Migrations/MigrationsBase.php b/tests/e2e/Services/Migrations/MigrationsBase.php index e6c1f81595..e49aedf643 100644 --- a/tests/e2e/Services/Migrations/MigrationsBase.php +++ b/tests/e2e/Services/Migrations/MigrationsBase.php @@ -4,6 +4,7 @@ namespace Tests\E2E\Services\Migrations; use CURLFile; use Tests\E2E\Client; +use Tests\E2E\General\UsageTest; use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Services\Functions\FunctionsBase; use Utopia\Database\Helpers\ID; @@ -20,13 +21,13 @@ trait MigrationsBase /** * @var array */ - protected static $destinationProject = []; + protected static array $destinationProject = []; /** * @param bool $fresh * @return array */ - public function getDesintationProject(bool $fresh = false): array + public function getDestinationProject(bool $fresh = false): array { if (!empty(self::$destinationProject) && !$fresh) { return self::$destinationProject; @@ -40,13 +41,12 @@ trait MigrationsBase return self::$destinationProject; } - public function performMigrationSync( - array $body, - ): array { + public function performMigrationSync(array $body): array + { $migration = $this->client->call(Client::METHOD_POST, '/migrations/appwrite', [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ], $body); $this->assertEquals(202, $migration['headers']['status-code']); @@ -57,8 +57,8 @@ trait MigrationsBase while ($attempts < 5) { $response = $this->client->call(Client::METHOD_GET, '/migrations/' . $migration['body']['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -83,12 +83,14 @@ trait MigrationsBase $attempts++; sleep(5); } + + return []; } /** * Appwrite E2E Migration Tests */ - public function testCreateAppwriteMigration() + public function testCreateAppwriteMigration(): void { $response = $this->performMigrationSync([ 'resources' => Appwrite::getSupportedResources(), @@ -106,7 +108,7 @@ trait MigrationsBase /** * Auth */ - public function testAppwriteMigrationAuthUserPassword() + public function testAppwriteMigrationAuthUserPassword(): void { $response = $this->client->call(Client::METHOD_POST, '/users', [ 'content-type' => 'application/json', @@ -145,8 +147,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/users/' . $user['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -158,8 +160,8 @@ trait MigrationsBase // Cleanup $this->client->call(Client::METHOD_DELETE, '/users/' . $user['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->client->call(Client::METHOD_DELETE, '/users/' . $user['$id'], [ @@ -169,7 +171,7 @@ trait MigrationsBase ]); } - public function testAppwriteMigrationAuthUserPhone() + public function testAppwriteMigrationAuthUserPhone(): void { $response = $this->client->call(Client::METHOD_POST, '/users', [ 'content-type' => 'application/json', @@ -207,8 +209,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/users/' . $user['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -225,12 +227,12 @@ trait MigrationsBase $this->client->call(Client::METHOD_DELETE, '/users/' . $user['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); } - public function testAppwriteMigrationAuthTeam() + public function testAppwriteMigrationAuthTeam(): void { $user = $this->client->call(Client::METHOD_POST, '/users', [ 'content-type' => 'application/json', @@ -310,8 +312,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/teams/' . $team['body']['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -321,8 +323,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/teams/' . $team['body']['$id'] . '/memberships', [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -343,8 +345,8 @@ trait MigrationsBase $this->client->call(Client::METHOD_DELETE, '/teams/' . $team['body']['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->client->call(Client::METHOD_DELETE, '/users/' . $user['body']['$id'], [ @@ -355,8 +357,8 @@ trait MigrationsBase $this->client->call(Client::METHOD_DELETE, '/users/' . $user['body']['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->client->call(Client::METHOD_DELETE, '/teams/' . $team['body']['$id'], [ @@ -367,15 +369,15 @@ trait MigrationsBase $this->client->call(Client::METHOD_DELETE, '/teams/' . $team['body']['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); } /** * Databases */ - public function testAppwriteMigrationDatabase() + public function testAppwriteMigrationDatabase(): array { $response = $this->client->call(Client::METHOD_POST, '/databases', [ 'content-type' => 'application/json', @@ -401,7 +403,6 @@ trait MigrationsBase 'apiKey' => $this->getProject()['apiKey'], ]); - $this->assertEquals('completed', $result['status']); $this->assertEquals([Resource::TYPE_DATABASE], $result['resources']); $this->assertArrayHasKey(Resource::TYPE_DATABASE, $result['statusCounters']); @@ -413,8 +414,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId, [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -427,8 +428,8 @@ trait MigrationsBase // Cleanup on destination $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); return [ @@ -439,7 +440,7 @@ trait MigrationsBase /** * @depends testAppwriteMigrationDatabase */ - public function testAppwriteMigrationDatabasesCollection(array $data) + public function testAppwriteMigrationDatabasesCollection(array $data): array { $databaseId = $data['databaseId']; @@ -507,8 +508,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId, [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -519,8 +520,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/name', [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -533,8 +534,8 @@ trait MigrationsBase // Cleanup $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); return [ @@ -546,7 +547,7 @@ trait MigrationsBase /** * @depends testAppwriteMigrationDatabasesCollection */ - public function testAppwriteMigrationDatabasesDocument(array $data) + public function testAppwriteMigrationDatabasesDocument(array $data): void { $databaseId = $data['databaseId']; $collectionId = $data['collectionId']; @@ -580,6 +581,14 @@ trait MigrationsBase 'apiKey' => $this->getProject()['apiKey'], ]); + $finalStats = $this->client->call(Client::METHOD_GET, '/project/usage', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'startDate' => UsageTest::getYesterday(), + 'endDate' => UsageTest::getTomorrow(), + ]); + $this->assertEquals('completed', $result['status']); $this->assertEquals([Resource::TYPE_DATABASE, Resource::TYPE_COLLECTION, Resource::TYPE_ATTRIBUTE, Resource::TYPE_DOCUMENT], $result['resources']); @@ -595,8 +604,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId, [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -608,15 +617,15 @@ trait MigrationsBase // Cleanup $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); } /** * Storage */ - public function testAppwriteMigrationStorageBucket() + public function testAppwriteMigrationStorageBucket(): void { $bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [ 'content-type' => 'application/json', @@ -664,8 +673,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucket['body']['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -684,8 +693,8 @@ trait MigrationsBase // Cleanup $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucket['body']['$id'], [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucket['body']['$id'], [ @@ -695,7 +704,7 @@ trait MigrationsBase ]); } - public function testAppwriteMigrationStorageFiles() + public function testAppwriteMigrationStorageFiles(): void { $bucket = $this->client->call(Client::METHOD_POST, '/storage/buckets', [ 'content-type' => 'application/json', @@ -768,8 +777,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -787,15 +796,15 @@ trait MigrationsBase $this->client->call(Client::METHOD_DELETE, '/storage/buckets/' . $bucketId . '/files/' . $fileId, [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); } /** * Functions */ - public function testAppwriteMigrationFunction() + public function testAppwriteMigrationFunction(): void { $functionId = $this->setupFunction([ 'functionId' => ID::unique(), @@ -840,8 +849,8 @@ trait MigrationsBase $response = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); $this->assertEquals(200, $response['headers']['status-code']); @@ -857,8 +866,8 @@ trait MigrationsBase $this->assertEventually(function () use ($functionId) { $deployments = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/deployments/', array_merge([ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ])); $this->assertEquals(200, $deployments['headers']['status-code']); @@ -871,8 +880,8 @@ trait MigrationsBase // Attempt execution $execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ], [ 'body' => 'test' ]); @@ -889,8 +898,8 @@ trait MigrationsBase $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId, [ 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getDesintationProject()['$id'], - 'x-appwrite-key' => $this->getDesintationProject()['apiKey'], + 'x-appwrite-project' => $this->getDestinationProject()['$id'], + 'x-appwrite-key' => $this->getDestinationProject()['apiKey'], ]); } } diff --git a/tests/e2e/Services/Teams/TeamsBaseClient.php b/tests/e2e/Services/Teams/TeamsBaseClient.php index 3aad86c670..3fcd9c043d 100644 --- a/tests/e2e/Services/Teams/TeamsBaseClient.php +++ b/tests/e2e/Services/Teams/TeamsBaseClient.php @@ -226,10 +226,6 @@ trait TeamsBaseClient $this->assertEquals($response['body']['teamId'], substr($lastEmail['text'], strpos($lastEmail['text'], '&teamId=', 0) + 8, 20)); $this->assertEquals($teamName, substr($lastEmail['text'], strpos($lastEmail['text'], '&teamName=', 0) + 10, 7)); - $secret = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256); - $membershipUid = substr($lastEmail['text'], strpos($lastEmail['text'], '?membershipId=', 0) + 14, 20); - $userUid = substr($lastEmail['text'], strpos($lastEmail['text'], '&userId=', 0) + 8, 20); - /** * Test with UserId * Create user @@ -308,6 +304,11 @@ trait TeamsBaseClient $this->assertEquals(201, $response['headers']['status-code']); + $lastEmail = $this->getLastEmail(); + $membershipUid = substr($lastEmail['text'], strpos($lastEmail['text'], '?membershipId=', 0) + 14, 20); + $userUid = substr($lastEmail['text'], strpos($lastEmail['text'], '&userId=', 0) + 8, 20); + $secret = substr($lastEmail['text'], strpos($lastEmail['text'], '&secret=', 0) + 8, 256); + /** * Test for FAILURE */ diff --git a/tests/e2e/Services/Teams/TeamsBaseServer.php b/tests/e2e/Services/Teams/TeamsBaseServer.php index bade16cf2f..0c6d85e276 100644 --- a/tests/e2e/Services/Teams/TeamsBaseServer.php +++ b/tests/e2e/Services/Teams/TeamsBaseServer.php @@ -175,17 +175,10 @@ trait TeamsBaseServer $userUid = $response['body']['userId']; $membershipUid = $response['body']['$id']; - // $response = $this->client->call(Client::METHOD_GET, '/users/'.$userUid, array_merge([ - // 'content-type' => 'application/json', - // 'x-appwrite-project' => $this->getProject()['$id'], - // ], $this->getHeaders()), []); + /** + * Test for FAILURE + */ - // $this->assertEquals($userUid, $response['body']['$id']); - // $this->assertContains('team:'.$teamUid, $response['body']['roles']); - // $this->assertContains('team:'.$teamUid.'/admin', $response['body']['roles']); - // $this->assertContains('team:'.$teamUid.'/editor', $response['body']['roles']); - - // test for resending invitation $response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([ 'content-type' => 'application/json', 'x-appwrite-project' => $this->getProject()['$id'], @@ -196,11 +189,7 @@ trait TeamsBaseServer 'url' => 'http://localhost:5000/join-us#title' ]); - $this->assertEquals(201, $response['headers']['status-code']); - - /** - * Test for FAILURE - */ + $this->assertEquals(409, $response['headers']['status-code']); // membership already created $response = $this->client->call(Client::METHOD_POST, '/teams/' . $teamUid . '/memberships', array_merge([ 'content-type' => 'application/json', diff --git a/tests/resources/docker/docker-compose.yml b/tests/resources/docker/docker-compose.yml index da7c4f0833..67bb9a1aef 100644 --- a/tests/resources/docker/docker-compose.yml +++ b/tests/resources/docker/docker-compose.yml @@ -87,9 +87,9 @@ services: - _APP_COMPUTE_MEMORY - _APP_EXECUTOR_HOST - appwrite-worker-usage: - entrypoint: worker-usage - container_name: appwrite-worker-usage + appwrite-worker-stats-usage: + entrypoint: worker-stats-usage + container_name: appwrite-worker-stats-usage build: context: . restart: unless-stopped diff --git a/tests/resources/functions/node/index.js b/tests/resources/functions/node/index.js index 041e4a8c12..e8eb938a15 100644 --- a/tests/resources/functions/node/index.js +++ b/tests/resources/functions/node/index.js @@ -14,6 +14,8 @@ module.exports = async(context) => { 'APPWRITE_FUNCTION_USER_ID' : context.req.headers['x-appwrite-user-id'] ?? '', 'APPWRITE_FUNCTION_JWT' : context.req.headers['x-appwrite-user-jwt'] ?? '', 'APPWRITE_FUNCTION_PROJECT_ID' : process.env.APPWRITE_FUNCTION_PROJECT_ID, + 'APPWRITE_FUNCTION_MEMORY' : process.env.APPWRITE_FUNCTION_MEMORY, + 'APPWRITE_FUNCTION_CPUS' : process.env.APPWRITE_FUNCTION_CPUS, 'CUSTOM_VARIABLE' : process.env.CUSTOM_VARIABLE }); } \ No newline at end of file diff --git a/tests/resources/functions/php-large/index.php b/tests/resources/functions/php-large/index.php index 98104fbf35..abcb2e53d9 100644 --- a/tests/resources/functions/php-large/index.php +++ b/tests/resources/functions/php-large/index.php @@ -9,8 +9,8 @@ return function ($context) { 'APPWRITE_FUNCTION_RUNTIME_NAME' => \getenv('APPWRITE_FUNCTION_RUNTIME_NAME') ?: '', 'APPWRITE_FUNCTION_RUNTIME_VERSION' => \getenv('APPWRITE_FUNCTION_RUNTIME_VERSION') ?: '', 'APPWRITE_REGION' => \getenv('APPWRITE_REGION') ?: '', - 'APPWRITE_COMPUTE_CPUS' => \getenv('APPWRITE_COMPUTE_CPUS') ?: '', - 'APPWRITE_COMPUTE_MEMORY' => \getenv('APPWRITE_COMPUTE_MEMORY') ?: '', + 'APPWRITE_FUNCTION_CPUS' => \getenv('APPWRITE_FUNCTION_CPUS') ?: '', + 'APPWRITE_FUNCTION_MEMORY' => \getenv('APPWRITE_FUNCTION_MEMORY') ?: '', 'UNICODE_TEST' => "êä" ]); }; diff --git a/tests/resources/functions/php/index.php b/tests/resources/functions/php/index.php index c115a78bf3..27a9418b3c 100644 --- a/tests/resources/functions/php/index.php +++ b/tests/resources/functions/php/index.php @@ -27,7 +27,7 @@ return function ($context) { 'APPWRITE_REGION' => \getenv('APPWRITE_REGION') ?: '', 'UNICODE_TEST' => "êä", 'GLOBAL_VARIABLE' => \getenv('GLOBAL_VARIABLE') ?: '', - 'APPWRITE_COMPUTE_CPUS' => \getenv('APPWRITE_COMPUTE_CPUS') ?: '', - 'APPWRITE_COMPUTE_MEMORY' => \getenv('APPWRITE_COMPUTE_MEMORY') ?: '', + 'APPWRITE_FUNCTION_CPUS' => \getenv('APPWRITE_FUNCTION_CPUS') ?: '', + 'APPWRITE_FUNCTION_MEMORY' => \getenv('APPWRITE_FUNCTION_MEMORY') ?: '', ], \intval($statusCode)); }; diff --git a/tests/unit/Auth/KeyTest.php b/tests/unit/Auth/KeyTest.php new file mode 100644 index 0000000000..8ae2114697 --- /dev/null +++ b/tests/unit/Auth/KeyTest.php @@ -0,0 +1,56 @@ + $projectId,]); + $decoded = Key::decode($project, $key); + + $this->assertEquals($projectId, $decoded->getProjectId()); + $this->assertEquals(API_KEY_DYNAMIC, $decoded->getType()); + $this->assertEquals(Auth::USER_ROLE_APPS, $decoded->getRole()); + $this->assertEquals(\array_merge($scopes, $roleScopes), $decoded->getScopes()); + } + + private static function generateKey( + string $projectId, + bool $usage, + array $scopes, + ): string { + $jwt = new JWT( + key: System::getEnv('_APP_OPENSSL_KEY_V1'), + algo: 'HS256', + maxAge: 86400, + leeway: 0, + ); + + $apiKey = $jwt->encode([ + 'projectId' => $projectId, + 'usage' => $usage, + 'scopes' => $scopes, + ]); + + return API_KEY_DYNAMIC . '_' . $apiKey; + } +}