diff --git a/.env b/.env
index fff40e5f7..89b6252c0 100644
--- a/.env
+++ b/.env
@@ -41,6 +41,7 @@ _APP_SMTP_PORT=1025
_APP_SMTP_SECURE=
_APP_SMTP_USERNAME=
_APP_SMTP_PASSWORD=
+_APP_USERS_STATS_RECIPIENTS=
_APP_HAMSTER_INTERVAL=86400
_APP_HAMSTER_TIME=21:00
_APP_MIXPANEL_TOKEN=
@@ -76,3 +77,5 @@ _APP_GRAPHQL_MAX_DEPTH=3
_APP_REGION=default
_APP_DOCKER_HUB_USERNAME=
_APP_DOCKER_HUB_PASSWORD=
+_APP_CONSOLE_GITHUB_SECRET=
+_APP_CONSOLE_GITHUB_APP_ID=
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
new file mode 100644
index 000000000..4d7fbb7b7
--- /dev/null
+++ b/.github/workflows/publish.yml
@@ -0,0 +1,46 @@
+name: "Build and Publish"
+
+on:
+ push:
+ tags:
+ - appwrite-*
+
+jobs:
+ build-publish:
+ name: Build and Publish
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v3
+ with:
+ fetch-depth: 2
+ submodules: recursive
+ ref: feat-db-pools-master
+
+ - name: Login to Docker Hub
+ uses: docker/login-action@v2
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Extract metadata (tags, labels) for Docker
+ id: meta
+ uses: docker/metadata-action@v4
+ with:
+ images: appwrite/cloud
+ tags: |
+ type=ref,event=tag
+
+ - name: Build & Publish to DockerHub
+ uses: docker/build-push-action@v4
+ with:
+ context: .
+ platforms: linux/amd64
+ build-args: |
+ VERSION=${{ steps.meta.outputs.version }}
+ VITE_APPWRITE_GROWTH_ENDPOINT=https://growth.appwrite.io/v1
+ VITE_GA_PROJECT=G-L7G2B6PLDS
+ VITE_CONSOLE_MODE=cloud
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
\ No newline at end of file
diff --git a/.gitmodules b/.gitmodules
index 6785e119c..4a4bf6aef 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,4 +1,4 @@
[submodule "app/console"]
path = app/console
url = https://github.com/appwrite/console
- branch = 2.2.2
+ branch = cloud-cards
diff --git a/Dockerfile b/Dockerfile
index c7ea1e196..4eddc2076 100755
--- a/Dockerfile
+++ b/Dockerfile
@@ -29,7 +29,7 @@ ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT
RUN npm ci
RUN npm run build
-FROM appwrite/base:0.2.0 as final
+FROM appwrite/base:0.2.2 as final
LABEL maintainer="team@appwrite.io"
@@ -91,6 +91,7 @@ COPY --from=node /usr/local/src/console/build /usr/src/code/console
# Add Source Code
COPY ./app /usr/src/code/app
+COPY ./public /usr/src/code/public
COPY ./bin /usr/local/bin
COPY ./docs /usr/src/code/docs
COPY ./src /usr/src/code/src
@@ -112,7 +113,11 @@ RUN mkdir -p /storage/uploads && \
# Executables
RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/patch-delete-schedule-updated-at-attribute && \
- chmod +x /usr/local/bin/maintenance && \
+ chmod +x /usr/local/bin/clear-card-cache && \
+ chmod +x /usr/local/bin/calc-users-stats && \
+ chmod +x /usr/local/bin/calc-tier-stats && \
+ chmod +x /usr/local/bin/patch-delete-project-collections && \
+ chmod +x /usr/local/bin/maintenance && \
chmod +x /usr/local/bin/volume-sync && \
chmod +x /usr/local/bin/install && \
chmod +x /usr/local/bin/migrate && \
diff --git a/app/cli.php b/app/cli.php
index 81fb1dff8..d1dd88577 100644
--- a/app/cli.php
+++ b/app/cli.php
@@ -61,7 +61,7 @@ CLI::setResource('dbForConsole', function ($pools, $cache) {
$dbForConsole->setNamespace('console');
// Ensure tables exist
- $collections = Config::getParam('collections', []);
+ $collections = Config::getParam('collections', [])['console'];
$last = \array_key_last($collections);
if (!($dbForConsole->exists($dbForConsole->getDefaultDatabase(), $last))) { /** TODO cache ready variable using registry */
diff --git a/app/config/cloud/contributors.json b/app/config/cloud/contributors.json
new file mode 100644
index 000000000..efa36c3cb
--- /dev/null
+++ b/app/config/cloud/contributors.json
@@ -0,0 +1 @@
+[1297371,1759475,6360216,20852629,5857008,19310830,9708641,26739219,23742426,22174310,77877486,1477010,29069505,62933155,45863583,42211,176163,58045728,7091609,31401437,1911066,66096031,22432834,43054051,79051850,22895284,100597998,91385411,49699333,7818620,11004008,54898623,27856297,33250853,49375670,30630364,50206,47822499,7481165,52557347,13681567,20492520,51369094,51821861,50256986,28431370,13692220,24373771,15938422,27148250,14805534,36137226,36071208,40193621,56179878,53281158,48085134,19358691,32528768,19733683,37348419,13719696,18309412,42106787,53618500,5306011,4408379,35486736,18586611,15062564,27011453,56090587,44623032,25107942,81188,33922418,16717633,32809211,69401139,25815659,4104127,20889958,3102249,12476526,2635185,28495651,50957556,2791280,31023616,20955511,835733,471907,69008866,44906587,45271396,76054330,45748739,41908747,23402178,47356149,8921,36632821,3668741,71702982,29725587,26303198,785830,51410502,60089135,2847349,43172716,48546075,8216525,41161981,51828039,4334997,80918302,38534289,47860497,80036766,41341387,49818988,58487637,29237374,46913894,5148229,4377199,29686102,26272249,75117692,4090256,27357868,33062368,38664231,46695441,743291,22633385,6368283,11593067,45097959,43381712,3284228,1972717,33012425,61755381,25405707,3144291,44156359,5497267,7423905,20716175,28586681,5975506,23518097,22187384,24191952,7768078,971530,51240166,55633427,34207400,77061285,11719476,35950229,66742927,34406802,802933,50047839,39148877,26602940,9693472,44273767,19362725,31209978,30521594,686298,6237394,35039730,42580581,36671793,8502129,8466918,81866614,54903252,28373606,13381361,72331432,30694270,5355510,8209163,86675510,9453522,42496309,56145786,2149381,393945,22084723,52621436,8872447,5575392,29619660,5547479,8852116,11151445,4717349,17725274,65615065,18537755,29292618,53044263,26597930,10313411,55998629,77529288,17404636,33729848,19422168,17916404,66111735,10329006,33502846,398230,81643826,105039167,47522632,91655303,9774614,10603631,284924,60857954,22885912,116552306,36103454,794606,27729549,1754457,36594527,13899668,78664749,47406531,27698189,5305654,53345517,6756412,29176704,77790497,47504894,37251540,52361778,52200375,1351177,66022861,73975409,25745396,31433638,37118134,43210805,20317665,11923975,47187468,16362381,36751163,14959876,32362757,65529384,52352285,74085816,3628535,43902034,75667593,26132902,466713,617558,96806061,33605526,11290524,43621940,12446314,17146935,55018955,56096559,79797000,40014186,34449936,58387964,23368207,42414965,44056349,33743031,12294525,58251592,33755729,9021747,932084,11428067,97121933,80122730,60894542,58583793,56051809,32243289,9934371,90936802,74638775,65399526,77604,64524822,47782249,43633955,42793632,55969597,72334601,82395440,92818577,60866204,65016769,23725091,45892107,55308895,86314140,82756460,47685349,63562160,73419211,1613216,50882624,91469717,46166258,60927324,41763158,83607556,2171717,50497814,39427312,61322830,40076195,39419448,29397545,55090719,53259730,20885012,64558515,69677883,55741087,72426535,46033036,68477507,30376878,73700530,25518600,29922887,36229969,47573417,40424087,49054503,16880385,22801227,72848513,64347914,814402,49149679,55017867,49481876,67067955,31439735,63878173,80322286,43746210,17332970,22702905,62476876,89888292,75736952,54059881,90782137,63588969,57111920,63330165,70258211,46371923,17837758,59364507,52203828,60147326,18481195,74822422,9803078,67309607,60410049,47360939,19922556,90848252,24698014,58886915,63579762,96648934,68523530,60518745,37345795,3929651,54993657,52061363,43019989,5787917,94674993,71593494,17143469,10288548,1830380,71510505,59124772,2335145,70798495,46474346,49263351,52062536,63151043,65248303,26071571,53626355,43992469,60785452,63467479,71837281,19490891,58628586,38250310,7271718,1110414,57227290,11625672,85063520,88965873,70096901,42029519,85363195,64471630,69353350,66922161,2221746,100430077,12299813,62690310,68282006,99184676,2450,22989561,22212661,59973863,11232940,76688923,22321353,77732479,84286404,32268377,34828782,23068019,57074509,24620969,20735983,26173690,75809937,49760818,86646105,52617262]
\ No newline at end of file
diff --git a/app/config/cloud/employees.json b/app/config/cloud/employees.json
new file mode 100644
index 000000000..1575a0588
--- /dev/null
+++ b/app/config/cloud/employees.json
@@ -0,0 +1,33 @@
+{
+ "eldad@appwrite.io": { "memberSince": "2020-10-15", "spot": "0", "gitHub": "eldadfux" },
+ "christy@appwrite.io": { "memberSince": "2020-12-01", "spot": "1", "gitHub": "christyjacob4" },
+ "torsten@appwrite.io": { "memberSince": "2020-12-28", "spot": "2", "gitHub": "torstendittmann" },
+ "damodar@appwrite.io": { "memberSince": "2021-01-02", "spot": "3", "gitHub": "lohanidamodar" },
+ "bradley@appwrite.io": { "memberSince": "2021-05-21", "spot": "5", "gitHub": "PineappleIOnic" },
+ "jake@appwrite.io": { "memberSince": "2021-06-28", "spot": "6", "gitHub": "abnegate" },
+ "sara@appwrite.io": { "memberSince": "2021-08-16", "spot": "7", "gitHub": "sarakaandorp" },
+ "matej@appwrite.io": { "memberSince": "2021-08-23", "spot": "8", "gitHub": "meldiron" },
+ "aditya@appwrite.io": { "memberSince": "2021-09-01", "spot": "9", "gitHub": "adityaoberai" },
+ "wess@appwrite.io": { "memberSince": "2021-11-08", "spot": "12", "gitHub": "wess" },
+ "may@appwrite.io": { "memberSince": "2021-11-28", "spot": "14", "gitHub": "MayEnder" },
+ "elad@appwrite.io": { "memberSince": "2021-12-19", "spot": "15", "gitHub": "elad2412" },
+ "vincent@appwrite.io": { "memberSince": "2022-01-01", "spot": "16", "gitHub": "gewenyu99" },
+ "haimantika@appwrite.io": { "memberSince": "2022-04-01", "spot": "18", "gitHub": "Haimantika" },
+ "chen@appwrite.io": { "memberSince": "2022-01-24", "spot": "19", "gitHub": "chenparnasa" },
+ "tessa@appwrite.io": { "memberSince": "2022-04-21", "spot": "20", "gitHub": "tessamero" },
+ "shimon@appwrite.io": { "memberSince": "2022-05-01", "spot": "23", "gitHub": "shimonewman" },
+ "shmuel@appwrite.io": { "memberSince": "2022-03-20", "spot": "24", "gitHub": "fogelito" },
+ "arman@appwrite.io": { "memberSince": "2022-04-04", "spot": "25", "gitHub": "ArmanNik" },
+ "carla@appwrite.io": { "memberSince": "2022-04-04", "spot": "26", "gitHub": "heyCarla" },
+ "emma@appwrite.io": { "memberSince": "2022-05-08", "spot": "27", "gitHub": "emmacarpagnano1" },
+ "dylan@appwrite.io": { "memberSince": "2022-05-09", "spot": "28", "gitHub": "DylanG-64" },
+ "steven@appwrite.io": { "memberSince": "2022-07-01", "spot": "30", "gitHub": "stnguyen90" },
+ "jyoti@appwrite.io": { "memberSince": "2022-10-24", "spot": "31", "gitHub": "joeyouss" },
+ "jade@appwrite.io": { "memberSince": "2022-10-31", "spot": "32", "gitHub": "dajebp" },
+ "khushboo@appwrite.io": { "memberSince": "2021-11-08", "spot": "13", "gitHub": "vermakhushboo" },
+ "thomas@appwrite.io": { "memberSince": "2022-11-03", "spot": "34", "gitHub": "TGlide" },
+ "holly@appwrite.io": { "memberSince": "2022-12-05", "spot": "35", "gitHub": "HollyBarclay" },
+ "laura@appwrite.io": { "memberSince": "2023-01-25", "spot": "36", "gitHub": "LauraDuRy" },
+ "caio@appwrite.io": { "memberSince": "2023-03-27", "spot": "37", "gitHub": "ariascaio" },
+ "luke@appwrite.io": { "memberSince": "2023-05-04", "spot": "38", "gitHub": "loks0n" }
+}
diff --git a/app/config/cloud/heroes.json b/app/config/cloud/heroes.json
new file mode 100644
index 000000000..3fc81b3b3
--- /dev/null
+++ b/app/config/cloud/heroes.json
@@ -0,0 +1,9 @@
+{
+ "bishwajeet.techmaster@gmail.com": { "memberSince": "2023-02-07" },
+ "lucasaudart@gmail.com": { "memberSince": "2023-02-07" },
+ "tkarmakar27112000@gmail.com": { "memberSince": "2023-02-07" },
+ "alves.mckl@gmail.com": { "memberSince": "2023-02-07" },
+ "dpns_nampula@rnlay.com": { "memberSince": "2023-02-07" },
+ "a.stephensimon@outlook.com": { "memberSince": "2023-02-07" },
+ "hidianapham@gmail.com": { "memberSince": "2023-02-07" }
+}
diff --git a/app/config/collections.php b/app/config/collections.php
index 12904a1e2..5fd29d3ab 100644
--- a/app/config/collections.php
+++ b/app/config/collections.php
@@ -17,1297 +17,7 @@ $auth = Config::getParam('auth', []);
* indexes => list of indexes
*/
-$collections = [
- 'databases' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('databases'),
- 'name' => 'Databases',
- 'attributes' => [
- [
- '$id' => ID::custom('name'),
- 'type' => Database::VAR_STRING,
- 'size' => 256,
- 'required' => true,
- 'signed' => true,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('search'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_fulltext_search'),
- 'type' => Database::INDEX_FULLTEXT,
- 'attributes' => ['search'],
- 'lengths' => [],
- 'orders' => [],
- ],
- [
- '$id' => ID::custom('_key_name'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['name'],
- 'lengths' => [256],
- 'orders' => [Database::ORDER_ASC],
- ],
- ],
- ],
- 'collections' => [
- '$collection' => ID::custom('databases'),
- '$id' => ID::custom('collections'),
- 'name' => 'Collections',
- 'attributes' => [
- [
- '$id' => ID::custom('databaseInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('databaseId'),
- 'type' => Database::VAR_STRING,
- 'signed' => true,
- 'size' => Database::LENGTH_KEY,
- 'format' => '',
- 'filters' => [],
- 'required' => true,
- 'default' => null,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('name'),
- 'type' => Database::VAR_STRING,
- 'size' => 256,
- 'required' => true,
- 'signed' => true,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('enabled'),
- 'type' => Database::VAR_BOOLEAN,
- 'signed' => true,
- 'size' => 0,
- 'format' => '',
- 'filters' => [],
- 'required' => true,
- 'default' => null,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('documentSecurity'),
- 'type' => Database::VAR_BOOLEAN,
- 'signed' => true,
- 'size' => 0,
- 'format' => '',
- 'filters' => [],
- 'required' => true,
- 'default' => null,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('attributes'),
- 'type' => Database::VAR_STRING,
- 'size' => 1000000,
- 'required' => false,
- 'signed' => true,
- 'array' => false,
- 'filters' => ['subQueryAttributes'],
- ],
- [
- '$id' => ID::custom('indexes'),
- 'type' => Database::VAR_STRING,
- 'size' => 1000000,
- 'required' => false,
- 'signed' => true,
- 'array' => false,
- 'filters' => ['subQueryIndexes'],
- ],
- [
- '$id' => ID::custom('search'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_fulltext_search'),
- 'type' => Database::INDEX_FULLTEXT,
- 'attributes' => ['search'],
- 'lengths' => [],
- 'orders' => [],
- ],
- [
- '$id' => ID::custom('_key_name'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['name'],
- 'lengths' => [256],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_enabled'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['enabled'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_documentSecurity'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['documentSecurity'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- ],
- ],
-
- 'attributes' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('attributes'),
- 'name' => 'Attributes',
- 'attributes' => [
- [
- '$id' => ID::custom('databaseInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('databaseId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => false,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('collectionInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('collectionId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('key'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('type'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('status'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('size'),
- 'type' => Database::VAR_INTEGER,
- 'format' => '',
- 'size' => 0,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('required'),
- 'type' => Database::VAR_BOOLEAN,
- 'format' => '',
- 'size' => 0,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('default'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['casting'],
- ],
- [
- '$id' => ID::custom('signed'),
- 'type' => Database::VAR_BOOLEAN,
- 'size' => 0,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('array'),
- 'type' => Database::VAR_BOOLEAN,
- 'size' => 0,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('format'),
- 'type' => Database::VAR_STRING,
- 'size' => 64,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('formatOptions'),
- 'type' => Database::VAR_STRING,
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => new stdClass(),
- 'array' => false,
- 'filters' => ['json', 'range', 'enum'],
- ],
- [
- '$id' => ID::custom('filters'),
- 'type' => Database::VAR_STRING,
- 'size' => 64,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => true,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_key_db_collection'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['databaseInternalId', 'collectionInternalId'],
- 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY],
- 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
- ],
- ],
- ],
-
- 'indexes' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('indexes'),
- 'name' => 'Indexes',
- 'attributes' => [
- [
- '$id' => ID::custom('databaseInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('databaseId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => false,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('collectionInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('collectionId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('key'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('type'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('status'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('attributes'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => true,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('lengths'),
- 'type' => Database::VAR_INTEGER,
- 'format' => '',
- 'size' => 0,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => true,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('orders'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 4,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => true,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_key_db_collection'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['databaseInternalId', 'collectionInternalId'],
- 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY],
- 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
- ],
- ],
- ],
-
- 'projects' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('projects'),
- 'name' => 'Projects',
- 'attributes' => [
- [
- '$id' => ID::custom('teamInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('teamId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('name'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 128,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('region'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 128,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('description'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('database'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('logo'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('url'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('version'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('legalName'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('legalCountry'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('legalState'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('legalCity'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('legalAddress'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('legalTaxId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('services'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => [],
- 'array' => false,
- 'filters' => ['json'],
- ],
- [
- '$id' => ID::custom('auths'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => [],
- 'array' => false,
- 'filters' => ['json'],
- ],
- [
- '$id' => ID::custom('authProviders'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => [],
- 'array' => false,
- 'filters' => ['json', 'encrypt'],
- ],
- [
- '$id' => ID::custom('platforms'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['subQueryPlatforms'],
- ],
- [
- '$id' => ID::custom('webhooks'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['subQueryWebhooks'],
- ],
- [
- '$id' => ID::custom('keys'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['subQueryKeys'],
- ],
- [
- '$id' => ID::custom('domains'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['subQueryDomains'],
- ],
- [
- '$id' => ID::custom('search'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_key_search'),
- 'type' => Database::INDEX_FULLTEXT,
- 'attributes' => ['search'],
- 'lengths' => [],
- 'orders' => [],
- ],
- [
- '$id' => ID::custom('_key_name'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['name'],
- 'lengths' => [128],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_team'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['teamId'],
- 'lengths' => [Database::LENGTH_KEY],
- 'orders' => [Database::ORDER_ASC],
- ],
- ],
- ],
-
- 'schedules' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('schedules'),
- 'name' => 'schedules',
- 'attributes' => [
- [
- '$id' => ID::custom('resourceType'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 100,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('resourceInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('resourceId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('resourceUpdatedAt'),
- 'type' => Database::VAR_DATETIME,
- 'format' => '',
- 'size' => 0,
- 'signed' => false,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['datetime'],
- ],
- [
- '$id' => ID::custom('projectId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('schedule'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 100,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('active'),
- 'type' => Database::VAR_BOOLEAN,
- 'signed' => true,
- 'size' => 0,
- 'format' => '',
- 'filters' => [],
- 'required' => false,
- 'default' => null,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('region'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 10,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_key_region_resourceType_resourceUpdatedAt'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['region', 'resourceType','resourceUpdatedAt'],
- 'lengths' => [],
- 'orders' => [],
- ],
- [
- '$id' => ID::custom('_key_region_resourceType_projectId_resourceId'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['region', 'resourceType', 'projectId', 'resourceId'],
- 'lengths' => [],
- 'orders' => [],
- ],
- ],
- ],
-
- 'platforms' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('platforms'),
- 'name' => 'platforms',
- 'attributes' => [
- [
- '$id' => ID::custom('projectInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('projectId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('type'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('name'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('key'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('store'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('hostname'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 256,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ]
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_key_project'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['projectInternalId'],
- 'lengths' => [Database::LENGTH_KEY],
- 'orders' => [Database::ORDER_ASC],
- ],
- ],
- ],
-
- 'domains' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('domains'),
- 'name' => 'domains',
- 'attributes' => [
- [
- '$id' => ID::custom('projectInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('projectId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('updated'),
- 'type' => Database::VAR_DATETIME,
- 'format' => '',
- 'size' => 0,
- 'signed' => false,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['datetime'],
- ],
- [
- '$id' => ID::custom('domain'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('tld'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('registerable'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('verification'),
- 'type' => Database::VAR_BOOLEAN,
- 'format' => '',
- 'size' => 0,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('certificateId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_key_project'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['projectInternalId'],
- 'lengths' => [Database::LENGTH_KEY],
- 'orders' => [Database::ORDER_ASC],
- ],
- ],
- ],
-
- 'keys' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('keys'),
- 'name' => 'keys',
- 'attributes' => [
- [
- '$id' => ID::custom('projectInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('projectId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => 0,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('name'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('scopes'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => true,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('secret'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 512, // Output of \bin2hex(\random_bytes(128)) => string(256) doubling for encryption
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => ['encrypt'],
- ],
- [
- '$id' => ID::custom('expire'),
- 'type' => Database::VAR_DATETIME,
- 'format' => '',
- 'size' => 0,
- 'signed' => false,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['datetime'],
- ],
- [
- '$id' => ID::custom('accessedAt'),
- 'type' => Database::VAR_DATETIME,
- 'format' => '',
- 'size' => 0,
- 'signed' => false,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['datetime'],
- ],
- [
- '$id' => ID::custom('sdks'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => true,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_key_project'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['projectInternalId'],
- 'lengths' => [Database::LENGTH_KEY],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => '_key_accessedAt',
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['accessedAt'],
- 'lengths' => [],
- 'orders' => [],
- ],
- ],
- ],
-
- 'webhooks' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('webhooks'),
- 'name' => 'webhooks',
- 'attributes' => [
- [
- '$id' => ID::custom('projectInternalId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('projectId'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('name'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('url'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('httpUser'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('httpPass'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY, // TODO will the length suffice after encryption?
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['encrypt'],
- ],
- [
- '$id' => ID::custom('security'),
- 'type' => Database::VAR_BOOLEAN,
- 'format' => '',
- 'size' => 0,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('events'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => true,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('signatureKey'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 2048,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_key_project'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['projectInternalId'],
- 'lengths' => [Database::LENGTH_KEY],
- 'orders' => [Database::ORDER_ASC],
- ]
- ],
- ],
-
+ $commonCollections = [
'users' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('users'),
@@ -2186,6 +896,610 @@ $collections = [
],
],
+ 'buckets' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('buckets'),
+ 'name' => 'Buckets',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('enabled'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'signed' => true,
+ 'size' => 0,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => true,
+ 'array' => false,
+ ],
+ [
+ '$id' => ID::custom('name'),
+ 'type' => Database::VAR_STRING,
+ 'signed' => true,
+ 'size' => 128,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => true,
+ 'array' => false,
+ ],
+ [
+ '$id' => ID::custom('fileSecurity'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'signed' => true,
+ 'size' => 1,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => false,
+ 'array' => false,
+ ],
+ [
+ '$id' => ID::custom('maximumFileSize'),
+ 'type' => Database::VAR_INTEGER,
+ 'signed' => false,
+ 'size' => 8,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => true,
+ 'array' => false,
+ ],
+ [
+ '$id' => ID::custom('allowedFileExtensions'),
+ 'type' => Database::VAR_STRING,
+ 'signed' => true,
+ 'size' => 64,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => true,
+ 'array' => true,
+ ],
+ [
+ '$id' => 'compression',
+ 'type' => Database::VAR_STRING,
+ 'signed' => true,
+ 'size' => 10,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => true,
+ 'array' => false,
+ ],
+ [
+ '$id' => ID::custom('encryption'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'signed' => true,
+ 'size' => 0,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => true,
+ 'array' => false,
+ ],
+ [
+ '$id' => ID::custom('antivirus'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'signed' => true,
+ 'size' => 0,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => true,
+ 'array' => false,
+ ],
+ [
+ '$id' => ID::custom('search'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_fulltext_name'),
+ 'type' => Database::INDEX_FULLTEXT,
+ 'attributes' => ['name'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_search'),
+ 'type' => Database::INDEX_FULLTEXT,
+ 'attributes' => ['search'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_enabled'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['enabled'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_name'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['name'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_fileSecurity'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['fileSecurity'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_maximumFileSize'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['maximumFileSize'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_encryption'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['encryption'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_antivirus'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['antivirus'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ ]
+ ],
+
+ 'stats' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('stats'),
+ 'name' => 'Stats',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('metric'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 255,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('region'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 255,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('value'),
+ 'type' => Database::VAR_INTEGER,
+ 'format' => '',
+ 'size' => 8,
+ 'signed' => false,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('time'),
+ 'type' => Database::VAR_DATETIME,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => false,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['datetime'],
+ ],
+ [
+ '$id' => ID::custom('period'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 4,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('type'),
+ 'type' => Database::VAR_INTEGER,
+ 'format' => '',
+ 'size' => 1,
+ 'signed' => false,
+ 'required' => true,
+ 'default' => 0, // 0 -> count, 1 -> sum
+ 'array' => false,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_key_time'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['time'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_DESC],
+ ],
+ [
+ '$id' => ID::custom('_key_period_time'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['period', 'time'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_metric_period_time'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['metric', 'period', 'time'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_DESC],
+ ],
+ ],
+ ],
+ ];
+
+ $projectCollections = array_merge([
+ 'databases' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('databases'),
+ 'name' => 'Databases',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('name'),
+ 'type' => Database::VAR_STRING,
+ 'size' => 256,
+ 'required' => true,
+ 'signed' => true,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('search'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_fulltext_search'),
+ 'type' => Database::INDEX_FULLTEXT,
+ 'attributes' => ['search'],
+ 'lengths' => [],
+ 'orders' => [],
+ ],
+ [
+ '$id' => ID::custom('_key_name'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['name'],
+ 'lengths' => [256],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ ],
+ ],
+
+ 'attributes' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('attributes'),
+ 'name' => 'Attributes',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('databaseInternalId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('databaseId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => false,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('collectionInternalId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('collectionId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('key'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('type'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('status'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('size'),
+ 'type' => Database::VAR_INTEGER,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('required'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('default'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['casting'],
+ ],
+ [
+ '$id' => ID::custom('signed'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'size' => 0,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('array'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'size' => 0,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('format'),
+ 'type' => Database::VAR_STRING,
+ 'size' => 64,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('formatOptions'),
+ 'type' => Database::VAR_STRING,
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => new stdClass(),
+ 'array' => false,
+ 'filters' => ['json', 'range', 'enum'],
+ ],
+ [
+ '$id' => ID::custom('filters'),
+ 'type' => Database::VAR_STRING,
+ 'size' => 64,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => true,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_key_db_collection'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['databaseInternalId', 'collectionInternalId'],
+ 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY],
+ 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
+ ],
+ ],
+ ],
+
+ 'indexes' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('indexes'),
+ 'name' => 'Indexes',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('databaseInternalId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('databaseId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => false,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('collectionInternalId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('collectionId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('key'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('type'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('status'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('attributes'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => true,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('lengths'),
+ 'type' => Database::VAR_INTEGER,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => true,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('orders'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 4,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => true,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_key_db_collection'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['databaseInternalId', 'collectionInternalId'],
+ 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY],
+ 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
+ ],
+ ],
+ ],
+
'functions' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('functions'),
@@ -2922,6 +2236,970 @@ $collections = [
],
],
+ 'variables' => [
+ '$collection' => Database::METADATA,
+ '$id' => 'variables',
+ 'name' => 'variables',
+ 'attributes' => [
+ [
+ '$id' => 'functionInternalId',
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => 'functionId',
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => 'key',
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => 'value',
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 8192,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [ 'encrypt' ]
+ ],
+ [
+ '$id' => ID::custom('search'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => '_key_function',
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['functionInternalId'],
+ 'lengths' => [Database::LENGTH_KEY],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => '_key_uniqueKey',
+ 'type' => Database::INDEX_UNIQUE,
+ 'attributes' => ['functionInternalId', 'key'],
+ 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY],
+ 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
+ ],
+ [
+ '$id' => '_key_key',
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['key'],
+ 'lengths' => [Database::LENGTH_KEY],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_fulltext_search'),
+ 'type' => Database::INDEX_FULLTEXT,
+ 'attributes' => ['search'],
+ 'lengths' => [],
+ 'orders' => [],
+ ],
+ ],
+ ],
+
+ 'cache' => [
+ '$collection' => Database::METADATA,
+ '$id' => 'cache',
+ 'name' => 'Cache',
+ 'attributes' => [
+ [
+ '$id' => 'resource',
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 255,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => 'accessedAt',
+ 'type' => Database::VAR_DATETIME,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => false,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['datetime'],
+ ],
+ [
+ '$id' => 'signature',
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 255,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => '_key_accessedAt',
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['accessedAt'],
+ 'lengths' => [],
+ 'orders' => [],
+ ],
+ [
+ '$id' => '_key_resource',
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['resource'],
+ 'lengths' => [],
+ 'orders' => [],
+ ],
+ ],
+ ],
+ ], $commonCollections);
+
+ $consoleCollections = array_merge([
+ 'projects' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('projects'),
+ 'name' => 'Projects',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('teamInternalId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('teamId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('name'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 128,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('region'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 128,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('description'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('database'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('logo'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('url'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('version'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('legalName'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('legalCountry'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('legalState'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('legalCity'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('legalAddress'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('legalTaxId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('services'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => [],
+ 'array' => false,
+ 'filters' => ['json'],
+ ],
+ [
+ '$id' => ID::custom('auths'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => [],
+ 'array' => false,
+ 'filters' => ['json'],
+ ],
+ [
+ '$id' => ID::custom('authProviders'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => [],
+ 'array' => false,
+ 'filters' => ['json', 'encrypt'],
+ ],
+ [
+ '$id' => ID::custom('platforms'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['subQueryPlatforms'],
+ ],
+ [
+ '$id' => ID::custom('webhooks'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['subQueryWebhooks'],
+ ],
+ [
+ '$id' => ID::custom('keys'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['subQueryKeys'],
+ ],
+ [
+ '$id' => ID::custom('domains'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['subQueryDomains'],
+ ],
+ [
+ '$id' => ID::custom('search'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16384,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_key_search'),
+ 'type' => Database::INDEX_FULLTEXT,
+ 'attributes' => ['search'],
+ 'lengths' => [],
+ 'orders' => [],
+ ],
+ [
+ '$id' => ID::custom('_key_name'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['name'],
+ 'lengths' => [128],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_team'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['teamId'],
+ 'lengths' => [Database::LENGTH_KEY],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ ],
+ ],
+
+ 'schedules' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('schedules'),
+ 'name' => 'schedules',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('resourceType'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 100,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('resourceId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('resourceUpdatedAt'),
+ 'type' => Database::VAR_DATETIME,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => false,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['datetime'],
+ ],
+ [
+ '$id' => ID::custom('projectId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('schedule'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 100,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('active'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'signed' => true,
+ 'size' => 0,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ ],
+ [
+ '$id' => ID::custom('region'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 10,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_key_region_resourceType_resourceUpdatedAt'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['region', 'resourceType','resourceUpdatedAt'],
+ 'lengths' => [],
+ 'orders' => [],
+ ],
+ [
+ '$id' => ID::custom('_key_region_resourceType_projectId_resourceId'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['region', 'resourceType', 'projectId', 'resourceId'],
+ 'lengths' => [],
+ 'orders' => [],
+ ],
+ ],
+ ],
+
+ 'platforms' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('platforms'),
+ 'name' => 'platforms',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('projectInternalId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('projectId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('type'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 16,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('name'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('key'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('store'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('hostname'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 256,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ]
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_key_project'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['projectInternalId'],
+ 'lengths' => [Database::LENGTH_KEY],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ ],
+ ],
+
+ 'domains' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('domains'),
+ 'name' => 'domains',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('projectInternalId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('projectId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('updated'),
+ 'type' => Database::VAR_DATETIME,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => false,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['datetime'],
+ ],
+ [
+ '$id' => ID::custom('domain'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('tld'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('registerable'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('verification'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('certificateId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_key_project'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['projectInternalId'],
+ 'lengths' => [Database::LENGTH_KEY],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ ],
+ ],
+
+ 'keys' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('keys'),
+ 'name' => 'keys',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('projectInternalId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('projectId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => 0,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('name'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('scopes'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => true,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('secret'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 512, // Output of \bin2hex(\random_bytes(128)) => string(256) doubling for encryption
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['encrypt'],
+ ],
+ [
+ '$id' => ID::custom('expire'),
+ 'type' => Database::VAR_DATETIME,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => false,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['datetime'],
+ ],
+ [
+ '$id' => ID::custom('accessedAt'),
+ 'type' => Database::VAR_DATETIME,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => false,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['datetime'],
+ ],
+ [
+ '$id' => ID::custom('sdks'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => true,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_key_project'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['projectInternalId'],
+ 'lengths' => [Database::LENGTH_KEY],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => '_key_accessedAt',
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['accessedAt'],
+ 'lengths' => [],
+ 'orders' => [],
+ ],
+ ],
+ ],
+
+ 'webhooks' => [
+ '$collection' => ID::custom(Database::METADATA),
+ '$id' => ID::custom('webhooks'),
+ 'name' => 'webhooks',
+ 'attributes' => [
+ [
+ '$id' => ID::custom('projectInternalId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('projectId'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('name'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('url'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('httpUser'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('httpPass'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY, // TODO will the length suffice after encryption?
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => ['encrypt'],
+ ],
+ [
+ '$id' => ID::custom('security'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'format' => '',
+ 'size' => 0,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('events'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => Database::LENGTH_KEY,
+ 'signed' => true,
+ 'required' => true,
+ 'default' => null,
+ 'array' => true,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('signatureKey'),
+ 'type' => Database::VAR_STRING,
+ 'format' => '',
+ 'size' => 2048,
+ 'signed' => true,
+ 'required' => false,
+ 'default' => null,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ ],
+ 'indexes' => [
+ [
+ '$id' => ID::custom('_key_project'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['projectInternalId'],
+ 'lengths' => [Database::LENGTH_KEY],
+ 'orders' => [Database::ORDER_ASC],
+ ]
+ ],
+ ],
+
'certificates' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('certificates'),
@@ -3008,286 +3286,6 @@ $collections = [
],
],
- 'buckets' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('buckets'),
- 'name' => 'Buckets',
- 'attributes' => [
- [
- '$id' => ID::custom('enabled'),
- 'type' => Database::VAR_BOOLEAN,
- 'signed' => true,
- 'size' => 0,
- 'format' => '',
- 'filters' => [],
- 'required' => true,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('name'),
- 'type' => Database::VAR_STRING,
- 'signed' => true,
- 'size' => 128,
- 'format' => '',
- 'filters' => [],
- 'required' => true,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('fileSecurity'),
- 'type' => Database::VAR_BOOLEAN,
- 'signed' => true,
- 'size' => 1,
- 'format' => '',
- 'filters' => [],
- 'required' => false,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('maximumFileSize'),
- 'type' => Database::VAR_INTEGER,
- 'signed' => false,
- 'size' => 8,
- 'format' => '',
- 'filters' => [],
- 'required' => true,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('allowedFileExtensions'),
- 'type' => Database::VAR_STRING,
- 'signed' => true,
- 'size' => 64,
- 'format' => '',
- 'filters' => [],
- 'required' => true,
- 'array' => true,
- ],
- [
- '$id' => 'compression',
- 'type' => Database::VAR_STRING,
- 'signed' => true,
- 'size' => 10,
- 'format' => '',
- 'filters' => [],
- 'required' => true,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('encryption'),
- 'type' => Database::VAR_BOOLEAN,
- 'signed' => true,
- 'size' => 0,
- 'format' => '',
- 'filters' => [],
- 'required' => true,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('antivirus'),
- 'type' => Database::VAR_BOOLEAN,
- 'signed' => true,
- 'size' => 0,
- 'format' => '',
- 'filters' => [],
- 'required' => true,
- 'array' => false,
- ],
- [
- '$id' => ID::custom('search'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 16384,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_fulltext_name'),
- 'type' => Database::INDEX_FULLTEXT,
- 'attributes' => ['name'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_search'),
- 'type' => Database::INDEX_FULLTEXT,
- 'attributes' => ['search'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_enabled'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['enabled'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_name'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['name'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_fileSecurity'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['fileSecurity'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_maximumFileSize'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['maximumFileSize'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_encryption'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['encryption'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_antivirus'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['antivirus'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- ]
- ],
-
- 'stats' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('stats'),
- 'name' => 'Stats',
- 'attributes' => [
- [
- '$id' => ID::custom('metric'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 255,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('region'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 255,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('value'),
- 'type' => Database::VAR_INTEGER,
- 'format' => '',
- 'size' => 8,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => ID::custom('time'),
- 'type' => Database::VAR_DATETIME,
- 'format' => '',
- 'size' => 0,
- 'signed' => false,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['datetime'],
- ],
- [
- '$id' => ID::custom('period'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 4,
- 'signed' => true,
- 'required' => true,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_key_time'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['time'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_DESC],
- ],
- [
- '$id' => ID::custom('_key_period_time'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['period', 'time'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => ID::custom('_key_metric_period_time'),
- 'type' => Database::INDEX_UNIQUE,
- 'attributes' => ['metric', 'period', 'time'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_DESC],
- ],
- ],
- ],
- 'statsLogger' => [
- '$collection' => ID::custom(Database::METADATA),
- '$id' => ID::custom('statsLogger'),
- 'name' => 'StatsLogger',
- 'attributes' => [
- [
- '$id' => ID::custom('time'),
- 'type' => Database::VAR_DATETIME,
- 'format' => '',
- 'size' => 0,
- 'signed' => false,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['datetime'],
- ],
- [
- '$id' => ID::custom('metrics'),
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 5012,
- 'signed' => true,
- 'required' => false,
- 'default' => [],
- 'array' => false,
- 'filters' => ['json'],
- ],
- ],
- 'indexes' => [
- [
- '$id' => ID::custom('_key_time'),
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['time'],
- 'lengths' => [],
- 'orders' => [Database::ORDER_DESC],
- ],
- ],
- ],
'realtime' => [
'$collection' => ID::custom(Database::METADATA),
'$id' => ID::custom('realtime'),
@@ -3337,63 +3335,9 @@ $collections = [
],
]
],
+ ], $commonCollections);
- 'cache' => [
- '$collection' => Database::METADATA,
- '$id' => 'cache',
- 'name' => 'Cache',
- 'attributes' => [
- [
- '$id' => 'resource',
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 255,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => 'accessedAt',
- 'type' => Database::VAR_DATETIME,
- 'format' => '',
- 'size' => 0,
- 'signed' => false,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => ['datetime'],
- ],
- [
- '$id' => 'signature',
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 255,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- ],
- 'indexes' => [
- [
- '$id' => '_key_accessedAt',
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['accessedAt'],
- 'lengths' => [],
- 'orders' => [],
- ],
- [
- '$id' => '_key_resource',
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['resource'],
- 'lengths' => [],
- 'orders' => [],
- ],
- ],
- ],
+ $bucketCollections = [
'files' => [
'$collection' => ID::custom('buckets'),
'$id' => ID::custom('files'),
@@ -3657,14 +3601,16 @@ $collections = [
],
]
],
+ ];
- 'variables' => [
- '$collection' => Database::METADATA,
- '$id' => 'variables',
- 'name' => 'variables',
+ $dbCollections = [
+ 'collections' => [
+ '$collection' => ID::custom('databases'),
+ '$id' => ID::custom('collections'),
+ 'name' => 'Collections',
'attributes' => [
[
- '$id' => 'functionInternalId',
+ '$id' => ID::custom('databaseInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
@@ -3675,37 +3621,64 @@ $collections = [
'filters' => [],
],
[
- '$id' => 'functionId',
+ '$id' => ID::custom('databaseId'),
'type' => Database::VAR_STRING,
- 'format' => '',
+ 'signed' => true,
'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
- 'filters' => [],
- ],
- [
- '$id' => 'key',
- 'type' => Database::VAR_STRING,
'format' => '',
- 'size' => Database::LENGTH_KEY,
- 'signed' => true,
- 'required' => false,
- 'default' => null,
- 'array' => false,
'filters' => [],
- ],
- [
- '$id' => 'value',
- 'type' => Database::VAR_STRING,
- 'format' => '',
- 'size' => 8192,
- 'signed' => true,
'required' => true,
'default' => null,
'array' => false,
- 'filters' => [ 'encrypt' ]
+ ],
+ [
+ '$id' => ID::custom('name'),
+ 'type' => Database::VAR_STRING,
+ 'size' => 256,
+ 'required' => true,
+ 'signed' => true,
+ 'array' => false,
+ 'filters' => [],
+ ],
+ [
+ '$id' => ID::custom('enabled'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'signed' => true,
+ 'size' => 0,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ ],
+ [
+ '$id' => ID::custom('documentSecurity'),
+ 'type' => Database::VAR_BOOLEAN,
+ 'signed' => true,
+ 'size' => 0,
+ 'format' => '',
+ 'filters' => [],
+ 'required' => true,
+ 'default' => null,
+ 'array' => false,
+ ],
+ [
+ '$id' => ID::custom('attributes'),
+ 'type' => Database::VAR_STRING,
+ 'size' => 1000000,
+ 'required' => false,
+ 'signed' => true,
+ 'array' => false,
+ 'filters' => ['subQueryAttributes'],
+ ],
+ [
+ '$id' => ID::custom('indexes'),
+ 'type' => Database::VAR_STRING,
+ 'size' => 1000000,
+ 'required' => false,
+ 'signed' => true,
+ 'array' => false,
+ 'filters' => ['subQueryIndexes'],
],
[
'$id' => ID::custom('search'),
@@ -3720,27 +3693,6 @@ $collections = [
],
],
'indexes' => [
- [
- '$id' => '_key_function',
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['functionInternalId'],
- 'lengths' => [Database::LENGTH_KEY],
- 'orders' => [Database::ORDER_ASC],
- ],
- [
- '$id' => '_key_uniqueKey',
- 'type' => Database::INDEX_UNIQUE,
- 'attributes' => ['functionInternalId', 'key'],
- 'lengths' => [Database::LENGTH_KEY, Database::LENGTH_KEY],
- 'orders' => [Database::ORDER_ASC, Database::ORDER_ASC],
- ],
- [
- '$id' => '_key_key',
- 'type' => Database::INDEX_KEY,
- 'attributes' => ['key'],
- 'lengths' => [Database::LENGTH_KEY],
- 'orders' => [Database::ORDER_ASC],
- ],
[
'$id' => ID::custom('_fulltext_search'),
'type' => Database::INDEX_FULLTEXT,
@@ -3748,8 +3700,37 @@ $collections = [
'lengths' => [],
'orders' => [],
],
+ [
+ '$id' => ID::custom('_key_name'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['name'],
+ 'lengths' => [256],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_enabled'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['enabled'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
+ [
+ '$id' => ID::custom('_key_documentSecurity'),
+ 'type' => Database::INDEX_KEY,
+ 'attributes' => ['documentSecurity'],
+ 'lengths' => [],
+ 'orders' => [Database::ORDER_ASC],
+ ],
],
],
-];
+ ];
-return $collections;
+
+ $collections = [
+ 'projects' => $projectCollections,
+ 'console' => $consoleCollections,
+ 'buckets' => $bucketCollections,
+ 'databases' => $dbCollections
+ ];
+
+ return $collections;
diff --git a/app/config/errors.php b/app/config/errors.php
index f90d1dd77..6f6b48750 100644
--- a/app/config/errors.php
+++ b/app/config/errors.php
@@ -112,7 +112,7 @@ return [
],
Exception::USER_BLOCKED => [
'name' => Exception::USER_BLOCKED,
- 'description' => 'The current user has been blocked. You can unblock the user from the Appwrite console.',
+ 'description' => 'The current user has been blocked.',
'code' => 401,
],
Exception::USER_INVALID_TOKEN => [
@@ -484,6 +484,11 @@ return [
'description' => 'Project with the requested ID could not be found. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.',
'code' => 404,
],
+ Exception::PROJECT_ALREADY_EXISTS => [
+ 'name' => Exception::PROJECT_ALREADY_EXISTS,
+ 'description' => 'Project with the requested ID already exists.',
+ 'code' => 409,
+ ],
Exception::PROJECT_UNKNOWN => [
'name' => Exception::PROJECT_UNKNOWN,
'description' => 'The project ID is either missing or not valid. Please check the value of the X-Appwrite-Project header to ensure the correct project ID is being used.',
@@ -541,9 +546,14 @@ return [
],
Exception::DOMAIN_ALREADY_EXISTS => [
'name' => Exception::DOMAIN_ALREADY_EXISTS,
- 'description' => 'A Domain with the requested ID already exists.',
+ 'description' => 'The requested domain is currently in use by a project.',
'code' => 409,
],
+ Exception::DOMAIN_FORBIDDEN => [
+ 'name' => Exception::DOMAIN_FORBIDDEN,
+ 'description' => 'The requested domain cannot be used as a custom domain.',
+ 'code' => 403,
+ ],
Exception::VARIABLE_NOT_FOUND => [
'name' => Exception::VARIABLE_NOT_FOUND,
'description' => 'Variable with the requested ID could not be found.',
diff --git a/app/controllers/api/account.php b/app/controllers/api/account.php
index b2f6f7adb..d986140bf 100644
--- a/app/controllers/api/account.php
+++ b/app/controllers/api/account.php
@@ -572,6 +572,10 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$name = $oauth2->getUserName($accessToken);
$email = $oauth2->getUserEmail($accessToken);
+ if (empty($email)) {
+ throw new Exception(Exception::USER_UNAUTHORIZED, 'OAuth provider failed to return email.');
+ }
+
/**
* Is verified is not used yet, since we don't know after an accout is created anymore if it was verified or not.
*/
@@ -1813,6 +1817,12 @@ App::patch('/v1/account/status')
$response->addHeader('X-Fallback-Cookies', \json_encode([]));
}
+ $protocol = $request->getProtocol();
+ $response
+ ->addCookie(Auth::$cookieName . '_legacy', '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
+ ->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
+ ;
+
$response->dynamic($user, Response::MODEL_ACCOUNT);
});
diff --git a/app/controllers/api/avatars.php b/app/controllers/api/avatars.php
index 19daaea20..00425a501 100644
--- a/app/controllers/api/avatars.php
+++ b/app/controllers/api/avatars.php
@@ -7,9 +7,16 @@ use Appwrite\Utopia\Response;
use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
use Utopia\App;
+use Utopia\CLI\Console;
use Utopia\Config\Config;
+use Utopia\Database\Database;
+use Utopia\Database\DateTime;
use Utopia\Database\Document;
+use Utopia\Database\Validator\Authorization;
+use Utopia\Database\Validator\UID;
use Utopia\Image\Image;
+use Utopia\Logger\Log;
+use Utopia\Logger\Logger;
use Utopia\Validator\Boolean;
use Utopia\Validator\HexColor;
use Utopia\Validator\Range;
@@ -49,11 +56,139 @@ $avatarCallback = function (string $type, string $code, int $width, int $height,
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->setContentType('image/png')
- ->file($data)
- ;
+ ->file($data);
unset($image);
};
+$getUserGitHub = function (string $userId, Document $project, Database $dbForProject, Database $dbForConsole, ?Logger $logger) {
+ try {
+ $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
+
+ $sessions = $user->getAttribute('sessions', []);
+
+ $gitHubSession = null;
+ foreach ($sessions as $session) {
+ if ($session->getAttribute('provider', '') === 'github') {
+ $gitHubSession = $session;
+ break;
+ }
+ }
+
+ if (empty($gitHubSession)) {
+ throw new Exception(Exception::GENERAL_UNKNOWN, 'GitHub session not found.');
+ }
+
+ $provider = $gitHubSession->getAttribute('provider', '');
+ $accessToken = $gitHubSession->getAttribute('providerAccessToken');
+ $accessTokenExpiry = $gitHubSession->getAttribute('providerAccessTokenExpiry');
+ $refreshToken = $gitHubSession->getAttribute('providerRefreshToken');
+
+ $appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? '';
+ $appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}';
+
+ $className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
+
+ if (!\class_exists($className)) {
+ throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
+ }
+
+ $oauth2 = new $className($appId, $appSecret, '', [], []);
+
+ $isExpired = new \DateTime($accessTokenExpiry) < new \DateTime('now');
+ if ($isExpired) {
+ try {
+ $oauth2->refreshTokens($refreshToken);
+
+ $accessToken = $oauth2->getAccessToken('');
+ $refreshToken = $oauth2->getRefreshToken('');
+
+ $verificationId = $oauth2->getUserID($accessToken);
+
+ if (empty($verificationId)) {
+ throw new \Exception("Locked tokens."); // Race codition, handeled in catch
+ }
+
+ $gitHubSession
+ ->setAttribute('providerAccessToken', $accessToken)
+ ->setAttribute('providerRefreshToken', $refreshToken)
+ ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry('')));
+
+ Authorization::skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
+
+ $dbForProject->deleteCachedDocument('users', $user->getId());
+ } catch (Throwable $err) {
+ $index = 0;
+ do {
+ $previousAccessToken = $gitHubSession->getAttribute('providerAccessToken');
+
+ $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
+ $sessions = $user->getAttribute('sessions', []);
+
+ $gitHubSession = new Document();
+ foreach ($sessions as $session) {
+ if ($session->getAttribute('provider', '') === 'github') {
+ $gitHubSession = $session;
+ break;
+ }
+ }
+
+ $accessToken = $gitHubSession->getAttribute('providerAccessToken');
+
+ if ($accessToken !== $previousAccessToken) {
+ break;
+ }
+
+ $index++;
+ \usleep(500000);
+ } while ($index < 10);
+ }
+ }
+
+ $oauth2 = new $className($appId, $appSecret, '', [], []);
+ $githubUser = $oauth2->getUserSlug($accessToken);
+ $githubId = $oauth2->getUserID($accessToken);
+
+ return [
+ 'name' => $githubUser,
+ 'id' => $githubId
+ ];
+ } catch (Exception $error) {
+ if ($logger) {
+ $version = App::getEnv('_APP_VERSION', 'UNKNOWN');
+
+ $log = new Log();
+ $log->setNamespace('console');
+ $log->setServer(\gethostname());
+ $log->setVersion($version);
+ $log->setType(Log::TYPE_ERROR);
+ $log->setMessage($error->getMessage());
+
+ $log->addTag('code', $error->getCode());
+ $log->addTag('verboseType', get_class($error));
+
+ $log->addExtra('file', $error->getFile());
+ $log->addExtra('line', $error->getLine());
+ $log->addExtra('trace', $error->getTraceAsString());
+ $log->addExtra('detailedTrace', $error->getTrace());
+
+ $log->setAction('avatarsGetGitHub');
+
+ $isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
+ $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
+
+ $responseCode = $logger->addLog($log);
+ Console::info('GitHub error log pushed with status code: ' . $responseCode);
+ }
+
+ Console::warning("Failed: {$error->getMessage()}");
+ Console::warning($error->getTraceAsString());
+
+ return [];
+ }
+
+ return [];
+};
+
App::get('/v1/avatars/credit-cards/:code')
->desc('Get Credit Card Icon')
->groups(['api', 'avatars'])
@@ -160,8 +295,7 @@ App::get('/v1/avatars/image')
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->setContentType('image/png')
- ->file($data)
- ;
+ ->file($data);
unset($image);
});
@@ -274,8 +408,7 @@ App::get('/v1/avatars/favicon')
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->setContentType('image/x-icon')
- ->file($data)
- ;
+ ->file($data);
}
$fetch = @\file_get_contents($outputHref, false);
@@ -292,8 +425,7 @@ App::get('/v1/avatars/favicon')
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + 60 * 60 * 24 * 30) . ' GMT')
->setContentType('image/png')
- ->file($data)
- ;
+ ->file($data);
unset($image);
});
@@ -334,8 +466,7 @@ App::get('/v1/avatars/qr')
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->setContentType('image/png')
- ->send($image->output('png', 9))
- ;
+ ->send($image->output('png', 9));
});
App::get('/v1/avatars/initials')
@@ -419,6 +550,680 @@ App::get('/v1/avatars/initials')
$response
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
->setContentType('image/png')
- ->file($image->getImageBlob())
- ;
+ ->file($image->getImageBlob());
+ });
+
+App::get('/v1/cards/cloud')
+ ->desc('Get Front Of Cloud Card')
+ ->groups(['api', 'avatars'])
+ ->label('scope', 'avatars.read')
+ ->label('cache', true)
+ ->label('cache.resourceType', 'cards/cloud')
+ ->label('cache.resource', 'card/{request.userId}')
+ ->label('docs', false)
+ ->label('origin', '*')
+ ->param('userId', '', new UID(), 'User ID.', true)
+ ->param('mock', '', new WhiteList(['employee', 'employee-2digit', 'hero', 'contributor', 'normal', 'platinum', 'normal-no-github', 'normal-long']), 'Mocking behaviour.', true)
+ ->param('width', 0, new Range(0, 512), 'Resize image width, Pass an integer between 0 to 512.', true)
+ ->param('height', 0, new Range(0, 320), 'Resize image height, Pass an integer between 0 to 320.', true)
+ ->inject('user')
+ ->inject('project')
+ ->inject('dbForProject')
+ ->inject('dbForConsole')
+ ->inject('response')
+ ->inject('heroes')
+ ->inject('contributors')
+ ->inject('employees')
+ ->inject('logger')
+ ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
+ $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
+
+ if ($user->isEmpty() && empty($mock)) {
+ throw new Exception(Exception::USER_NOT_FOUND);
+ }
+
+ if (!$mock) {
+ $name = $user->getAttribute('name', 'Anonymous');
+ $email = $user->getAttribute('email', '');
+ $createdAt = new \DateTime($user->getCreatedAt());
+
+ $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger);
+ $githubName = $gitHub['name'] ?? '';
+ $githubId = $gitHub['id'] ?? '';
+
+ $isHero = \array_key_exists($email, $heroes);
+ $isContributor = \in_array($githubId, $contributors);
+ $isEmployee = \array_key_exists($email, $employees);
+ $employeeNumber = $isEmployee ? $employees[$email]['spot'] : '';
+
+ if ($isHero) {
+ $createdAt = new \DateTime($heroes[$email]['memberSince'] ?? '');
+ } elseif ($isEmployee) {
+ $createdAt = new \DateTime($employees[$email]['memberSince'] ?? '');
+ }
+
+ if (!$isEmployee && !empty($githubName)) {
+ $employeeGitHub = \array_search(\strtolower($githubName), \array_map(fn ($employee) => \strtolower($employee['gitHub']) ?? '', $employees));
+ if (!empty($employeeGitHub)) {
+ $isEmployee = true;
+ $employeeNumber = $isEmployee ? $employees[$employeeGitHub]['spot'] : '';
+ $createdAt = new \DateTime($employees[$employeeGitHub]['memberSince'] ?? '');
+ }
+ }
+
+ $isPlatinum = $user->getInternalId() % 100 === 0;
+ } else {
+ $name = $mock === 'normal-long' ? 'Sir First Walter O\'Brian Junior' : 'Walter O\'Brian';
+ $createdAt = new \DateTime('now');
+ $githubName = $mock === 'normal-no-github' ? '' : ($mock === 'normal-long' ? 'sir-first-walterobrian-junior' : 'walterobrian');
+ $isHero = $mock === 'hero';
+ $isContributor = $mock === 'contributor';
+ $isEmployee = \str_starts_with($mock, 'employee');
+ $employeeNumber = match ($mock) {
+ 'employee' => '1',
+ 'employee-2digit' => '18',
+ default => ''
+ };
+
+ $isPlatinum = $mock === 'platinum';
+ }
+
+ if ($isEmployee) {
+ $isContributor = false;
+ $isHero = false;
+ }
+
+ if ($isHero) {
+ $isContributor = false;
+ $isEmployee = false;
+ }
+
+ if ($isContributor) {
+ $isHero = false;
+ $isEmployee = false;
+ }
+
+ $isGolden = $isEmployee || $isHero || $isContributor;
+ $isPlatinum = $isGolden ? false : $isPlatinum;
+ $memberSince = \strtoupper('Member since ' . $createdAt->format('M') . ' ' . $createdAt->format('d') . ', ' . $createdAt->format('o'));
+
+ $imagePath = $isGolden ? 'front-golden.png' : ($isPlatinum ? 'front-platinum.png' : 'front.png');
+
+ $baseImage = new \Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $imagePath);
+
+ if ($isEmployee) {
+ $image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/employee.png');
+ $image->setGravity(Imagick::GRAVITY_CENTER);
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 793, 35);
+
+ $text = new \ImagickDraw();
+ $text->setTextAlignment(Imagick::ALIGN_CENTER);
+ $text->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
+ $text->setFillColor(new \ImagickPixel('#FFFADF'));
+ $text->setFontSize(\strlen($employeeNumber) <= 2 ? 54 : 48);
+ $text->setFontWeight(700);
+ $metricsText = $baseImage->queryFontMetrics($text, $employeeNumber);
+
+ $hashtag = new \ImagickDraw();
+ $hashtag->setTextAlignment(Imagick::ALIGN_CENTER);
+ $hashtag->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
+ $hashtag->setFillColor(new \ImagickPixel('#FFFADF'));
+ $hashtag->setFontSize(28);
+ $hashtag->setFontWeight(700);
+ $metricsHashtag = $baseImage->queryFontMetrics($hashtag, '#');
+
+ $startX = 898;
+ $totalWidth = $metricsHashtag['textWidth'] + 12 + $metricsText['textWidth'];
+
+ $hashtagX = ($metricsHashtag['textWidth'] / 2);
+ $textX = $hashtagX + 12 + ($metricsText['textWidth'] / 2);
+
+ $hashtagX -= $totalWidth / 2;
+ $textX -= $totalWidth / 2;
+
+ $hashtagX += $startX;
+ $textX += $startX;
+
+ $baseImage->annotateImage($hashtag, $hashtagX, 150, 0, '#');
+ $baseImage->annotateImage($text, $textX, 150, 0, $employeeNumber);
+ }
+
+ if ($isContributor) {
+ $image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/contributor.png');
+ $image->setGravity(Imagick::GRAVITY_CENTER);
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 793, 34);
+ }
+
+ if ($isHero) {
+ $image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/hero.png');
+ $image->setGravity(Imagick::GRAVITY_CENTER);
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 793, 34);
+ }
+
+ setlocale(LC_ALL, "en_US.utf8");
+ // $name = \iconv("utf-8", "ascii//TRANSLIT", $name);
+ // $memberSince = \iconv("utf-8", "ascii//TRANSLIT", $memberSince);
+ // $githubName = \iconv("utf-8", "ascii//TRANSLIT", $githubName);
+
+ $text = new \ImagickDraw();
+ $text->setTextAlignment(Imagick::ALIGN_CENTER);
+ $text->setFont(__DIR__ . '/../../../public/fonts/Poppins-Bold.ttf');
+ $text->setFillColor(new \ImagickPixel('#FFFFFF'));
+
+ if (\strlen($name) > 32) {
+ $name = \substr($name, 0, 32);
+ }
+
+ if (\strlen($name) <= 23) {
+ $text->setFontSize(80);
+ $scalingDown = false;
+ } else {
+ $text->setFontSize(54);
+ $scalingDown = true;
+ }
+ $text->setFontWeight(700);
+ $baseImage->annotateImage($text, 512, 477, 0, $name);
+
+ $text = new \ImagickDraw();
+ $text->setTextAlignment(Imagick::ALIGN_CENTER);
+ $text->setFont(__DIR__ . '/../../../public/fonts/Inter-SemiBold.ttf');
+ $text->setFillColor(new \ImagickPixel($isGolden || $isPlatinum ? '#FFFFFF' : '#FFB9CC'));
+ $text->setFontSize(27);
+ $text->setFontWeight(600);
+ $text->setTextKerning(1.08);
+ $baseImage->annotateImage($text, 512, 541, 0, \strtoupper($memberSince));
+
+ if (!empty($githubName)) {
+ $text = new \ImagickDraw();
+ $text->setTextAlignment(Imagick::ALIGN_CENTER);
+ $text->setFont(__DIR__ . '/../../../public/fonts/Inter-Regular.ttf');
+ $text->setFillColor(new \ImagickPixel('#FFFFFF'));
+ $text->setFontSize($scalingDown ? 28 : 32);
+ $text->setFontWeight(400);
+ $metrics = $baseImage->queryFontMetrics($text, $githubName);
+
+ $baseImage->annotateImage($text, 512 + 20 + 4, 373 + ($scalingDown ? 2 : 0), 0, $githubName);
+
+ $image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github.png');
+ $image->setGravity(Imagick::GRAVITY_CENTER);
+ $precisionFix = 5;
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 512 - ($metrics['textWidth'] / 2) - 20 - 4, 373 - ($metrics['textHeight'] - $precisionFix));
+ }
+
+ if (!empty($width) || !empty($height)) {
+ $baseImage->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
+ }
+
+ $response
+ ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
+ ->setContentType('image/png')
+ ->file($baseImage->getImageBlob());
+ });
+
+App::get('/v1/cards/cloud-back')
+ ->desc('Get Back Of Cloud Card')
+ ->groups(['api', 'avatars'])
+ ->label('scope', 'avatars.read')
+ ->label('cache', true)
+ ->label('cache.resourceType', 'cards/cloud-back')
+ ->label('cache.resource', 'card-back/{request.userId}')
+ ->label('docs', false)
+ ->label('origin', '*')
+ ->param('userId', '', new UID(), 'User ID.', true)
+ ->param('mock', '', new WhiteList(['golden', 'normal', 'platinum']), 'Mocking behaviour.', true)
+ ->param('width', 0, new Range(0, 512), 'Resize image width, Pass an integer between 0 to 512.', true)
+ ->param('height', 0, new Range(0, 320), 'Resize image height, Pass an integer between 0 to 320.', true)
+ ->inject('user')
+ ->inject('project')
+ ->inject('dbForProject')
+ ->inject('dbForConsole')
+ ->inject('response')
+ ->inject('heroes')
+ ->inject('contributors')
+ ->inject('employees')
+ ->inject('logger')
+ ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
+ $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
+
+ if ($user->isEmpty() && empty($mock)) {
+ throw new Exception(Exception::USER_NOT_FOUND);
+ }
+
+ if (!$mock) {
+ $userId = $user->getId();
+ $email = $user->getAttribute('email', '');
+
+ $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger);
+ $githubId = $gitHub['id'] ?? '';
+
+ $isHero = \array_key_exists($email, $heroes);
+ $isContributor = \in_array($githubId, $contributors);
+ $isEmployee = \array_key_exists($email, $employees);
+
+ $isGolden = $isEmployee || $isHero || $isContributor;
+ $isPlatinum = $user->getInternalId() % 100 === 0;
+ } else {
+ $userId = '63e0bcf3c3eb803ba530';
+
+ $isGolden = $mock === 'golden';
+ $isPlatinum = $mock === 'platinum';
+ }
+
+ $userId = 'UID ' . $userId;
+
+ $isPlatinum = $isGolden ? false : $isPlatinum;
+
+ $imagePath = $isGolden ? 'back-golden.png' : ($isPlatinum ? 'back-platinum.png' : 'back.png');
+
+ $baseImage = new \Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $imagePath);
+
+ setlocale(LC_ALL, "en_US.utf8");
+ // $userId = \iconv("utf-8", "ascii//TRANSLIT", $userId);
+
+ $text = new \ImagickDraw();
+ $text->setTextAlignment(Imagick::ALIGN_CENTER);
+ $text->setFont(__DIR__ . '/../../../public/fonts/SourceCodePro-Regular.ttf');
+ $text->setFillColor(new \ImagickPixel($isGolden ? '#664A1E' : ($isPlatinum ? '#555555' : '#E8E9F0')));
+ $text->setFontSize(28);
+ $text->setFontWeight(400);
+ $baseImage->annotateImage($text, 512, 596, 0, $userId);
+
+ if (!empty($width) || !empty($height)) {
+ $baseImage->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
+ }
+
+ $response
+ ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
+ ->setContentType('image/png')
+ ->file($baseImage->getImageBlob());
+ });
+
+App::get('/v1/cards/cloud-og')
+ ->desc('Get OG Image From Cloud Card')
+ ->groups(['api', 'avatars'])
+ ->label('scope', 'avatars.read')
+ ->label('cache', true)
+ ->label('cache.resourceType', 'cards/cloud-og')
+ ->label('cache.resource', 'card-og/{request.userId}')
+ ->label('docs', false)
+ ->label('origin', '*')
+ ->param('userId', '', new UID(), 'User ID.', true)
+ ->param('mock', '', new WhiteList(['employee', 'employee-2digit', 'hero', 'contributor', 'normal', 'platinum', 'normal-no-github', 'normal-long', 'normal-long-right', 'normal-long-middle', 'normal-bg2', 'normal-bg3', 'normal-right', 'normal-middle', 'platinum-right', 'platinum-middle', 'hero-middle', 'hero-right', 'contributor-right', 'employee-right', 'contributor-middle', 'employee-middle', 'employee-2digit-middle', 'employee-2digit-right']), 'Mocking behaviour.', true)
+ ->param('width', 0, new Range(0, 1024), 'Resize image card width, Pass an integer between 0 to 1024.', true)
+ ->param('height', 0, new Range(0, 1024), 'Resize image card height, Pass an integer between 0 to 1024.', true)
+ ->inject('user')
+ ->inject('project')
+ ->inject('dbForProject')
+ ->inject('dbForConsole')
+ ->inject('response')
+ ->inject('heroes')
+ ->inject('contributors')
+ ->inject('employees')
+ ->inject('logger')
+ ->action(function (string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForConsole, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger) use ($getUserGitHub) {
+ $user = Authorization::skip(fn () => $dbForConsole->getDocument('users', $userId));
+
+ if ($user->isEmpty() && empty($mock)) {
+ throw new Exception(Exception::USER_NOT_FOUND);
+ }
+
+ if (!$mock) {
+ $internalId = $user->getInternalId();
+ $bgVariation = $internalId % 3 === 0 ? '1' : ($internalId % 3 === 1 ? '2' : '3');
+ $cardVariation = $internalId % 3 === 0 ? '1' : ($internalId % 3 === 1 ? '2' : '3');
+
+ $name = $user->getAttribute('name', 'Anonymous');
+ $email = $user->getAttribute('email', '');
+ $createdAt = new \DateTime($user->getCreatedAt());
+
+ $gitHub = $getUserGitHub($user->getId(), $project, $dbForProject, $dbForConsole, $logger);
+ $githubName = $gitHub['name'] ?? '';
+ $githubId = $gitHub['id'] ?? '';
+
+ $isHero = \array_key_exists($email, $heroes);
+ $isContributor = \in_array($githubId, $contributors);
+ $isEmployee = \array_key_exists($email, $employees);
+ $employeeNumber = $isEmployee ? $employees[$email]['spot'] : '';
+
+ if ($isHero) {
+ $createdAt = new \DateTime($heroes[$email]['memberSince'] ?? '');
+ } elseif ($isEmployee) {
+ $createdAt = new \DateTime($employees[$email]['memberSince'] ?? '');
+ }
+
+ if (!$isEmployee && !empty($githubName)) {
+ $employeeGitHub = \array_search(\strtolower($githubName), \array_map(fn ($employee) => \strtolower($employee['gitHub']) ?? '', $employees));
+ if (!empty($employeeGitHub)) {
+ $isEmployee = true;
+ $employeeNumber = $isEmployee ? $employees[$employeeGitHub]['spot'] : '';
+ $createdAt = new \DateTime($employees[$employeeGitHub]['memberSince'] ?? '');
+ }
+ }
+
+ $isPlatinum = $user->getInternalId() % 100 === 0;
+ } else {
+ $bgVariation = \str_ends_with($mock, '-bg2') ? '2' : (\str_ends_with($mock, '-bg3') ? '3' : '1');
+ $cardVariation = \str_ends_with($mock, '-right') ? '2' : (\str_ends_with($mock, '-middle') ? '3' : '1');
+ $name = \str_starts_with($mock, 'normal-long') ? 'Sir First Walter O\'Brian Junior' : 'Walter O\'Brian';
+ $createdAt = new \DateTime('now');
+ $githubName = $mock === 'normal-no-github' ? '' : (\str_starts_with($mock, 'normal-long') ? 'sir-first-walterobrian-junior' : 'walterobrian');
+ $isHero = \str_starts_with($mock, 'hero');
+ $isContributor = \str_starts_with($mock, 'contributor');
+ $isEmployee = \str_starts_with($mock, 'employee');
+ $employeeNumber = match ($mock) {
+ 'employee' => '1',
+ 'employee-right' => '1',
+ 'employee-middle' => '1',
+ 'employee-2digit' => '18',
+ 'employee-2digit-right' => '18',
+ 'employee-2digit-middle' => '18',
+ default => ''
+ };
+
+ $isPlatinum = \str_starts_with($mock, 'platinum');
+ }
+
+ if ($isEmployee) {
+ $isContributor = false;
+ $isHero = false;
+ }
+
+ if ($isHero) {
+ $isContributor = false;
+ $isEmployee = false;
+ }
+
+ if ($isContributor) {
+ $isHero = false;
+ $isEmployee = false;
+ }
+
+ $isGolden = $isEmployee || $isHero || $isContributor;
+ $isPlatinum = $isGolden ? false : $isPlatinum;
+ $memberSince = \strtoupper('Member since ' . $createdAt->format('M') . ' ' . $createdAt->format('d') . ', ' . $createdAt->format('o'));
+
+ $baseImage = new \Imagick(__DIR__ . "/../../../public/images/cards/cloud/og-background{$bgVariation}.png");
+
+ $cardType = $isGolden ? '-golden' : ($isPlatinum ? '-platinum' : '');
+
+ $image = new Imagick(__DIR__ . "/../../../public/images/cards/cloud/og-card{$cardType}{$cardVariation}.png");
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 1008 / 2 - $image->getImageWidth() / 2, 1008 / 2 - $image->getImageHeight() / 2);
+
+ $imageLogo = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/og-background-logo.png');
+ $imageShadow = new Imagick(__DIR__ . "/../../../public/images/cards/cloud/og-shadow{$cardType}.png");
+ if ($cardVariation === '1') {
+ $baseImage->compositeImage($imageLogo, Imagick::COMPOSITE_OVER, 32, 1008 - $imageLogo->getImageHeight() - 32);
+ $baseImage->compositeImage($imageShadow, Imagick::COMPOSITE_OVER, -450, 700);
+ } elseif ($cardVariation === '2') {
+ $baseImage->compositeImage($imageLogo, Imagick::COMPOSITE_OVER, 1008 - $imageLogo->getImageWidth() - 32, 1008 - $imageLogo->getImageHeight() - 32);
+ $baseImage->compositeImage($imageShadow, Imagick::COMPOSITE_OVER, -20, 710);
+ } else {
+ $baseImage->compositeImage($imageLogo, Imagick::COMPOSITE_OVER, 1008 - $imageLogo->getImageWidth() - 32, 1008 - $imageLogo->getImageHeight() - 32);
+ $baseImage->compositeImage($imageShadow, Imagick::COMPOSITE_OVER, -135, 710);
+ }
+
+ if ($isEmployee) {
+ $file = $cardVariation === '3' ? 'employee-skew.png' : 'employee.png';
+ $image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $file);
+ $image->setGravity(Imagick::GRAVITY_CENTER);
+
+ $hashtag = new \ImagickDraw();
+ $hashtag->setTextAlignment(Imagick::ALIGN_LEFT);
+ $hashtag->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
+ $hashtag->setFillColor(new \ImagickPixel('#FFFADF'));
+ $hashtag->setFontSize(20);
+ $hashtag->setFontWeight(700);
+
+ $text = new \ImagickDraw();
+ $text->setTextAlignment(Imagick::ALIGN_LEFT);
+ $text->setFont(__DIR__ . '/../../../public/fonts/Inter-Bold.ttf');
+ $text->setFillColor(new \ImagickPixel('#FFFADF'));
+ $text->setFontSize(\strlen($employeeNumber) <= 1 ? 36 : 28);
+ $text->setFontWeight(700);
+
+ if ($cardVariation === '3') {
+ $hashtag->setFontSize(16);
+ $text->setFontSize(\strlen($employeeNumber) <= 1 ? 30 : 26);
+
+ $hashtag->skewY(20);
+ $hashtag->skewX(20);
+ $text->skewY(20);
+ $text->skewX(20);
+ }
+
+ $metricsHashtag = $baseImage->queryFontMetrics($hashtag, '#');
+ $metricsText = $baseImage->queryFontMetrics($text, $employeeNumber);
+
+ $group = new Imagick();
+ $groupWidth = $metricsHashtag['textWidth'] + 6 + $metricsText['textWidth'];
+
+ if ($cardVariation === '1') {
+ $group->newImage($groupWidth, $metricsText['textHeight'], '#00000000');
+ $group->annotateImage($hashtag, 0, $metricsText['textHeight'], 0, '#');
+ $group->annotateImage($text, $metricsHashtag['textWidth'] + 6, $metricsText['textHeight'], 0, $employeeNumber);
+
+ $image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
+ $image->rotateImage(new ImagickPixel('#00000000'), -20);
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 612, 203);
+
+ $group->rotateImage(new ImagickPixel('#00000000'), -22);
+
+ if (\strlen($employeeNumber) <= 1) {
+ $baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 660, 245);
+ } else {
+ $baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 655, 247);
+ }
+ } elseif ($cardVariation === '2') {
+ $group->newImage($groupWidth, $metricsText['textHeight'], '#00000000');
+ $group->annotateImage($hashtag, 0, $metricsText['textHeight'], 0, '#');
+ $group->annotateImage($text, $metricsHashtag['textWidth'] + 6, $metricsText['textHeight'], 0, $employeeNumber);
+
+ $image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
+ $image->rotateImage(new ImagickPixel('#00000000'), 30);
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 715, 425);
+
+ $group->rotateImage(new ImagickPixel('#00000000'), 32);
+
+ if (\strlen($employeeNumber) <= 1) {
+ $baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 775, 465);
+ } else {
+ $baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 767, 470);
+ }
+ } else {
+ $group->newImage(300, 300, '#00000000');
+
+ $hashtag->annotation(0, $metricsText['textHeight'], '#');
+ $text->annotation($metricsHashtag['textWidth'] + 2, $metricsText['textHeight'], $employeeNumber);
+
+ $group->drawImage($hashtag);
+ $group->drawImage($text);
+
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 640, 293);
+
+ if (\strlen($employeeNumber) <= 1) {
+ $baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 670, 317);
+ } else {
+ $baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, 663, 322);
+ }
+ }
+ }
+
+ if ($isContributor) {
+ $file = $cardVariation === '3' ? 'contributor-skew.png' : 'contributor.png';
+ $image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $file);
+ $image->setGravity(Imagick::GRAVITY_CENTER);
+
+ if ($cardVariation === '1') {
+ $image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
+ $image->rotateImage(new ImagickPixel('#00000000'), -20);
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 612, 203);
+ } elseif ($cardVariation === '2') {
+ $image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
+ $image->rotateImage(new ImagickPixel('#00000000'), 30);
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 715, 425);
+ } else {
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 640, 293);
+ }
+ }
+
+ if ($isHero) {
+ $file = $cardVariation === '3' ? 'hero-skew.png' : 'hero.png';
+ $image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/' . $file);
+ $image->setGravity(Imagick::GRAVITY_CENTER);
+
+ if ($cardVariation === '1') {
+ $image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
+ $image->rotateImage(new ImagickPixel('#00000000'), -20);
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 612, 203);
+ } elseif ($cardVariation === '2') {
+ $image->resizeImage(120, 120, Imagick::FILTER_LANCZOS, 1);
+ $image->rotateImage(new ImagickPixel('#00000000'), 30);
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 715, 425);
+ } else {
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 640, 293);
+ }
+ }
+
+ setlocale(LC_ALL, "en_US.utf8");
+ // $name = \iconv("utf-8", "ascii//TRANSLIT", $name);
+ // $memberSince = \iconv("utf-8", "ascii//TRANSLIT", $memberSince);
+ // $githubName = \iconv("utf-8", "ascii//TRANSLIT", $githubName);
+
+ $textName = new \ImagickDraw();
+ $textName->setTextAlignment(Imagick::ALIGN_CENTER);
+ $textName->setFont(__DIR__ . '/../../../public/fonts/Poppins-Bold.ttf');
+ $textName->setFillColor(new \ImagickPixel('#FFFFFF'));
+
+ if (\strlen($name) > 32) {
+ $name = \substr($name, 0, 32);
+ }
+
+ if ($cardVariation === '1') {
+ if (\strlen($name) <= 23) {
+ $scalingDown = false;
+ $textName->setFontSize(54);
+ } else {
+ $scalingDown = true;
+ $textName->setFontSize(36);
+ }
+ } elseif ($cardVariation === '2') {
+ if (\strlen($name) <= 23) {
+ $scalingDown = false;
+ $textName->setFontSize(50);
+ } else {
+ $scalingDown = true;
+ $textName->setFontSize(34);
+ }
+ } else {
+ if (\strlen($name) <= 23) {
+ $scalingDown = false;
+ $textName->setFontSize(44);
+ } else {
+ $scalingDown = true;
+ $textName->setFontSize(32);
+ }
+ }
+
+ $textName->setFontWeight(700);
+
+ $textMember = new \ImagickDraw();
+ $textMember->setTextAlignment(Imagick::ALIGN_CENTER);
+ $textMember->setFont(__DIR__ . '/../../../public/fonts/Inter-Medium.ttf');
+ $textMember->setFillColor(new \ImagickPixel($isGolden || $isPlatinum ? '#FFFFFF' : '#FFB9CC'));
+ $textMember->setFontWeight(500);
+ $textMember->setTextKerning(1.12);
+
+ if ($cardVariation === '1') {
+ $textMember->setFontSize(21);
+
+ $baseImage->annotateImage($textName, 550, 600, -22, $name);
+ $baseImage->annotateImage($textMember, 585, 635, -22, $memberSince);
+ } elseif ($cardVariation === '2') {
+ $textMember->setFontSize(20);
+
+ $baseImage->annotateImage($textName, 435, 590, 31.37, $name);
+ $baseImage->annotateImage($textMember, 412, 628, 31.37, $memberSince);
+ } else {
+ $textMember->setFontSize(16);
+
+ $textName->skewY(20);
+ $textName->skewX(20);
+ $textName->annotation(320, 700, $name);
+
+ $textMember->skewY(20);
+ $textMember->skewX(20);
+ $textMember->annotation(330, 735, $memberSince);
+
+ $baseImage->drawImage($textName);
+ $baseImage->drawImage($textMember);
+ }
+
+ if (!empty($githubName)) {
+ $text = new \ImagickDraw();
+ $text->setTextAlignment(Imagick::ALIGN_LEFT);
+ $text->setFont(__DIR__ . '/../../../public/fonts/Inter-Regular.ttf');
+ $text->setFillColor(new \ImagickPixel('#FFFFFF'));
+ $text->setFontSize($scalingDown ? 16 : 20);
+ $text->setFontWeight(400);
+
+ if ($cardVariation === '1') {
+ $metrics = $baseImage->queryFontMetrics($text, $githubName);
+
+ $group = new Imagick();
+ $groupWidth = $metrics['textWidth'] + 32 + 4;
+ $group->newImage($groupWidth, $metrics['textHeight'] + 10, '#00000000');
+ $image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github.png');
+ $image->setGravity(Imagick::GRAVITY_CENTER);
+ $image->resizeImage(32, 32, Imagick::FILTER_LANCZOS, 1);
+ $precisionFix = -1;
+
+ $group->compositeImage($image, Imagick::COMPOSITE_OVER, 0, 0);
+ $group->annotateImage($text, 32 + 4, $metrics['textHeight'] - $precisionFix, 0, $githubName);
+
+ $group->rotateImage(new ImagickPixel('#00000000'), -22);
+ $x = 510 - $group->getImageWidth() / 2;
+ $y = 530 - $group->getImageHeight() / 2;
+ $baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, $x, $y);
+ } elseif ($cardVariation === '2') {
+ $metrics = $baseImage->queryFontMetrics($text, $githubName);
+
+ $group = new Imagick();
+ $groupWidth = $metrics['textWidth'] + 32 + 4;
+ $group->newImage($groupWidth, $metrics['textHeight'] + 10, '#00000000');
+ $image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github.png');
+ $image->setGravity(Imagick::GRAVITY_CENTER);
+ $image->resizeImage(32, 32, Imagick::FILTER_LANCZOS, 1);
+ $precisionFix = -1;
+
+ $group->compositeImage($image, Imagick::COMPOSITE_OVER, 0, 0);
+ $group->annotateImage($text, 32 + 4, $metrics['textHeight'] - $precisionFix, 0, $githubName);
+
+ $group->rotateImage(new ImagickPixel('#00000000'), 31.11);
+ $x = 485 - $group->getImageWidth() / 2;
+ $y = 530 - $group->getImageHeight() / 2;
+ $baseImage->compositeImage($group, Imagick::COMPOSITE_OVER, $x, $y);
+ } else {
+ $text->skewY(20);
+ $text->skewX(20);
+ $text->setTextAlignment(\Imagick::ALIGN_CENTER);
+
+ $text->annotation(320 + 15 + 2, 640, $githubName);
+ $metrics = $baseImage->queryFontMetrics($text, $githubName);
+
+ $image = new Imagick(__DIR__ . '/../../../public/images/cards/cloud/github-skew.png');
+ $image->setGravity(Imagick::GRAVITY_CENTER);
+ $baseImage->compositeImage($image, Imagick::COMPOSITE_OVER, 512 - ($metrics['textWidth'] / 2), 518 + \strlen($githubName) * 1.3);
+
+ $baseImage->drawImage($text);
+ }
+ }
+
+ if (!empty($width) || !empty($height)) {
+ $baseImage->resizeImage($width, $height, Imagick::FILTER_LANCZOS, 1);
+ }
+
+ $response
+ ->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
+ ->setContentType('image/png')
+ ->file($baseImage->getImageBlob());
});
diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php
index 4601baf03..4c243c4c8 100644
--- a/app/controllers/api/databases.php
+++ b/app/controllers/api/databases.php
@@ -176,7 +176,7 @@ App::post('/v1/databases')
]));
$database = $dbForProject->getDocument('databases', $databaseId);
- $collections = Config::getParam('collections', [])['collections'] ?? [];
+ $collections = (Config::getParam('collections', [])['databases'] ?? [])['collections'] ?? [];
if (empty($collections)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'The "collections" collection is not configured.');
}
diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php
index 30bae50ac..d2f6fc6c0 100644
--- a/app/controllers/api/projects.php
+++ b/app/controllers/api/projects.php
@@ -18,17 +18,18 @@ use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
-use Utopia\Database\DateTime;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Query;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\DatetimeValidator;
+use Utopia\Database\DateTime;
use Utopia\Database\Validator\UID;
use Utopia\Domains\Domain;
use Appwrite\Extend\Exception;
use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Utopia\Cache\Cache;
use Utopia\Pools\Group;
+use Utopia\Database\Exception\Duplicate;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\Hostname;
@@ -88,46 +89,81 @@ App::post('/v1/projects')
}
$projectId = ($projectId == 'unique()') ? ID::unique() : $projectId;
+
+ $backups['database_db_fra1_02'] = ['from' => '7:30', 'to' => '8:15'];
+ $backups['database_db_fra1_03'] = ['from' => '10:30', 'to' => '11:15'];
+ $backups['database_db_fra1_04'] = ['from' => '13:30', 'to' => '14:15'];
+ $backups['database_db_fra1_05'] = ['from' => '4:30', 'to' => '5:15'];
+
$databases = Config::getParam('pools-database', []);
- $database = $databases[array_rand($databases)];
+
+ /**
+ * Extract db from list while backing
+ */
+ if (count($databases) > 1) {
+ $now = new \DateTime();
+
+ foreach ($databases as $index => $database) {
+ if (empty($backups[$database])) {
+ continue;
+ }
+ $backup = $backups[$database];
+ $from = \DateTime::createFromFormat('H:i', $backup['from']);
+ $to = \DateTime::createFromFormat('H:i', $backup['to']);
+ if ($now >= $from && $now <= $to) {
+ unset($databases[$index]);
+ break;
+ }
+ }
+ }
+
+ if ($index = array_search('database_db_fra1_05', $databases)) {
+ $database = $databases[$index];
+ } else {
+ $database = $databases[array_rand($databases)];
+ }
if ($projectId === 'console') {
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
}
- $project = $dbForConsole->createDocument('projects', new Document([
- '$id' => $projectId,
- '$permissions' => [
- Permission::read(Role::team(ID::custom($teamId))),
- Permission::update(Role::team(ID::custom($teamId), 'owner')),
- Permission::update(Role::team(ID::custom($teamId), 'developer')),
- Permission::delete(Role::team(ID::custom($teamId), 'owner')),
- Permission::delete(Role::team(ID::custom($teamId), 'developer')),
- ],
- 'name' => $name,
- 'teamInternalId' => $team->getInternalId(),
- 'teamId' => $team->getId(),
- 'region' => $region,
- 'description' => $description,
- 'logo' => $logo,
- 'url' => $url,
- 'version' => APP_VERSION_STABLE,
- 'legalName' => $legalName,
- 'legalCountry' => $legalCountry,
- 'legalState' => $legalState,
- 'legalCity' => $legalCity,
- 'legalAddress' => $legalAddress,
- 'legalTaxId' => ID::custom($legalTaxId),
- 'services' => new stdClass(),
- 'platforms' => null,
- 'authProviders' => [],
- 'webhooks' => null,
- 'keys' => null,
- 'domains' => null,
- 'auths' => $auths,
- 'search' => implode(' ', [$projectId, $name]),
- 'database' => $database,
- ]));
+ try {
+ $project = $dbForConsole->createDocument('projects', new Document([
+ '$id' => $projectId,
+ '$permissions' => [
+ Permission::read(Role::team(ID::custom($teamId))),
+ Permission::update(Role::team(ID::custom($teamId), 'owner')),
+ Permission::update(Role::team(ID::custom($teamId), 'developer')),
+ Permission::delete(Role::team(ID::custom($teamId), 'owner')),
+ Permission::delete(Role::team(ID::custom($teamId), 'developer')),
+ ],
+ 'name' => $name,
+ 'teamInternalId' => $team->getInternalId(),
+ 'teamId' => $team->getId(),
+ 'region' => $region,
+ 'description' => $description,
+ 'logo' => $logo,
+ 'url' => $url,
+ 'version' => APP_VERSION_STABLE,
+ 'legalName' => $legalName,
+ 'legalCountry' => $legalCountry,
+ 'legalState' => $legalState,
+ 'legalCity' => $legalCity,
+ 'legalAddress' => $legalAddress,
+ 'legalTaxId' => ID::custom($legalTaxId),
+ 'services' => new stdClass(),
+ 'platforms' => null,
+ 'authProviders' => [],
+ 'webhooks' => null,
+ 'keys' => null,
+ 'domains' => null,
+ 'auths' => $auths,
+ 'search' => implode(' ', [$projectId, $name]),
+ 'database' => $database
+ ]));
+ } catch (Duplicate $th) {
+ throw new Exception(Exception::PROJECT_ALREADY_EXISTS);
+ }
$dbForProject = new Database($pools->get($database)->pop()->getResource(), $cache);
$dbForProject->setNamespace("_{$project->getInternalId()}");
@@ -140,7 +176,7 @@ App::post('/v1/projects')
$adapter->setup();
/** @var array $collections */
- $collections = Config::getParam('collections', []);
+ $collections = Config::getParam('collections', [])['projects'] ?? [];
foreach ($collections as $key => $collection) {
if (($collection['$collection'] ?? '') !== Database::METADATA) {
@@ -1221,9 +1257,12 @@ App::post('/v1/projects/:projectId/domains')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
+ if ($domain === App::getEnv('_APP_DOMAIN', '') || $domain === App::getEnv('_APP_DOMAIN_TARGET', '')) {
+ throw new Exception(Exception::DOMAIN_FORBIDDEN);
+ }
+
$document = $dbForConsole->findOne('domains', [
- Query::equal('domain', [$domain]),
- Query::equal('projectInternalId', [$project->getInternalId()]),
+ Query::equal('domain', [$domain])
]);
if ($document && !$document->isEmpty()) {
@@ -1421,6 +1460,10 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
throw new Exception(Exception::DOMAIN_NOT_FOUND);
}
+ if ($domain->getAttribute('domain') === App::getEnv('_APP_DOMAIN', '') || $domain->getAttribute('domain') === App::getEnv('_APP_DOMAIN_TARGET', '')) {
+ throw new Exception(Exception::DOMAIN_FORBIDDEN);
+ }
+
$dbForConsole->deleteDocument('domains', $domain->getId());
$dbForConsole->deleteCachedDocument('projects', $project->getId());
diff --git a/app/controllers/api/storage.php b/app/controllers/api/storage.php
index ab5febf8f..e545fcb2a 100644
--- a/app/controllers/api/storage.php
+++ b/app/controllers/api/storage.php
@@ -79,7 +79,7 @@ App::post('/v1/storage/buckets')
$permissions = Permission::aggregate($permissions);
try {
- $files = Config::getParam('collections', [])['files'] ?? [];
+ $files = (Config::getParam('collections', [])['buckets'] ?? [])['files'] ?? [];
if (empty($files)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Files collection is not configured.');
}
@@ -352,8 +352,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
->inject('mode')
->inject('deviceFiles')
->inject('deviceLocal')
- ->inject('deletes')
- ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal, Delete $deletes) {
+ ->action(function (string $bucketId, string $fileId, mixed $file, ?array $permissions, Request $request, Response $response, Database $dbForProject, Document $user, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
@@ -649,11 +648,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
->setContext('bucket', $bucket)
;
- $deletes
- ->setType(DELETE_TYPE_CACHE_BY_RESOURCE)
- ->setResource('file/' . $file->getId())
- ;
-
$metadata = null; // was causing leaks as it was passed by reference
$response
diff --git a/app/controllers/general.php b/app/controllers/general.php
index afb9b9f33..365995990 100644
--- a/app/controllers/general.php
+++ b/app/controllers/general.php
@@ -425,6 +425,7 @@ App::error()
$log->setType(Log::TYPE_ERROR);
$log->setMessage($error->getMessage());
+ $log->addTag('database', $project->getAttribute('database', 'console'));
$log->addTag('method', $route->getMethod());
$log->addTag('url', $route->getPath());
$log->addTag('verboseType', get_class($error));
@@ -437,7 +438,7 @@ App::error()
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('detailedTrace', $error->getTrace());
- $log->addExtra('roles', Authorization::$roles);
+ $log->addExtra('roles', Authorization::getRoles());
$action = $route->getLabel("sdk.namespace", "UNKNOWN_NAMESPACE") . '.' . $route->getLabel("sdk.method", "UNKNOWN_METHOD");
$log->setAction($action);
@@ -580,7 +581,7 @@ App::get('/humans.txt')
$response->text($template->render(false));
});
-App::get('/.well-known/acme-challenge')
+App::get('/.well-known/acme-challenge/*')
->desc('SSL Verification')
->label('scope', 'public')
->label('docs', false)
diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php
index 9fe0574df..566a67a0f 100644
--- a/app/controllers/shared/api.php
+++ b/app/controllers/shared/api.php
@@ -549,7 +549,7 @@ App::shutdown()
]) ;
$signature = md5($data);
- $cacheLog = $dbForProject->getDocument('cache', $key);
+ $cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$accessedAt = $cacheLog->getAttribute('accessedAt', '');
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
diff --git a/app/controllers/web/console.php b/app/controllers/web/console.php
index 8115f0928..571049d9e 100644
--- a/app/controllers/web/console.php
+++ b/app/controllers/web/console.php
@@ -1,19 +1,61 @@
alias('/')
+ ->alias('auth/*')
->alias('/invite')
->alias('/login')
->alias('/recover')
- ->alias('/register')
+ ->alias('/register/*')
->groups(['web'])
->label('permission', 'public')
->label('scope', 'home')
+ ->inject('request')
->inject('response')
- ->action(function (Response $response) {
+ ->action(function (Request $request, Response $response) {
$fallback = file_get_contents(__DIR__ . '/../../../console/index.html');
+
+ // Card SSR
+ if (\str_starts_with($request->getURI(), '/card')) {
+ $urlCunks = \explode('/', $request->getURI());
+ $userId = $urlCunks[\count($urlCunks) - 1] ?? '';
+
+ $domain = $request->getProtocol() . '://' . $request->getHostname();
+
+ if (!empty($userId)) {
+ $ogImageUrl = $domain . '/v1/cards/cloud-og?userId=' . $userId;
+ } else {
+ $ogImageUrl = $domain . '/v1/cards/cloud-og?mock=normal';
+ }
+
+ $ogTags = [
+ '
Appwrite Cloud Card',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ '',
+ ];
+
+ $fallback = \str_replace('', \implode('', $ogTags), $fallback);
+ }
+
$response->html($fallback);
});
diff --git a/app/http.php b/app/http.php
index b1ed21b8d..8be7f88e8 100644
--- a/app/http.php
+++ b/app/http.php
@@ -85,10 +85,8 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
Console::success('[Setup] - Server database init started...');
- /** @var array $collections */
- $collections = Config::getParam('collections', []);
-
try {
+ $cache = $app->getResource('cache'); /** @var Utopia\Cache\Cache $cache */
Console::success('[Setup] - Creating database: appwrite...');
$dbForConsole->create();
} catch (\Exception $e) {
@@ -105,7 +103,10 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
$adapter->setup();
}
- foreach ($collections as $key => $collection) {
+ /** @var array $collections */
+ $collections = Config::getParam('collections', []);
+ $consoleCollections = $collections['console'];
+ foreach ($consoleCollections as $key => $collection) {
if (($collection['$collection'] ?? '') !== Database::METADATA) {
continue;
}
@@ -177,7 +178,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
$bucket = $dbForConsole->getDocument('buckets', 'default');
Console::success('[Setup] - Creating files collection for default bucket...');
- $files = $collections['files'] ?? [];
+ $files = $collections['buckets']['files'] ?? [];
if (empty($files)) {
throw new Exception('Files collection is not configured.');
}
diff --git a/app/init.php b/app/init.php
index f799ec41e..ee0f35b3b 100644
--- a/app/init.php
+++ b/app/init.php
@@ -1053,6 +1053,11 @@ App::setResource('console', function () {
],
'authWhitelistEmails' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
'authWhitelistIPs' => (!empty(App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
+ 'authProviders' => [
+ 'githubEnabled' => true,
+ 'githubSecret' => App::getEnv('_APP_CONSOLE_GITHUB_SECRET', ''),
+ 'githubAppid' => App::getEnv('_APP_CONSOLE_GITHUB_APP_ID', '')
+ ],
]);
}, []);
@@ -1289,3 +1294,21 @@ App::setResource('schema', function ($utopia, $dbForProject) {
$params,
);
}, ['utopia', 'dbForProject']);
+
+App::setResource('contributors', function () {
+ $path = 'app/config/cloud/contributors.json';
+ $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
+ return $list;
+}, []);
+
+App::setResource('employees', function () {
+ $path = 'app/config/cloud/employees.json';
+ $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
+ return $list;
+}, []);
+
+App::setResource('heroes', function () {
+ $path = 'app/config/cloud/heroes.json';
+ $list = (file_exists($path)) ? json_decode(file_get_contents($path), true) : [];
+ return $list;
+}, []);
diff --git a/app/views/general/error.phtml b/app/views/general/error.phtml
index d5a11246f..69858f29e 100644
--- a/app/views/general/error.phtml
+++ b/app/views/general/error.phtml
@@ -17,49 +17,96 @@ $title = $this->getParam('title', '')
+
+
+
+
+
+
-
+
+
+
+
+
Error
+
+
Error ID:
+
+
+
Back to
+
-
- Error
-
-
-
- Error ID:
-
-
-
-
- Back to
-
-
-
-
-
-
Error Trace
-
-
-
- $value) : ?>
-
- |
-
-
-
-
-
-
- |
-
+
+
+
Error Trace
+
+
+ $value) : ?>
+
+ |
+
+
+
+
+
+
+ |
+
+
+
-
-
-
-
+
+
+
+
+
+
+
Powered by
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/worker.php b/app/worker.php
index 78671d731..6e0f689a4 100644
--- a/app/worker.php
+++ b/app/worker.php
@@ -126,16 +126,15 @@ $server
->error()
->inject('error')
->inject('logger')
- ->action(function (Throwable $error, Logger $logger) {
+ ->inject('log')
+ ->action(function (Throwable $error, Logger $logger, Log $log) {
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
if ($error instanceof PDOException) {
throw $error;
}
- if ($error->getCode() >= 500 || $error->getCode() === 0) {
- $log = new Log();
-
+ if ($logger && ($error->getCode() >= 500 || $error->getCode() === 0)) {
$log->setNamespace("appwrite-worker");
$log->setServer(\gethostname());
$log->setVersion($version);
@@ -153,7 +152,8 @@ $server
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
- $logger->addLog($log);
+ $responseCode = $logger->addLog($log);
+ Console::info('Usage stats log pushed with status code: ' . $responseCode);
}
Console::error('[Error] Type: ' . get_class($error));
diff --git a/app/workers/deletes.php b/app/workers/deletes.php
index 1f338087d..acc8b9cec 100644
--- a/app/workers/deletes.php
+++ b/app/workers/deletes.php
@@ -113,10 +113,10 @@ class DeletesV1 extends Worker
break;
case DELETE_TYPE_CACHE_BY_RESOURCE:
- $this->deleteCacheByResource($this->args['resource']);
+ $this->deleteCacheByResource($project, $this->args['resource']);
break;
case DELETE_TYPE_CACHE_BY_TIMESTAMP:
- $this->deleteCacheByDate();
+ $this->deleteCacheByDate($this->args['datetime']);
break;
case DELETE_TYPE_SCHEDULES:
$this->deleteSchedules($this->args['datetime']);
@@ -164,32 +164,54 @@ class DeletesV1 extends Worker
}
/**
+ * @param Document $project
* @param string $resource
+ * @throws Exception
*/
- protected function deleteCacheByResource(string $resource): void
+ protected function deleteCacheByResource(Document $project, string $resource): void
{
- $this->deleteCacheFiles([
- Query::equal('resource', [$resource]),
- ]);
+ $projectId = $project->getId();
+ $dbForProject = $this->getProjectDB($project);
+ $document = $dbForProject->findOne('cache', [Query::equal('resource', [$resource])]);
+
+ if ($document) {
+ $cache = new Cache(
+ new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId)
+ );
+
+ $this->deleteById(
+ $document,
+ $dbForProject,
+ function ($document) use ($cache, $projectId) {
+ $path = APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId . DIRECTORY_SEPARATOR . $document->getId();
+
+ if ($cache->purge($document->getId())) {
+ Console::success('Deleting cache file: ' . $path);
+ } else {
+ Console::error('Failed to delete cache file: ' . $path);
+ }
+ }
+ );
+ }
}
- protected function deleteCacheByDate(): void
+ /**
+ * @param string $datetime
+ * @throws Exception
+ */
+ protected function deleteCacheByDate(string $datetime): void
{
- $this->deleteCacheFiles([
- Query::lessThan('accessedAt', $this->args['datetime']),
- ]);
- }
-
- protected function deleteCacheFiles($query): void
- {
- $this->deleteForProjectIds(function (Document $project) use ($query) {
-
+ $this->deleteForProjectIds(function (Document $project) use ($datetime) {
$projectId = $project->getId();
$dbForProject = $this->getProjectDB($project);
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $projectId)
);
+ $query = [
+ Query::lessThan('accessedAt', $datetime),
+ ];
+
$this->deleteByGroup(
'cache',
$query,
@@ -207,9 +229,10 @@ class DeletesV1 extends Worker
});
}
+
/**
* @param Document $document database document
- * @param Document $projectId
+ * @param Document $project
*/
protected function deleteDatabase(Document $document, Document $project): void
{
@@ -662,19 +685,23 @@ class DeletesV1 extends Worker
$executionStart = \microtime(true);
- while ($sum === $limit) {
- $chunk++;
+ try {
+ while ($sum === $limit) {
+ $chunk++;
- $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries));
+ $results = $database->find($collection, \array_merge([Query::limit($limit)], $queries));
- $sum = count($results);
+ $sum = count($results);
- Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents');
+ Console::info('Deleting chunk #' . $chunk . '. Found ' . $sum . ' documents in collection ' . $database->getNamespace() . '_' . $collection);
- foreach ($results as $document) {
- $this->deleteById($document, $database, $callback);
- $count++;
+ foreach ($results as $document) {
+ $this->deleteById($document, $database, $callback);
+ $count++;
+ }
}
+ } catch (\Exception $e) {
+ Console::error($e->getMessage());
}
$executionEnd = \microtime(true);
diff --git a/app/workers/functions.php b/app/workers/functions.php
index 851defb5c..e38c6e67a 100644
--- a/app/workers/functions.php
+++ b/app/workers/functions.php
@@ -18,6 +18,7 @@ use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
+use Utopia\Logger\Log;
use Utopia\Queue\Server;
use Utopia\Database\Helpers\Role;
@@ -26,6 +27,7 @@ Authorization::setDefaultStatus(false);
Server::setResource('execute', function () {
return function (
+ Log $log,
Func $queueForFunctions,
Database $dbForProject,
Usage $queueForUsage,
@@ -39,12 +41,14 @@ Server::setResource('execute', function () {
string $eventData = null,
string $executionId = null,
) {
-
$user ??= new Document();
$functionId = $function->getId();
$functionInternalId = $function->getInternalId();
$deploymentId = $function->getAttribute('deployment', '');
+ $log->addTag('functionId', $functionId);
+ $log->addTag('projectId', $project->getId());
+
/** Check if deployment exists */
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
$deploymentInternalId = $deployment->getInternalId();
@@ -165,10 +169,8 @@ Server::setResource('execute', function () {
->setAttribute('statusCode', $th->getCode())
->setAttribute('stderr', $th->getMessage());
- Console::error($th->getTraceAsString());
- Console::error($th->getFile());
- Console::error($th->getLine());
- Console::error($th->getMessage());
+ $error = $th->getMessage();
+ $errorCode = $th->getCode();
}
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
@@ -259,8 +261,7 @@ $server->job()
while ($sum >= $limit) {
$functions = $dbForProject->find('functions', [
Query::limit($limit),
- Query::offset($offset),
- Query::orderAsc('name'),
+ Query::offset($offset)
]);
$sum = \count($functions);
@@ -303,6 +304,7 @@ $server->job()
$execution = new Document($payload['execution'] ?? []);
$user = new Document($payload['user'] ?? []);
$execute(
+ log: $log,
project: $project,
function: $function,
dbForProject: $dbForProject,
@@ -319,6 +321,7 @@ $server->job()
break;
case 'schedule':
$execute(
+ log: $log,
project: $project,
function: $function,
dbForProject: $dbForProject,
diff --git a/bin/calc-tier-stats b/bin/calc-tier-stats
new file mode 100644
index 000000000..c7fb71e6f
--- /dev/null
+++ b/bin/calc-tier-stats
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+php /usr/src/code/app/cli.php calc-tier-stats $@
\ No newline at end of file
diff --git a/bin/calc-users-stats b/bin/calc-users-stats
new file mode 100644
index 000000000..60472ed7a
--- /dev/null
+++ b/bin/calc-users-stats
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+php /usr/src/code/app/cli.php calc-users-stats $@
\ No newline at end of file
diff --git a/bin/clear-card-cache b/bin/clear-card-cache
new file mode 100644
index 000000000..b39bc1365
--- /dev/null
+++ b/bin/clear-card-cache
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+php /usr/src/code/app/cli.php clear-card-cache $@
\ No newline at end of file
diff --git a/bin/patch-delete-project-collections b/bin/patch-delete-project-collections
new file mode 100644
index 000000000..8bf0736cf
--- /dev/null
+++ b/bin/patch-delete-project-collections
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+php /usr/src/code/app/cli.php patch-delete-project-collections $@
\ No newline at end of file
diff --git a/composer.json b/composer.json
index dc7cab83c..1d84101aa 100644
--- a/composer.json
+++ b/composer.json
@@ -50,18 +50,18 @@
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.30.*",
- "utopia-php/queue": "0.5.*",
+ "utopia-php/domains": "1.1.*",
+ "utopia-php/dsn": "0.1.*",
+ "utopia-php/framework": "0.26.*",
+ "utopia-php/image": "0.5.*",
+ "utopia-php/locale": "0.4.*",
+ "utopia-php/logger": "0.3.*",
+ "utopia-php/messaging": "0.1.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.3.*",
"utopia-php/pools": "0.4.*",
"utopia-php/preloader": "0.2.*",
- "utopia-php/domains": "1.1.*",
- "utopia-php/framework": "0.26.*",
- "utopia-php/image": "0.5.*",
- "utopia-php/dsn": "0.1.*",
- "utopia-php/locale": "0.4.*",
- "utopia-php/logger": "0.3.*",
- "utopia-php/messaging": "0.1.*",
+ "utopia-php/queue": "0.5.*",
"utopia-php/registry": "0.5.*",
"utopia-php/storage": "0.14.*",
"utopia-php/swoole": "0.5.*",
@@ -73,7 +73,8 @@
"chillerlan/php-qrcode": "4.3.3",
"adhocore/jwt": "1.1.2",
"webonyx/graphql-php": "14.11.*",
- "slickdeals/statsd": "3.1.0"
+ "slickdeals/statsd": "3.1.0",
+ "league/csv": "9.7.1"
},
"repositories": [
{
diff --git a/composer.lock b/composer.lock
index db08a9345..2b699470c 100644
--- a/composer.lock
+++ b/composer.lock
@@ -300,16 +300,16 @@
},
{
"name": "colinmollenhour/credis",
- "version": "v1.14.0",
+ "version": "v1.15.0",
"source": {
"type": "git",
"url": "https://github.com/colinmollenhour/credis.git",
- "reference": "dccc8a46586475075fbb012d8bd523b8a938c2dc"
+ "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/dccc8a46586475075fbb012d8bd523b8a938c2dc",
- "reference": "dccc8a46586475075fbb012d8bd523b8a938c2dc",
+ "url": "https://api.github.com/repos/colinmollenhour/credis/zipball/28810439de1d9597b7ba11794ed9479fb6f3de7c",
+ "reference": "28810439de1d9597b7ba11794ed9479fb6f3de7c",
"shasum": ""
},
"require": {
@@ -341,9 +341,9 @@
"homepage": "https://github.com/colinmollenhour/credis",
"support": {
"issues": "https://github.com/colinmollenhour/credis/issues",
- "source": "https://github.com/colinmollenhour/credis/tree/v1.14.0"
+ "source": "https://github.com/colinmollenhour/credis/tree/v1.15.0"
},
- "time": "2022-11-09T01:18:39+00:00"
+ "time": "2023-04-18T15:34:23+00:00"
},
{
"name": "composer/package-versions-deprecated",
@@ -600,6 +600,90 @@
},
"time": "2022-11-29T16:25:20+00:00"
},
+ {
+ "name": "league/csv",
+ "version": "9.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/thephpleague/csv.git",
+ "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/thephpleague/csv/zipball/0ec57e8264ec92565974ead0d1724cf1026e10c1",
+ "reference": "0ec57e8264ec92565974ead0d1724cf1026e10c1",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-mbstring": "*",
+ "php": "^7.3 || ^8.0"
+ },
+ "require-dev": {
+ "ext-curl": "*",
+ "ext-dom": "*",
+ "friendsofphp/php-cs-fixer": "^2.16",
+ "phpstan/phpstan": "^0.12.0",
+ "phpstan/phpstan-phpunit": "^0.12.0",
+ "phpstan/phpstan-strict-rules": "^0.12.0",
+ "phpunit/phpunit": "^9.5"
+ },
+ "suggest": {
+ "ext-dom": "Required to use the XMLConverter and or the HTMLConverter classes",
+ "ext-iconv": "Needed to ease transcoding CSV using iconv stream filters"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "9.x-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/functions_include.php"
+ ],
+ "psr-4": {
+ "League\\Csv\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Ignace Nyamagana Butera",
+ "email": "nyamsprod@gmail.com",
+ "homepage": "https://github.com/nyamsprod/",
+ "role": "Developer"
+ }
+ ],
+ "description": "CSV data manipulation made easy in PHP",
+ "homepage": "http://csv.thephpleague.com",
+ "keywords": [
+ "convert",
+ "csv",
+ "export",
+ "filter",
+ "import",
+ "read",
+ "transform",
+ "write"
+ ],
+ "support": {
+ "docs": "https://csv.thephpleague.com",
+ "issues": "https://github.com/thephpleague/csv/issues",
+ "rss": "https://github.com/thephpleague/csv/releases.atom",
+ "source": "https://github.com/thephpleague/csv"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sponsors/nyamsprod",
+ "type": "github"
+ }
+ ],
+ "time": "2021-04-17T16:32:08+00:00"
+ },
{
"name": "matomo/device-detector",
"version": "6.0.0",
@@ -1588,16 +1672,16 @@
},
{
"name": "utopia-php/framework",
- "version": "0.26.0",
+ "version": "0.26.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/framework.git",
- "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e"
+ "reference": "7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/utopia-php/framework/zipball/e8da5576370366d3bf9c574ec855f8c96fe4f34e",
- "reference": "e8da5576370366d3bf9c574ec855f8c96fe4f34e",
+ "url": "https://api.github.com/repos/utopia-php/framework/zipball/7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d",
+ "reference": "7a18a2b0d6c8dba878c926b73650fd2c4eeaa70d",
"shasum": ""
},
"require": {
@@ -1626,9 +1710,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/framework/issues",
- "source": "https://github.com/utopia-php/framework/tree/0.26.0"
+ "source": "https://github.com/utopia-php/framework/tree/0.26.3"
},
- "time": "2023-01-13T08:14:43+00:00"
+ "time": "2023-06-03T14:01:15+00:00"
},
{
"name": "utopia-php/image",
@@ -2099,16 +2183,16 @@
},
{
"name": "utopia-php/queue",
- "version": "0.5.2",
+ "version": "0.5.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/queue.git",
- "reference": "310271c5cd477541208d7fa74a4dea64df8e04a0"
+ "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/utopia-php/queue/zipball/310271c5cd477541208d7fa74a4dea64df8e04a0",
- "reference": "310271c5cd477541208d7fa74a4dea64df8e04a0",
+ "url": "https://api.github.com/repos/utopia-php/queue/zipball/8e8b6cb27172713fe5d8b7b092ce68516caf129a",
+ "reference": "8e8b6cb27172713fe5d8b7b092ce68516caf129a",
"shasum": ""
},
"require": {
@@ -2154,9 +2238,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/queue/issues",
- "source": "https://github.com/utopia-php/queue/tree/0.5.2"
+ "source": "https://github.com/utopia-php/queue/tree/0.5.3"
},
- "time": "2023-03-07T08:54:10+00:00"
+ "time": "2023-05-24T19:06:04+00:00"
},
{
"name": "utopia-php/registry",
@@ -2608,25 +2692,29 @@
},
{
"name": "doctrine/deprecations",
- "version": "v1.0.0",
+ "version": "v1.1.1",
"source": {
"type": "git",
"url": "https://github.com/doctrine/deprecations.git",
- "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de"
+ "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/doctrine/deprecations/zipball/0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
- "reference": "0e2a4f1f8cdfc7a92ec3b01c9334898c806b30de",
+ "url": "https://api.github.com/repos/doctrine/deprecations/zipball/612a3ee5ab0d5dd97b7cf3874a6efe24325efac3",
+ "reference": "612a3ee5ab0d5dd97b7cf3874a6efe24325efac3",
"shasum": ""
},
"require": {
- "php": "^7.1|^8.0"
+ "php": "^7.1 || ^8.0"
},
"require-dev": {
"doctrine/coding-standard": "^9",
- "phpunit/phpunit": "^7.5|^8.5|^9.5",
- "psr/log": "^1|^2|^3"
+ "phpstan/phpstan": "1.4.10 || 1.10.15",
+ "phpstan/phpstan-phpunit": "^1.0",
+ "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5",
+ "psalm/plugin-phpunit": "0.18.4",
+ "psr/log": "^1 || ^2 || ^3",
+ "vimeo/psalm": "4.30.0 || 5.12.0"
},
"suggest": {
"psr/log": "Allows logging deprecations via PSR-3 logger implementation"
@@ -2645,9 +2733,9 @@
"homepage": "https://www.doctrine-project.org/",
"support": {
"issues": "https://github.com/doctrine/deprecations/issues",
- "source": "https://github.com/doctrine/deprecations/tree/v1.0.0"
+ "source": "https://github.com/doctrine/deprecations/tree/v1.1.1"
},
- "time": "2022-05-02T15:47:09+00:00"
+ "time": "2023-06-03T09:27:29+00:00"
},
{
"name": "doctrine/instantiator",
@@ -2721,16 +2809,16 @@
},
{
"name": "matthiasmullie/minify",
- "version": "1.3.70",
+ "version": "1.3.71",
"source": {
"type": "git",
"url": "https://github.com/matthiasmullie/minify.git",
- "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b"
+ "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/2807d9f9bece6877577ad44acb5c801bb3ae536b",
- "reference": "2807d9f9bece6877577ad44acb5c801bb3ae536b",
+ "url": "https://api.github.com/repos/matthiasmullie/minify/zipball/ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1",
+ "reference": "ae42a47d7fecc1fbb7277b2f2d84c37a33edc3b1",
"shasum": ""
},
"require": {
@@ -2780,7 +2868,7 @@
],
"support": {
"issues": "https://github.com/matthiasmullie/minify/issues",
- "source": "https://github.com/matthiasmullie/minify/tree/1.3.70"
+ "source": "https://github.com/matthiasmullie/minify/tree/1.3.71"
},
"funding": [
{
@@ -2788,7 +2876,7 @@
"type": "github"
}
],
- "time": "2022-12-09T12:56:44+00:00"
+ "time": "2023-04-25T20:33:03+00:00"
},
{
"name": "matthiasmullie/path-converter",
@@ -2904,16 +2992,16 @@
},
{
"name": "nikic/php-parser",
- "version": "v4.15.4",
+ "version": "v4.15.5",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
- "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290"
+ "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
- "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e",
+ "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e",
"shasum": ""
},
"require": {
@@ -2954,9 +3042,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
- "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4"
+ "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5"
},
- "time": "2023-03-05T19:49:14+00:00"
+ "time": "2023-05-19T20:20:00+00:00"
},
{
"name": "phar-io/manifest",
@@ -3307,22 +3395,24 @@
},
{
"name": "phpstan/phpdoc-parser",
- "version": "1.16.1",
+ "version": "1.22.0",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
- "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571"
+ "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571",
- "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/ec58baf7b3c7f1c81b3b00617c953249fb8cf30c",
+ "reference": "ec58baf7b3c7f1c81b3b00617c953249fb8cf30c",
"shasum": ""
},
"require": {
"php": "^7.2 || ^8.0"
},
"require-dev": {
+ "doctrine/annotations": "^2.0",
+ "nikic/php-parser": "^4.15",
"php-parallel-lint/php-parallel-lint": "^1.2",
"phpstan/extension-installer": "^1.0",
"phpstan/phpstan": "^1.5",
@@ -3346,9 +3436,9 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
- "source": "https://github.com/phpstan/phpdoc-parser/tree/1.16.1"
+ "source": "https://github.com/phpstan/phpdoc-parser/tree/1.22.0"
},
- "time": "2023-02-07T18:11:17+00:00"
+ "time": "2023-06-01T12:35:21+00:00"
},
{
"name": "phpunit/php-code-coverage",
@@ -4071,16 +4161,16 @@
},
{
"name": "sebastian/diff",
- "version": "4.0.4",
+ "version": "4.0.5",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d"
+ "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d",
- "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
+ "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131",
"shasum": ""
},
"require": {
@@ -4125,7 +4215,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/diff/issues",
- "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4"
+ "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5"
},
"funding": [
{
@@ -4133,7 +4223,7 @@
"type": "github"
}
],
- "time": "2020-10-26T13:10:38+00:00"
+ "time": "2023-05-07T05:35:17+00:00"
},
{
"name": "sebastian/environment",
@@ -5100,16 +5190,16 @@
},
{
"name": "twig/twig",
- "version": "v3.5.1",
+ "version": "v3.6.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
- "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15"
+ "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/twigphp/Twig/zipball/a6e0510cc793912b451fd40ab983a1d28f611c15",
- "reference": "a6e0510cc793912b451fd40ab983a1d28f611c15",
+ "url": "https://api.github.com/repos/twigphp/Twig/zipball/7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd",
+ "reference": "7e7d5839d4bec168dfeef0ac66d5c5a2edbabffd",
"shasum": ""
},
"require": {
@@ -5118,15 +5208,10 @@
"symfony/polyfill-mbstring": "^1.3"
},
"require-dev": {
- "psr/container": "^1.0",
+ "psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0"
},
"type": "library",
- "extra": {
- "branch-alias": {
- "dev-master": "3.5-dev"
- }
- },
"autoload": {
"psr-4": {
"Twig\\": "src/"
@@ -5160,7 +5245,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
- "source": "https://github.com/twigphp/Twig/tree/v3.5.1"
+ "source": "https://github.com/twigphp/Twig/tree/v3.6.1"
},
"funding": [
{
@@ -5172,7 +5257,7 @@
"type": "tidelift"
}
],
- "time": "2023-02-08T07:49:20+00:00"
+ "time": "2023-06-08T12:52:13+00:00"
}
],
"aliases": [],
diff --git a/docker-compose.yml b/docker-compose.yml
index 625243c1f..059e1ac8c 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -134,6 +134,7 @@ services:
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
+ - _APP_USERS_STATS_RECIPIENTS
- _APP_USAGE_STATS
- _APP_STORAGE_LIMIT
- _APP_STORAGE_PREVIEW_LIMIT
@@ -163,6 +164,8 @@ services:
- _APP_GRAPHQL_MAX_BATCH_SIZE
- _APP_GRAPHQL_MAX_COMPLEXITY
- _APP_GRAPHQL_MAX_DEPTH
+ - _APP_CONSOLE_GITHUB_APP_ID
+ - _APP_CONSOLE_GITHUB_SECRET
appwrite-realtime:
entrypoint: realtime
@@ -478,6 +481,8 @@ services:
- _APP_USAGE_STATS
- _APP_DOCKER_HUB_USERNAME
- _APP_DOCKER_HUB_PASSWORD
+ - _APP_LOGGING_CONFIG
+ - _APP_LOGGING_PROVIDER
appwrite-worker-mails:
entrypoint: worker-mails
diff --git a/phpunit.xml b/phpunit.xml
index 082025d1f..5b4bcdb99 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -7,7 +7,7 @@
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
- >
+>
diff --git a/public/fonts/Inter-Bold.ttf b/public/fonts/Inter-Bold.ttf
new file mode 100644
index 000000000..8e82c70d1
Binary files /dev/null and b/public/fonts/Inter-Bold.ttf differ
diff --git a/public/fonts/Inter-Medium.ttf b/public/fonts/Inter-Medium.ttf
new file mode 100644
index 000000000..9a3396fc4
Binary files /dev/null and b/public/fonts/Inter-Medium.ttf differ
diff --git a/public/fonts/Inter-Regular.ttf b/public/fonts/Inter-Regular.ttf
new file mode 100644
index 000000000..8d4eebf20
Binary files /dev/null and b/public/fonts/Inter-Regular.ttf differ
diff --git a/public/fonts/Inter-SemiBold.ttf b/public/fonts/Inter-SemiBold.ttf
new file mode 100644
index 000000000..c6aeeb16a
Binary files /dev/null and b/public/fonts/Inter-SemiBold.ttf differ
diff --git a/public/fonts/Poppins-Bold.ttf b/public/fonts/Poppins-Bold.ttf
new file mode 100644
index 000000000..00559eeb2
Binary files /dev/null and b/public/fonts/Poppins-Bold.ttf differ
diff --git a/public/fonts/SourceCodePro-Regular.ttf b/public/fonts/SourceCodePro-Regular.ttf
new file mode 100644
index 000000000..c37a7b78d
Binary files /dev/null and b/public/fonts/SourceCodePro-Regular.ttf differ
diff --git a/public/images/cards/cloud/back-golden.png b/public/images/cards/cloud/back-golden.png
new file mode 100644
index 000000000..68f1114c4
Binary files /dev/null and b/public/images/cards/cloud/back-golden.png differ
diff --git a/public/images/cards/cloud/back-platinum.png b/public/images/cards/cloud/back-platinum.png
new file mode 100644
index 000000000..cd56d227a
Binary files /dev/null and b/public/images/cards/cloud/back-platinum.png differ
diff --git a/public/images/cards/cloud/back.png b/public/images/cards/cloud/back.png
new file mode 100644
index 000000000..bb8e5b0b2
Binary files /dev/null and b/public/images/cards/cloud/back.png differ
diff --git a/public/images/cards/cloud/contributor-skew.png b/public/images/cards/cloud/contributor-skew.png
new file mode 100644
index 000000000..c80b40990
Binary files /dev/null and b/public/images/cards/cloud/contributor-skew.png differ
diff --git a/public/images/cards/cloud/contributor.png b/public/images/cards/cloud/contributor.png
new file mode 100644
index 000000000..203f0f18e
Binary files /dev/null and b/public/images/cards/cloud/contributor.png differ
diff --git a/public/images/cards/cloud/employee-skew.png b/public/images/cards/cloud/employee-skew.png
new file mode 100644
index 000000000..487b50c1d
Binary files /dev/null and b/public/images/cards/cloud/employee-skew.png differ
diff --git a/public/images/cards/cloud/employee.png b/public/images/cards/cloud/employee.png
new file mode 100644
index 000000000..587c1feb8
Binary files /dev/null and b/public/images/cards/cloud/employee.png differ
diff --git a/public/images/cards/cloud/front-golden.png b/public/images/cards/cloud/front-golden.png
new file mode 100644
index 000000000..6f7c251de
Binary files /dev/null and b/public/images/cards/cloud/front-golden.png differ
diff --git a/public/images/cards/cloud/front-platinum.png b/public/images/cards/cloud/front-platinum.png
new file mode 100644
index 000000000..0bea38e9f
Binary files /dev/null and b/public/images/cards/cloud/front-platinum.png differ
diff --git a/public/images/cards/cloud/front.png b/public/images/cards/cloud/front.png
new file mode 100644
index 000000000..19dcad74b
Binary files /dev/null and b/public/images/cards/cloud/front.png differ
diff --git a/public/images/cards/cloud/github-skew.png b/public/images/cards/cloud/github-skew.png
new file mode 100644
index 000000000..8f836f9bd
Binary files /dev/null and b/public/images/cards/cloud/github-skew.png differ
diff --git a/public/images/cards/cloud/github.png b/public/images/cards/cloud/github.png
new file mode 100644
index 000000000..b0ba724d4
Binary files /dev/null and b/public/images/cards/cloud/github.png differ
diff --git a/public/images/cards/cloud/hero-skew.png b/public/images/cards/cloud/hero-skew.png
new file mode 100644
index 000000000..3928f6635
Binary files /dev/null and b/public/images/cards/cloud/hero-skew.png differ
diff --git a/public/images/cards/cloud/hero.png b/public/images/cards/cloud/hero.png
new file mode 100644
index 000000000..811df0f8c
Binary files /dev/null and b/public/images/cards/cloud/hero.png differ
diff --git a/public/images/cards/cloud/og-background-logo.png b/public/images/cards/cloud/og-background-logo.png
new file mode 100644
index 000000000..c426c8917
Binary files /dev/null and b/public/images/cards/cloud/og-background-logo.png differ
diff --git a/public/images/cards/cloud/og-background1.png b/public/images/cards/cloud/og-background1.png
new file mode 100644
index 000000000..cbd7221fe
Binary files /dev/null and b/public/images/cards/cloud/og-background1.png differ
diff --git a/public/images/cards/cloud/og-background2.png b/public/images/cards/cloud/og-background2.png
new file mode 100644
index 000000000..f62d46f8e
Binary files /dev/null and b/public/images/cards/cloud/og-background2.png differ
diff --git a/public/images/cards/cloud/og-background3.png b/public/images/cards/cloud/og-background3.png
new file mode 100644
index 000000000..3746cb620
Binary files /dev/null and b/public/images/cards/cloud/og-background3.png differ
diff --git a/public/images/cards/cloud/og-card-golden1.png b/public/images/cards/cloud/og-card-golden1.png
new file mode 100644
index 000000000..563d64f98
Binary files /dev/null and b/public/images/cards/cloud/og-card-golden1.png differ
diff --git a/public/images/cards/cloud/og-card-golden2.png b/public/images/cards/cloud/og-card-golden2.png
new file mode 100644
index 000000000..70331f1fc
Binary files /dev/null and b/public/images/cards/cloud/og-card-golden2.png differ
diff --git a/public/images/cards/cloud/og-card-golden3.png b/public/images/cards/cloud/og-card-golden3.png
new file mode 100644
index 000000000..f03ee1d26
Binary files /dev/null and b/public/images/cards/cloud/og-card-golden3.png differ
diff --git a/public/images/cards/cloud/og-card-platinum1.png b/public/images/cards/cloud/og-card-platinum1.png
new file mode 100644
index 000000000..736ae50af
Binary files /dev/null and b/public/images/cards/cloud/og-card-platinum1.png differ
diff --git a/public/images/cards/cloud/og-card-platinum2.png b/public/images/cards/cloud/og-card-platinum2.png
new file mode 100644
index 000000000..bfc975c40
Binary files /dev/null and b/public/images/cards/cloud/og-card-platinum2.png differ
diff --git a/public/images/cards/cloud/og-card-platinum3.png b/public/images/cards/cloud/og-card-platinum3.png
new file mode 100644
index 000000000..4f08ffcd0
Binary files /dev/null and b/public/images/cards/cloud/og-card-platinum3.png differ
diff --git a/public/images/cards/cloud/og-card1.png b/public/images/cards/cloud/og-card1.png
new file mode 100644
index 000000000..8cf9b7fd1
Binary files /dev/null and b/public/images/cards/cloud/og-card1.png differ
diff --git a/public/images/cards/cloud/og-card2.png b/public/images/cards/cloud/og-card2.png
new file mode 100644
index 000000000..eb5ad1d80
Binary files /dev/null and b/public/images/cards/cloud/og-card2.png differ
diff --git a/public/images/cards/cloud/og-card3.png b/public/images/cards/cloud/og-card3.png
new file mode 100644
index 000000000..1766b24d9
Binary files /dev/null and b/public/images/cards/cloud/og-card3.png differ
diff --git a/public/images/cards/cloud/og-shadow-golden.png b/public/images/cards/cloud/og-shadow-golden.png
new file mode 100644
index 000000000..1421d03a2
Binary files /dev/null and b/public/images/cards/cloud/og-shadow-golden.png differ
diff --git a/public/images/cards/cloud/og-shadow-platinum.png b/public/images/cards/cloud/og-shadow-platinum.png
new file mode 100644
index 000000000..29d8404dc
Binary files /dev/null and b/public/images/cards/cloud/og-shadow-platinum.png differ
diff --git a/public/images/cards/cloud/og-shadow.png b/public/images/cards/cloud/og-shadow.png
new file mode 100644
index 000000000..ccb005c58
Binary files /dev/null and b/public/images/cards/cloud/og-shadow.png differ
diff --git a/src/Appwrite/Auth/OAuth2/Github.php b/src/Appwrite/Auth/OAuth2/Github.php
index c582617d6..8b9208fc0 100644
--- a/src/Appwrite/Auth/OAuth2/Github.php
+++ b/src/Appwrite/Auth/OAuth2/Github.php
@@ -158,6 +158,18 @@ class Github extends OAuth2
return $user['name'] ?? '';
}
+ /**
+ * @param string $accessToken
+ *
+ * @return string
+ */
+ public function getUserSlug(string $accessToken): string
+ {
+ $user = $this->getUser($accessToken);
+
+ return $user['login'] ?? '';
+ }
+
/**
* @param string $accessToken
*
@@ -171,13 +183,27 @@ class Github extends OAuth2
$emails = $this->request('GET', 'https://api.github.com/user/emails', ['Authorization: token ' . \urlencode($accessToken)]);
$emails = \json_decode($emails, true);
+
+ $verifiedEmail = null;
+ $primaryEmail = null;
+
foreach ($emails as $email) {
if (isset($email['verified']) && $email['verified'] === true) {
- $this->user['email'] = $email['email'];
- $this->user['verified'] = $email['verified'];
- break;
+ $verifiedEmail = $email;
+
+ if (isset($email['primary']) && $email['primary'] === true) {
+ $primaryEmail = $email;
+ }
}
}
+
+ if (!empty($primaryEmail)) {
+ $this->user['email'] = $primaryEmail['email'];
+ $this->user['verified'] = $primaryEmail['verified'];
+ } elseif (!empty($verifiedEmail)) {
+ $this->user['email'] = $verifiedEmail['email'];
+ $this->user['verified'] = $verifiedEmail['verified'];
+ }
}
return $this->user;
diff --git a/src/Appwrite/Event/Validator/Event.php b/src/Appwrite/Event/Validator/Event.php
index 4bf501f2d..3f2290048 100644
--- a/src/Appwrite/Event/Validator/Event.php
+++ b/src/Appwrite/Event/Validator/Event.php
@@ -7,6 +7,11 @@ use Utopia\Validator;
class Event extends Validator
{
+ /**
+ * @var string
+ */
+ protected string $message = 'Event is not valid.';
+
/**
* Get Description.
*
@@ -16,7 +21,7 @@ class Event extends Validator
*/
public function getDescription(): string
{
- return 'Event is not valid.';
+ return $this->message;
}
/**
@@ -40,6 +45,12 @@ class Event extends Validator
* Identify all sections of the pattern.
*/
$type = $parts[0] ?? false;
+
+ if ($type == 'functions') {
+ $this->message = 'Triggering a function on a function event is not allowed.';
+ return false;
+ }
+
$resource = $parts[1] ?? false;
$hasSubResource = $count > 3 && ($events[$type]['$resource'] ?? false) && ($events[$type][$parts[2]]['$resource'] ?? false);
$hasSubSubResource = $count > 5 && $hasSubResource && ($events[$type][$parts[2]][$parts[4]]['$resource'] ?? false);
diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php
index d6014fbb1..a66c7a170 100644
--- a/src/Appwrite/Extend/Exception.php
+++ b/src/Appwrite/Extend/Exception.php
@@ -157,6 +157,7 @@ class Exception extends \Exception
public const PROJECT_UNKNOWN = 'project_unknown';
public const PROJECT_PROVIDER_DISABLED = 'project_provider_disabled';
public const PROJECT_PROVIDER_UNSUPPORTED = 'project_provider_unsupported';
+ public const PROJECT_ALREADY_EXISTS = 'project_already_exists';
public const PROJECT_INVALID_SUCCESS_URL = 'project_invalid_success_url';
public const PROJECT_INVALID_FAILURE_URL = 'project_invalid_failure_url';
public const PROJECT_RESERVED_PROJECT = 'project_reserved_project';
@@ -178,6 +179,7 @@ class Exception extends \Exception
/** Domain */
public const DOMAIN_NOT_FOUND = 'domain_not_found';
public const DOMAIN_ALREADY_EXISTS = 'domain_already_exists';
+ public const DOMAIN_FORBIDDEN = 'domain_forbidden';
public const DOMAIN_VERIFICATION_FAILED = 'domain_verification_failed';
public const DOMAIN_TARGET_INVALID = 'domain_target_invalid';
diff --git a/src/Appwrite/Platform/Services/Tasks.php b/src/Appwrite/Platform/Services/Tasks.php
index 927abc63a..9e919127b 100644
--- a/src/Appwrite/Platform/Services/Tasks.php
+++ b/src/Appwrite/Platform/Services/Tasks.php
@@ -14,10 +14,14 @@ use Appwrite\Platform\Tasks\Specs;
use Appwrite\Platform\Tasks\SSL;
use Appwrite\Platform\Tasks\Hamster;
use Appwrite\Platform\Tasks\PatchDeleteScheduleUpdatedAtAttribute;
+use Appwrite\Platform\Tasks\ClearCardCache;
use Appwrite\Platform\Tasks\Usage;
use Appwrite\Platform\Tasks\Vars;
use Appwrite\Platform\Tasks\Version;
use Appwrite\Platform\Tasks\VolumeSync;
+use Appwrite\Platform\Tasks\CalcUsersStats;
+use Appwrite\Platform\Tasks\CalcTierStats;
+use Appwrite\Platform\Tasks\PatchDeleteProjectCollections;
class Tasks extends Service
{
@@ -33,11 +37,16 @@ class Tasks extends Service
->addAction(Install::getName(), new Install())
->addAction(Maintenance::getName(), new Maintenance())
->addAction(PatchCreateMissingSchedules::getName(), new PatchCreateMissingSchedules())
+ ->addAction(ClearCardCache::getName(), new ClearCardCache())
->addAction(PatchDeleteScheduleUpdatedAtAttribute::getName(), new PatchDeleteScheduleUpdatedAtAttribute())
->addAction(Schedule::getName(), new Schedule())
->addAction(Migrate::getName(), new Migrate())
->addAction(SDKs::getName(), new SDKs())
->addAction(VolumeSync::getName(), new VolumeSync())
- ->addAction(Specs::getName(), new Specs());
+ ->addAction(Specs::getName(), new Specs())
+ ->addAction(CalcUsersStats::getName(), new CalcUsersStats())
+ ->addAction(CalcTierStats::getName(), new CalcTierStats())
+ ->addAction(PatchDeleteProjectCollections::getName(), new PatchDeleteProjectCollections())
+ ;
}
}
diff --git a/src/Appwrite/Platform/Tasks/CalcTierStats.php b/src/Appwrite/Platform/Tasks/CalcTierStats.php
new file mode 100644
index 000000000..d66070994
--- /dev/null
+++ b/src/Appwrite/Platform/Tasks/CalcTierStats.php
@@ -0,0 +1,351 @@
+ 'Requests',
+ 'project.$all.network.bandwidth' => 'Bandwidth',
+
+ ];
+
+ public static function getName(): string
+ {
+ return 'calc-tier-stats';
+ }
+
+ public function __construct()
+ {
+
+ $this
+ ->desc('Get stats for projects')
+ ->inject('pools')
+ ->inject('cache')
+ ->inject('dbForConsole')
+ ->inject('register')
+ ->callback(function (Group $pools, Cache $cache, Database $dbForConsole, Registry $register) {
+ $this->action($pools, $cache, $dbForConsole, $register);
+ });
+ }
+
+ /**
+ * @throws \Utopia\Exception
+ * @throws CannotInsertRecord
+ */
+ public function action(Group $pools, Cache $cache, Database $dbForConsole, Registry $register): void
+ {
+ //docker compose exec -t appwrite calc-tier-stats
+
+ Console::title('Cloud free tier stats calculation V1');
+ Console::success(APP_NAME . ' cloud free tier stats calculation has started');
+
+ /* Initialise new Utopia app */
+ $app = new App('UTC');
+ $console = $app->getResource('console');
+
+ /** CSV stuff */
+ $this->date = date('Y-m-d');
+ $this->path = "{$this->directory}/tier_stats_{$this->date}.csv";
+ $csv = Writer::createFromPath($this->path, 'w');
+ $csv->insertOne($this->columns);
+
+ /** Database connections */
+ $totalProjects = $dbForConsole->count('projects');
+ Console::success("Found a total of: {$totalProjects} projects");
+
+ $projects = [$console];
+ $count = 0;
+ $limit = 30;
+ $sum = 30;
+ $offset = 0;
+ while (!empty($projects)) {
+ foreach ($projects as $project) {
+
+ /**
+ * Skip user projects with id 'console'
+ */
+ if ($project->getId() === 'console') {
+ continue;
+ }
+
+ Console::info("Getting stats for {$project->getId()}");
+
+ try {
+ $db = $project->getAttribute('database');
+ $adapter = $pools
+ ->get($db)
+ ->pop()
+ ->getResource();
+
+ $dbForProject = new Database($adapter, $cache);
+ $dbForProject->setDefaultDatabase('appwrite');
+ $dbForProject->setNamespace('_' . $project->getInternalId());
+
+ /** Get Project ID */
+ $stats['Project ID'] = $project->getId();
+
+ $stats['Organization ID'] = $project->getAttribute('teamId', null);
+
+ /** Get Total Members */
+ $teamInternalId = $project->getAttribute('teamInternalId', null);
+ if ($teamInternalId) {
+ $stats['Organization Members'] = $dbForConsole->count('memberships', [
+ Query::equal('teamInternalId', [$teamInternalId])
+ ]);
+ } else {
+ $stats['Organization Members'] = 0;
+ }
+
+ /** Get Total internal Teams */
+ try {
+ $stats['Teams'] = $dbForProject->count('teams', []);
+ } catch (\Throwable) {
+ $stats['Teams'] = 0;
+ }
+
+ /** Get Total users */
+ try {
+ $stats['Users'] = $dbForProject->count('users', []);
+ } catch (\Throwable) {
+ $stats['Users'] = 0;
+ }
+
+ /** Get Usage stats */
+ $range = '90d';
+ $periods = [
+ '90d' => [
+ 'period' => '1d',
+ 'limit' => 90,
+ ],
+ ];
+
+ $tmp = [];
+ $metrics = $this->usageStats;
+ Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$tmp) {
+ foreach ($metrics as $metric => $name) {
+ $limit = $periods[$range]['limit'];
+ $period = $periods[$range]['period'];
+
+ $requestDocs = $dbForProject->find('stats', [
+ Query::equal('period', [$period]),
+ Query::equal('metric', [$metric]),
+ Query::limit($limit),
+ Query::orderDesc('time'),
+ ]);
+
+ $tmp[$metric] = [];
+ foreach ($requestDocs as $requestDoc) {
+ if (empty($requestDoc)) {
+ continue;
+ }
+
+ $tmp[$metric][] = [
+ 'value' => $requestDoc->getAttribute('value'),
+ 'date' => $requestDoc->getAttribute('time'),
+ ];
+ }
+
+ $tmp[$metric] = array_reverse($tmp[$metric]);
+ $tmp[$metric] = array_sum(array_column($tmp[$metric], 'value'));
+ }
+ });
+
+ foreach ($tmp as $key => $value) {
+ $stats[$metrics[$key]] = $value;
+ }
+
+ try {
+ /** Get Domains */
+ $stats['Domains'] = $dbForConsole->count('domains', [
+ Query::equal('projectInternalId', [$project->getInternalId()]),
+ ]);
+ } catch (\Throwable) {
+ $stats['Domains'] = 0;
+ }
+
+ try {
+ /** Get Api keys */
+ $stats['Api keys'] = $dbForConsole->count('keys', [
+ Query::equal('projectInternalId', [$project->getInternalId()]),
+ ]);
+ } catch (\Throwable) {
+ $stats['Api keys'] = 0;
+ }
+
+ try {
+ /** Get Webhooks */
+ $stats['Webhooks'] = $dbForConsole->count('webhooks', [
+ Query::equal('projectInternalId', [$project->getInternalId()]),
+ ]);
+ } catch (\Throwable) {
+ $stats['Webhooks'] = 0;
+ }
+
+ try {
+ /** Get Platforms */
+ $stats['Platforms'] = $dbForConsole->count('platforms', [
+ Query::equal('projectInternalId', [$project->getInternalId()]),
+ ]);
+ } catch (\Throwable) {
+ $stats['Platforms'] = 0;
+ }
+
+ /** Get Files & Buckets */
+ $filesCount = 0;
+ $filesSum = 0;
+ $maxFileSize = 0;
+ $counter = 0;
+ try {
+ $buckets = $dbForProject->find('buckets', []);
+ foreach ($buckets as $bucket) {
+ $file = $dbForProject->findOne('bucket_' . $bucket->getInternalId(), [Query::orderDesc('sizeOriginal'),]);
+ if (empty($file)) {
+ continue;
+ }
+ $filesSum += $dbForProject->sum('bucket_' . $bucket->getInternalId(), 'sizeOriginal', [], 0);
+ $filesCount += $dbForProject->count('bucket_' . $bucket->getInternalId(), []);
+ if ($file->getAttribute('sizeOriginal') > $maxFileSize) {
+ $maxFileSize = $file->getAttribute('sizeOriginal');
+ }
+ $counter++;
+ }
+ } catch (\Throwable) {
+ ;
+ }
+ $stats['Buckets'] = $counter;
+ $stats['Files'] = $filesCount;
+ $stats['Storage (bytes)'] = $filesSum;
+ $stats['Max File Size (bytes)'] = $maxFileSize;
+
+
+ try {
+ /** Get Total Functions */
+ $stats['Databases'] = $dbForProject->count('databases', []);
+ } catch (\Throwable) {
+ $stats['Databases'] = 0;
+ }
+
+ /** Get Total Functions */
+ try {
+ $stats['Functions'] = $dbForProject->count('functions', []);
+ } catch (\Throwable) {
+ $stats['Functions'] = 0;
+ }
+
+ /** Get Total Deployments */
+ try {
+ $stats['Deployments'] = $dbForProject->count('deployments', []);
+ } catch (\Throwable) {
+ $stats['Deployments'] = 0;
+ }
+
+ /** Get Total Executions */
+ try {
+ $stats['Executions'] = $dbForProject->count('executions', []);
+ } catch (\Throwable) {
+ $stats['Executions'] = 0;
+ }
+
+ $csv->insertOne(array_values($stats));
+ } catch (\Throwable $th) {
+ Console::error('Failed on project ("' . $project->getId() . '") version with error on File: ' . $th->getFile() . ' line no: ' . $th->getline() . ' with message: ' . $th->getMessage());
+ } finally {
+ $pools
+ ->get($db)
+ ->reclaim();
+ }
+ }
+
+ $sum = \count($projects);
+
+ $projects = $dbForConsole->find('projects', [
+ Query::limit($limit),
+ Query::offset($offset),
+ ]);
+
+ $offset = $offset + $limit;
+ $count = $count + $sum;
+ }
+
+ Console::log('Iterated through ' . $count - 1 . '/' . $totalProjects . ' projects...');
+
+ $pools
+ ->get('console')
+ ->reclaim();
+
+ /** @var PHPMailer $mail */
+ $mail = $register->get('smtp');
+
+ $mail->clearAddresses();
+ $mail->clearAllRecipients();
+ $mail->clearReplyTos();
+ $mail->clearAttachments();
+ $mail->clearBCCs();
+ $mail->clearCCs();
+
+ try {
+ /** Addresses */
+ $mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster');
+ $recipients = explode(',', App::getEnv('_APP_USERS_STATS_RECIPIENTS', ''));
+
+ foreach ($recipients as $recipient) {
+ $mail->addAddress($recipient);
+ }
+
+ /** Attachments */
+ $mail->addAttachment($this->path);
+
+ /** Content */
+ $mail->Subject = "Cloud Report for {$this->date}";
+ $mail->Body = "Please find the daily cloud report atttached";
+ $mail->send();
+ Console::success('Email has been sent!');
+ } catch (Exception $e) {
+ Console::error("Message could not be sent. Mailer Error: {$mail->ErrorInfo}");
+ }
+ }
+}
diff --git a/src/Appwrite/Platform/Tasks/CalcUsersStats.php b/src/Appwrite/Platform/Tasks/CalcUsersStats.php
new file mode 100644
index 000000000..6310fe17b
--- /dev/null
+++ b/src/Appwrite/Platform/Tasks/CalcUsersStats.php
@@ -0,0 +1,176 @@
+desc('Get stats for projects')
+ ->inject('pools')
+ ->inject('cache')
+ ->inject('dbForConsole')
+ ->inject('register')
+ ->callback(function (Group $pools, Cache $cache, Database $dbForConsole, Registry $register) {
+ $this->action($pools, $cache, $dbForConsole, $register);
+ });
+ }
+
+ public function action(Group $pools, Cache $cache, Database $dbForConsole, Registry $register): void
+ {
+ //docker compose exec -t appwrite calc-users-stats
+
+ Console::title('Cloud Users calculation V1');
+ Console::success(APP_NAME . ' cloud Users calculation has started');
+
+ /* Initialise new Utopia app */
+ $app = new App('UTC');
+ $console = $app->getResource('console');
+
+ /** CSV stuff */
+ $this->date = date('Y-m-d');
+ $this->path = "{$this->directory}/users_stats_{$this->date}.csv";
+ $csv = Writer::createFromPath($this->path, 'w');
+ $csv->insertOne($this->columns);
+
+ /** Database connections */
+ $totalProjects = $dbForConsole->count('projects');
+ Console::success("Found a total of: {$totalProjects} projects");
+
+ $projects = [$console];
+ $count = 0;
+ $limit = 30;
+ $sum = 30;
+ $offset = 0;
+ while (!empty($projects)) {
+ foreach ($projects as $project) {
+
+ /**
+ * Skip user projects with id 'console'
+ */
+ if ($project->getId() === 'console') {
+ continue;
+ }
+
+ Console::info("Getting stats for {$project->getId()}");
+
+ try {
+ $db = $project->getAttribute('database');
+ $adapter = $pools
+ ->get($db)
+ ->pop()
+ ->getResource();
+
+ $dbForProject = new Database($adapter, $cache);
+ $dbForProject->setDefaultDatabase('appwrite');
+ $dbForProject->setNamespace('_' . $project->getInternalId());
+
+ /** Get Project ID */
+ $stats['Project ID'] = $project->getId();
+
+ /** Get Project Name */
+ $stats['Project Name'] = $project->getAttribute('name');
+
+
+ /** Get Team Name and Id */
+ $teamId = $project->getAttribute('teamId', null);
+ $teamName = null;
+ if ($teamId) {
+ $team = $dbForConsole->getDocument('teams', $teamId);
+ $teamName = $team->getAttribute('name');
+ }
+
+ $stats['Team ID'] = $teamId;
+ $stats['Team name'] = $teamName;
+
+ /** Get Total Users */
+ $stats['users'] = $dbForProject->count('users', []);
+
+ $csv->insertOne(array_values($stats));
+ } catch (\Throwable $th) {
+ Console::error('Failed to update project ("' . $project->getId() . '") version with error: ' . $th->getMessage());
+ } finally {
+ $pools
+ ->get($db)
+ ->reclaim();
+ }
+ }
+
+ $sum = \count($projects);
+
+ $projects = $dbForConsole->find('projects', [
+ Query::limit($limit),
+ Query::offset($offset),
+ ]);
+
+ $offset = $offset + $limit;
+ $count = $count + $sum;
+ }
+ Console::log('Iterated through ' . $count - 1 . '/' . $totalProjects . ' projects...');
+ $pools
+ ->get('console')
+ ->reclaim();
+
+ /** @var PHPMailer $mail */
+ $mail = $register->get('smtp');
+
+ $mail->clearAddresses();
+ $mail->clearAllRecipients();
+ $mail->clearReplyTos();
+ $mail->clearAttachments();
+ $mail->clearBCCs();
+ $mail->clearCCs();
+
+ try {
+ /** Addresses */
+ $mail->setFrom(App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), 'Appwrite Cloud Hamster');
+ $recipients = explode(',', App::getEnv('_APP_USERS_STATS_RECIPIENTS', ''));
+
+ foreach ($recipients as $recipient) {
+ $mail->addAddress($recipient);
+ }
+
+ /** Attachments */
+ $mail->addAttachment($this->path);
+
+ /** Content */
+ $mail->Subject = "Cloud Report for {$this->date}";
+ $mail->Body = "Please find the daily cloud report atttached";
+ $mail->send();
+ Console::success('Email has been sent!');
+ } catch (Exception $e) {
+ Console::error("Message could not be sent. Mailer Error: {$mail->ErrorInfo}");
+ }
+ }
+}
diff --git a/src/Appwrite/Platform/Tasks/ClearCardCache.php b/src/Appwrite/Platform/Tasks/ClearCardCache.php
new file mode 100644
index 000000000..d3153b995
--- /dev/null
+++ b/src/Appwrite/Platform/Tasks/ClearCardCache.php
@@ -0,0 +1,62 @@
+desc('Deletes card cache for specific user')
+ ->param('userId', '', new UID(), 'User UID.', false)
+ ->inject('dbForConsole')
+ ->callback(fn (string $userId, Database $dbForConsole) => $this->action($userId, $dbForConsole));
+ }
+
+ public function action(string $userId, Database $dbForConsole): void
+ {
+ Authorization::disable();
+ Authorization::setDefaultStatus(false);
+
+ Console::title('ClearCardCache V1');
+ Console::success(APP_NAME . ' ClearCardCache v1 has started');
+ $resources = ['card/' . $userId, 'card-back/' . $userId, 'card-og/' . $userId];
+
+ $caches = Authorization::skip(fn () => $dbForConsole->find('cache', [
+ Query::equal('resource', $resources),
+ Query::limit(100)
+ ]));
+
+ $count = \count($caches);
+ Console::info("Going to delete {$count} cache records in 10 seconds...");
+ \sleep(10);
+
+ foreach ($caches as $cache) {
+ $key = $cache->getId();
+
+ $cacheFolder = new Cache(
+ new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-console')
+ );
+
+ $cacheFolder->purge($key);
+
+ Authorization::skip(fn () => $dbForConsole->deleteDocument('cache', $cache->getId()));
+ }
+
+ Console::success(APP_NAME . ' ClearCardCache v1 has finished');
+ }
+}
diff --git a/src/Appwrite/Platform/Tasks/Hamster.php b/src/Appwrite/Platform/Tasks/Hamster.php
index 3e3cdd9d7..5b04c338e 100644
--- a/src/Appwrite/Platform/Tasks/Hamster.php
+++ b/src/Appwrite/Platform/Tasks/Hamster.php
@@ -2,6 +2,7 @@
namespace Appwrite\Platform\Tasks;
+use Appwrite\Network\Validator\Origin;
use Exception;
use Utopia\App;
use Utopia\Platform\Action;
@@ -12,6 +13,7 @@ use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Analytics\Adapter\Mixpanel;
use Utopia\Analytics\Event;
+use Utopia\Config\Config;
use Utopia\Database\Document;
use Utopia\Pools\Group;
@@ -98,6 +100,12 @@ class Hamster extends Action
/** Get Total Functions */
$statsPerProject['custom_functions'] = $dbForProject->count('functions', [], APP_LIMIT_COUNT);
+ foreach (\array_keys(Config::getParam('runtimes')) as $runtime) {
+ $statsPerProject['custom_functions_' . $runtime] = $dbForProject->count('functions', [
+ Query::equal('runtime', [$runtime]),
+ ], APP_LIMIT_COUNT);
+ }
+
/** Get Total Deployments */
$statsPerProject['custom_deployments'] = $dbForProject->count('deployments', [], APP_LIMIT_COUNT);
@@ -136,7 +144,10 @@ class Hamster extends Action
}
/** Get Domains */
- $statsPerProject['custom_domains'] = $dbForProject->count('domains', [], APP_LIMIT_COUNT);
+ $statsPerProject['custom_domains'] = $dbForConsole->count('domains', [
+ Query::equal('projectInternalId', [$project->getInternalId()]),
+ Query::limit(APP_LIMIT_COUNT)
+ ]);
/** Get Platforms */
$platforms = $dbForConsole->find('platforms', [
@@ -152,7 +163,7 @@ class Hamster extends Action
return $platform['type'] === 'android';
}));
- $statsPerProject['custom_platforms_iOS'] = sizeof(array_filter($platforms, function ($platform) {
+ $statsPerProject['custom_platforms_apple'] = sizeof(array_filter($platforms, function ($platform) {
return str_contains($platform['type'], 'apple');
}));
@@ -160,6 +171,19 @@ class Hamster extends Action
return str_contains($platform['type'], 'flutter');
}));
+ $flutterPlatforms = [Origin::CLIENT_TYPE_FLUTTER_ANDROID, Origin::CLIENT_TYPE_FLUTTER_IOS, Origin::CLIENT_TYPE_FLUTTER_MACOS, Origin::CLIENT_TYPE_FLUTTER_WINDOWS, Origin::CLIENT_TYPE_FLUTTER_LINUX];
+
+ foreach ($flutterPlatforms as $flutterPlatform) {
+ $statsPerProject['custom_platforms_' . $flutterPlatform] = sizeof(array_filter($platforms, function ($platform) use ($flutterPlatform) {
+ return $platform['type'] === $flutterPlatform;
+ }));
+ }
+
+ $statsPerProject['custom_platforms_api_keys'] = $dbForConsole->count('keys', [
+ Query::equal('projectInternalId', [$project->getInternalId()]),
+ Query::limit(APP_LIMIT_COUNT)
+ ]);
+
/** Get Usage $statsPerProject */
$periods = [
'infinity' => [
diff --git a/src/Appwrite/Platform/Tasks/PatchDeleteProjectCollections.php b/src/Appwrite/Platform/Tasks/PatchDeleteProjectCollections.php
new file mode 100644
index 000000000..a909e6859
--- /dev/null
+++ b/src/Appwrite/Platform/Tasks/PatchDeleteProjectCollections.php
@@ -0,0 +1,129 @@
+desc('Delete unnecessary project collections')
+ ->param('offset', 0, new Numeric(), 'Resume deletion from param pos', true)
+ ->inject('pools')
+ ->inject('cache')
+ ->inject('dbForConsole')
+ ->callback(function (int $offset, Group $pools, Cache $cache, Database $dbForConsole) {
+ $this->action($offset, $pools, $cache, $dbForConsole);
+ });
+ }
+
+ public function action(int $offset, Group $pools, Cache $cache, Database $dbForConsole): void
+ {
+ //docker compose exec -t appwrite patch-delete-project-collections
+
+ Console::title('Delete project collections V1');
+ Console::success(APP_NAME . ' delete project collections has started');
+
+ /* Initialise new Utopia app */
+ $app = new App('UTC');
+ $console = $app->getResource('console');
+
+ /** Database connections */
+ $totalProjects = $dbForConsole->count('projects');
+ Console::success("Found a total of: {$totalProjects} projects");
+
+ $projects = [$console];
+ $count = 0;
+ $limit = 50;
+ $sum = 50;
+ $offset = $offset;
+ while (!empty($projects)) {
+ foreach ($projects as $project) {
+
+ /**
+ * Skip user projects with id 'console'
+ */
+ if ($project->getId() === 'console') {
+ continue;
+ }
+
+ Console::info("Deleting collections for {$project->getId()}");
+
+ try {
+ $db = $project->getAttribute('database');
+ $adapter = $pools
+ ->get($db)
+ ->pop()
+ ->getResource();
+
+ $dbForProject = new Database($adapter, $cache);
+ $dbForProject->setDefaultDatabase(App::getEnv('_APP_DB_SCHEMA', 'appwrite'));
+ $dbForProject->setNamespace('_' . $project->getInternalId());
+
+ foreach ($this->names as $name) {
+ if (empty($name)) {
+ continue;
+ }
+ if ($dbForProject->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'), $name)) {
+ if ($dbForProject->deleteCollection($name)) {
+ Console::log('Deleted ' . $name);
+ } else {
+ Console::error('Failed to delete ' . $name);
+ }
+ }
+ }
+ } catch (\Throwable $th) {
+ Console::error('Failed on project ("' . $project->getId() . '") version with error: ' . $th->getMessage());
+ } finally {
+ $pools
+ ->get($db)
+ ->reclaim();
+ }
+ }
+
+ $sum = \count($projects);
+
+ $projects = $dbForConsole->find('projects', [
+ Query::limit($limit),
+ Query::offset($offset),
+ ]);
+
+ if (!empty($projects)) {
+ Console::log('Querying..... offset=' . $offset . ' , limit=' . $limit . ', count=' . $count);
+ }
+
+ $offset = $offset + $limit;
+ $count = $count + $sum;
+ }
+ Console::log('Iterated through ' . $count - 1 . '/' . $totalProjects . ' projects...');
+ $pools
+ ->get('console')
+ ->reclaim();
+ }
+}
diff --git a/src/Appwrite/Resque/Worker.php b/src/Appwrite/Resque/Worker.php
index 182d78a5b..146500e74 100644
--- a/src/Appwrite/Resque/Worker.php
+++ b/src/Appwrite/Resque/Worker.php
@@ -174,6 +174,7 @@ abstract class Worker
* @throws Exception
*/
protected static $databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
+
protected function getProjectDB(Document $project): Database
{
global $register;
@@ -218,6 +219,14 @@ abstract class Worker
$pools = $register->get('pools'); /** @var Group $pools */
+ $databaseName = 'console';
+
+ if (isset(self::$databases[$databaseName])) {
+ $database = self::$databases[$databaseName];
+ $database->setNamespace('console');
+ return $database;
+ }
+
$dbAdapter = $pools
->get('console')
->pop()
@@ -226,6 +235,8 @@ abstract class Worker
$database = new Database($dbAdapter, $this->getCache());
+ self::$databases[$databaseName] = $database;
+
$database->setNamespace('console');
return $database;
diff --git a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php
index 89c82c8e8..154b77690 100644
--- a/src/Appwrite/Utopia/Database/Validator/Queries/Base.php
+++ b/src/Appwrite/Utopia/Database/Validator/Queries/Base.php
@@ -22,7 +22,9 @@ class Base extends Queries
*/
public function __construct(string $collection, array $allowedAttributes)
{
- $collection = Config::getParam('collections', [])[$collection];
+ $config = Config::getParam('collections', []);
+ $collections = array_merge($config['console'], $config['projects'], $config['buckets'], $config['databases']);
+ $collection = $collections[$collection];
// array for constant lookup time
$allowedAttributesLookup = [];
foreach ($allowedAttributes as $attribute) {
diff --git a/tests/e2e/Client.php b/tests/e2e/Client.php
index e98472b2b..803c970d8 100644
--- a/tests/e2e/Client.php
+++ b/tests/e2e/Client.php
@@ -160,17 +160,14 @@ class Client
* @param array $params
* @param array $headers
* @param bool $decode
- * @return array|string
+ * @return array
* @throws Exception
*/
- public function call(string $method, string $path = '', array $headers = [], array $params = [], bool $decode = true)
+ public function call(string $method, string $path = '', array $headers = [], array $params = [], bool $decode = true): array
{
$headers = array_merge($this->headers, $headers);
$ch = curl_init($this->endpoint . $path . (($method == self::METHOD_GET && !empty($params)) ? '?' . http_build_query($params) : ''));
$responseHeaders = [];
- $responseStatus = -1;
- $responseType = '';
- $responseBody = '';
switch ($headers['content-type']) {
case 'application/json':
@@ -220,7 +217,7 @@ class Client
curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
}
- // Allow self signed certificates
+ // Allow self-signed certificates
if ($this->selfSigned) {
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
@@ -230,22 +227,18 @@ class Client
$responseType = $responseHeaders['content-type'] ?? '';
$responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
- if ($decode) {
- switch (substr($responseType, 0, strpos($responseType, ';'))) {
- case 'application/json':
- $json = json_decode($responseBody, true);
+ if ($decode && substr($responseType, 0, strpos($responseType, ';')) == 'application/json') {
+ $json = json_decode($responseBody, true);
- if ($json === null) {
- throw new Exception('Failed to parse response: ' . $responseBody);
- }
-
- $responseBody = $json;
- $json = null;
- break;
+ if ($json === null) {
+ throw new Exception('Failed to parse response: ' . $responseBody);
}
+
+ $responseBody = $json;
+ $json = null;
}
- if ((curl_errno($ch)/* || 200 != $responseStatus*/)) {
+ if ((curl_errno($ch))) {
throw new Exception(curl_error($ch) . ' with status code ' . $responseStatus, $responseStatus);
}
@@ -273,7 +266,7 @@ class Client
{
$cookies = [];
- parse_str(strtr($cookie, array('&' => '%26', '+' => '%2B', ';' => '&')), $cookies);
+ parse_str(strtr($cookie, ['&' => '%26', '+' => '%2B', ';' => '&']), $cookies);
return $cookies;
}
diff --git a/tests/e2e/General/HTTPTest.php b/tests/e2e/General/HTTPTest.php
index 09f744f27..9b2ac0735 100644
--- a/tests/e2e/General/HTTPTest.php
+++ b/tests/e2e/General/HTTPTest.php
@@ -12,6 +12,12 @@ class HTTPTest extends Scope
use ProjectNone;
use SideNone;
+ public function setUp(): void
+ {
+ parent::setUp();
+ $this->client->setEndpoint('http://localhost');
+ }
+
public function testOptions()
{
/**
@@ -32,24 +38,6 @@ class HTTPTest extends Scope
$this->assertEmpty($response['body']);
}
- public function testError()
- {
- /**
- * Test for SUCCESS
- */
- $this->markTestIncomplete('This test needs to be updated for the new console.');
- // $response = $this->client->call(Client::METHOD_GET, '/error', \array_merge([
- // 'origin' => 'http://localhost',
- // 'content-type' => 'application/json',
- // ]), []);
-
- // $this->assertEquals(404, $response['headers']['status-code']);
- // $this->assertEquals('Not Found', $response['body']['message']);
- // $this->assertEquals(Exception::GENERAL_ROUTE_NOT_FOUND, $response['body']['type']);
- // $this->assertEquals(404, $response['body']['code']);
- // $this->assertEquals('dev', $response['body']['version']);
- }
-
public function testHumans()
{
/**
@@ -57,7 +45,7 @@ class HTTPTest extends Scope
*/
$response = $this->client->call(Client::METHOD_GET, '/humans.txt', \array_merge([
'origin' => 'http://localhost',
- ]), []);
+ ]));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertStringContainsString('# humanstxt.org/', $response['body']);
@@ -70,7 +58,7 @@ class HTTPTest extends Scope
*/
$response = $this->client->call(Client::METHOD_GET, '/robots.txt', \array_merge([
'origin' => 'http://localhost',
- ]), []);
+ ]));
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertStringContainsString('# robotstxt.org/', $response['body']);
@@ -87,7 +75,7 @@ class HTTPTest extends Scope
*/
$response = $this->client->call(Client::METHOD_GET, '/.well-known/acme-challenge/8DdIKX257k6Dih5s_saeVMpTnjPJdKO5Ase0OCiJrIg', \array_merge([
'origin' => 'http://localhost',
- ]), []);
+ ]));
$this->assertEquals(404, $response['headers']['status-code']);
// 'Unknown path', but validation passed
@@ -97,7 +85,7 @@ class HTTPTest extends Scope
*/
$response = $this->client->call(Client::METHOD_GET, '/.well-known/acme-challenge/../../../../../../../etc/passwd', \array_merge([
'origin' => 'http://localhost',
- ]), []);
+ ]));
$this->assertEquals(400, $response['headers']['status-code']);
@@ -171,4 +159,15 @@ class HTTPTest extends Scope
$this->assertIsString($body['server-ruby']);
$this->assertIsString($body['console-cli']);
}
+
+ public function testDefaultOAuth2()
+ {
+ $response = $this->client->call(Client::METHOD_GET, '/auth/oauth2/success', $this->getHeaders());
+
+ $this->assertEquals(200, $response['headers']['status-code']);
+
+ $response = $this->client->call(Client::METHOD_GET, '/auth/oauth2/failure', $this->getHeaders());
+
+ $this->assertEquals(200, $response['headers']['status-code']);
+ }
}
diff --git a/tests/e2e/Scopes/ProjectCustom.php b/tests/e2e/Scopes/ProjectCustom.php
index 4d780d04c..dfdfcfe2a 100644
--- a/tests/e2e/Scopes/ProjectCustom.php
+++ b/tests/e2e/Scopes/ProjectCustom.php
@@ -104,7 +104,7 @@ trait ProjectCustom
'name' => 'Webhook Test',
'events' => [
'databases.*',
- 'functions.*',
+ // 'functions.*', TODO @christyjacob4 : enable test once we allow functions.* events
'buckets.*',
'teams.*',
'users.*'
diff --git a/tests/e2e/Scopes/Scope.php b/tests/e2e/Scopes/Scope.php
index 42057e8e9..14eb83897 100644
--- a/tests/e2e/Scopes/Scope.php
+++ b/tests/e2e/Scopes/Scope.php
@@ -17,10 +17,7 @@ abstract class Scope extends TestCase
protected function setUp(): void
{
$this->client = new Client();
-
- $this->client
- ->setEndpoint($this->endpoint)
- ;
+ $this->client->setEndpoint($this->endpoint);
}
protected function tearDown(): void
@@ -45,10 +42,10 @@ abstract class Scope extends TestCase
{
sleep(2);
- $resquest = json_decode(file_get_contents('http://request-catcher:5000/__last_request__'), true);
- $resquest['data'] = json_decode($resquest['data'], true);
+ $request = json_decode(file_get_contents('http://request-catcher:5000/__last_request__'), true);
+ $request['data'] = json_decode($request['data'], true);
- return $resquest;
+ return $request;
}
/**
diff --git a/tests/e2e/Services/Account/AccountCustomClientTest.php b/tests/e2e/Services/Account/AccountCustomClientTest.php
index bdaf3a199..35329e9cb 100644
--- a/tests/e2e/Services/Account/AccountCustomClientTest.php
+++ b/tests/e2e/Services/Account/AccountCustomClientTest.php
@@ -252,6 +252,8 @@ class AccountCustomClientTest extends Scope
]);
$this->assertEquals($response['headers']['status-code'], 200);
+ $this->assertStringContainsString('a_session_' . $this->getProject()['$id'] . '=deleted', $response['headers']['set-cookie']);
+ $this->assertEquals('[]', $response['headers']['x-fallback-cookies']);
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
'origin' => 'http://localhost',
diff --git a/tests/e2e/Services/Databases/DatabasesBase.php b/tests/e2e/Services/Databases/DatabasesBase.php
index e81a89ac9..19bbb25dc 100644
--- a/tests/e2e/Services/Databases/DatabasesBase.php
+++ b/tests/e2e/Services/Databases/DatabasesBase.php
@@ -981,6 +981,12 @@ trait DatabasesBase
$this->assertEquals(400, $document4['headers']['status-code']);
+ // Delete document 4 with incomplete path
+ $this->assertEquals(404, $this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId . '/collections/' . $data['moviesId'] . '/documents/', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()))['headers']['status-code']);
+
return $data;
}
@@ -2869,7 +2875,7 @@ trait DatabasesBase
$databaseId = $database['body']['$id'];
// Create collection
- $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/', array_merge([
+ $movies = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php
index aba0c6e5b..3baf92a87 100644
--- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php
+++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php
@@ -3,6 +3,7 @@
namespace Tests\E2E\Services\Projects;
use Appwrite\Auth\Auth;
+use Appwrite\Extend\Exception;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\ProjectConsole;
use Tests\E2E\Scopes\SideClient;
@@ -96,7 +97,37 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(400, $response['headers']['status-code']);
- return ['projectId' => $projectId];
+ return [
+ 'projectId' => $projectId,
+ 'teamId' => $team['body']['$id']
+ ];
+ }
+
+ /**
+ * @depends testCreateProject
+ */
+ public function testCreateDuplicateProject($data)
+ {
+ $teamId = $data['teamId'] ?? '';
+ $projectId = $data['projectId'] ?? '';
+
+ /**
+ * Test for FAILURE
+ */
+ $response = $this->client->call(Client::METHOD_POST, '/projects', array_merge([
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $this->getProject()['$id'],
+ ], $this->getHeaders()), [
+ 'projectId' => $projectId,
+ 'name' => 'Project Duplicate',
+ 'teamId' => $teamId,
+ 'region' => 'default'
+ ]);
+
+ $this->assertEquals(409, $response['headers']['status-code']);
+ $this->assertEquals(409, $response['body']['code']);
+ $this->assertEquals(Exception::PROJECT_ALREADY_EXISTS, $response['body']['type']);
+ $this->assertEquals('Project with the requested ID already exists.', $response['body']['message']);
}
/**
@@ -781,11 +812,11 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals($response['headers']['status-code'], 501);
- $response = $this->client->call(Client::METHOD_POST, '/account/anonymous', array_merge([
+ $response = $this->client->call(Client::METHOD_POST, '/account/sessions/anonymous', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $id,
- ]), []);
+ ]));
$this->assertEquals($response['headers']['status-code'], 501);
@@ -841,6 +872,19 @@ class ProjectsConsoleClientTest extends Scope
'name' => $name,
]);
+ $email = uniqid() . 'user@localhost.test';
+
+ $response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
+ 'origin' => 'http://localhost',
+ 'content-type' => 'application/json',
+ 'x-appwrite-project' => $id,
+ ]), [
+ 'userId' => ID::unique(),
+ 'email' => $email,
+ 'password' => $password,
+ 'name' => $name,
+ ]);
+
$this->assertEquals($response['headers']['status-code'], 501);
/**
@@ -856,6 +900,8 @@ class ProjectsConsoleClientTest extends Scope
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertNotEmpty($response['body']['$id']);
+ $email = uniqid() . 'user@localhost.test';
+
$response = $this->client->call(Client::METHOD_POST, '/account', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
diff --git a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php
index cd2fb6f13..66ea935bc 100644
--- a/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php
+++ b/tests/e2e/Services/Realtime/RealtimeConsoleClientTest.php
@@ -497,7 +497,7 @@ class RealtimeConsoleClientTest extends Scope
$this->assertArrayHasKey('timestamp', $response['data']);
$this->assertCount(1, $response['data']['channels']);
$this->assertContains('console', $response['data']['channels']);
- $this->assertContains("functions.{$functionId}.deployments.{$deploymentId}.create", $response['data']['events']);
+ // $this->assertContains("functions.{$functionId}.deployments.{$deploymentId}.create", $response['data']['events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertNotEmpty($response['data']['payload']);
$client->close();
diff --git a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php
index c20cb58f4..26a97f97b 100644
--- a/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php
+++ b/tests/e2e/Services/Webhooks/WebhooksCustomServerTest.php
@@ -415,10 +415,10 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
- $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
@@ -474,10 +474,10 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
- $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
@@ -529,12 +529,12 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
- $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.deployments.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.*.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.deployments.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.deployments.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.*.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.deployments.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
@@ -572,16 +572,16 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
- $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.deployments.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.deployments.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.*.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.*.deployments.{$deploymentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.deployments.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.deployments.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.deployments.{$deploymentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.deployments.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.deployments.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.*.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.*.deployments.{$deploymentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.deployments.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.deployments.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.deployments.{$deploymentId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
@@ -620,16 +620,16 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
- $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.executions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.executions.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.*.executions.{$executionId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.*.executions.{$executionId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.executions.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.executions.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.executions.{$executionId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.executions.{$executionId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.executions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.executions.*.create', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.*.executions.{$executionId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.*.executions.{$executionId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.executions.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.executions.*.create", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.executions.{$executionId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.executions.{$executionId}.create", $webhook['headers']['X-Appwrite-Webhook-Events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
@@ -643,16 +643,16 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
- $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.executions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.executions.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.*.executions.{$executionId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.*.executions.{$executionId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.executions.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.executions.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.executions.{$executionId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.executions.{$executionId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.executions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.executions.*.update', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.*.executions.{$executionId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.*.executions.{$executionId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.executions.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.executions.*.update", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.executions.{$executionId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.executions.{$executionId}.update", $webhook['headers']['X-Appwrite-Webhook-Events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
@@ -688,16 +688,16 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
- $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.deployments.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.deployments.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.*.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.*.deployments.{$deploymentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.deployments.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.deployments.*.delete", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.deployments.{$deploymentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.deployments.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.deployments.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.*.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.*.deployments.{$deploymentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.deployments.*", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.deployments.*.delete", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.deployments.{$deploymentId}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.deployments.{$deploymentId}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
@@ -733,10 +733,10 @@ class WebhooksCustomServerTest extends Scope
$this->assertEquals($webhook['method'], 'POST');
$this->assertEquals($webhook['headers']['Content-Type'], 'application/json');
$this->assertEquals($webhook['headers']['User-Agent'], 'Appwrite-Server vdev. Please report abuse at security@appwrite.io');
- $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString('functions.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
- $this->assertStringContainsString("functions.{$id}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString('functions.*.delete', $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}", $webhook['headers']['X-Appwrite-Webhook-Events']);
+ // $this->assertStringContainsString("functions.{$id}.delete", $webhook['headers']['X-Appwrite-Webhook-Events']); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Signature'], $signatureExpected);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Id'] ?? '', $this->getProject()['webhookId']);
$this->assertEquals($webhook['headers']['X-Appwrite-Webhook-Project-Id'] ?? '', $this->getProject()['$id']);
diff --git a/tests/unit/Event/Validator/EventValidatorTest.php b/tests/unit/Event/Validator/EventValidatorTest.php
index e9f652ade..cf62ecca6 100644
--- a/tests/unit/Event/Validator/EventValidatorTest.php
+++ b/tests/unit/Event/Validator/EventValidatorTest.php
@@ -50,7 +50,7 @@ class EventValidatorTest extends TestCase
$this->assertTrue($this->object->isValid('databases.books'));
$this->assertTrue($this->object->isValid('databases.books.collections.chapters'));
$this->assertTrue($this->object->isValid('databases.books.collections.*'));
- $this->assertTrue($this->object->isValid('functions.*'));
+ // $this->assertTrue($this->object->isValid('functions.*')); TODO @christyjacob4 : enable test once we allow functions.* events
$this->assertTrue($this->object->isValid('buckets.*'));
$this->assertTrue($this->object->isValid('teams.*'));
$this->assertTrue($this->object->isValid('users.*'));
diff --git a/tests/unit/General/CollectionsTest.php b/tests/unit/General/CollectionsTest.php
index 1d648f93c..73a9ccd0c 100644
--- a/tests/unit/General/CollectionsTest.php
+++ b/tests/unit/General/CollectionsTest.php
@@ -15,16 +15,18 @@ class CollectionsTest extends TestCase
public function testDuplicateRules(): void
{
- foreach ($this->collections as $key => $collection) {
- if (array_key_exists('attributes', $collection)) {
- foreach ($collection['attributes'] as $check) {
- $occurrences = 0;
- foreach ($collection['attributes'] as $attribute) {
- if ($attribute['$id'] == $check['$id']) {
- $occurrences++;
+ foreach ($this->collections as $key => $sections) {
+ foreach ($sections as $key => $collection) {
+ if (array_key_exists('attributes', $collection)) {
+ foreach ($collection['attributes'] as $check) {
+ $occurrences = 0;
+ foreach ($collection['attributes'] as $attribute) {
+ if ($attribute['$id'] == $check['$id']) {
+ $occurrences++;
+ }
}
+ $this->assertEquals(1, $occurrences);
}
- $this->assertEquals(1, $occurrences);
}
}
}