Compare commits

..
Author SHA1 Message Date
Matej Bačo 23b245e058 Leftover 2025-03-17 16:03:52 +01:00
Matej Bačo ae5267cda7 Fix response model for adapters (frameworks) 2025-03-17 16:02:16 +01:00
Matej BačoandGitHub f1b5807174 Merge pull request #9519 from appwrite/fix-realtime-deployments
Fix: Realtime events in build worker
2025-03-17 15:30:54 +01:00
Matej Bačo 6e77221da0 Fix CI/CD preps 2025-03-17 14:25:29 +01:00
Matej Bačo 4de935a322 Fix CI/CD flaky test 2025-03-17 14:16:31 +01:00
Matej Bačo 86b66d70bf Debug CI/CD 2025-03-17 14:07:27 +01:00
Matej Bačo 828b9349c3 Improve build worker realtime, new tests 2025-03-17 13:21:36 +01:00
Matej Bačo 313a6b0f7e Fix bug in usage test 2025-03-17 12:29:44 +01:00
Matej Bačo 04140c2acc Fix crashing init 2025-03-17 12:08:01 +01:00
Matej Bačo 0f961f0d04 Fix composer conflict 2025-03-17 11:55:14 +01:00
Matej Bačo 25f7741d3c Linter fix 2025-03-17 11:53:23 +01:00
Matej Bačo 2ff339dad4 Re-implement init changes 2025-03-17 11:53:09 +01:00
Matej Bačo 91dd4c9c6f Merge branch '1.7.x' into feat-sites 2025-03-17 11:47:22 +01:00
Matej Bačo 8413909b12 Merge branch '1.6.x' into 1.7.x 2025-03-17 11:42:30 +01:00
Matej Bačo a4d4b6ffb5 Fix tests 2025-03-17 11:41:02 +01:00
Matej Bačo cbdd344490 Remove framework env var 2025-03-17 11:21:52 +01:00
Eldad A. FuxandGitHub 5a5d43f3d0 Merge pull request #9515 from appwrite/feat-env-to-system
Feat env vars to system lib
2025-03-16 17:09:47 +01:00
Eldad A. FuxandGitHub 3e462ded0a Merge pull request #9408 from appwrite/feat-dynamic-regions
Feat dynamic regions
2025-03-16 17:05:04 +01:00
Eldad Fux de96b2511a Merge remote-tracking branch 'origin/1.6.x' into feat-dynamic-regions 2025-03-16 16:31:09 +01:00
Eldad Fux 90899b92e3 Change envs to system lib 2025-03-16 10:36:32 +01:00
Eldad A. FuxandGitHub 971a4b8f30 Merge pull request #9511 from appwrite/feat-eldad-cleanups
Cleanups
2025-03-15 19:15:32 +01:00
Eldad Fux 418185b914 fixed format 2025-03-15 14:43:51 +01:00
Eldad Fux 167011ce87 Fixed timelimit 2025-03-15 14:38:01 +01:00
Eldad Fux 3d69a35d5a Fixed tests 2025-03-15 14:32:18 +01:00
Eldad Fux aac90ea8b8 Fixed format 2025-03-15 14:17:47 +01:00
Eldad Fux 56aa503146 Fixed paths 2025-03-15 13:57:12 +01:00
Eldad Fux 875a60fe07 fixed paths 2025-03-15 13:53:00 +01:00
Eldad Fux 3427755695 Merge remote-tracking branch 'origin/1.6.x' into feat-eldad-cleanups 2025-03-15 13:45:31 +01:00
Jake BarnbyandGitHub 68238193d2 Merge pull request #9512 from appwrite/feat-improve-ordering
Improve delete ordering
2025-03-16 01:34:02 +13:00
Eldad Fux 07926534bf Fixed namespace 2025-03-15 13:24:56 +01:00
Eldad Fux a2a0a1405f plurals 2025-03-15 13:23:02 +01:00
Eldad Fux 195edd19ac wip 2025-03-15 13:18:34 +01:00
Eldad Fux 6cf333141c Merge remote-tracking branch 'origin/1.6.x' into feat-eldad-cleanups 2025-03-15 13:18:30 +01:00
Jake Barnby 12dbb1d50d Reapply "Improve deletes queries"
This reverts commit 9dff192508.
2025-03-16 01:02:44 +13:00
Jake Barnby 9dff192508 Revert "Improve deletes queries"
This reverts commit 650627bfb4.
2025-03-16 01:02:41 +13:00
Jake Barnby 650627bfb4 Improve deletes queries 2025-03-16 01:01:13 +13:00
Eldad Fux 4016895b8f Fixed DB test 2025-03-15 12:58:41 +01:00
Eldad Fux 3b7aea22c2 Fixed test 2025-03-15 12:42:34 +01:00
Eldad Fux 50fe0bf98d Code review fix 2025-03-15 12:39:15 +01:00
Eldad Fux 4f0a7de868 Fixes 2025-03-15 12:34:49 +01:00
Eldad Fux 1933e48d9c Cleanups 2025-03-15 12:00:36 +01:00
Steven NguyenandGitHub 90228a5ffb Merge pull request #9294 from appwrite/feat-1.6.1-release-prep
Prepare 1.6.1 release
2025-03-14 16:38:55 -07:00
Steven NguyenandGitHub 063c2be38f Merge pull request #9495 from appwrite/chore-bump-console-5.2.53
Bump console to version 5.2.53
2025-03-14 16:35:22 -07:00
Steven NguyenandGitHub ff9845c9b9 Merge pull request #9496 from appwrite/main
Sync main into 1.6.x
2025-03-14 16:35:01 -07:00
Steven NguyenandGitHub bd5bfc04ef Merge pull request #9466 from appwrite/fix-certificates-in-deletes
Fix certificates in deletes worker
2025-03-14 16:34:21 -07:00
Matej Bačo 7a131d3508 Post-merge fixes 2025-03-14 13:56:37 +01:00
Matej Bačo d77dbebe8b Merge branch '1.7.x' into feat-sites 2025-03-14 13:53:34 +01:00
Matej Bačo 8eadaec676 Merge branch '1.6.x' into 1.7.x 2025-03-14 13:50:53 +01:00
Matej BačoandGitHub c55030387f Merge pull request #9508 from appwrite/feat-rules-improvements
Feat: Add rule attributes
2025-03-14 13:35:05 +01:00
Matej Bačo 5f2512dc4f code quality improvement 2025-03-14 12:07:38 +01:00
Matej Bačo f6fc425c98 Add rule attributes 2025-03-14 09:37:44 +01:00
Jake BarnbyandGitHub 810d650aba Merge pull request #9507 from appwrite/feat-improve-delete-queries
Feat improve delete queries
2025-03-14 18:37:56 +13:00
Jake Barnby 817a3808e5 Add 5 min timeout for workers and tasks DBs 2025-03-14 16:40:57 +13:00
Jake Barnby d96910138f Update lock 2025-03-14 15:40:05 +13:00
Jake Barnby b8673ac07f Merge remote-tracking branch 'origin/1.6.x' into feat-improve-delete-queries
# Conflicts:
#	composer.lock
2025-03-14 15:34:18 +13:00
Jake Barnby 246c8ce939 Add order on inequality queries 2025-03-14 15:29:52 +13:00
Jake Barnby 49d89a4206 Force clear timeout to ensure it's not set 2025-03-14 15:29:23 +13:00
Matej Bačo ba45c191c5 Updat console 2025-03-13 16:20:36 +01:00
Matej Bačo 74f9464c18 Fix site specs for vcs deployment 2025-03-13 15:39:41 +01:00
Matej Bačo c5dfabdb7c Fix commit deployments, allow for sites 2025-03-13 15:38:29 +01:00
Matej BačoandGitHub bfdd2b65dc Merge pull request #9504 from appwrite/fix-github-comment
Fix github comment
2025-03-13 15:17:13 +01:00
Christy JacobandGitHub 2c803b262c Merge pull request #9505 from appwrite/PLA-2640
update queue to 0.9.*
2025-03-13 18:51:41 +05:30
Matej Bačo 9bc793b216 Fix realtime tests 2025-03-13 14:08:14 +01:00
Fabian Gruber fcf15689e8 update queue to 0.9.* 2025-03-13 14:02:32 +01:00
Christy JacobandGitHub aef38aa114 Merge pull request #9414 from appwrite/bump-sdk
chore: bump sdk
2025-03-13 17:29:56 +05:30
Chirag AggarwalandGitHub d81f2f8b5c Merge branch '1.6.x' into bump-sdk 2025-03-13 11:01:14 +05:30
Chirag Aggarwal e119bf6777 chore: update changelogs 2025-03-13 05:30:09 +00:00
Steven Nguyen 7402145c9d Bump console to version 5.2.53 2025-03-12 15:22:43 -07:00
Chirag Aggarwal 74f4ebf90e chore: update sdk generator 2025-03-12 10:26:01 +00:00
Chirag Aggarwal e3f59c696d bump: python version 2025-03-11 05:43:30 +00:00
Chirag Aggarwal 107a185953 Merge branch '1.6.x' into bump-sdk 2025-03-11 05:30:47 +00:00
Steven Nguyen f6b5deda6d fix: add missing _APP_EMAIL_CERTIFICATES env var to deletes worker
The deletes worker uses the certificates resource to delete
certificates, but the certificates resource requires the
_APP_EMAIL_CERTIFICATES env var to be set or it will throw an exception
and not process any delete jobs. This commit adds the missing env var
to the deletes worker.

Also add _APP_SYSTEM_SECURITY_EMAIL_ADDRESS as a fallback.
2025-03-07 12:46:14 -08:00
Chirag Aggarwal d9fc6b70ad chore: bump react native 2025-03-07 13:18:20 +00:00
Chirag Aggarwal a0605b14cb chore: regen specs and update vers 2025-03-07 04:14:45 +00:00
Chirag Aggarwal 6c652ba3ae Merge branch '1.6.x' into bump-sdk 2025-03-06 17:04:05 +00:00
Chirag Aggarwal 6ad34a7bf7 chore: sdk versions and corresponding docs 2025-02-27 05:05:31 +00:00
Chirag Aggarwal 57c7ebb53d chore: bump sdk 2025-02-26 17:51:00 +00:00
Eldad Fux ad56782b49 Fixed tests 2025-02-26 10:19:51 +01:00
Eldad Fux ce5a04a37d Merge remote-tracking branch 'origin/1.6.x' into feat-dynamic-regions 2025-02-26 07:52:40 +01:00
Eldad Fux e8fb2f8ae1 Changed config format 2025-02-25 20:25:54 +01:00
Eldad Fux 697c30f559 Init 2025-02-25 09:31:24 +01:00
Steven NguyenandGitHub 34d587d7c5 Merge pull request #9122 from gurjeetsinghvirdee/152-sdk-cli-command-update
Fix outdated CLI commands in documentation
2025-02-21 13:59:50 -08:00
Steven NguyenandGitHub 0547dbc454 fix: use github var instead of secret for DOCKERHUB_USERNAME
DOCKERHUB_USERNAME was moved to be a variable since it doesn't need to
be secret.
2025-02-03 22:34:08 +00:00
Gurjeet Singh VirdeeandGitHub a3324b7453 Merge branch 'main' into 152-sdk-cli-command-update 2025-02-01 03:43:11 +05:30
Steven NguyenandGitHub df017bccb2 chore: bump appwrite version to 1.6.1 2025-01-31 22:00:32 +00:00
Eldad A. FuxandGitHub c4ab6f5aff Merge pull request #9285 from appwrite/LauraDuRy-patch-1
replaced init for cloud link
2025-01-29 12:06:46 +01:00
Laura Du RyandGitHub 891c1c4093 Update README.md 2025-01-29 10:04:15 +01:00
Laura Du RyandGitHub 507c8bd3ec Update README.md 2025-01-29 10:02:40 +01:00
Laura Du RyandGitHub 2de4e71662 Update README.md 2025-01-29 09:58:04 +01:00
Gurjeet Singh VirdeeandGitHub 8508e6009b refactor: cleaned up syntax by removing unnecessary '=' 2025-01-24 19:13:56 +05:30
Gurjeet Singh VirdeeandGitHub a39341ada8 fix: add missing single quote 2025-01-08 14:48:54 +05:30
Gurjeet Singh VirdeeandGitHub c451074278 restored the permissions term 2025-01-06 01:29:06 +05:30
Gurjeet Singh VirdeeandGitHub cce6222184 docs: update CLI commands in documentation 2024-12-20 22:24:39 +05:30
Steven NguyenandGitHub 6e09bcf6c6 Bump appwrite version to 1.6.1-RC1 2024-11-27 18:10:28 +00:00
59 changed files with 2617 additions and 2374 deletions
-1
View File
@@ -82,7 +82,6 @@ _APP_EXECUTOR_SECRET=your-secret-key
_APP_EXECUTOR_HOST=http://exc1/v1
_APP_FUNCTIONS_RUNTIMES=php-8.0,node-18.0,python-3.9,ruby-3.1
_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.29
_APP_SITES_FRAMEWORKS=sveltekit,nextjs,nuxt,astro,remix,flutter,other,react,vue,analog,angular,vite
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_DELAY=
_APP_MAINTENANCE_RETENTION_CACHE=2592000
+1 -1
View File
@@ -26,7 +26,7 @@ jobs:
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
username: ${{ vars.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract metadata (tags, labels) for Docker
+8
View File
@@ -122,6 +122,14 @@ jobs:
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d
sleep 10
- name: Wait for Open Runtimes
timeout-minutes: 3
run: |
while ! docker compose logs openruntimes-executor | grep -q "Executor is ready."; do
echo "Waiting for Executor to come online"
sleep 1
done
- name: Run General Tests
run: docker compose exec -T appwrite test /usr/src/code/tests/e2e/General --debug
+3 -5
View File
@@ -1,4 +1,4 @@
> Appwrite Init has concluded! You can check out all the latest announcements [on our Init website](https://appwrite.io/init) :rocket:
> [Get started with Appwrite](https://apwr.dev/appcloud)
<br />
<p align="center">
@@ -24,11 +24,9 @@
English | [简体中文](README-CN.md)
[**Announcing Appwrite Cloud Public Beta! Sign up today!**](https://cloud.appwrite.io)
Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker<nobr> microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.
Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, and [more services](https://appwrite.io/docs).
Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and [more services](https://appwrite.io/docs).
<p align="center">
<br />
@@ -52,7 +50,7 @@ Table of Contents:
- [Upgrade from an Older Version](#upgrade-from-an-older-version)
- [One-Click Setups](#one-click-setups)
- [Getting Started](#getting-started)
- [Services](#services)
- [Products](#products)
- [SDKs](#sdks)
- [Client](#client)
- [Server](#server)
+3 -4
View File
@@ -25,7 +25,7 @@ use Utopia\Queue\Publisher;
use Utopia\Registry\Registry;
use Utopia\System\System;
// overwriting runtimes to be architectur agnostic for CLI
// overwriting runtimes to be architecture agnostic for CLI
Config::setParam('runtimes', (new Runtimes('v5'))->getAll(supported: false));
// require controllers after overwriting runtimes
@@ -43,8 +43,7 @@ CLI::setResource('cache', function ($pools) {
$adapters[] = $pools
->get($value)
->pop()
->getResource()
;
->getResource();
}
return new Cache(new Sharding($adapters));
@@ -187,7 +186,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_TASK)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
// set tenant
+36
View File
@@ -1155,6 +1155,28 @@ return [
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('owner'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16,
'signed' => true,
'required' => false,
'default' => '', // "Appwrite" or empty string
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('region'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16,
'signed' => true,
'required' => true,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
@@ -1241,6 +1263,20 @@ return [
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_owner'),
'type' => Database::INDEX_KEY,
'attributes' => ['owner'],
'lengths' => [16],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_region'),
'type' => Database::INDEX_KEY,
'attributes' => ['region'],
'lengths' => [16],
'orders' => [Database::ORDER_ASC],
],
],
],
+1
View File
@@ -27,6 +27,7 @@ $console = [
'hostname' => 'localhost',
], // Current host is added on app init
],
'region' => 'fra',
'legalName' => '',
'legalCountry' => '',
'legalState' => '',
-5
View File
@@ -553,11 +553,6 @@ return [
'description' => 'Site with the requested ID could not be found.',
'code' => 404,
],
Exception::SITE_FRAMEWORK_UNSUPPORTED => [
'name' => Exception::SITE_FRAMEWORK_UNSUPPORTED,
'description' => 'The requested framework is either inactive or unsupported. Please check the value of the _APP_SITES_FRAMEWORKS environment variable.',
'code' => 404,
],
Exception::SITE_TEMPLATE_NOT_FOUND => [
'name' => Exception::SITE_TEMPLATE_NOT_FOUND,
'description' => 'Site Template with the requested ID could not be found.',
+42 -7
View File
@@ -1,11 +1,46 @@
<?php
/**
* Continent codes with names and approximate central coordinates
*
* Coordinates represent approximate geographical centers of each continent
* Note: These are simplified centroids and may not represent the exact geographical center
*/
return [
'AF',
'AN',
'AS',
'EU',
'NA',
'OC',
'SA',
'AF' => [
'name' => 'Africa',
'latitude' => 8.7832,
'longitude' => 34.5085
],
'AN' => [
'name' => 'Antarctica',
'latitude' => -82.8628,
'longitude' => 135.0000
],
'AS' => [
'name' => 'Asia',
'latitude' => 34.0479,
'longitude' => 100.6197
],
'EU' => [
'name' => 'Europe',
'latitude' => 54.5260,
'longitude' => 15.2551
],
'NA' => [
'name' => 'North America',
'latitude' => 54.5260,
'longitude' => -105.2551
],
'OC' => [
'name' => 'Oceania',
'latitude' => -22.7359,
'longitude' => 140.0188
],
'SA' => [
'name' => 'South America',
'latitude' => -8.7832,
'longitude' => -55.4915
],
];
+199 -198
View File
@@ -1,209 +1,210 @@
<?php
/**
* ISO 3166 standard country codes
* ISO 3166 standard country codes with country names and coordinates
* https://www.iso.org/iso-3166-country-codes.html
*
* Source:
* https://www.iso.org/obp/ui/#search/code/
* Coordinates source: Natural Earth Data (approximate country centroids)
*/
return [
'AF',
'AO',
'AL',
'AD',
'AE',
'AR',
'AM',
'AG',
'AU',
'AT',
'AZ',
'BI',
'BE',
'BJ',
'BF',
'BD',
'BG',
'BH',
'BS',
'BA',
'BY',
'BZ',
'BO',
'BR',
'BB',
'BN',
'BT',
'BW',
'CF',
'CA',
'CH',
'CL',
'CN',
'CI',
'CM',
'CD',
'CG',
'CO',
'KM',
'CV',
'CR',
'CU',
'CY',
'CZ',
'DE',
'DJ',
'DM',
'DK',
'DO',
'DZ',
'EC',
'EG',
'ER',
'ES',
'EE',
'ET',
'FI',
'FJ',
'FR',
'FM',
'GA',
'GB',
'GE',
'GH',
'GN',
'GM',
'GW',
'GQ',
'GR',
'GD',
'GT',
'GY',
'HK',
'HN',
'HR',
'HT',
'HU',
'ID',
'IN',
'IE',
'IR',
'IQ',
'IS',
'IL',
'IT',
'JM',
'JO',
'JP',
'KZ',
'KE',
'KG',
'KH',
'KI',
'KN',
'KR',
'KW',
'LA',
'LB',
'LR',
'LY',
'LC',
'LI',
'LK',
'LS',
'LT',
'LU',
'LV',
'MA',
'MC',
'MD',
'MG',
'MV',
'MX',
'MH',
'MK',
'ML',
'MT',
'MM',
'ME',
'MN',
'MZ',
'MR',
'MU',
'MW',
'MY',
'NA',
'NE',
'NG',
'NI',
'NL',
'NO',
'NP',
'NR',
'NZ',
'OM',
'PK',
'PS',
'PA',
'PE',
'PH',
'PW',
'PG',
'PL',
'KP',
'PT',
'PY',
'QA',
'RO',
'RU',
'RW',
'SA',
'SD',
'SN',
'SG',
'SB',
'SL',
'SV',
'SM',
'SO',
'RS',
'SS',
'ST',
'SR',
'SK',
'SI',
'SE',
'SZ',
'SC',
'SY',
'TD',
'TG',
'TH',
'TJ',
'TM',
'TL',
'TO',
'TT',
'TN',
'TR',
'TV',
'TZ',
'TW',
'UG',
'UA',
'UY',
'US',
'UZ',
'VA',
'VC',
'VE',
'VN',
'VU',
'WS',
'YE',
'ZA',
'ZM',
'ZW',
'AF' => ['name' => 'Afghanistan', 'latitude' => 33.0, 'longitude' => 66.0],
'AO' => ['name' => 'Angola', 'latitude' => -12.5, 'longitude' => 18.5],
'AL' => ['name' => 'Albania', 'latitude' => 41.0, 'longitude' => 20.0],
'AD' => ['name' => 'Andorra', 'latitude' => 42.5, 'longitude' => 1.6],
'AE' => ['name' => 'United Arab Emirates', 'latitude' => 24.0, 'longitude' => 54.0],
'AR' => ['name' => 'Argentina', 'latitude' => -34.0, 'longitude' => -64.0],
'AM' => ['name' => 'Armenia', 'latitude' => 40.0, 'longitude' => 45.0],
'AG' => ['name' => 'Antigua and Barbuda', 'latitude' => 17.05, 'longitude' => -61.8],
'AU' => ['name' => 'Australia', 'latitude' => -25.0, 'longitude' => 135.0],
'AT' => ['name' => 'Austria', 'latitude' => 47.3, 'longitude' => 13.3],
'AZ' => ['name' => 'Azerbaijan', 'latitude' => 40.5, 'longitude' => 47.5],
'BI' => ['name' => 'Burundi', 'latitude' => -3.5, 'longitude' => 30.0],
'BE' => ['name' => 'Belgium', 'latitude' => 50.8, 'longitude' => 4.0],
'BJ' => ['name' => 'Benin', 'latitude' => 9.5, 'longitude' => 2.25],
'BF' => ['name' => 'Burkina Faso', 'latitude' => 13.0, 'longitude' => -2.0],
'BD' => ['name' => 'Bangladesh', 'latitude' => 24.0, 'longitude' => 90.0],
'BG' => ['name' => 'Bulgaria', 'latitude' => 43.0, 'longitude' => 25.0],
'BH' => ['name' => 'Bahrain', 'latitude' => 26.0, 'longitude' => 50.5],
'BS' => ['name' => 'Bahamas', 'latitude' => 24.25, 'longitude' => -76.0],
'BA' => ['name' => 'Bosnia and Herzegovina', 'latitude' => 44.0, 'longitude' => 18.0],
'BY' => ['name' => 'Belarus', 'latitude' => 53.0, 'longitude' => 28.0],
'BZ' => ['name' => 'Belize', 'latitude' => 17.25, 'longitude' => -88.75],
'BO' => ['name' => 'Bolivia', 'latitude' => -17.0, 'longitude' => -65.0],
'BR' => ['name' => 'Brazil', 'latitude' => -10.0, 'longitude' => -55.0],
'BB' => ['name' => 'Barbados', 'latitude' => 13.17, 'longitude' => -59.53],
'BN' => ['name' => 'Brunei', 'latitude' => 4.5, 'longitude' => 114.67],
'BT' => ['name' => 'Bhutan', 'latitude' => 27.5, 'longitude' => 90.5],
'BW' => ['name' => 'Botswana', 'latitude' => -22.0, 'longitude' => 24.0],
'CF' => ['name' => 'Central African Republic', 'latitude' => 7.0, 'longitude' => 21.0],
'CA' => ['name' => 'Canada', 'latitude' => 60.0, 'longitude' => -95.0],
'CH' => ['name' => 'Switzerland', 'latitude' => 47.0, 'longitude' => 8.0],
'CL' => ['name' => 'Chile', 'latitude' => -30.0, 'longitude' => -71.0],
'CN' => ['name' => 'China', 'latitude' => 35.0, 'longitude' => 105.0],
'CI' => ['name' => 'Côte d\'Ivoire', 'latitude' => 8.0, 'longitude' => -5.0],
'CM' => ['name' => 'Cameroon', 'latitude' => 6.0, 'longitude' => 12.0],
'CD' => ['name' => 'Democratic Republic of the Congo', 'latitude' => -2.5, 'longitude' => 23.5],
'CG' => ['name' => 'Republic of the Congo', 'latitude' => -1.0, 'longitude' => 15.0],
'CO' => ['name' => 'Colombia', 'latitude' => 4.0, 'longitude' => -72.0],
'KM' => ['name' => 'Comoros', 'latitude' => -12.17, 'longitude' => 44.25],
'CV' => ['name' => 'Cape Verde', 'latitude' => 16.0, 'longitude' => -24.0],
'CR' => ['name' => 'Costa Rica', 'latitude' => 10.0, 'longitude' => -84.0],
'CU' => ['name' => 'Cuba', 'latitude' => 21.5, 'longitude' => -80.0],
'CY' => ['name' => 'Cyprus', 'latitude' => 35.0, 'longitude' => 33.0],
'CZ' => ['name' => 'Czech Republic', 'latitude' => 49.75, 'longitude' => 15.5],
'DE' => ['name' => 'Germany', 'latitude' => 51.0, 'longitude' => 9.0],
'DJ' => ['name' => 'Djibouti', 'latitude' => 11.5, 'longitude' => 43.0],
'DM' => ['name' => 'Dominica', 'latitude' => 15.42, 'longitude' => -61.33],
'DK' => ['name' => 'Denmark', 'latitude' => 56.0, 'longitude' => 10.0],
'DO' => ['name' => 'Dominican Republic', 'latitude' => 19.0, 'longitude' => -70.67],
'DZ' => ['name' => 'Algeria', 'latitude' => 28.0, 'longitude' => 3.0],
'EC' => ['name' => 'Ecuador', 'latitude' => -2.0, 'longitude' => -77.5],
'EG' => ['name' => 'Egypt', 'latitude' => 27.0, 'longitude' => 30.0],
'ER' => ['name' => 'Eritrea', 'latitude' => 15.0, 'longitude' => 39.0],
'ES' => ['name' => 'Spain', 'latitude' => 40.0, 'longitude' => -4.0],
'EE' => ['name' => 'Estonia', 'latitude' => 59.0, 'longitude' => 26.0],
'ET' => ['name' => 'Ethiopia', 'latitude' => 8.0, 'longitude' => 38.0],
'FI' => ['name' => 'Finland', 'latitude' => 64.0, 'longitude' => 26.0],
'FJ' => ['name' => 'Fiji', 'latitude' => -18.0, 'longitude' => 175.0],
'FR' => ['name' => 'France', 'latitude' => 46.0, 'longitude' => 2.0],
'FM' => ['name' => 'Micronesia', 'latitude' => 6.92, 'longitude' => 158.25],
'GA' => ['name' => 'Gabon', 'latitude' => -1.0, 'longitude' => 11.75],
'GB' => ['name' => 'United Kingdom', 'latitude' => 54.0, 'longitude' => -2.0],
'GE' => ['name' => 'Georgia', 'latitude' => 42.0, 'longitude' => 43.5],
'GH' => ['name' => 'Ghana', 'latitude' => 8.0, 'longitude' => -2.0],
'GN' => ['name' => 'Guinea', 'latitude' => 11.0, 'longitude' => -10.0],
'GM' => ['name' => 'Gambia', 'latitude' => 13.47, 'longitude' => -16.57],
'GW' => ['name' => 'Guinea-Bissau', 'latitude' => 12.0, 'longitude' => -15.0],
'GQ' => ['name' => 'Equatorial Guinea', 'latitude' => 2.0, 'longitude' => 10.0],
'GR' => ['name' => 'Greece', 'latitude' => 39.0, 'longitude' => 22.0],
'GD' => ['name' => 'Grenada', 'latitude' => 12.12, 'longitude' => -61.67],
'GT' => ['name' => 'Guatemala', 'latitude' => 15.5, 'longitude' => -90.25],
'GY' => ['name' => 'Guyana', 'latitude' => 5.0, 'longitude' => -59.0],
'HK' => ['name' => 'Hong Kong', 'latitude' => 22.25, 'longitude' => 114.17],
'HN' => ['name' => 'Honduras', 'latitude' => 15.0, 'longitude' => -86.5],
'HR' => ['name' => 'Croatia', 'latitude' => 45.17, 'longitude' => 15.5],
'HT' => ['name' => 'Haiti', 'latitude' => 19.0, 'longitude' => -72.42],
'HU' => ['name' => 'Hungary', 'latitude' => 47.0, 'longitude' => 20.0],
'ID' => ['name' => 'Indonesia', 'latitude' => -5.0, 'longitude' => 120.0],
'IN' => ['name' => 'India', 'latitude' => 20.0, 'longitude' => 77.0],
'IE' => ['name' => 'Ireland', 'latitude' => 53.0, 'longitude' => -8.0],
'IR' => ['name' => 'Iran', 'latitude' => 32.0, 'longitude' => 53.0],
'IQ' => ['name' => 'Iraq', 'latitude' => 33.0, 'longitude' => 44.0],
'IS' => ['name' => 'Iceland', 'latitude' => 65.0, 'longitude' => -18.0],
'IL' => ['name' => 'Israel', 'latitude' => 31.5, 'longitude' => 34.75],
'IT' => ['name' => 'Italy', 'latitude' => 42.83, 'longitude' => 12.83],
'JM' => ['name' => 'Jamaica', 'latitude' => 18.25, 'longitude' => -77.5],
'JO' => ['name' => 'Jordan', 'latitude' => 31.0, 'longitude' => 36.0],
'JP' => ['name' => 'Japan', 'latitude' => 36.0, 'longitude' => 138.0],
'KZ' => ['name' => 'Kazakhstan', 'latitude' => 48.0, 'longitude' => 68.0],
'KE' => ['name' => 'Kenya', 'latitude' => 1.0, 'longitude' => 38.0],
'KG' => ['name' => 'Kyrgyzstan', 'latitude' => 41.0, 'longitude' => 75.0],
'KH' => ['name' => 'Cambodia', 'latitude' => 13.0, 'longitude' => 105.0],
'KI' => ['name' => 'Kiribati', 'latitude' => 1.42, 'longitude' => 173.0],
'KN' => ['name' => 'Saint Kitts and Nevis', 'latitude' => 17.33, 'longitude' => -62.75],
'KR' => ['name' => 'South Korea', 'latitude' => 37.0, 'longitude' => 127.5],
'KW' => ['name' => 'Kuwait', 'latitude' => 29.34, 'longitude' => 47.66],
'LA' => ['name' => 'Laos', 'latitude' => 18.0, 'longitude' => 105.0],
'LB' => ['name' => 'Lebanon', 'latitude' => 33.83, 'longitude' => 35.83],
'LR' => ['name' => 'Liberia', 'latitude' => 6.5, 'longitude' => -9.5],
'LY' => ['name' => 'Libya', 'latitude' => 25.0, 'longitude' => 17.0],
'LC' => ['name' => 'Saint Lucia', 'latitude' => 13.88, 'longitude' => -61.13],
'LI' => ['name' => 'Liechtenstein', 'latitude' => 47.17, 'longitude' => 9.53],
'LK' => ['name' => 'Sri Lanka', 'latitude' => 7.0, 'longitude' => 81.0],
'LS' => ['name' => 'Lesotho', 'latitude' => -29.5, 'longitude' => 28.5],
'LT' => ['name' => 'Lithuania', 'latitude' => 56.0, 'longitude' => 24.0],
'LU' => ['name' => 'Luxembourg', 'latitude' => 49.75, 'longitude' => 6.17],
'LV' => ['name' => 'latitudevia', 'latitude' => 57.0, 'longitude' => 25.0],
'MA' => ['name' => 'Morocco', 'latitude' => 32.0, 'longitude' => -5.0],
'MC' => ['name' => 'Monaco', 'latitude' => 43.73, 'longitude' => 7.4],
'MD' => ['name' => 'Moldova', 'latitude' => 47.0, 'longitude' => 29.0],
'MG' => ['name' => 'Madagascar', 'latitude' => -20.0, 'longitude' => 47.0],
'MV' => ['name' => 'Maldives', 'latitude' => 3.25, 'longitude' => 73.0],
'MX' => ['name' => 'Mexico', 'latitude' => 23.0, 'longitude' => -102.0],
'MH' => ['name' => 'Marshall Islands', 'latitude' => 9.0, 'longitude' => 168.0],
'MK' => ['name' => 'North Macedonia', 'latitude' => 41.83, 'longitude' => 22.0],
'ML' => ['name' => 'Mali', 'latitude' => 17.0, 'longitude' => -4.0],
'MT' => ['name' => 'Malta', 'latitude' => 35.83, 'longitude' => 14.58],
'MM' => ['name' => 'Myanmar', 'latitude' => 22.0, 'longitude' => 98.0],
'ME' => ['name' => 'Montenegro', 'latitude' => 42.5, 'longitude' => 19.3],
'MN' => ['name' => 'Mongolia', 'latitude' => 46.0, 'longitude' => 105.0],
'MZ' => ['name' => 'Mozambique', 'latitude' => -18.25, 'longitude' => 35.0],
'MR' => ['name' => 'Mauritania', 'latitude' => 20.0, 'longitude' => -12.0],
'MU' => ['name' => 'Mauritius', 'latitude' => -20.28, 'longitude' => 57.55],
'MW' => ['name' => 'Malawi', 'latitude' => -13.5, 'longitude' => 34.0],
'MY' => ['name' => 'Malaysia', 'latitude' => 2.5, 'longitude' => 112.5],
'NA' => ['name' => 'Namibia', 'latitude' => -22.0, 'longitude' => 17.0],
'NE' => ['name' => 'Niger', 'latitude' => 16.0, 'longitude' => 8.0],
'NG' => ['name' => 'Nigeria', 'latitude' => 10.0, 'longitude' => 8.0],
'NI' => ['name' => 'Nicaragua', 'latitude' => 13.0, 'longitude' => -85.0],
'NL' => ['name' => 'Netherlands', 'latitude' => 52.5, 'longitude' => 5.75],
'NO' => ['name' => 'Norway', 'latitude' => 62.0, 'longitude' => 10.0],
'NP' => ['name' => 'Nepal', 'latitude' => 28.0, 'longitude' => 84.0],
'NR' => ['name' => 'Nauru', 'latitude' => -0.53, 'longitude' => 166.92],
'NZ' => ['name' => 'New Zealand', 'latitude' => -41.0, 'longitude' => 174.0],
'OM' => ['name' => 'Oman', 'latitude' => 21.0, 'longitude' => 57.0],
'PK' => ['name' => 'Pakistan', 'latitude' => 30.0, 'longitude' => 70.0],
'PS' => ['name' => 'Palestine', 'latitude' => 31.9, 'longitude' => 35.2],
'PA' => ['name' => 'Panama', 'latitude' => 9.0, 'longitude' => -80.0],
'PE' => ['name' => 'Peru', 'latitude' => -10.0, 'longitude' => -76.0],
'PH' => ['name' => 'Philippines', 'latitude' => 13.0, 'longitude' => 122.0],
'PW' => ['name' => 'Palau', 'latitude' => 7.5, 'longitude' => 134.5],
'PG' => ['name' => 'Papua New Guinea', 'latitude' => -6.0, 'longitude' => 147.0],
'PL' => ['name' => 'Poland', 'latitude' => 52.0, 'longitude' => 20.0],
'KP' => ['name' => 'North Korea', 'latitude' => 40.0, 'longitude' => 127.0],
'PT' => ['name' => 'Portugal', 'latitude' => 39.5, 'longitude' => -8.0],
'PY' => ['name' => 'Paraguay', 'latitude' => -23.0, 'longitude' => -58.0],
'QA' => ['name' => 'Qatar', 'latitude' => 25.5, 'longitude' => 51.25],
'RO' => ['name' => 'Romania', 'latitude' => 46.0, 'longitude' => 25.0],
'RU' => ['name' => 'Russia', 'latitude' => 60.0, 'longitude' => 100.0],
'RW' => ['name' => 'Rwanda', 'latitude' => -2.0, 'longitude' => 30.0],
'SA' => ['name' => 'Saudi Arabia', 'latitude' => 25.0, 'longitude' => 45.0],
'SD' => ['name' => 'Sudan', 'latitude' => 15.0, 'longitude' => 30.0],
'SN' => ['name' => 'Senegal', 'latitude' => 14.0, 'longitude' => -14.0],
'SG' => ['name' => 'Singapore', 'latitude' => 1.37, 'longitude' => 103.8],
'SB' => ['name' => 'Solomon Islands', 'latitude' => -8.0, 'longitude' => 159.0],
'SL' => ['name' => 'Sierra Leone', 'latitude' => 8.5, 'longitude' => -11.5],
'SV' => ['name' => 'El Salvador', 'latitude' => 13.83, 'longitude' => -88.92],
'SM' => ['name' => 'San Marino', 'latitude' => 43.77, 'longitude' => 12.42],
'SO' => ['name' => 'Somalia', 'latitude' => 10.0, 'longitude' => 49.0],
'RS' => ['name' => 'Serbia', 'latitude' => 44.0, 'longitude' => 21.0],
'SS' => ['name' => 'South Sudan', 'latitude' => 8.0, 'longitude' => 30.0],
'ST' => ['name' => 'São Tomé and Príncipe', 'latitude' => 1.0, 'longitude' => 7.0],
'SR' => ['name' => 'Suriname', 'latitude' => 4.0, 'longitude' => -56.0],
'SK' => ['name' => 'Slovakia', 'latitude' => 48.67, 'longitude' => 19.5],
'SI' => ['name' => 'Slovenia', 'latitude' => 46.0, 'longitude' => 15.0],
'SE' => ['name' => 'Sweden', 'latitude' => 62.0, 'longitude' => 15.0],
'SZ' => ['name' => 'Eswatini', 'latitude' => -26.5, 'longitude' => 31.5],
'SC' => ['name' => 'Seychelles', 'latitude' => -4.58, 'longitude' => 55.67],
'SY' => ['name' => 'Syria', 'latitude' => 35.0, 'longitude' => 38.0],
'TD' => ['name' => 'Chad', 'latitude' => 15.0, 'longitude' => 19.0],
'TG' => ['name' => 'Togo', 'latitude' => 8.0, 'longitude' => 1.17],
'TH' => ['name' => 'Thailand', 'latitude' => 15.0, 'longitude' => 100.0],
'TJ' => ['name' => 'Tajikistan', 'latitude' => 39.0, 'longitude' => 71.0],
'TM' => ['name' => 'Turkmenistan', 'latitude' => 40.0, 'longitude' => 60.0],
'TL' => ['name' => 'Timor-Leste', 'latitude' => -8.83, 'longitude' => 125.92],
'TO' => ['name' => 'Tonga', 'latitude' => -20.0, 'longitude' => -175.0],
'TT' => ['name' => 'Trinidad and Tobago', 'latitude' => 11.0, 'longitude' => -61.0],
'TN' => ['name' => 'Tunisia', 'latitude' => 34.0, 'longitude' => 9.0],
'TR' => ['name' => 'Turkey', 'latitude' => 39.0, 'longitude' => 35.0],
'TV' => ['name' => 'Tuvalu', 'latitude' => -8.0, 'longitude' => 178.0],
'TZ' => ['name' => 'Tanzania', 'latitude' => -6.0, 'longitude' => 35.0],
'TW' => ['name' => 'Taiwan', 'latitude' => 23.5, 'longitude' => 121.0],
'UG' => ['name' => 'Uganda', 'latitude' => 1.0, 'longitude' => 32.0],
'UA' => ['name' => 'Ukraine', 'latitude' => 49.0, 'longitude' => 32.0],
'UY' => ['name' => 'Uruguay', 'latitude' => -33.0, 'longitude' => -56.0],
'US' => ['name' => 'United States', 'latitude' => 38.0, 'longitude' => -97.0],
'UZ' => ['name' => 'Uzbekistan', 'latitude' => 41.0, 'longitude' => 64.0],
'VA' => ['name' => 'Vatican City', 'latitude' => 41.9, 'longitude' => 12.45],
'VC' => ['name' => 'Saint Vincent and the Grenadines', 'latitude' => 13.25, 'longitude' => -61.2],
'VE' => ['name' => 'Venezuela', 'latitude' => 8.0, 'longitude' => -66.0],
'VN' => ['name' => 'Vietnam', 'latitude' => 16.0, 'longitude' => 106.0],
'VU' => ['name' => 'Vanuatu', 'latitude' => -16.0, 'longitude' => 167.0],
'WS' => ['name' => 'Samoa', 'latitude' => -13.58, 'longitude' => -172.33],
'YE' => ['name' => 'Yemen', 'latitude' => 15.0, 'longitude' => 48.0],
'ZA' => ['name' => 'South Africa', 'latitude' => -29.0, 'longitude' => 24.0],
'ZM' => ['name' => 'Zambia', 'latitude' => -15.0, 'longitude' => 30.0],
'ZW' => ['name' => 'Zimbabwe', 'latitude' => -20.0, 'longitude' => 30.0],
];
+9 -9
View File
@@ -11,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '16.1.0',
'version' => '17.0.1',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -59,7 +59,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '13.1.1',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -77,7 +77,7 @@ return [
[
'key' => 'apple',
'name' => 'Apple',
'version' => '7.1.0',
'version' => '9.0.0',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -134,7 +134,7 @@ return [
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.6.0',
'version' => '0.7.1',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -217,7 +217,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '6.2.0',
'version' => '6.2.2',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -245,7 +245,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '14.2.0',
'version' => '15.0.1',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -299,7 +299,7 @@ return [
[
'key' => 'python',
'name' => 'Python',
'version' => '6.2.0',
'version' => '9.0.2',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -371,7 +371,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '12.2.0',
'version' => '14.0.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -411,7 +411,7 @@ return [
[
'key' => 'swift',
'name' => 'Swift',
'version' => '6.2.0',
'version' => '8.0.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
@@ -10510,17 +10510,18 @@
"properties": {
"type": {
"type": "string",
"description": "Type of reference passed. Allowed values are: branch",
"description": "Type of reference passed. Allowed values are: branch, commit",
"x-example": "branch",
"enum": [
"branch"
"branch",
"commit"
],
"x-enum-name": "VCSDeploymentType",
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name, commit hash",
"x-example": "<REFERENCE>"
},
"activate": {
@@ -26068,18 +26069,19 @@
"properties": {
"type": {
"type": "string",
"description": "Type of reference passed. Allowed values are: branch",
"description": "Type of reference passed. Allowed values are: branch, commit",
"x-example": "branch",
"enum": [
"branch",
"commit"
"commit",
"tag"
],
"x-enum-name": "VCSDeploymentType",
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name, commit hash",
"x-example": "<REFERENCE>"
},
"activate": {
@@ -9379,17 +9379,18 @@
"properties": {
"type": {
"type": "string",
"description": "Type of reference passed. Allowed values are: branch",
"description": "Type of reference passed. Allowed values are: branch, commit",
"x-example": "branch",
"enum": [
"branch"
"branch",
"commit"
],
"x-enum-name": "VCSDeploymentType",
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name, commit hash",
"x-example": "<REFERENCE>"
},
"activate": {
@@ -17947,18 +17948,19 @@
"properties": {
"type": {
"type": "string",
"description": "Type of reference passed. Allowed values are: branch",
"description": "Type of reference passed. Allowed values are: branch, commit",
"x-example": "branch",
"enum": [
"branch",
"commit"
"commit",
"tag"
],
"x-enum-name": "VCSDeploymentType",
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name, commit hash",
"x-example": "<REFERENCE>"
},
"activate": {
@@ -10687,18 +10687,19 @@
"properties": {
"type": {
"type": "string",
"description": "Type of reference passed. Allowed values are: branch",
"description": "Type of reference passed. Allowed values are: branch, commit",
"default": null,
"x-example": "branch",
"enum": [
"branch"
"branch",
"commit"
],
"x-enum-name": "VCSDeploymentType",
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name, commit hash",
"default": null,
"x-example": "<REFERENCE>"
},
@@ -26596,19 +26597,20 @@
"properties": {
"type": {
"type": "string",
"description": "Type of reference passed. Allowed values are: branch",
"description": "Type of reference passed. Allowed values are: branch, commit",
"default": null,
"x-example": "branch",
"enum": [
"branch",
"commit"
"commit",
"tag"
],
"x-enum-name": "VCSDeploymentType",
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name, commit hash",
"default": null,
"x-example": "<REFERENCE>"
},
+8 -6
View File
@@ -9557,18 +9557,19 @@
"properties": {
"type": {
"type": "string",
"description": "Type of reference passed. Allowed values are: branch",
"description": "Type of reference passed. Allowed values are: branch, commit",
"default": null,
"x-example": "branch",
"enum": [
"branch"
"branch",
"commit"
],
"x-enum-name": "VCSDeploymentType",
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name, commit hash",
"default": null,
"x-example": "<REFERENCE>"
},
@@ -18445,19 +18446,20 @@
"properties": {
"type": {
"type": "string",
"description": "Type of reference passed. Allowed values are: branch",
"description": "Type of reference passed. Allowed values are: branch, commit",
"default": null,
"x-example": "branch",
"enum": [
"branch",
"commit"
"commit",
"tag"
],
"x-enum-name": "VCSDeploymentType",
"x-enum-keys": []
},
"reference": {
"type": "string",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name",
"description": "VCS reference to create deployment from. Depending on type this can be: branch name, commit hash",
"default": null,
"x-example": "<REFERENCE>"
},
+2 -2
View File
@@ -118,7 +118,7 @@ App::get('/v1/locale/countries')
->inject('response')
->inject('locale')
->action(function (Response $response, Locale $locale) {
$list = Config::getParam('locale-countries'); /* @var $list array */
$list = array_keys(Config::getParam('locale-countries')); /* @var $list array */
$output = [];
foreach ($list as $value) {
@@ -229,7 +229,7 @@ App::get('/v1/locale/continents')
->inject('response')
->inject('locale')
->action(function (Response $response, Locale $locale) {
$list = Config::getParam('locale-continents');
$list = array_keys(Config::getParam('locale-continents'));
foreach ($list as $value) {
$output[] = new Document([
+8 -2
View File
@@ -277,6 +277,8 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
@@ -302,6 +304,8 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
} catch (Duplicate $err) {
@@ -331,6 +335,8 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
} catch (Duplicate $err) {
@@ -724,7 +730,7 @@ App::post('/v1/vcs/github/installations/:installationId/detections')
}
$frameworks = Config::getParam('frameworks');
if (!\in_array($framework, array_keys($frameworks), true)) {
if (!\in_array($framework, \array_keys($frameworks), true)) {
$framework = 'other';
}
$output->setAttribute('framework', $framework);
@@ -876,7 +882,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
}
$frameworks = Config::getParam('frameworks');
if (!\in_array($framework, array_keys($frameworks), true)) {
if (!\in_array($framework, \array_keys($frameworks), true)) {
$framework = 'other';
}
$repo['framework'] = $framework;
+10 -2
View File
@@ -736,6 +736,12 @@ App::init()
]);
}
$owner = '';
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
if (!empty($functionsDomain) && \str_ends_with($domain->get(), $functionsDomain)) {
$owner = 'Appwrite';
}
if ($domainDocument->isEmpty()) {
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain->get()) : ID::unique();
$domainDocument = new Document([
@@ -744,9 +750,11 @@ App::init()
'domain' => $domain->get(),
'type' => 'api',
'status' => 'verifying',
'projectId' => 'console',
'projectInternalId' => 'console',
'projectId' => $console->getId(),
'projectInternalId' => $console->getInternalId(),
'search' => implode(' ', [$ruleId, $domain->get()]),
'owner' => $owner,
'region' => $console->getAttribute('region')
]);
$domainDocument = $dbForPlatform->createDocument('rules', $domainDocument);
+9 -1925
View File
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
<?php
use Utopia\Config\Config;
Config::load('template-runtimes', __DIR__ . '/../config/template-runtimes.php');
Config::load('events', __DIR__ . '/../config/events.php');
Config::load('auth', __DIR__ . '/../config/auth.php');
Config::load('apis', __DIR__ . '/../config/apis.php'); // List of APIs
Config::load('errors', __DIR__ . '/../config/errors.php');
Config::load('oAuthProviders', __DIR__ . '/../config/oAuthProviders.php');
Config::load('platforms', __DIR__ . '/../config/platforms.php');
Config::load('console', __DIR__ . '/../config/console.php');
Config::load('collections', __DIR__ . '/../config/collections.php');
Config::load('frameworks', __DIR__ . '/../config/frameworks.php');
Config::load('runtimes', __DIR__ . '/../config/runtimes.php');
Config::load('runtimes-v2', __DIR__ . '/../config/runtimes-v2.php');
Config::load('usage', __DIR__ . '/../config/usage.php');
Config::load('roles', __DIR__ . '/../config/roles.php'); // User roles and scopes
Config::load('scopes', __DIR__ . '/../config/scopes.php'); // User roles and scopes
Config::load('services', __DIR__ . '/../config/services.php'); // List of services
Config::load('variables', __DIR__ . '/../config/variables.php'); // List of env variables
Config::load('regions', __DIR__ . '/../config/regions.php'); // List of available regions
Config::load('avatar-browsers', __DIR__ . '/../config/avatars/browsers.php');
Config::load('avatar-credit-cards', __DIR__ . '/../config/avatars/credit-cards.php');
Config::load('avatar-flags', __DIR__ . '/../config/avatars/flags.php');
Config::load('locale-codes', __DIR__ . '/../config/locale/codes.php');
Config::load('locale-currencies', __DIR__ . '/../config/locale/currencies.php');
Config::load('locale-eu', __DIR__ . '/../config/locale/eu.php');
Config::load('locale-languages', __DIR__ . '/../config/locale/languages.php');
Config::load('locale-phones', __DIR__ . '/../config/locale/phones.php');
Config::load('locale-countries', __DIR__ . '/../config/locale/countries.php');
Config::load('locale-continents', __DIR__ . '/../config/locale/continents.php');
Config::load('locale-templates', __DIR__ . '/../config/locale/templates.php');
Config::load('storage-logos', __DIR__ . '/../config/storage/logos.php');
Config::load('storage-mimes', __DIR__ . '/../config/storage/mimes.php');
Config::load('storage-inputs', __DIR__ . '/../config/storage/inputs.php');
Config::load('storage-outputs', __DIR__ . '/../config/storage/outputs.php');
Config::load('specifications', __DIR__ . '/../config/specifications.php');
Config::load('templates-function', __DIR__ . '/../config/templates/function.php');
Config::load('templates-site', __DIR__ . '/../config/templates/site.php');
+256
View File
@@ -0,0 +1,256 @@
<?php
use Appwrite\Platform\Modules\Compute\Specification;
const APP_NAME = 'Appwrite';
const APP_DOMAIN = 'appwrite.io';
const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address
const APP_EMAIL_SECURITY = ''; // Default security email address
const APP_USERAGENT = APP_NAME . '-Server v%s. Please report abuse at %s';
const APP_MODE_DEFAULT = 'default';
const APP_MODE_ADMIN = 'admin';
const APP_PAGING_LIMIT = 12;
const APP_LIMIT_COUNT = 5000;
const APP_LIMIT_USERS = 10_000;
const APP_LIMIT_USER_PASSWORD_HISTORY = 20;
const APP_LIMIT_USER_SESSIONS_MAX = 100;
const APP_LIMIT_USER_SESSIONS_DEFAULT = 10;
const APP_LIMIT_ANTIVIRUS = 20_000_000; //20MB
const APP_LIMIT_ENCRYPTION = 20_000_000; //20MB
const APP_LIMIT_COMPRESSION = 20_000_000; //20MB
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
const APP_LIMIT_ARRAY_LABELS_SIZE = 1000; // Default maximum of how many labels elements can there be in API parameter that expects array value
const APP_LIMIT_ARRAY_ELEMENT_SIZE = 4096; // Default maximum length of element in array parameter represented by maximum URL length.
const APP_LIMIT_SUBQUERY = 1000;
const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000;
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours
const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 4318;
const APP_VERSION_STABLE = '1.7.0';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime';
const APP_DATABASE_ATTRIBUTE_URL = 'url';
const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
const APP_DATABASE_ATTRIBUTE_STRING_MAX_LENGTH = 1_073_741_824; // 2^32 bits / 4 bits per char
const APP_DATABASE_TIMEOUT_MILLISECONDS_API = 15 * 1000; // 15 seconds
const APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER = 300 * 1000; // 5 minutes
const APP_DATABASE_TIMEOUT_MILLISECONDS_TASK = 300 * 1000; // 5 minutes
const APP_DATABASE_QUERY_MAX_VALUES = 500;
const APP_STORAGE_UPLOADS = '/storage/uploads';
const APP_STORAGE_SITES = '/storage/sites';
const APP_STORAGE_FUNCTIONS = '/storage/functions';
const APP_STORAGE_BUILDS = '/storage/builds';
const APP_STORAGE_CACHE = '/storage/cache';
const APP_STORAGE_CERTIFICATES = '/storage/certificates';
const APP_STORAGE_CONFIG = '/storage/config';
const APP_STORAGE_READ_BUFFER = 20 * (1000 * 1000); //20MB other names `APP_STORAGE_MEMORY_LIMIT`, `APP_STORAGE_MEMORY_BUFFER`, `APP_STORAGE_READ_LIMIT`, `APP_STORAGE_BUFFER_LIMIT`
const APP_SOCIAL_TWITTER = 'https://twitter.com/appwrite';
const APP_SOCIAL_TWITTER_HANDLE = 'appwrite';
const APP_SOCIAL_FACEBOOK = 'https://www.facebook.com/appwrite.io';
const APP_SOCIAL_LINKEDIN = 'https://www.linkedin.com/company/appwrite';
const APP_SOCIAL_INSTAGRAM = 'https://www.instagram.com/appwrite.io';
const APP_SOCIAL_GITHUB = 'https://github.com/appwrite';
const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord';
const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244';
const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
const APP_HOSTNAME_INTERNAL = 'appwrite';
const APP_COMPUTE_CPUS_DEFAULT = 0.5;
const APP_COMPUTE_MEMORY_DEFAULT = 512;
const APP_COMPUTE_SPECIFICATION_DEFAULT = Specification::S_1VCPU_512MB;
const APP_PLATFORM_SERVER = 'server';
const APP_PLATFORM_CLIENT = 'client';
const APP_PLATFORM_CONSOLE = 'console';
// Database Reconnect
const DATABASE_RECONNECT_SLEEP = 2;
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
// Database Worker Types
const DATABASE_TYPE_CREATE_ATTRIBUTE = 'createAttribute';
const DATABASE_TYPE_CREATE_INDEX = 'createIndex';
const DATABASE_TYPE_DELETE_ATTRIBUTE = 'deleteAttribute';
const DATABASE_TYPE_DELETE_INDEX = 'deleteIndex';
const DATABASE_TYPE_DELETE_COLLECTION = 'deleteCollection';
const DATABASE_TYPE_DELETE_DATABASE = 'deleteDatabase';
// Build Worker Types
const BUILD_TYPE_DEPLOYMENT = 'deployment';
const BUILD_TYPE_RETRY = 'retry';
// Deletion Types
const DELETE_TYPE_DATABASES = 'databases';
const DELETE_TYPE_DOCUMENT = 'document';
const DELETE_TYPE_COLLECTIONS = 'collections';
const DELETE_TYPE_PROJECTS = 'projects';
const DELETE_TYPE_SITES = 'sites';
const DELETE_TYPE_FUNCTIONS = 'functions';
const DELETE_TYPE_DEPLOYMENTS = 'deployments';
const DELETE_TYPE_USERS = 'users';
const DELETE_TYPE_TEAM_PROJECTS = 'teams_projects';
const DELETE_TYPE_EXECUTIONS = 'executions';
const DELETE_TYPE_AUDIT = 'audit';
const DELETE_TYPE_ABUSE = 'abuse';
const DELETE_TYPE_USAGE = 'usage';
const DELETE_TYPE_REALTIME = 'realtime';
const DELETE_TYPE_BUCKETS = 'buckets';
const DELETE_TYPE_INSTALLATIONS = 'installations';
const DELETE_TYPE_RULES = 'rules';
const DELETE_TYPE_SESSIONS = 'sessions';
const DELETE_TYPE_CACHE_BY_TIMESTAMP = 'cacheByTimeStamp';
const DELETE_TYPE_CACHE_BY_RESOURCE = 'cacheByResource';
const DELETE_TYPE_SCHEDULES = 'schedules';
const DELETE_TYPE_TOPIC = 'topic';
const DELETE_TYPE_TARGET = 'target';
const DELETE_TYPE_EXPIRED_TARGETS = 'invalid_targets';
const DELETE_TYPE_SESSION_TARGETS = 'session_targets';
const DELETE_TYPE_MAINTENANCE = 'maintenance';
// Message types
const MESSAGE_SEND_TYPE_INTERNAL = 'internal';
const MESSAGE_SEND_TYPE_EXTERNAL = 'external';
// Mail Types
const MAIL_TYPE_VERIFICATION = 'verification';
const MAIL_TYPE_MAGIC_SESSION = 'magicSession';
const MAIL_TYPE_RECOVERY = 'recovery';
const MAIL_TYPE_INVITATION = 'invitation';
const MAIL_TYPE_CERTIFICATE = 'certificate';
// Auth Types
const APP_AUTH_TYPE_SESSION = 'Session';
const APP_AUTH_TYPE_JWT = 'JWT';
const APP_AUTH_TYPE_KEY = 'Key';
const APP_AUTH_TYPE_ADMIN = 'Admin';
// Response related
const MAX_OUTPUT_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB
// Function headers
const FUNCTION_ALLOWLIST_HEADERS_REQUEST = ['content-type', 'agent', 'content-length', 'host'];
const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
// Message types
const MESSAGE_TYPE_EMAIL = 'email';
const MESSAGE_TYPE_SMS = 'sms';
const MESSAGE_TYPE_PUSH = 'push';
// API key types
const API_KEY_STANDARD = 'standard';
const API_KEY_DYNAMIC = 'dynamic';
// Usage metrics
const METRIC_TEAMS = 'teams';
const METRIC_USERS = 'users';
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';
const METRIC_MESSAGES_SENT = METRIC_MESSAGES . '.sent';
const METRIC_MESSAGES_FAILED = METRIC_MESSAGES . '.failed';
const METRIC_MESSAGES_TYPE = METRIC_MESSAGES . '.{type}';
const METRIC_MESSAGES_TYPE_SENT = METRIC_MESSAGES . '.{type}.sent';
const METRIC_MESSAGES_TYPE_FAILED = METRIC_MESSAGES . '.{type}.failed';
const METRIC_MESSAGES_TYPE_PROVIDER = METRIC_MESSAGES . '.{type}.{provider}';
const METRIC_MESSAGES_TYPE_PROVIDER_SENT = METRIC_MESSAGES . '.{type}.{provider}.sent';
const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provider}.failed';
const METRIC_SESSIONS = 'sessions';
const METRIC_DATABASES = 'databases';
const METRIC_COLLECTIONS = 'collections';
const METRIC_DATABASES_STORAGE = 'databases.storage';
const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections';
const METRIC_DATABASE_ID_STORAGE = '{databaseInternalId}.databases.storage';
const METRIC_DOCUMENTS = 'documents';
const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents';
const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE = '{databaseInternalId}.{collectionInternalId}.databases.storage';
const METRIC_DATABASES_OPERATIONS_READS = 'databases.operations.reads';
const METRIC_DATABASE_ID_OPERATIONS_READS = '{databaseInternalId}.databases.operations.reads';
const METRIC_DATABASES_OPERATIONS_WRITES = 'databases.operations.writes';
const METRIC_DATABASE_ID_OPERATIONS_WRITES = '{databaseInternalId}.databases.operations.writes';
const METRIC_BUCKETS = 'buckets';
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';
const METRIC_FUNCTIONS = 'functions';
const METRIC_DEPLOYMENTS = 'deployments';
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
const METRIC_BUILDS = 'builds';
const METRIC_BUILDS_SUCCESS = 'builds.success';
const METRIC_BUILDS_FAILED = 'builds.failed';
const METRIC_BUILDS_STORAGE = 'builds.storage';
const METRIC_BUILDS_COMPUTE = 'builds.compute';
const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success';
const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed';
const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds';
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
const METRIC_FUNCTION_ID_BUILDS_SUCCESS = '{functionInternalId}.builds.success';
const METRIC_FUNCTION_ID_BUILDS_FAILED = '{functionInternalId}.builds.failed';
const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.compute.success';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed';
const METRIC_FUNCTION_ID_BUILDS_MB_SECONDS = '{functionInternalId}.builds.mbSeconds';
const METRIC_SITES_ID_BUILDS = 'sites.{siteInternalId}.builds';
const METRIC_SITES_ID_BUILDS_SUCCESS = 'sites.{siteInternalId}.builds.success';
const METRIC_SITES_ID_BUILDS_FAILED = 'sites.{siteInternalId}.builds.failed';
const METRIC_SITES_ID_BUILDS_STORAGE = 'sites.{siteInternalId}.builds.storage';
const METRIC_SITES_ID_BUILDS_COMPUTE = 'sites.{siteInternalId}.builds.compute';
const METRIC_SITES_ID_BUILDS_COMPUTE_SUCCESS = 'sites.{siteInternalId}.builds.compute.success';
const METRIC_SITES_ID_BUILDS_COMPUTE_FAILED = 'sites.{siteInternalId}.builds.compute.failed';
const METRIC_SITES_ID_BUILDS_MB_SECONDS = 'sites.{siteInternalId}.builds.mbSeconds';
const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
const METRIC_EXECUTIONS = 'executions';
const METRIC_EXECUTIONS_COMPUTE = 'executions.compute';
const METRIC_EXECUTIONS_MB_SECONDS = 'executions.mbSeconds';
const METRIC_FUNCTION_ID_EXECUTIONS = '{functionInternalId}.executions';
const METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE = '{functionInternalId}.executions.compute';
const METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS = '{functionInternalId}.executions.mbSeconds';
const METRIC_SITE_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_SITE_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
const METRIC_SITE_ID_BUILDS = '{siteInternalId}.builds';
const METRIC_SITE_ID_BUILDS_STORAGE = '{siteInternalId}.builds.storage';
const METRIC_SITE_ID_BUILDS_COMPUTE = '{siteInternalId}.builds.compute';
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_TARGETS = 'targets';
const METRIC_PROVIDER_TYPE_TARGETS = '{providerType}.targets';
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
const RESOURCE_TYPE_PROJECTS = 'projects';
const RESOURCE_TYPE_FUNCTIONS = 'functions';
const RESOURCE_TYPE_SITES = 'sites';
const RESOURCE_TYPE_DATABASES = 'databases';
const RESOURCE_TYPE_BUCKETS = 'buckets';
const RESOURCE_TYPE_PROVIDERS = 'providers';
const RESOURCE_TYPE_TOPICS = 'topics';
const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers';
const RESOURCE_TYPE_MESSAGES = 'messages';
+404
View File
@@ -0,0 +1,404 @@
<?php
use Appwrite\OpenSSL\OpenSSL;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\System\System;
Database::addFilter(
'casting',
function (mixed $value) {
return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION);
},
function (mixed $value) {
if (is_null($value)) {
return;
}
return json_decode($value, true)['value'];
}
);
Database::addFilter(
'enum',
function (mixed $value, Document $attribute) {
if ($attribute->isSet('elements')) {
$attribute->removeAttribute('elements');
}
return $value;
},
function (mixed $value, Document $attribute) {
$formatOptions = \json_decode($attribute->getAttribute('formatOptions', '[]'), true);
if (isset($formatOptions['elements'])) {
$attribute->setAttribute('elements', $formatOptions['elements']);
}
return $value;
}
);
Database::addFilter(
'range',
function (mixed $value, Document $attribute) {
if ($attribute->isSet('min')) {
$attribute->removeAttribute('min');
}
if ($attribute->isSet('max')) {
$attribute->removeAttribute('max');
}
return $value;
},
function (mixed $value, Document $attribute) {
$formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true);
if (isset($formatOptions['min']) || isset($formatOptions['max'])) {
$attribute
->setAttribute('min', $formatOptions['min'])
->setAttribute('max', $formatOptions['max']);
}
return $value;
}
);
Database::addFilter(
'subQueryAttributes',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
$attributes = $database->find('attributes', [
Query::equal('collectionInternalId', [$document->getInternalId()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getLimitForAttributes()),
]);
foreach ($attributes as $attribute) {
if ($attribute->getAttribute('type') === Database::VAR_RELATIONSHIP) {
$options = $attribute->getAttribute('options');
foreach ($options as $key => $value) {
$attribute->setAttribute($key, $value);
}
$attribute->removeAttribute('options');
}
}
return $attributes;
}
);
Database::addFilter(
'subQueryIndexes',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('indexes', [
Query::equal('collectionInternalId', [$document->getInternalId()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getLimitForIndexes()),
]);
}
);
Database::addFilter(
'subQueryPlatforms',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('platforms', [
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
Database::addFilter(
'subQueryKeys',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('keys', [
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
Database::addFilter(
'subQueryWebhooks',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('webhooks', [
Query::equal('projectInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
Database::addFilter(
'subQuerySessions',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database->find('sessions', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryTokens',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
->find('tokens', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryChallenges',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
->find('challenges', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryAuthenticators',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
->find('authenticators', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryMemberships',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
->find('memberships', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
}
);
Database::addFilter(
'subQueryVariables',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('variables', [
Query::equal('resourceInternalId', [$document->getInternalId()]),
Query::equal('resourceType', ['function', 'site']),
Query::limit(APP_LIMIT_SUBQUERY),
]);
}
);
Database::addFilter(
'encrypt',
function (mixed $value) {
$key = System::getEnv('_APP_OPENSSL_KEY_V1');
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
$tag = null;
return json_encode([
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
'method' => OpenSSL::CIPHER_AES_128_GCM,
'iv' => \bin2hex($iv),
'tag' => \bin2hex($tag ?? ''),
'version' => '1',
]);
},
function (mixed $value) {
if (is_null($value)) {
return;
}
$value = json_decode($value, true);
$key = System::getEnv('_APP_OPENSSL_KEY_V' . $value['version']);
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
}
);
Database::addFilter(
'subQueryProjectVariables',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
->find('variables', [
Query::equal('resourceType', ['project']),
Query::limit(APP_LIMIT_SUBQUERY)
]);
}
);
Database::addFilter(
'userSearch',
function (mixed $value, Document $user) {
$searchValues = [
$user->getId(),
$user->getAttribute('email', ''),
$user->getAttribute('name', ''),
$user->getAttribute('phone', '')
];
foreach ($user->getAttribute('labels', []) as $label) {
$searchValues[] = 'label:' . $label;
}
$search = implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
Database::addFilter(
'subQueryTargets',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
->find('targets', [
Query::equal('userInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBQUERY)
]));
}
);
Database::addFilter(
'subQueryTopicTargets',
function (mixed $value) {
return;
},
function (mixed $value, Document $document, Database $database) {
$targetIds = Authorization::skip(fn () => \array_map(
fn ($document) => $document->getAttribute('targetInternalId'),
$database->find('subscribers', [
Query::equal('topicInternalId', [$document->getInternalId()]),
Query::limit(APP_LIMIT_SUBSCRIBERS_SUBQUERY)
])
));
if (\count($targetIds) > 0) {
return $database->skipValidation(fn () => $database->find('targets', [
Query::equal('$internalId', $targetIds)
]));
}
return [];
}
);
Database::addFilter(
'providerSearch',
function (mixed $value, Document $provider) {
$searchValues = [
$provider->getId(),
$provider->getAttribute('name', ''),
$provider->getAttribute('provider', ''),
$provider->getAttribute('type', '')
];
$search = \implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
Database::addFilter(
'topicSearch',
function (mixed $value, Document $topic) {
$searchValues = [
$topic->getId(),
$topic->getAttribute('name', ''),
$topic->getAttribute('description', ''),
];
$search = \implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
Database::addFilter(
'messageSearch',
function (mixed $value, Document $message) {
$searchValues = [
$message->getId(),
$message->getAttribute('description', ''),
$message->getAttribute('status', ''),
];
$data = \json_decode($message->getAttribute('data', []), true);
$providerType = $message->getAttribute('providerType', '');
switch ($providerType) {
case MESSAGE_TYPE_EMAIL:
$searchValues[] = $data['subject'];
$searchValues[] = MESSAGE_TYPE_EMAIL;
break;
case MESSAGE_TYPE_SMS:
$searchValues[] = $data['content'];
$searchValues[] = MESSAGE_TYPE_SMS;
break;
case MESSAGE_TYPE_PUSH:
$searchValues[] = $data['title'] ?? '';
$searchValues[] = MESSAGE_TYPE_PUSH;
break;
}
$search = \implode(' ', \array_filter($searchValues));
return $search;
},
function (mixed $value) {
return $value;
}
);
+43
View File
@@ -0,0 +1,43 @@
<?php
use Appwrite\Network\Validator\Email;
use Utopia\Database\Database;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Structure;
use Utopia\Validator\IP;
use Utopia\Validator\Range;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () {
return new Email();
}, Database::VAR_STRING);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_DATETIME, function () {
return new DatetimeValidator();
}, Database::VAR_DATETIME);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) {
$elements = $attribute['formatOptions']['elements'] ?? [];
return new WhiteList($elements, true);
}, Database::VAR_STRING);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () {
return new IP();
}, Database::VAR_STRING);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () {
return new URL();
}, Database::VAR_STRING);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
return new Range($min, $max, Range::TYPE_INTEGER);
}, Database::VAR_INTEGER);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
return new Range($min, $max, Range::TYPE_FLOAT);
}, Database::VAR_FLOAT);
+23
View File
@@ -0,0 +1,23 @@
<?php
use Utopia\Config\Config;
use Utopia\Locale\Locale;
Locale::$exceptions = false;
$locales = Config::getParam('locale-codes', []);
foreach ($locales as $locale) {
$code = $locale['code'];
$path = __DIR__ . '/../config/locale/translations/' . $code . '.json';
if (!\file_exists($path)) {
$path = __DIR__ . '/../config/locale/translations/' . \substr($code, 0, 2) . '.json'; // if `ar-ae` doesn't exist, look for `ar`
if (!\file_exists($path)) {
$path = __DIR__ . '/../config/locale/translations/en.json'; // if none translation exists, use default from `en.json`
}
}
Locale::setLanguageFromJSON($code, $path);
}
+340
View File
@@ -0,0 +1,340 @@
<?php
use Appwrite\Extend\Exception;
use Appwrite\GraphQL\Promises\Adapter\Swoole;
use Appwrite\Hooks\Hooks;
use Appwrite\PubSub\Adapter\Redis as PubSub;
use Appwrite\URL\URL as AppwriteURL;
use MaxMind\Db\Reader;
use PHPMailer\PHPMailer\PHPMailer;
use Swoole\Database\PDOProxy;
use Utopia\App;
use Utopia\Cache\Adapter\Redis as RedisCache;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Adapter\MariaDB;
use Utopia\Database\Adapter\MySQL;
use Utopia\Database\Adapter\SQL;
use Utopia\Domains\Validator\PublicDomain;
use Utopia\DSN\DSN;
use Utopia\Logger\Adapter\AppSignal;
use Utopia\Logger\Adapter\LogOwl;
use Utopia\Logger\Adapter\Raygun;
use Utopia\Logger\Adapter\Sentry;
use Utopia\Logger\Logger;
use Utopia\Pools\Group;
use Utopia\Pools\Pool;
use Utopia\Queue;
use Utopia\Registry\Registry;
use Utopia\System\System;
$register = new Registry();
App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
if (!App::isProduction()) {
// Allow specific domains to skip public domain validation in dev environment
// Useful for existing tests involving webhooks
PublicDomain::allow(['request-catcher']);
}
$register->set('logger', function () {
// Register error logger
$providerName = System::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = System::getEnv('_APP_LOGGING_CONFIG', '');
if (empty($providerConfig)) {
return;
}
try {
$loggingProvider = new DSN($providerConfig ?? '');
$providerName = $loggingProvider->getScheme();
$providerConfig = match ($providerName) {
'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()],
'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()],
default => ['key' => $loggingProvider->getHost()],
};
} catch (Throwable $th) {
// Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables
Console::warning('Using deprecated logging configuration. Please update your configuration to use DSN format.' . $th->getMessage());
$configChunks = \explode(";", $providerConfig);
$providerConfig = match ($providerName) {
'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',],
'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''],
default => ['key' => $providerConfig],
};
}
if (empty($providerName) || empty($providerConfig)) {
return;
}
if (!Logger::hasProvider($providerName)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled");
}
try {
$adapter = match ($providerName) {
'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']),
'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']),
'raygun' => new Raygun($providerConfig['key']),
'appsignal' => new AppSignal($providerConfig['key']),
default => null
};
} catch (Throwable $th) {
$adapter = null;
}
if ($adapter === null) {
Console::error("Logging provider not supported. Logging is disabled");
return;
}
return new Logger($adapter);
});
$register->set('pools', function () {
$group = new Group();
$fallbackForDB = 'db_main=' . AppwriteURL::unparse([
'scheme' => 'mariadb',
'host' => System::getEnv('_APP_DB_HOST', 'mariadb'),
'port' => System::getEnv('_APP_DB_PORT', '3306'),
'user' => System::getEnv('_APP_DB_USER', ''),
'pass' => System::getEnv('_APP_DB_PASS', ''),
'path' => System::getEnv('_APP_DB_SCHEMA', ''),
]);
$fallbackForRedis = 'redis_main=' . AppwriteURL::unparse([
'scheme' => 'redis',
'host' => System::getEnv('_APP_REDIS_HOST', 'redis'),
'port' => System::getEnv('_APP_REDIS_PORT', '6379'),
'user' => System::getEnv('_APP_REDIS_USER', ''),
'pass' => System::getEnv('_APP_REDIS_PASS', ''),
]);
$connections = [
'console' => [
'type' => 'database',
'dsns' => $fallbackForDB,
'multiple' => false,
'schemes' => ['mariadb', 'mysql'],
],
'database' => [
'type' => 'database',
'dsns' => $fallbackForDB,
'multiple' => true,
'schemes' => ['mariadb', 'mysql'],
],
'logs' => [
'type' => 'database',
'dsns' => System::getEnv('_APP_CONNECTIONS_DB_LOGS', $fallbackForDB),
'multiple' => false,
'schemes' => ['mariadb', 'mysql'],
],
'publisher' => [
'type' => 'publisher',
'dsns' => $fallbackForRedis,
'multiple' => false,
'schemes' => ['redis'],
],
'consumer' => [
'type' => 'consumer',
'dsns' => $fallbackForRedis,
'multiple' => false,
'schemes' => ['redis'],
],
'pubsub' => [
'type' => 'pubsub',
'dsns' => $fallbackForRedis,
'multiple' => false,
'schemes' => ['redis'],
],
'cache' => [
'type' => 'cache',
'dsns' => $fallbackForRedis,
'multiple' => true,
'schemes' => ['redis'],
],
];
$maxConnections = System::getEnv('_APP_CONNECTIONS_MAX', 151);
$instanceConnections = $maxConnections / System::getEnv('_APP_POOL_CLIENTS', 14);
$multiprocessing = System::getEnv('_APP_SERVER_MULTIPROCESS', 'disabled') === 'enabled';
if ($multiprocessing) {
$workerCount = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intval(System::getEnv('_APP_WORKER_PER_CORE', 6));
} else {
$workerCount = 1;
}
if ($workerCount > $instanceConnections) {
throw new \Exception('Pool size is too small. Increase the number of allowed database connections or decrease the number of workers.', 500);
}
$poolSize = (int)($instanceConnections / $workerCount);
foreach ($connections as $key => $connection) {
$type = $connection['type'] ?? '';
$multiple = $connection['multiple'] ?? false;
$schemes = $connection['schemes'] ?? [];
$config = [];
$dsns = explode(',', $connection['dsns'] ?? '');
foreach ($dsns as &$dsn) {
$dsn = explode('=', $dsn);
$name = ($multiple) ? $key . '_' . $dsn[0] : $key;
$dsn = $dsn[1] ?? '';
$config[] = $name;
if (empty($dsn)) {
//throw new Exception(Exception::GENERAL_SERVER_ERROR, "Missing value for DSN connection in {$key}");
continue;
}
$dsn = new DSN($dsn);
$dsnHost = $dsn->getHost();
$dsnPort = $dsn->getPort();
$dsnUser = $dsn->getUser();
$dsnPass = $dsn->getPassword();
$dsnScheme = $dsn->getScheme();
$dsnDatabase = $dsn->getPath();
if (!in_array($dsnScheme, $schemes)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Invalid console database scheme");
}
/**
* Get Resource
*
* Creation could be reused across connection types like database, cache, queue, etc.
*
* Resource assignment to an adapter will happen below.
*/
$resource = match ($dsnScheme) {
'mysql',
'mariadb' => function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array(
PDO::ATTR_TIMEOUT => 3, // Seconds
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
PDO::ATTR_EMULATE_PREPARES => true,
PDO::ATTR_STRINGIFY_FETCHES => true
));
});
},
'redis' => function () use ($dsnHost, $dsnPort, $dsnPass) {
$redis = new \Redis();
@$redis->pconnect($dsnHost, (int)$dsnPort);
if ($dsnPass) {
$redis->auth($dsnPass);
}
$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
return $redis;
},
default => throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Invalid scheme'),
};
$pool = new Pool($name, $poolSize, function () use ($type, $resource, $dsn) {
// Get Adapter
switch ($type) {
case 'database':
$adapter = match ($dsn->getScheme()) {
'mariadb' => new MariaDB($resource()),
'mysql' => new MySQL($resource()),
default => null
};
$adapter->setDatabase($dsn->getPath());
return $adapter;
case 'pubsub':
return match ($dsn->getScheme()) {
'redis' => new PubSub($resource()),
default => null
};
case 'publisher':
case 'consumer':
return match ($dsn->getScheme()) {
'redis' => new Queue\Broker\Redis(new Queue\Connection\Redis($dsn->getHost(), $dsn->getPort())),
default => null
};
case 'cache':
return match ($dsn->getScheme()) {
'redis' => new RedisCache($resource()),
default => null
};
default:
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Server error: Missing adapter implementation.");
}
});
$group->add($pool);
}
Config::setParam('pools-' . $key, $config);
}
return $group;
});
$register->set('db', function () {
// This is usually for our workers or CLI commands scope
$dbHost = System::getEnv('_APP_DB_HOST', '');
$dbPort = System::getEnv('_APP_DB_PORT', '');
$dbUser = System::getEnv('_APP_DB_USER', '');
$dbPass = System::getEnv('_APP_DB_PASS', '');
$dbScheme = System::getEnv('_APP_DB_SCHEMA', '');
return new PDO(
"mysql:host={$dbHost};port={$dbPort};dbname={$dbScheme};charset=utf8mb4",
$dbUser,
$dbPass,
SQL::getPDOAttributes()
);
});
$register->set('smtp', function () {
$mail = new PHPMailer(true);
$mail->isSMTP();
$username = System::getEnv('_APP_SMTP_USERNAME');
$password = System::getEnv('_APP_SMTP_PASSWORD');
$mail->XMailer = 'Appwrite Mailer';
$mail->Host = System::getEnv('_APP_SMTP_HOST', 'smtp');
$mail->Port = System::getEnv('_APP_SMTP_PORT', 25);
$mail->SMTPAuth = !empty($username) && !empty($password);
$mail->Username = $username;
$mail->Password = $password;
$mail->SMTPSecure = System::getEnv('_APP_SMTP_SECURE', '');
$mail->SMTPAutoTLS = false;
$mail->CharSet = 'UTF-8';
$from = \urldecode(System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
$email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$mail->setFrom($email, $from);
$mail->addReplyTo($email, $from);
$mail->isHTML(true);
return $mail;
});
$register->set('geodb', function () {
return new Reader(__DIR__ . '/../assets/dbip/dbip-country-lite-2024-09.mmdb');
});
$register->set('passwordsDictionary', function () {
$content = \file_get_contents(__DIR__ . '/../assets/security/10k-common-passwords');
$content = explode("\n", $content);
$content = array_flip($content);
return $content;
});
$register->set('promiseAdapter', function () {
return new Swoole();
});
$register->set('hooks', function () {
return new Hooks();
});
+836
View File
@@ -0,0 +1,836 @@
<?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;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Delete;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Event\Migration;
use Appwrite\Event\Realtime;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\Webhook;
use Appwrite\Extend\Exception;
use Appwrite\GraphQL\Schema;
use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Request;
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
use Utopia\App;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\DSN\DSN;
use Utopia\Locale\Locale;
use Utopia\Logger\Log;
use Utopia\Pools\Group;
use Utopia\Queue\Publisher;
use Utopia\Storage\Device;
use Utopia\Storage\Device\Backblaze;
use Utopia\Storage\Device\DOSpaces;
use Utopia\Storage\Device\Linode;
use Utopia\Storage\Device\Local;
use Utopia\Storage\Device\S3;
use Utopia\Storage\Device\Wasabi;
use Utopia\Storage\Storage;
use Utopia\System\System;
use Utopia\Validator\Hostname;
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
// Runtime Execution
App::setResource('log', fn () => new Log());
App::setResource('logger', function ($register) {
return $register->get('logger');
}, ['register']);
App::setResource('hooks', function ($register) {
return $register->get('hooks');
}, ['register']);
App::setResource('register', fn () => $register);
App::setResource('locale', fn () => new Locale(System::getEnv('_APP_LOCALE', 'en')));
App::setResource('localeCodes', function () {
return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []));
});
// Queues
App::setResource('publisher', function (Group $pools) {
return $pools->get('publisher')->pop()->getResource();
}, ['pools']);
App::setResource('consumer', function (Group $pools) {
return $pools->get('consumer')->pop()->getResource();
}, ['pools']);
App::setResource('queueForMessaging', function (Publisher $publisher) {
return new Messaging($publisher);
}, ['publisher']);
App::setResource('queueForMails', function (Publisher $publisher) {
return new Mail($publisher);
}, ['publisher']);
App::setResource('queueForBuilds', function (Publisher $publisher) {
return new Build($publisher);
}, ['publisher']);
App::setResource('queueForDatabase', function (Publisher $publisher) {
return new EventDatabase($publisher);
}, ['publisher']);
App::setResource('queueForDeletes', function (Publisher $publisher) {
return new Delete($publisher);
}, ['publisher']);
App::setResource('queueForEvents', function (Publisher $publisher) {
return new Event($publisher);
}, ['publisher']);
App::setResource('queueForWebhooks', function (Publisher $publisher) {
return new Webhook($publisher);
}, ['publisher']);
App::setResource('queueForRealtime', function () {
return new Realtime();
}, []);
App::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
App::setResource('queueForAudits', function (Publisher $publisher) {
return new Audit($publisher);
}, ['publisher']);
App::setResource('queueForFunctions', function (Publisher $publisher) {
return new Func($publisher);
}, ['publisher']);
App::setResource('queueForCertificates', function (Publisher $publisher) {
return new Certificate($publisher);
}, ['publisher']);
App::setResource('queueForMigrations', function (Publisher $publisher) {
return new Migration($publisher);
}, ['publisher']);
App::setResource('clients', function ($request, $console, $project) {
$console->setAttribute('platforms', [ // Always allow current host
'$collection' => ID::custom('platforms'),
'name' => 'Current Host',
'type' => Origin::CLIENT_TYPE_WEB,
'hostname' => $request->getHostname(),
], Document::SET_TYPE_APPEND);
$hostnames = explode(',', System::getEnv('_APP_CONSOLE_HOSTNAMES', ''));
$validator = new Hostname();
foreach ($hostnames as $hostname) {
$hostname = trim($hostname);
if (!$validator->isValid($hostname)) {
continue;
}
$console->setAttribute('platforms', [
'$collection' => ID::custom('platforms'),
'type' => Origin::CLIENT_TYPE_WEB,
'name' => $hostname,
'hostname' => $hostname,
], Document::SET_TYPE_APPEND);
}
/**
* Get All verified client URLs for both console and current projects
* + Filter for duplicated entries
*/
$clientsConsole = \array_map(
fn ($node) => $node['hostname'],
\array_filter(
$console->getAttribute('platforms', []),
fn ($node) => (isset($node['type']) && ($node['type'] === Origin::CLIENT_TYPE_WEB) && !empty($node['hostname']))
)
);
$clients = $clientsConsole;
$platforms = $project->getAttribute('platforms', []);
foreach ($platforms as $node) {
if (
isset($node['type']) &&
($node['type'] === Origin::CLIENT_TYPE_WEB ||
$node['type'] === Origin::CLIENT_TYPE_FLUTTER_WEB) &&
!empty($node['hostname'])
) {
$clients[] = $node['hostname'];
}
}
return \array_unique($clients);
}, ['request', 'console', 'project']);
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForPlatform) {
/** @var Appwrite\Utopia\Request $request */
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Database\Database $dbForPlatform */
/** @var string $mode */
Authorization::setDefaultStatus(true);
Auth::setCookieName('a_session_' . $project->getId());
if (APP_MODE_ADMIN === $mode) {
Auth::setCookieName('a_session_' . $console->getId());
}
$session = Auth::decodeSession(
$request->getCookie(
Auth::$cookieName, // Get sessions
$request->getCookie(Auth::$cookieName . '_legacy', '')
)
);
// Get session from header for SSR clients
if (empty($session['id']) && empty($session['secret'])) {
$sessionHeader = $request->getHeader('x-appwrite-session', '');
if (!empty($sessionHeader)) {
$session = Auth::decodeSession($sessionHeader);
}
}
// Get fallback session from old clients (no SameSite support) or clients who block 3rd-party cookies
if ($response) {
$response->addHeader('X-Debug-Fallback', 'false');
}
if (empty($session['id']) && empty($session['secret'])) {
if ($response) {
$response->addHeader('X-Debug-Fallback', 'true');
}
$fallback = $request->getHeader('x-fallback-cookies', '');
$fallback = \json_decode($fallback, true);
$session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : ''));
}
Auth::$unique = $session['id'] ?? '';
Auth::$secret = $session['secret'] ?? '';
if (APP_MODE_ADMIN !== $mode) {
if ($project->isEmpty()) {
$user = new Document([]);
} else {
if ($project->getId() === 'console') {
$user = $dbForPlatform->getDocument('users', Auth::$unique);
} else {
$user = $dbForProject->getDocument('users', Auth::$unique);
}
}
} else {
$user = $dbForPlatform->getDocument('users', Auth::$unique);
}
if (
$user->isEmpty() // Check a document has been found in the DB
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)
) { // Validate user has valid login token
$user = new Document([]);
}
// if (APP_MODE_ADMIN === $mode) {
// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
// } else {
// $user = new Document([]);
// }
// }
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
try {
$payload = $jwt->decode($authJWT);
} catch (JWTException $error) {
throw new Exception(Exception::USER_JWT_INVALID, 'Failed to verify JWT. ' . $error->getMessage());
}
$jwtUserId = $payload['userId'] ?? '';
if (!empty($jwtUserId)) {
$user = $dbForProject->getDocument('users', $jwtUserId);
}
$jwtSessionId = $payload['sessionId'] ?? '';
if (!empty($jwtSessionId)) {
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
$user = new Document([]);
}
}
}
$dbForProject->setMetadata('user', $user->getId());
$dbForPlatform->setMetadata('user', $user->getId());
return $user;
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform']);
App::setResource('project', function ($dbForPlatform, $request, $console) {
/** @var Appwrite\Utopia\Request $request */
/** @var Utopia\Database\Database $dbForPlatform */
/** @var Utopia\Database\Document $console */
$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', ''));
if (empty($projectId) || $projectId === 'console') {
return $console;
}
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
return $project;
}, ['dbForPlatform', 'request', 'console']);
App::setResource('session', function (Document $user) {
if ($user->isEmpty()) {
return;
}
$sessions = $user->getAttribute('sessions', []);
$sessionId = Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret);
if (!$sessionId) {
return;
}
foreach ($sessions as $session) {/** @var Document $session */
if ($sessionId === $session->getId()) {
return $session;
}
}
return;
}, ['user']);
App::setResource('console', function () {
return new Document(Config::getParam('console'));
}, []);
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
try {
$dsn = new DSN($project->getAttribute('database'));
} catch (\InvalidArgumentException) {
// TODO: Temporary until all projects are using shared tables
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
$dbAdapter = $pools
->get($dsn->getHost())
->pop()
->getResource();
$database = new Database($dbAdapter, $cache);
$database
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getInternalId());
}
return $database;
}, ['pools', 'dbForPlatform', 'cache', 'project']);
App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
$dbAdapter = $pools
->get('console')
->pop()
->getResource();
$database = new Database($dbAdapter, $cache);
$database
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', 'console')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
return $database;
}, ['pools', 'cache']);
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
try {
$dsn = new DSN($project->getAttribute('database'));
} catch (\InvalidArgumentException) {
// TODO: Temporary until all projects are using shared tables
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
$configure = (function (Database $database) use ($project, $dsn) {
$database
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
$database
->setSharedTables(true)
->setTenant($project->getInternalId())
->setNamespace($dsn->getParam('namespace'));
} else {
$database
->setSharedTables(false)
->setTenant(null)
->setNamespace('_' . $project->getInternalId());
}
});
if (isset($databases[$dsn->getHost()])) {
$database = $databases[$dsn->getHost()];
$configure($database);
return $database;
}
$dbAdapter = $pools
->get($dsn->getHost())
->pop()
->getResource();
$database = new Database($dbAdapter, $cache);
$databases[$dsn->getHost()] = $database;
$configure($database);
return $database;
};
}, ['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_API)
->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 = [];
foreach ($list as $value) {
$adapters[] = $pools
->get($value)
->pop()
->getResource()
;
}
return new Cache(new Sharding($adapters));
}, ['pools']);
App::setResource('redis', function () {
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
$port = System::getEnv('_APP_REDIS_PORT', 6379);
$pass = System::getEnv('_APP_REDIS_PASS', '');
$redis = new \Redis();
@$redis->pconnect($host, (int)$port);
if ($pass) {
$redis->auth($pass);
}
$redis->setOption(\Redis::OPT_READ_TIMEOUT, -1);
return $redis;
});
App::setResource('timelimit', function (\Redis $redis) {
return function (string $key, int $limit, int $time) use ($redis) {
return new TimeLimitRedis($key, $limit, $time, $redis);
};
}, ['redis']);
App::setResource('deviceForLocal', function () {
return new Local();
});
App::setResource('deviceForFiles', function ($project) {
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
}, ['project']);
App::setResource('deviceForSites', function ($project) {
return getDevice(APP_STORAGE_SITES . '/app-' . $project->getId());
}, ['project']);
App::setResource('deviceForFunctions', function ($project) {
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
}, ['project']);
App::setResource('deviceForBuilds', function ($project) {
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
}, ['project']);
function getDevice(string $root, string $connection = ''): Device
{
$connection = !empty($connection) ? $connection : System::getEnv('_APP_CONNECTIONS_STORAGE', '');
if (!empty($connection)) {
$acl = 'private';
$device = Storage::DEVICE_LOCAL;
$accessKey = '';
$accessSecret = '';
$bucket = '';
$region = '';
$url = System::getEnv('_APP_STORAGE_S3_ENDPOINT', '');
try {
$dsn = new DSN($connection);
$device = $dsn->getScheme();
$accessKey = $dsn->getUser() ?? '';
$accessSecret = $dsn->getPassword() ?? '';
$bucket = $dsn->getPath() ?? '';
$region = $dsn->getParam('region');
} catch (\Throwable $e) {
Console::warning($e->getMessage() . 'Invalid DSN. Defaulting to Local device.');
}
switch ($device) {
case Storage::DEVICE_S3:
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);
return $device;
case Storage::DEVICE_BACKBLAZE:
return new Backblaze($root, $accessKey, $accessSecret, $bucket, $region, $acl);
case Storage::DEVICE_LINODE:
return new Linode($root, $accessKey, $accessSecret, $bucket, $region, $acl);
case Storage::DEVICE_WASABI:
return new Wasabi($root, $accessKey, $accessSecret, $bucket, $region, $acl);
case Storage::DEVICE_LOCAL:
default:
return new Local($root);
}
} else {
switch (strtolower(System::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) ?? '')) {
case Storage::DEVICE_LOCAL:
default:
return new Local($root);
case Storage::DEVICE_S3:
$s3AccessKey = System::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
$s3SecretKey = System::getEnv('_APP_STORAGE_S3_SECRET', '');
$s3Region = System::getEnv('_APP_STORAGE_S3_REGION', '');
$s3Bucket = System::getEnv('_APP_STORAGE_S3_BUCKET', '');
$s3Acl = 'private';
$s3EndpointUrl = System::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', '');
$doSpacesRegion = System::getEnv('_APP_STORAGE_DO_SPACES_REGION', '');
$doSpacesBucket = System::getEnv('_APP_STORAGE_DO_SPACES_BUCKET', '');
$doSpacesAcl = 'private';
$device = new DOSpaces($root, $doSpacesAccessKey, $doSpacesSecretKey, $doSpacesBucket, $doSpacesRegion, $doSpacesAcl);
$device->setHttpVersion(S3::HTTP_VERSION_1_1);
return $device;
case Storage::DEVICE_BACKBLAZE:
$backblazeAccessKey = System::getEnv('_APP_STORAGE_BACKBLAZE_ACCESS_KEY', '');
$backblazeSecretKey = System::getEnv('_APP_STORAGE_BACKBLAZE_SECRET', '');
$backblazeRegion = System::getEnv('_APP_STORAGE_BACKBLAZE_REGION', '');
$backblazeBucket = System::getEnv('_APP_STORAGE_BACKBLAZE_BUCKET', '');
$backblazeAcl = 'private';
return new Backblaze($root, $backblazeAccessKey, $backblazeSecretKey, $backblazeBucket, $backblazeRegion, $backblazeAcl);
case Storage::DEVICE_LINODE:
$linodeAccessKey = System::getEnv('_APP_STORAGE_LINODE_ACCESS_KEY', '');
$linodeSecretKey = System::getEnv('_APP_STORAGE_LINODE_SECRET', '');
$linodeRegion = System::getEnv('_APP_STORAGE_LINODE_REGION', '');
$linodeBucket = System::getEnv('_APP_STORAGE_LINODE_BUCKET', '');
$linodeAcl = 'private';
return new Linode($root, $linodeAccessKey, $linodeSecretKey, $linodeBucket, $linodeRegion, $linodeAcl);
case Storage::DEVICE_WASABI:
$wasabiAccessKey = System::getEnv('_APP_STORAGE_WASABI_ACCESS_KEY', '');
$wasabiSecretKey = System::getEnv('_APP_STORAGE_WASABI_SECRET', '');
$wasabiRegion = System::getEnv('_APP_STORAGE_WASABI_REGION', '');
$wasabiBucket = System::getEnv('_APP_STORAGE_WASABI_BUCKET', '');
$wasabiAcl = 'private';
return new Wasabi($root, $wasabiAccessKey, $wasabiSecretKey, $wasabiBucket, $wasabiRegion, $wasabiAcl);
}
}
}
App::setResource('mode', function ($request) {
/** @var Appwrite\Utopia\Request $request */
/**
* Defines the mode for the request:
* - 'default' => Requests for Client and Server Side
* - 'admin' => Request from the Console on non-console projects
*/
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT));
}, ['request']);
App::setResource('geodb', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('geodb');
}, ['register']);
App::setResource('passwordsDictionary', function ($register) {
/** @var Utopia\Registry\Registry $register */
return $register->get('passwordsDictionary');
}, ['register']);
App::setResource('servers', function () {
$platforms = Config::getParam('platforms');
$server = $platforms[APP_PLATFORM_SERVER];
$languages = array_map(function ($language) {
return strtolower($language['name']);
}, $server['sdks']);
return $languages;
});
App::setResource('promiseAdapter', function ($register) {
return $register->get('promiseAdapter');
}, ['register']);
App::setResource('schema', function ($utopia, $dbForProject) {
$complexity = function (int $complexity, array $args) {
$queries = Query::parseQueries($args['queries'] ?? []);
$query = Query::getByType($queries, [Query::TYPE_LIMIT])[0] ?? null;
$limit = $query ? $query->getValue() : APP_LIMIT_LIST_DEFAULT;
return $complexity * $limit;
};
$attributes = function (int $limit, int $offset) use ($dbForProject) {
$attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [
Query::limit($limit),
Query::offset($offset),
]));
return \array_map(function ($attr) {
return $attr->getArrayCopy();
}, $attrs);
};
$urls = [
'list' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents";
},
'create' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents";
},
'read' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
},
'update' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
},
'delete' => function (string $databaseId, string $collectionId, array $args) {
return "/v1/databases/$databaseId/collections/$collectionId/documents/{$args['documentId']}";
},
];
$params = [
'list' => function (string $databaseId, string $collectionId, array $args) {
return [ 'queries' => $args['queries']];
},
'create' => function (string $databaseId, string $collectionId, array $args) {
$id = $args['id'] ?? 'unique()';
$permissions = $args['permissions'] ?? null;
unset($args['id']);
unset($args['permissions']);
// Order must be the same as the route params
return [
'databaseId' => $databaseId,
'documentId' => $id,
'collectionId' => $collectionId,
'data' => $args,
'permissions' => $permissions,
];
},
'update' => function (string $databaseId, string $collectionId, array $args) {
$documentId = $args['id'];
$permissions = $args['permissions'] ?? null;
unset($args['id']);
unset($args['permissions']);
// Order must be the same as the route params
return [
'databaseId' => $databaseId,
'collectionId' => $collectionId,
'documentId' => $documentId,
'data' => $args,
'permissions' => $permissions,
];
},
];
return Schema::build(
$utopia,
$complexity,
$attributes,
$urls,
$params,
);
}, ['utopia', 'dbForProject']);
App::setResource('contributors', function () {
$path = 'app/config/contributors.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
});
App::setResource('employees', function () {
$path = 'app/config/employees.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
});
App::setResource('heroes', function () {
$path = 'app/config/heroes.json';
$list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
return $list;
});
App::setResource('gitHub', function (Cache $cache) {
return new VcsGitHub($cache);
}, ['cache']);
App::setResource('requestTimestamp', function ($request) {
//TODO: Move this to the Request class itself
$timestampHeader = $request->getHeader('x-appwrite-timestamp');
$requestTimestamp = null;
if (!empty($timestampHeader)) {
try {
$requestTimestamp = new \DateTime($timestampHeader);
} catch (\Throwable $e) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid X-Appwrite-Timestamp header value');
}
}
return $requestTimestamp;
}, ['request']);
App::setResource('plan', function (array $plan = []) {
return [];
});
App::setResource('smsRates', function () {
return [];
});
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request) {
$teamInternalId = '';
if ($project->getId() !== 'console') {
$teamInternalId = $project->getAttribute('teamInternalId', '');
} else {
$route = $utopia->match($request);
$path = $route->getPath();
if (str_starts_with($path, '/v1/projects/:projectId')) {
$uri = $request->getURI();
$pid = explode('/', $uri)[3];
$p = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $pid));
$teamInternalId = $p->getAttribute('teamInternalId', '');
} elseif ($path === '/v1/projects') {
$teamId = $request->getParam('teamId', '');
$team = Authorization::skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
return $team;
}
}
$team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) {
return $dbForPlatform->findOne('teams', [
Query::equal('$internalId', [$teamInternalId]),
]);
});
return $team;
}, ['project', 'dbForPlatform', 'utopia', 'request']);
App::setResource(
'isResourceBlocked',
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
);
App::setResource('previewHostname', function (Request $request, ?Key $apiKey) {
$allowed = false;
if (App::isDevelopment()) {
$allowed = true;
} elseif (!\is_null($apiKey) && $apiKey->getHostnameOverride() === true) {
$allowed = true;
}
if ($allowed) {
$host = $request->getQuery('appwrite-hostname', $request->getHeader('x-appwrite-hostname', '')) ?? '';
if (!empty($host)) {
return $host;
}
}
return '';
}, ['request', 'apiKey']);
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']);
+3 -2
View File
@@ -139,7 +139,6 @@ $image = $this->getParam('image', '');
- _APP_COMPUTE_CPUS
- _APP_COMPUTE_MEMORY
- _APP_FUNCTIONS_RUNTIMES
- _APP_SITES_FRAMEWORKS
- _APP_SITES_RUNTIMES
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
@@ -170,7 +169,7 @@ $image = $this->getParam('image', '');
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:5.2.27
image: <?php echo $organization; ?>/console:5.2.53
restart: unless-stopped
networks:
- appwrite
@@ -344,6 +343,8 @@ $image = $this->getParam('image', '');
- _APP_MAINTENANCE_RETENTION_ABUSE
- _APP_MAINTENANCE_RETENTION_AUDIT
- _APP_MAINTENANCE_RETENTION_EXECUTION
- _APP_SYSTEM_SECURITY_EMAIL_ADDRESS
- _APP_EMAIL_CERTIFICATES
appwrite-worker-databases:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
+5 -1
View File
@@ -112,6 +112,8 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
->setNamespace('_' . $project->getInternalId());
}
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
return $database;
}, ['cache', 'register', 'message', 'project', 'dbForPlatform']);
@@ -173,6 +175,8 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
->setNamespace('_' . $project->getInternalId());
}
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
return $database;
};
}, ['pools', 'dbForPlatform', 'cache']);
@@ -198,7 +202,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS)
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
// set tenant
+5 -5
View File
@@ -46,14 +46,14 @@
"ext-sockets": "*",
"appwrite/php-runtimes": "0.18.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.51.*",
"utopia-php/abuse": "0.52.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "0.54.0",
"utopia-php/audit": "0.55.*",
"utopia-php/cache": "0.12.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/detector": "0.1.*",
"utopia-php/database": "0.60.*",
"utopia-php/database": "0.61.*",
"utopia-php/domains": "0.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
@@ -64,10 +64,10 @@
"utopia-php/messaging": "0.16.*",
"utopia-php/migration": "0.6.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.3",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.5.*",
"utopia-php/preloader": "0.2.*",
"utopia-php/queue": "0.8.*",
"utopia-php/queue": "0.9.*",
"utopia-php/registry": "0.5.*",
"utopia-php/storage": "0.18.*",
"utopia-php/swoole": "0.8.*",
Generated
+61 -61
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "6e6444246ba207b6bf88c20340835fb5",
"content-hash": "566b9b71f179c9ff6368dbd353bc20d2",
"packages": [
{
"name": "adhocore/jwt",
@@ -709,16 +709,16 @@
},
{
"name": "google/protobuf",
"version": "v4.30.0",
"version": "v4.30.1",
"source": {
"type": "git",
"url": "https://github.com/protocolbuffers/protobuf-php.git",
"reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6"
"reference": "f29ba8a30dfd940efb3a8a75dc44446539101f24"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/e1d66682f6836aa87820400f0aa07d9eb566feb6",
"reference": "e1d66682f6836aa87820400f0aa07d9eb566feb6",
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/f29ba8a30dfd940efb3a8a75dc44446539101f24",
"reference": "f29ba8a30dfd940efb3a8a75dc44446539101f24",
"shasum": ""
},
"require": {
@@ -747,9 +747,9 @@
"proto"
],
"support": {
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.0"
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.1"
},
"time": "2025-03-04T22:54:49+00:00"
"time": "2025-03-13T21:08:17+00:00"
},
{
"name": "jean85/pretty-package-versions",
@@ -3364,16 +3364,16 @@
},
{
"name": "utopia-php/abuse",
"version": "0.51.0",
"version": "0.52.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "661687b03277f1d202a0e8cf9da6e58c97da2b5e"
"reference": "a0d6421e7e5baa3ac02755496dca9fdeaa814b93"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/661687b03277f1d202a0e8cf9da6e58c97da2b5e",
"reference": "661687b03277f1d202a0e8cf9da6e58c97da2b5e",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/a0d6421e7e5baa3ac02755496dca9fdeaa814b93",
"reference": "a0d6421e7e5baa3ac02755496dca9fdeaa814b93",
"shasum": ""
},
"require": {
@@ -3381,7 +3381,7 @@
"ext-pdo": "*",
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/database": "0.60.*"
"utopia-php/database": "0.*.*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -3409,9 +3409,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/0.51.0"
"source": "https://github.com/utopia-php/abuse/tree/0.52.0"
},
"time": "2025-02-17T11:10:18+00:00"
"time": "2025-03-06T03:48:29+00:00"
},
{
"name": "utopia-php/analytics",
@@ -3461,21 +3461,21 @@
},
{
"name": "utopia-php/audit",
"version": "0.54.0",
"version": "0.55.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "1b0cb8ac6bfbd7703e3f9a753c6ba59ff1c39975"
"reference": "9f8cfe5fa5d5011b8dbf93b710236dfa91dc5518"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/1b0cb8ac6bfbd7703e3f9a753c6ba59ff1c39975",
"reference": "1b0cb8ac6bfbd7703e3f9a753c6ba59ff1c39975",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/9f8cfe5fa5d5011b8dbf93b710236dfa91dc5518",
"reference": "9f8cfe5fa5d5011b8dbf93b710236dfa91dc5518",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "0.60.*"
"utopia-php/database": "0.*.*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -3502,9 +3502,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/0.54.0"
"source": "https://github.com/utopia-php/audit/tree/0.55.0"
},
"time": "2025-02-25T07:21:07+00:00"
"time": "2025-03-06T03:47:47+00:00"
},
{
"name": "utopia-php/cache",
@@ -3705,16 +3705,16 @@
},
{
"name": "utopia-php/database",
"version": "0.60.6",
"version": "0.61.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "f3c9aa964b39c6205069f038a26e709a15541406"
"reference": "349fbdf4bc088f7775c7dfb8b80239a617a88436"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/f3c9aa964b39c6205069f038a26e709a15541406",
"reference": "f3c9aa964b39c6205069f038a26e709a15541406",
"url": "https://api.github.com/repos/utopia-php/database/zipball/349fbdf4bc088f7775c7dfb8b80239a617a88436",
"reference": "349fbdf4bc088f7775c7dfb8b80239a617a88436",
"shasum": ""
},
"require": {
@@ -3755,9 +3755,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.60.6"
"source": "https://github.com/utopia-php/database/tree/0.61.2"
},
"time": "2025-03-05T01:23:14+00:00"
"time": "2025-03-15T11:47:42+00:00"
},
{
"name": "utopia-php/detector",
@@ -4204,16 +4204,16 @@
},
{
"name": "utopia-php/migration",
"version": "0.6.20",
"version": "0.6.22",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "8c9ba52196f50aaef4aa1903f0d8fe0c8d9997ba"
"reference": "a0269746bd318ff0993f5aa008675b971689d5b5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/8c9ba52196f50aaef4aa1903f0d8fe0c8d9997ba",
"reference": "8c9ba52196f50aaef4aa1903f0d8fe0c8d9997ba",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/a0269746bd318ff0993f5aa008675b971689d5b5",
"reference": "a0269746bd318ff0993f5aa008675b971689d5b5",
"shasum": ""
},
"require": {
@@ -4221,7 +4221,7 @@
"ext-curl": "*",
"ext-openssl": "*",
"php": ">=8.1",
"utopia-php/database": "0.60.*",
"utopia-php/database": "0.61.*",
"utopia-php/dsn": "0.2.*",
"utopia-php/framework": "0.33.*",
"utopia-php/storage": "0.18.*"
@@ -4254,9 +4254,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.6.20"
"source": "https://github.com/utopia-php/migration/tree/0.6.22"
},
"time": "2025-02-17T11:02:15+00:00"
"time": "2025-03-13T07:35:55+00:00"
},
{
"name": "utopia-php/mongo",
@@ -4370,16 +4370,16 @@
},
{
"name": "utopia-php/platform",
"version": "0.7.3",
"version": "0.7.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/platform.git",
"reference": "463c2d817c893d7dbb678c2eac7a8291f2710e25"
"reference": "a5b93d8177702ec458c3af9137663133c012b71b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/platform/zipball/463c2d817c893d7dbb678c2eac7a8291f2710e25",
"reference": "463c2d817c893d7dbb678c2eac7a8291f2710e25",
"url": "https://api.github.com/repos/utopia-php/platform/zipball/a5b93d8177702ec458c3af9137663133c012b71b",
"reference": "a5b93d8177702ec458c3af9137663133c012b71b",
"shasum": ""
},
"require": {
@@ -4388,7 +4388,7 @@
"php": ">=8.0",
"utopia-php/cli": "0.15.*",
"utopia-php/framework": "0.33.*",
"utopia-php/queue": "0.8.*"
"utopia-php/queue": "0.9.*"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -4414,9 +4414,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/platform/issues",
"source": "https://github.com/utopia-php/platform/tree/0.7.3"
"source": "https://github.com/utopia-php/platform/tree/0.7.4"
},
"time": "2025-02-04T15:09:00+00:00"
"time": "2025-03-13T13:00:12+00:00"
},
{
"name": "utopia-php/pools",
@@ -4524,16 +4524,16 @@
},
{
"name": "utopia-php/queue",
"version": "0.8.6",
"version": "0.9.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/queue.git",
"reference": "b713b997285c29d120bbcbe3d6e93762d850f87c"
"reference": "077075f1d57afa430f76c35ed3bf4616e0eee8e7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/b713b997285c29d120bbcbe3d6e93762d850f87c",
"reference": "b713b997285c29d120bbcbe3d6e93762d850f87c",
"url": "https://api.github.com/repos/utopia-php/queue/zipball/077075f1d57afa430f76c35ed3bf4616e0eee8e7",
"reference": "077075f1d57afa430f76c35ed3bf4616e0eee8e7",
"shasum": ""
},
"require": {
@@ -4583,9 +4583,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/queue/issues",
"source": "https://github.com/utopia-php/queue/tree/0.8.6"
"source": "https://github.com/utopia-php/queue/tree/0.9.0"
},
"time": "2025-02-10T03:35:00+00:00"
"time": "2025-03-13T12:22:41+00:00"
},
{
"name": "utopia-php/registry",
@@ -4855,16 +4855,16 @@
},
{
"name": "utopia-php/vcs",
"version": "0.9.3",
"version": "0.9.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "865a00c67e81a20938b883f9aa802303790dd3b5"
"reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/865a00c67e81a20938b883f9aa802303790dd3b5",
"reference": "865a00c67e81a20938b883f9aa802303790dd3b5",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/1a8d280b176acc99ea8d9e7364b8767cbb206b4a",
"reference": "1a8d280b176acc99ea8d9e7364b8767cbb206b4a",
"shasum": ""
},
"require": {
@@ -4899,9 +4899,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.9.3"
"source": "https://github.com/utopia-php/vcs/tree/0.9.4"
},
"time": "2025-02-26T16:33:35+00:00"
"time": "2025-03-13T10:09:45+00:00"
},
{
"name": "utopia-php/websocket",
@@ -5362,16 +5362,16 @@
},
{
"name": "laravel/pint",
"version": "v1.21.1",
"version": "v1.21.2",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86"
"reference": "370772e7d9e9da087678a0edf2b11b6960e40558"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/c44bffbb2334e90fba560933c45948fa4a3f3e86",
"reference": "c44bffbb2334e90fba560933c45948fa4a3f3e86",
"url": "https://api.github.com/repos/laravel/pint/zipball/370772e7d9e9da087678a0edf2b11b6960e40558",
"reference": "370772e7d9e9da087678a0edf2b11b6960e40558",
"shasum": ""
},
"require": {
@@ -5382,9 +5382,9 @@
"php": "^8.2.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.70.2",
"illuminate/view": "^11.44.1",
"larastan/larastan": "^3.1.0",
"friendsofphp/php-cs-fixer": "^3.72.0",
"illuminate/view": "^11.44.2",
"larastan/larastan": "^3.2.0",
"laravel-zero/framework": "^11.36.1",
"mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^2.3",
@@ -5424,7 +5424,7 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2025-03-11T03:22:21+00:00"
"time": "2025-03-14T22:31:42+00:00"
},
{
"name": "matthiasmullie/minify",
+1 -2
View File
@@ -166,7 +166,6 @@ services:
- _APP_COMPUTE_MEMORY
- _APP_FUNCTIONS_RUNTIMES
- _APP_SITES_RUNTIMES
- _APP_SITES_FRAMEWORKS
- _APP_DOMAIN_SITES
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
@@ -206,7 +205,7 @@ services:
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:5.3.0-sites-rc.24
image: appwrite/console:5.3.0-sites-rc.26
restart: unless-stopped
networks:
- appwrite
+4 -4
View File
@@ -59,7 +59,7 @@ My Awesome Function
You can now deploy this function using
```sh
$ appwrite deploy function
$ appwrite push function
? Which functions would you like to deploy? My Awesome Function (61d1a4c81dfcd95bc834)
Info Deploying function My Awesome Function ( 61d1a4c81dfcd95bc834 )
@@ -73,7 +73,7 @@ Your function has now been deployed on your Appwrite server! As soon as the buil
Similarly, you can deploy all your collections to your Appwrite server using
```sh
appwrite deploy collections
appwrite push collections
```
> ### Note
@@ -98,7 +98,7 @@ $ appwrite users list
To create a document you can use the following command
```sh
$ appwrite database createDocument --collectionId <ID> --documentId 'unique()' --data '{ "Name": "Iron Man" }' --permissions 'read("any")' 'read("team:abc")'
$ appwrite databases create-document --database-id <DATABASE_ID> --collection-id <COLLECTION_ID> --document-id "unique()" --data '{"name": "Walter O Brein"}' --permissions 'read("any")' 'read("team:abc")'
```
### Some Gotchas
@@ -140,4 +140,4 @@ The Appwrite CLI can also work in a CI environment. The initialisation of the CL
```sh
appwrite client --endpoint http://localhost/v1 --projectId <PROJECT_ID> --key <API KEY>
```
```
+12
View File
@@ -1,3 +1,15 @@
## 14.0.0
* Breaking changes:
* Changed the typing of `AppwriteException`'s response parameter from a `dynamic` object to an optional string (`?String`).
## 13.0.0
* Fixed realtime pong response.
* Fixed issues with `chunkedUpload` method.
* Fixed type mismatch bug where `List<dynamic>` was incorrectly causing runtime type errors.
* Updated return type of `updateMfaChallenge()` from raw data to properly typed `models.Session` object.
## 12.0.0
* Support for Appwrite 1.6
+10
View File
@@ -1,3 +1,13 @@
## 15.0.0
* Breaking changes:
* Changed the typing of `AppwriteException`'s response parameter from a `dynamic` object to an optional string (`?String`).
## 14.0.0
* Fixed realtime pong response.
* Fixed issues with `chunkedUpload` method.
## 13.0.0
* Fixed realtime reconnection issues
+1 -1
View File
@@ -6,7 +6,7 @@
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
stopOnFailure="true"
>
<extensions>
<extension class="Appwrite\Tests\TestHook" />
-1
View File
@@ -156,7 +156,6 @@ class Exception extends \Exception
/** Sites */
public const SITE_NOT_FOUND = 'site_not_found';
public const SITE_FRAMEWORK_UNSUPPORTED = 'site_framework_unsupported';
public const SITE_TEMPLATE_NOT_FOUND = 'site_template_not_found';
/** Functions */
+19 -14
View File
@@ -5,7 +5,6 @@ namespace Appwrite\Platform\Modules\Compute;
use Appwrite\Event\Build;
use Appwrite\Extend\Exception;
use Appwrite\Query;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
@@ -39,25 +38,29 @@ class Base extends Action
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
}
// TODO: Support tag and commit in future
$commitDetails = [];
$branchUrl = "";
$providerBranch = "";
// TODO: Support tag in future
if ($referenceType === 'branch') {
$providerBranch = empty($reference) ? $function->getAttribute('providerBranch', 'main') : $reference;
$branchUrl = "https://github.com/$owner/$repositoryName/tree/$providerBranch";
try {
$commitDetails = $github->getLatestCommit($owner, $repositoryName, $providerBranch);
} catch (\Throwable $error) {
// Ignore; deployment can continue
}
} elseif ($referenceType === 'commit') {
try {
$commitDetails = $github->getCommit($owner, $repositoryName, $reference);
} catch (\Throwable $error) {
// Ignore; deployment can continue
}
}
$authorUrl = "https://github.com/$owner";
$repositoryUrl = "https://github.com/$owner/$repositoryName";
$branchUrl = "https://github.com/$owner/$repositoryName/tree/$providerBranch";
$commitDetails = [];
if ($template->isEmpty()) {
try {
$commitDetails = $github->getLatestCommit($owner, $repositoryName, $providerBranch);
} catch (\Throwable $error) {
Console::warning('Failed to get latest commit details');
Console::warning($error->getMessage());
Console::warning($error->getTraceAsString());
}
}
$deployment = $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
@@ -207,6 +210,8 @@ class Base extends Action
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
@@ -59,9 +59,9 @@ class Create extends Base
],
))
->param('functionId', '', new UID(), 'Function ID.')
// 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')
// TODO: Support tag in future
->param('type', '', new WhiteList(['branch', 'commit']), 'Type of reference passed. Allowed values are: branch, commit')
->param('reference', '', new Text(255), 'VCS reference to create deployment from. Depending on type this can be: branch name, commit hash')
->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true)
->inject('request')
->inject('response')
@@ -190,6 +190,12 @@ class Builds extends Action
// Realtime preparation
$event = "{$resource->getCollection()}.[{$resourceKey}].deployments.[deploymentId].update";
$queueForRealtime
->setSubscribers(['console'])
->setProject($project)
->setEvent($event)
->setParam($resourceKey, $resource->getId())
->setParam('deploymentId', $deployment->getId());
$startTime = DateTime::now();
$durationStart = \microtime(true);
@@ -205,6 +211,10 @@ class Builds extends Action
$deployment->setAttribute('status', 'processing');
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
$source = $deployment->getAttribute('sourcePath', '');
$installationId = $deployment->getAttribute('installationId', '');
$providerRepositoryId = $deployment->getAttribute('providerRepositoryId', '');
@@ -277,6 +287,10 @@ class Builds extends Action
->setAttribute('sourcePath', $source)
->setAttribute('sourceSize', $directorySize);
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
}
} elseif ($isVcsEnabled) {
// VCS and VCS+Temaplte
@@ -384,15 +398,7 @@ class Builds extends Action
$deployment->setAttribute('providerCommitUrl', "https://github.com/$cloneOwner/$cloneRepository/commit/$providerCommitHash");
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
/**
* Trigger Realtime Event
*/
$queueForRealtime
->setSubscribers(['console'])
->setProject($project)
->setEvent($event)
->setParam($resourceKey, $resource->getId())
->setParam('deploymentId', $deployment->getId())
->setPayload($deployment->getArrayCopy())
->trigger();
}
@@ -433,6 +439,10 @@ class Builds extends Action
->setAttribute('sourceSize', $directorySize);
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
$this->runGitAction('processing', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform);
}
@@ -440,6 +450,10 @@ class Builds extends Action
$deployment->setAttribute('status', 'building');
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
if ($isVcsEnabled) {
$this->runGitAction('building', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform);
}
@@ -465,11 +479,6 @@ class Builds extends Action
/** Trigger Realtime Event */
$queueForRealtime
->setSubscribers(['console'])
->setProject($project)
->setEvent($event)
->setParam($resourceKey, $resource->getId())
->setParam('deploymentId', $deployment->getId())
->setPayload($deployment->getArrayCopy())
->trigger();
@@ -577,7 +586,6 @@ class Builds extends Action
try {
$command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . \trim(\escapeshellarg($command), "\'") . '"';
// TODO: Detect adapter if adapter is empty
$response = $executor->createRuntime(
deploymentId: $deployment->getId(),
projectId: $project->getId(),
@@ -598,13 +606,13 @@ class Builds extends Action
$err = $error;
}
}),
Co\go(function () use ($executor, $project, &$deployment, &$response, $dbForProject, $event, $timeout, &$err, $queueForRealtime, &$isCanceled, $resourceKey, $resource) {
Co\go(function () use ($executor, $project, &$deployment, &$response, $dbForProject, $timeout, &$err, $queueForRealtime, &$isCanceled) {
try {
$executor->getLogs(
deploymentId: $deployment->getId(),
projectId: $project->getId(),
timeout: $timeout,
callback: function ($logs) use (&$response, &$err, $dbForProject, $event, $project, &$isCanceled, &$deployment, $queueForRealtime, $resourceKey, $resource) {
callback: function ($logs) use (&$response, &$err, $dbForProject, &$isCanceled, &$deployment, $queueForRealtime) {
if ($isCanceled) {
return;
}
@@ -640,15 +648,7 @@ class Builds extends Action
$deployment = $deployment->setAttribute('buildLogs', $currentLogs);
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
/**
* Trigger Realtime Event
*/
$queueForRealtime
->setSubscribers(['console'])
->setProject($project)
->setEvent($event)
->setParam($resourceKey, $resource->getId())
->setParam('deploymentId', $deployment->getId())
->setPayload($deployment->getArrayCopy())
->trigger();
}
@@ -729,6 +729,10 @@ class Builds extends Action
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
if ($isVcsEnabled) {
$this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform);
}
@@ -841,6 +845,10 @@ class Builds extends Action
}
$dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
} catch (\Throwable $th) {
Console::warning("Screenshot failed to generate:");
Console::warning($th->getMessage());
@@ -873,7 +881,9 @@ class Builds extends Action
$queries[] = Query::equal("deploymentInternalId", [$oldDeploymentInternalId]);
}
$this->listRules($project, $queries, $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment) {
$rulesUpdated = false;
$this->listRules($project, $queries, $dbForPlatform, function (Document $rule) use ($dbForPlatform, $deployment, &$rulesUpdated) {
$rulesUpdated = true;
$rule = $rule
->setAttribute('deploymentId', $deployment->getId())
->setAttribute('deploymentInternalId', $deployment->getInternalId());
@@ -886,7 +896,6 @@ class Builds extends Action
$resource->setAttribute('deploymentId', $deployment->getId());
$resource->setAttribute('deploymentInternalId', $deployment->getInternalId());
$resource = $dbForProject->updateDocument('sites', $resource->getId(), $resource);
$queries = [
Query::equal("projectInternalId", [$project->getInternalId()]),
Query::equal("type", ["deployment"]),
@@ -937,6 +946,8 @@ class Builds extends Action
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]));
} catch (Duplicate $err) {
$rule = $dbForPlatform->getDocument('rules', $ruleId);
@@ -999,19 +1010,15 @@ class Builds extends Action
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
$queueForRealtime
->setPayload($deployment->getArrayCopy())
->trigger();
if ($isVcsEnabled) {
$this->runGitAction('failed', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform);
}
} finally {
/**
* Trigger Realtime Event
*/
$queueForRealtime
->setSubscribers(['console'])
->setProject($project)
->setEvent($event)
->setParam($resourceKey, $resource->getId())
->setParam('deploymentId', $deployment->getId())
->setPayload($deployment->getArrayCopy())
->trigger();
@@ -117,6 +117,14 @@ class Create extends Action
}
}
$owner = '';
if (
($functionsDomain != '' && \str_ends_with($domain->get(), $functionsDomain)) ||
($sitesDomain != '' && \str_ends_with($domain->get(), $sitesDomain))
) {
$owner = 'Appwrite';
}
$rule = new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -127,6 +135,8 @@ class Create extends Action
'trigger' => 'manual',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain->get()]),
'owner' => $owner,
'region' => $project->getAttribute('region')
]);
try {
@@ -125,6 +125,14 @@ class Create extends Action
}
}
$owner = '';
if (
($functionsDomain != '' && \str_ends_with($domain->get(), $functionsDomain)) ||
($sitesDomain != '' && \str_ends_with($domain->get(), $sitesDomain))
) {
$owner = 'Appwrite';
}
$rule = new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -141,6 +149,8 @@ class Create extends Action
'deploymentVcsProviderBranch' => $branch,
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain->get(), $branch]),
'owner' => $owner,
'region' => $project->getAttribute('region')
]);
try {
@@ -117,6 +117,14 @@ class Create extends Action
}
}
$owner = '';
if (
($functionsDomain != '' && \str_ends_with($domain->get(), $functionsDomain)) ||
($sitesDomain != '' && \str_ends_with($domain->get(), $sitesDomain))
) {
$owner = 'Appwrite';
}
$rule = new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -129,6 +137,8 @@ class Create extends Action
'redirectStatusCode' => $statusCode,
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain->get()]),
'owner' => $owner,
'region' => $project->getAttribute('region')
]);
try {
@@ -125,6 +125,14 @@ class Create extends Action
}
}
$owner = '';
if (
($functionsDomain != '' && \str_ends_with($domain->get(), $functionsDomain)) ||
($sitesDomain != '' && \str_ends_with($domain->get(), $sitesDomain))
) {
$owner = 'Appwrite';
}
$rule = new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -141,6 +149,8 @@ class Create extends Action
'deploymentVcsProviderBranch' => $branch,
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain->get(), $branch]),
'owner' => $owner,
'region' => $project->getAttribute('region')
]);
try {
@@ -271,6 +271,8 @@ class Create extends Action
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
} else {
@@ -148,6 +148,8 @@ class Create extends Action
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
@@ -183,6 +183,8 @@ class Create extends Base
'status' => 'verified',
'certificateId' => '',
'search' => implode(' ', [$ruleId, $domain]),
'owner' => 'Appwrite',
'region' => $project->getAttribute('region')
]))
);
@@ -60,8 +60,8 @@ class Create extends Base
))
->param('siteId', '', new UID(), 'Site ID.')
// TODO: Support tag in future
->param('type', '', new WhiteList(['branch', 'commit']), '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('type', '', new WhiteList(['branch', 'commit', 'tag']), 'Type of reference passed. Allowed values are: branch, commit')
->param('reference', '', new Text(255), 'VCS reference to create deployment from. Depending on type this can be: branch name, commit hash')
->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true)
->inject('request')
->inject('response')
@@ -11,7 +11,6 @@ use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Platform\Action;
use Utopia\Platform\Scope\HTTP;
use Utopia\System\System;
class XList extends Base
{
@@ -53,20 +52,15 @@ class XList extends Base
{
$frameworks = Config::getParam('frameworks');
$allowList = \array_filter(\explode(',', System::getEnv('_APP_SITES_FRAMEWORKS', '')));
$allowed = [];
foreach ($frameworks as $id => $framework) {
if (!empty($allowList) && !\in_array($id, $allowList)) {
continue;
foreach ($frameworks as $key => $framework) {
if (!empty($framework['adapters'])) {
$frameworks[$key]['adapters'] = \array_values($framework['adapters']);
}
$allowed[] = $framework;
}
$response->dynamic(new Document([
'total' => count($allowed),
'frameworks' => $allowed
'total' => count($frameworks),
'frameworks' => \array_values($frameworks)
]), Response::MODEL_FRAMEWORK_LIST);
}
}
@@ -63,7 +63,7 @@ class Create extends Base
))
->param('siteId', '', new CustomId(), 'Site ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
->param('name', '', new Text(128), 'Site name. Max length: 128 chars.')
->param('framework', '', new WhiteList(array_keys(Config::getParam('frameworks')), true), 'Sites framework.')
->param('framework', '', new WhiteList(\array_keys(Config::getParam('frameworks')), true), 'Sites framework.')
->param('enabled', true, new Boolean(), 'Is site enabled? When set to \'disabled\', users cannot access the site but Server SDKs with and API key can still access the site. No data is lost when this is toggled.', true) // TODO: Add logging param later
->param('timeout', 15, new Range(1, (int) System::getEnv('_APP_COMPUTE_TIMEOUT', 900)), 'Maximum request time in seconds.', true)
->param('installCommand', '', new Text(8192, 0), 'Install Command.', true)
@@ -126,12 +126,6 @@ class Create extends Base
$siteId = ($siteId == 'unique()') ? ID::unique() : $siteId;
$allowList = \array_filter(\explode(',', System::getEnv('_APP_SITES_FRAMEWORKS', '')));
if (!empty($allowList) && !\in_array($framework, $allowList)) {
throw new Exception(Exception::SITE_FRAMEWORK_UNSUPPORTED, 'Framework "' . $framework . '" is not supported');
}
$installation = $dbForPlatform->getDocument('installations', $installationId);
if (!empty($installationId) && $installation->isEmpty()) {
@@ -67,7 +67,7 @@ class Update extends Base
))
->param('siteId', '', new UID(), 'Site ID.')
->param('name', '', new Text(128), 'Site name. Max length: 128 chars.')
->param('framework', '', new WhiteList(array_keys(Config::getParam('frameworks')), true), 'Sites framework.')
->param('framework', '', new WhiteList(\array_keys(Config::getParam('frameworks')), true), 'Sites framework.')
->param('enabled', true, new Boolean(), 'Is site enabled? When set to \'disabled\', users cannot access the site but Server SDKs with and API key can still access the site. No data is lost when this is toggled.', true) // TODO: Add logging param later
->param('timeout', 15, new Range(1, (int) System::getEnv('_APP_COMPUTE_TIMEOUT', 900)), 'Maximum request time in seconds.', true)
->param('installCommand', '', new Text(8192, 0), 'Install Command.', true)
@@ -47,7 +47,7 @@ class XList extends Base
)
]
))
->param('frameworks', [], new ArrayList(new WhiteList(array_keys(Config::getParam('frameworks')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of frameworks allowed for filtering site templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' frameworks are allowed.', true)
->param('frameworks', [], new ArrayList(new WhiteList(\array_keys(Config::getParam('frameworks')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of frameworks allowed for filtering site templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' frameworks are allowed.', true)
->param('useCases', [], new ArrayList(new WhiteList(['dev-tools', 'starter', 'databases', 'ai', 'messaging', 'utilities']), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of use cases allowed for filtering site templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' use cases are allowed.', true)
->param('limit', 25, new Range(1, 5000), 'Limit the number of templates returned in the response. Default limit is 25, and maximum limit is 5000.', true)
->param('offset', 0, new Range(0, 5000), 'Offset the list of returned templates. Maximum offset is 5000.', true)
+19 -5
View File
@@ -389,6 +389,8 @@ class Deletes extends Action
$query = [
Query::lessThan('accessedAt', $datetime),
Query::orderDesc('accessedAt'),
Query::orderDesc('$internalId'),
];
$this->deleteByGroup(
@@ -416,22 +418,26 @@ class Deletes extends Action
*/
private function deleteUsageStats(Document $project, callable $getProjectDB, callable $getLogsDB, string $hourlyUsageRetentionDatetime): void
{
/** @var \Utopia\Database\Database $dbForProject*/
/** @var Database $dbForProject*/
$dbForProject = $getProjectDB($project);
// Delete Usage stats from projectDB
$this->deleteByGroup('stats', [
Query::lessThan('time', $hourlyUsageRetentionDatetime),
Query::orderDesc('time'),
Query::orderDesc('$internalId'),
Query::equal('period', ['1h']),
], $dbForProject);
if ($project->getId() !== 'console') {
/** @var \Utopia\Database\Database $dbForLogs*/
/** @var Database $dbForLogs*/
$dbForLogs = call_user_func($getLogsDB, $project);
// Delete Usage stats from logsDB
$this->deleteByGroup('stats', [
Query::lessThan('time', $hourlyUsageRetentionDatetime),
Query::orderDesc('time'),
Query::orderDesc('$internalId'),
Query::equal('period', ['1h']),
], $dbForLogs);
}
@@ -692,9 +698,12 @@ class Deletes extends Action
private function deleteExecutionLogs(Document $project, callable $getProjectDB, string $datetime): void
{
$dbForProject = $getProjectDB($project);
// Delete Executions
$this->deleteByGroup('executions', [
Query::lessThan('$createdAt', $datetime)
Query::lessThan('$createdAt', $datetime),
Query::orderDesc('$createdAt'),
Query::orderDesc('$internalId'),
], $dbForProject);
}
@@ -712,7 +721,9 @@ class Deletes extends Action
// Delete Sessions
$this->deleteByGroup('sessions', [
Query::lessThan('$createdAt', $expired)
Query::lessThan('$createdAt', $expired),
Query::orderDesc('$createdAt'),
Query::orderDesc('$internalId'),
], $dbForProject);
}
@@ -726,7 +737,9 @@ class Deletes extends Action
{
// Delete Dead Realtime Logs
$this->deleteByGroup('realtime', [
Query::lessThan('timestamp', $datetime)
Query::lessThan('timestamp', $datetime),
Query::orderDesc('timestamp'),
Query::orderDesc('$internalId'),
], $dbForPlatform);
}
@@ -746,6 +759,7 @@ class Deletes extends Action
$this->deleteByGroup(Audit::COLLECTION, [
Query::lessThan('time', $auditRetention),
Query::orderDesc('time'),
Query::orderDesc('$internalId'),
], $dbForProject);
} catch (DatabaseException $e) {
Console::error('Failed to delete audit logs for project ' . $projectId . ': ' . $e->getMessage());
+5 -2
View File
@@ -567,13 +567,16 @@ class Functions extends Action
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
$executionModel = new Execution();
$realtimeExecution = $executionModel->filter(new Document($execution->getArrayCopy()));
$realtimeExecution = $realtimeExecution->getArrayCopy(\array_keys($executionModel->getRules()));
$queueForEvents
->setProject($project)
->setUser($user)
->setEvent('functions.[functionId].executions.[executionId].update')
->setParam('functionId', $function->getId())
->setParam('executionId', $execution->getId())
->setPayload($execution->getArrayCopy(array_keys($executionModel->getRules())));
->setPayload($realtimeExecution);
/** Trigger Webhook */
$queueForWebhooks
@@ -587,8 +590,8 @@ class Functions extends Action
/** Trigger Realtime Events */
$queueForRealtime
->from($queueForEvents)
->setSubscribers(['console', $project->getId()])
->from($queueForEvents)
->trigger();
if (!empty($error)) {
@@ -145,7 +145,7 @@ class Execution extends Model
/**
* Convert DB structure to response model
*
* @return string
* @return Document
*/
public function filter(Document $document): Document
{
+10 -24
View File
@@ -882,37 +882,23 @@ class UsageTest extends Scope
$this->assertEquals(201, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$response = $this->client->call(
Client::METHOD_POST,
'/functions/' . $functionId . '/deployments',
array_merge([
'content-type' => 'multipart/form-data',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'entrypoint' => 'index.php',
'code' => $this->packageFunction('php'),
'activate' => true,
]
);
$deploymentId = $response['body']['$id'] ?? '';
$this->assertEquals(202, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
$this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['$createdAt']));
$this->assertEquals('index.php', $response['body']['entrypoint']);
// Wait for deployment to build.
sleep(self::WAIT + 20);
$deploymentId = $this->setupDeployment($functionId, [
'entrypoint' => 'index.php',
'code' => $this->packageFunction('php'),
'activate' => true,
]);
$this->assertNotEmpty($deploymentId);
$response = $this->client->call(
Client::METHOD_PATCH,
'/functions/' . $functionId . '/deployments/' . $deploymentId,
'/functions/' . $functionId . '/deployment',
array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id']
], $this->getHeaders()),
[
'deploymentId' => $deploymentId,
],
);
$this->assertEquals(200, $response['headers']['status-code']);
+2 -2
View File
@@ -228,8 +228,8 @@ trait LocaleBase
* Test for SUCCESS
*/
$languages = require(__DIR__ . '/../../../../app/config/locale/codes.php');
$defaultCountries = require(__DIR__ . '/../../../../app/config/locale/countries.php');
$defaultContinents = require(__DIR__ . '/../../../../app/config/locale/continents.php');
$defaultCountries = array_keys(require(__DIR__ . '/../../../../app/config/locale/countries.php'));
$defaultContinents = array_keys(require(__DIR__ . '/../../../../app/config/locale/continents.php'));
foreach ($languages as $lang) {
$response = $this->client->call(Client::METHOD_GET, '/locale/countries', [
@@ -534,10 +534,11 @@ class RealtimeConsoleClientTest extends Scope
'timeout' => 10
]);
$functionId = $response1['body']['$id'] ?? '';
$this->assertEquals(201, $response1['headers']['status-code']);
$functionId = $response1['body']['$id'];
$this->assertNotEmpty($functionId);
$projectId = 'console';
$client = $this->getWebsocket(['console'], [
@@ -570,6 +571,9 @@ class RealtimeConsoleClientTest extends Scope
$this->assertEquals(202, $deployment['headers']['status-code']);
$deploymentId = $deployment['body']['$id'];
$this->assertNotEmpty($deploymentId);
$response = json_decode($client->receive(), true);
$this->assertArrayHasKey('type', $response);
@@ -580,8 +584,48 @@ class RealtimeConsoleClientTest extends Scope
$this->assertCount(2, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
// $this->assertContains("functions.{$functionId}.deployments.{$deploymentId}.create", $response['data']['events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertNotEmpty($response['data']['payload']);
$this->assertEquals("waiting", $response['data']['payload']['status']);
$this->assertContains("functions.{$functionId}.deployments.{$deploymentId}.create", $response['data']['events']);
$response = json_decode($client->receive(), true);
$this->assertContains("functions.{$functionId}.deployments.{$deploymentId}.update", $response['data']['events']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertEquals("processing", $response['data']['payload']['status']);
$response = json_decode($client->receive(), true);
$this->assertContains("functions.{$functionId}.deployments.{$deploymentId}.update", $response['data']['events']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertEquals("building", $response['data']['payload']['status']);
$previousBuildLogs = null;
while (true) {
$response = json_decode($client->receive(), true);
$this->assertContains("functions.{$functionId}.deployments.{$deploymentId}.update", $response['data']['events']);
$this->assertContains('console', $response['data']['channels']);
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
$this->assertArrayHasKey('buildLogs', $response['data']['payload']);
// Ignore comparasion for first payload
if ($previousBuildLogs !== null) {
$this->assertNotEquals($previousBuildLogs, $response['data']['payload']['buildLogs']);
}
$previousBuildLogs = $response['data']['payload']['buildLogs'];
$this->assertThat(
$response['data']['payload']['status'],
$this->logicalOr(
$this->equalTo('building'),
$this->equalTo('ready'),
),
);
if ($response['data']['payload']['status'] === 'ready') {
break;
}
}
$client->close();
}
@@ -1364,6 +1364,11 @@ class SitesCustomServerTest extends Scope
$this->assertArrayHasKey('buildRuntime', $framework);
$this->assertArrayHasKey('runtimes', $framework);
$this->assertArrayHasKey('adapters', $framework);
$this->assertIsArray($framework['adapters']);
$this->assertArrayHasKey('key', $framework['adapters'][0]);
$this->assertArrayHasKey('installCommand', $framework['adapters'][0]);
$this->assertArrayHasKey('buildCommand', $framework['adapters'][0]);
$this->assertArrayHasKey('outputDirectory', $framework['adapters'][0]);
}
public function testSiteStatic(): void