Merge remote-tracking branch 'origin/1.7.x' into 1.8.x

# Conflicts:
#	app/config/specs/open-api3-latest-client.json
#	app/config/specs/open-api3-latest-console.json
#	app/config/specs/open-api3-latest-server.json
#	app/config/specs/swagger2-latest-client.json
#	app/config/specs/swagger2-latest-console.json
#	app/config/specs/swagger2-latest-server.json
#	app/controllers/api/databases.php
#	composer.lock
#	tests/e2e/Services/Databases/Legacy/DatabasesBase.php
#	tests/e2e/Services/Migrations/MigrationsBase.php
This commit is contained in:
Jake Barnby
2025-08-06 17:24:00 +12:00
24 changed files with 6335 additions and 664 deletions
+28 -6
View File
@@ -25,6 +25,7 @@ return [
'gitRepoName' => 'sdk-for-web',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/web/CHANGELOG.md'),
'demos' => [
[
'icon' => 'react.svg',
@@ -73,6 +74,7 @@ return [
'gitRepoName' => 'sdk-for-flutter',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/flutter/CHANGELOG.md'),
],
[
'key' => 'apple',
@@ -91,6 +93,7 @@ return [
'gitRepoName' => 'sdk-for-apple',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/apple/CHANGELOG.md'),
],
[
'key' => 'objective-c',
@@ -108,6 +111,7 @@ return [
'gitRepoName' => 'sdk-for-objective-c',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/objective-c/CHANGELOG.md'),
],
[
'key' => 'android',
@@ -130,6 +134,7 @@ return [
'Kotlin' => 'kotlin',
'Java' => 'java',
],
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/android/CHANGELOG.md'),
],
[
'key' => 'react-native',
@@ -148,6 +153,7 @@ return [
'gitRepoName' => 'sdk-for-react-native',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/react-native/CHANGELOG.md'),
],
[
'key' => 'graphql',
@@ -167,6 +173,7 @@ return [
'gitUserName' => '',
'gitBranch' => '',
'isSDK' => false,
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/graphql/CHANGELOG.md'),
],
[
'key' => 'rest',
@@ -186,6 +193,7 @@ return [
'gitUserName' => '',
'gitBranch' => '',
'isSDK' => false,
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/rest/CHANGELOG.md'),
],
],
],
@@ -199,8 +207,8 @@ return [
[
'key' => 'web',
'name' => 'Console',
'version' => '1.9.0',
'url' => 'https://github.com/appwrite/sdk-for-console',
'version' => '0.1.0',
'url' => '',
'package' => '',
'enabled' => true,
'beta' => false,
@@ -209,10 +217,11 @@ return [
'family' => APP_PLATFORM_CONSOLE,
'prism' => 'javascript',
'source' => \realpath(__DIR__ . '/../sdks/console-web'),
'gitUrl' => 'git@github.com:appwrite/sdk-for-console.git',
'gitUrl' => '',
'gitBranch' => 'dev',
'gitRepoName' => 'sdk-for-console',
'gitUserName' => 'appwrite',
'gitRepoName' => '',
'gitUserName' => '',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/console/CHANGELOG.md'),
],
[
'key' => 'cli',
@@ -232,6 +241,7 @@ return [
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'repoBranch' => 'master',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/cli/CHANGELOG.md'),
'exclude' => [
'services' => [
['name' => 'assistant'],
@@ -265,6 +275,7 @@ return [
'gitRepoName' => 'sdk-for-node',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/nodejs/CHANGELOG.md'),
],
[
'key' => 'deno',
@@ -283,6 +294,7 @@ return [
'gitRepoName' => 'sdk-for-deno',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/deno/CHANGELOG.md'),
],
[
'key' => 'php',
@@ -301,6 +313,7 @@ return [
'gitRepoName' => 'sdk-for-php',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/php/CHANGELOG.md'),
],
[
'key' => 'python',
@@ -319,6 +332,7 @@ return [
'gitRepoName' => 'sdk-for-python',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/python/CHANGELOG.md'),
],
[
'key' => 'ruby',
@@ -337,6 +351,7 @@ return [
'gitRepoName' => 'sdk-for-ruby',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/ruby/CHANGELOG.md'),
],
[
'key' => 'go',
@@ -355,11 +370,12 @@ return [
'gitRepoName' => 'sdk-for-go',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/go/CHANGELOG.md'),
],
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.14.1',
'version' => '0.15.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -373,6 +389,7 @@ return [
'gitRepoName' => 'sdk-for-dotnet',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/dotnet/CHANGELOG.md'),
],
[
'key' => 'dart',
@@ -391,6 +408,7 @@ return [
'gitRepoName' => 'sdk-for-dart',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/dart/CHANGELOG.md'),
],
[
'key' => 'kotlin',
@@ -413,6 +431,7 @@ return [
'Kotlin' => 'kotlin',
'Java' => 'java',
],
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/kotlin/CHANGELOG.md'),
],
[
'key' => 'swift',
@@ -431,6 +450,7 @@ return [
'gitRepoName' => 'sdk-for-swift',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/swift/CHANGELOG.md'),
],
[
'key' => 'graphql',
@@ -450,6 +470,7 @@ return [
'gitUserName' => '',
'gitBranch' => '',
'isSDK' => false,
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/graphql/CHANGELOG.md'),
],
[
'key' => 'rest',
@@ -469,6 +490,7 @@ return [
'gitUserName' => '',
'gitBranch' => '',
'isSDK' => false,
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/rest/CHANGELOG.md'),
],
],
],
+439 -50
View File
@@ -5361,7 +5361,7 @@
"scheduledAt": {
"type": "string",
"description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.",
"x-example": null
"x-example": "<SCHEDULED_AT>"
}
}
}
@@ -8033,7 +8033,8 @@
"any": {
"description": "Any",
"type": "object",
"additionalProperties": true
"additionalProperties": true,
"example": []
},
"error": {
"description": "Error",
@@ -8065,7 +8066,13 @@
"code",
"type",
"version"
]
],
"example": {
"message": "Not found",
"code": "404",
"type": "not_found",
"version": "1.0"
}
},
"documentList": {
"description": "Documents List",
@@ -8089,7 +8096,11 @@
"required": [
"total",
"documents"
]
],
"example": {
"total": 5,
"documents": ""
}
},
"sessionList": {
"description": "Sessions List",
@@ -8113,7 +8124,11 @@
"required": [
"total",
"sessions"
]
],
"example": {
"total": 5,
"sessions": ""
}
},
"identityList": {
"description": "Identities List",
@@ -8137,7 +8152,11 @@
"required": [
"total",
"identities"
]
],
"example": {
"total": 5,
"identities": ""
}
},
"logList": {
"description": "Logs List",
@@ -8161,7 +8180,11 @@
"required": [
"total",
"logs"
]
],
"example": {
"total": 5,
"logs": ""
}
},
"fileList": {
"description": "Files List",
@@ -8185,7 +8208,11 @@
"required": [
"total",
"files"
]
],
"example": {
"total": 5,
"files": ""
}
},
"teamList": {
"description": "Teams List",
@@ -8209,7 +8236,11 @@
"required": [
"total",
"teams"
]
],
"example": {
"total": 5,
"teams": ""
}
},
"membershipList": {
"description": "Memberships List",
@@ -8233,7 +8264,11 @@
"required": [
"total",
"memberships"
]
],
"example": {
"total": 5,
"memberships": ""
}
},
"executionList": {
"description": "Executions List",
@@ -8257,7 +8292,11 @@
"required": [
"total",
"executions"
]
],
"example": {
"total": 5,
"executions": ""
}
},
"countryList": {
"description": "Countries List",
@@ -8281,7 +8320,11 @@
"required": [
"total",
"countries"
]
],
"example": {
"total": 5,
"countries": ""
}
},
"continentList": {
"description": "Continents List",
@@ -8305,7 +8348,11 @@
"required": [
"total",
"continents"
]
],
"example": {
"total": 5,
"continents": ""
}
},
"languageList": {
"description": "Languages List",
@@ -8329,7 +8376,11 @@
"required": [
"total",
"languages"
]
],
"example": {
"total": 5,
"languages": ""
}
},
"currencyList": {
"description": "Currencies List",
@@ -8353,7 +8404,11 @@
"required": [
"total",
"currencies"
]
],
"example": {
"total": 5,
"currencies": ""
}
},
"phoneList": {
"description": "Phones List",
@@ -8377,7 +8432,11 @@
"required": [
"total",
"phones"
]
],
"example": {
"total": 5,
"phones": ""
}
},
"localeCodeList": {
"description": "Locale codes list",
@@ -8401,7 +8460,11 @@
"required": [
"total",
"localeCodes"
]
],
"example": {
"total": 5,
"localeCodes": ""
}
},
"document": {
"description": "Document",
@@ -8458,7 +8521,23 @@
"$createdAt",
"$updatedAt",
"$permissions"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$sequence": 1,
"$collectionId": "5e5ea5c15117e",
"$databaseId": "5e5ea5c15117e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"$permissions": [
"read(\"any\")"
],
"username": "john.doe",
"email": "john.doe@example.com",
"fullName": "John Doe",
"age": 30,
"isAdmin": false
}
},
"log": {
"description": "Log",
@@ -8592,7 +8671,30 @@
"deviceModel",
"countryCode",
"countryName"
]
],
"example": {
"event": "account.sessions.create",
"userId": "610fc2f985ee0",
"userEmail": "john@appwrite.io",
"userName": "John Doe",
"mode": "admin",
"ip": "127.0.0.1",
"time": "2020-10-15T06:38:00.000+00:00",
"osCode": "Mac",
"osName": "Mac",
"osVersion": "Mac",
"clientType": "browser",
"clientCode": "CM",
"clientName": "Chrome Mobile iOS",
"clientVersion": "84.0",
"clientEngine": "WebKit",
"clientEngineVersion": "605.1.15",
"deviceName": "smartphone",
"deviceBrand": "Google",
"deviceModel": "Nexus 5",
"countryCode": "US",
"countryName": "United States"
}
},
"user": {
"description": "User",
@@ -8753,7 +8855,33 @@
"prefs",
"targets",
"accessedAt"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"name": "John Doe",
"password": "$argon2id$v=19$m=2048,t=4,p=3$aUZjLnliVWRINmFNTWMudg$5S+x+7uA31xFnrHFT47yFwcJeaP0w92L\/4LdgrVRXxE",
"hash": "argon2",
"hashOptions": {},
"registration": "2020-10-15T06:38:00.000+00:00",
"status": true,
"labels": [
"vip"
],
"passwordUpdate": "2020-10-15T06:38:00.000+00:00",
"email": "john@appwrite.io",
"phone": "+4930901820",
"emailVerification": true,
"phoneVerification": true,
"mfa": true,
"prefs": {
"theme": "pink",
"timezone": "UTC"
},
"targets": [],
"accessedAt": "2020-10-15T06:38:00.000+00:00"
}
},
"algoMd5": {
"description": "AlgoMD5",
@@ -8767,7 +8895,10 @@
},
"required": [
"type"
]
],
"example": {
"type": "md5"
}
},
"algoSha": {
"description": "AlgoSHA",
@@ -8781,7 +8912,10 @@
},
"required": [
"type"
]
],
"example": {
"type": "sha"
}
},
"algoPhpass": {
"description": "AlgoPHPass",
@@ -8795,7 +8929,10 @@
},
"required": [
"type"
]
],
"example": {
"type": "phpass"
}
},
"algoBcrypt": {
"description": "AlgoBcrypt",
@@ -8809,7 +8946,10 @@
},
"required": [
"type"
]
],
"example": {
"type": "bcrypt"
}
},
"algoScrypt": {
"description": "AlgoScrypt",
@@ -8851,7 +8991,14 @@
"costMemory",
"costParallel",
"length"
]
],
"example": {
"type": "scrypt",
"costCpu": 8,
"costMemory": 14,
"costParallel": 1,
"length": 64
}
},
"algoScryptModified": {
"description": "AlgoScryptModified",
@@ -8883,7 +9030,13 @@
"salt",
"saltSeparator",
"signerKey"
]
],
"example": {
"type": "scryptMod",
"salt": "UxLMreBr6tYyjQ==",
"saltSeparator": "Bw==",
"signerKey": "XyEKE9RcTDeLEsL\/RjwPDBv\/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ=="
}
},
"algoArgon2": {
"description": "AlgoArgon2",
@@ -8918,12 +9071,23 @@
"memoryCost",
"timeCost",
"threads"
]
],
"example": {
"type": "argon2",
"memoryCost": 65536,
"timeCost": 4,
"threads": 3
}
},
"preferences": {
"description": "Preferences",
"type": "object",
"additionalProperties": true
"additionalProperties": true,
"example": {
"language": "en",
"timezone": "UTC",
"darkTheme": true
}
},
"session": {
"description": "Session",
@@ -9110,7 +9274,40 @@
"factors",
"secret",
"mfaUpdatedAt"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"userId": "5e5bb8c16897e",
"expire": "2020-10-15T06:38:00.000+00:00",
"provider": "email",
"providerUid": "user@example.com",
"providerAccessToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"providerAccessTokenExpiry": "2020-10-15T06:38:00.000+00:00",
"providerRefreshToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"ip": "127.0.0.1",
"osCode": "Mac",
"osName": "Mac",
"osVersion": "Mac",
"clientType": "browser",
"clientCode": "CM",
"clientName": "Chrome Mobile iOS",
"clientVersion": "84.0",
"clientEngine": "WebKit",
"clientEngineVersion": "605.1.15",
"deviceName": "smartphone",
"deviceBrand": "Google",
"deviceModel": "Nexus 5",
"countryCode": "US",
"countryName": "United States",
"current": true,
"factors": [
"email"
],
"secret": "5e5bb8c16897e",
"mfaUpdatedAt": "2020-10-15T06:38:00.000+00:00"
}
},
"identity": {
"description": "Identity",
@@ -9178,7 +9375,19 @@
"providerAccessToken",
"providerAccessTokenExpiry",
"providerRefreshToken"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"userId": "5e5bb8c16897e",
"provider": "email",
"providerUid": "5e5bb8c16897e",
"providerEmail": "user@example.com",
"providerAccessToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"providerAccessTokenExpiry": "2020-10-15T06:38:00.000+00:00",
"providerRefreshToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3"
}
},
"token": {
"description": "Token",
@@ -9222,7 +9431,15 @@
"secret",
"expire",
"phrase"
]
],
"example": {
"$id": "bb8ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"userId": "5e5ea5c168bb8",
"secret": "",
"expire": "2020-10-15T06:38:00.000+00:00",
"phrase": "Golden Fox"
}
},
"jwt": {
"description": "JWT",
@@ -9236,7 +9453,10 @@
},
"required": [
"jwt"
]
],
"example": {
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}
},
"locale": {
"description": "Locale",
@@ -9286,7 +9506,16 @@
"continent",
"eu",
"currency"
]
],
"example": {
"ip": "127.0.0.1",
"countryCode": "US",
"country": "United States",
"continentCode": "NA",
"continent": "North America",
"eu": false,
"currency": "USD"
}
},
"localeCode": {
"description": "LocaleCode",
@@ -9306,7 +9535,11 @@
"required": [
"code",
"name"
]
],
"example": {
"code": "en-us",
"name": "US"
}
},
"file": {
"description": "File",
@@ -9388,7 +9621,22 @@
"sizeOriginal",
"chunksTotal",
"chunksUploaded"
]
],
"example": {
"$id": "5e5ea5c16897e",
"bucketId": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"$permissions": [
"read(\"any\")"
],
"name": "Pink.png",
"signature": "5d529fd02b544198ae075bd57c1762bb",
"mimeType": "image\/png",
"sizeOriginal": 17890,
"chunksTotal": 17890,
"chunksUploaded": 17890
}
},
"team": {
"description": "Team",
@@ -9439,7 +9687,18 @@
"name",
"total",
"prefs"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"name": "VIP",
"total": 7,
"prefs": {
"theme": "pink",
"timezone": "UTC"
}
}
},
"membership": {
"description": "Membership",
@@ -9530,7 +9789,24 @@
"confirm",
"mfa",
"roles"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"userId": "5e5ea5c16897e",
"userName": "John Doe",
"userEmail": "john@appwrite.io",
"teamId": "5e5ea5c16897e",
"teamName": "VIP",
"invited": "2020-10-15T06:38:00.000+00:00",
"joined": "2020-10-15T06:38:00.000+00:00",
"confirm": false,
"mfa": false,
"roles": [
"owner"
]
}
},
"execution": {
"description": "Execution",
@@ -9661,7 +9937,36 @@
"logs",
"errors",
"duration"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"$permissions": [
"any"
],
"functionId": "5e5ea6g16897e",
"trigger": "http",
"status": "processing",
"requestMethod": "GET",
"requestPath": "\/articles?id=5",
"requestHeaders": [
{
"Content-Type": "application\/json"
}
],
"responseStatusCode": 200,
"responseBody": "",
"responseHeaders": [
{
"Content-Type": "application\/json"
}
],
"logs": "",
"errors": "",
"duration": 0.4,
"scheduledAt": "2020-10-15T06:38:00.000+00:00"
}
},
"country": {
"description": "Country",
@@ -9681,7 +9986,11 @@
"required": [
"name",
"code"
]
],
"example": {
"name": "United States",
"code": "US"
}
},
"continent": {
"description": "Continent",
@@ -9701,7 +10010,11 @@
"required": [
"name",
"code"
]
],
"example": {
"name": "Europe",
"code": "EU"
}
},
"language": {
"description": "Language",
@@ -9727,7 +10040,12 @@
"name",
"code",
"nativeName"
]
],
"example": {
"name": "Italian",
"code": "it",
"nativeName": "Italiano"
}
},
"currency": {
"description": "Currency",
@@ -9779,7 +10097,16 @@
"rounding",
"code",
"namePlural"
]
],
"example": {
"symbol": "$",
"name": "US dollar",
"symbolNative": "$",
"decimalDigits": 2,
"rounding": 0,
"code": "USD",
"namePlural": "US dollars"
}
},
"phone": {
"description": "Phone",
@@ -9805,7 +10132,12 @@
"code",
"countryCode",
"countryName"
]
],
"example": {
"code": "+1",
"countryCode": "US",
"countryName": "United States"
}
},
"headers": {
"description": "Headers",
@@ -9825,7 +10157,11 @@
"required": [
"name",
"value"
]
],
"example": {
"name": "Content-Type",
"value": "application\/json"
}
},
"mfaChallenge": {
"description": "MFA Challenge",
@@ -9857,7 +10193,13 @@
"$createdAt",
"userId",
"expire"
]
],
"example": {
"$id": "bb8ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"userId": "5e5ea5c168bb8",
"expire": "2020-10-15T06:38:00.000+00:00"
}
},
"mfaRecoveryCodes": {
"description": "MFA Recovery Codes",
@@ -9877,7 +10219,13 @@
},
"required": [
"recoveryCodes"
]
],
"example": {
"recoveryCodes": [
"a3kf0-s0cl2",
"s0co1-as98s"
]
}
},
"mfaType": {
"description": "MFAType",
@@ -9897,7 +10245,11 @@
"required": [
"secret",
"uri"
]
],
"example": {
"secret": true,
"uri": true
}
},
"mfaFactors": {
"description": "MFAFactors",
@@ -9929,7 +10281,13 @@
"phone",
"email",
"recoveryCode"
]
],
"example": {
"totp": true,
"phone": true,
"email": true,
"recoveryCode": true
}
},
"subscriber": {
"description": "Subscriber",
@@ -10003,7 +10361,27 @@
"userName",
"topicId",
"providerType"
]
],
"example": {
"$id": "259125845563242502",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"targetId": "259125845563242502",
"target": {
"$id": "259125845563242502",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"providerType": "email",
"providerId": "259125845563242502",
"name": "ageon-app-email",
"identifier": "random-mail@email.org",
"userId": "5e5ea5c16897e"
},
"userId": "5e5ea5c16897e",
"userName": "Aegon Targaryen",
"topicId": "259125845563242502",
"providerType": "email"
}
},
"target": {
"description": "Target",
@@ -10065,7 +10443,18 @@
"providerType",
"identifier",
"expired"
]
],
"example": {
"$id": "259125845563242502",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"name": "Apple iPhone 12",
"userId": "259125845563242502",
"providerId": "259125845563242502",
"providerType": "email",
"identifier": "token",
"expired": false
}
}
},
"securitySchemes": {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+432 -49
View File
@@ -5461,7 +5461,7 @@
"type": "string",
"description": "Scheduled execution time in [ISO 8601](https:\/\/www.iso.org\/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.",
"default": null,
"x-example": null
"x-example": "<SCHEDULED_AT>"
}
}
}
@@ -8094,7 +8094,8 @@
"any": {
"description": "Any",
"type": "object",
"additionalProperties": true
"additionalProperties": true,
"example": []
},
"documentList": {
"description": "Documents List",
@@ -8119,7 +8120,11 @@
"required": [
"total",
"documents"
]
],
"example": {
"total": 5,
"documents": ""
}
},
"sessionList": {
"description": "Sessions List",
@@ -8144,7 +8149,11 @@
"required": [
"total",
"sessions"
]
],
"example": {
"total": 5,
"sessions": ""
}
},
"identityList": {
"description": "Identities List",
@@ -8169,7 +8178,11 @@
"required": [
"total",
"identities"
]
],
"example": {
"total": 5,
"identities": ""
}
},
"logList": {
"description": "Logs List",
@@ -8194,7 +8207,11 @@
"required": [
"total",
"logs"
]
],
"example": {
"total": 5,
"logs": ""
}
},
"fileList": {
"description": "Files List",
@@ -8219,7 +8236,11 @@
"required": [
"total",
"files"
]
],
"example": {
"total": 5,
"files": ""
}
},
"teamList": {
"description": "Teams List",
@@ -8244,7 +8265,11 @@
"required": [
"total",
"teams"
]
],
"example": {
"total": 5,
"teams": ""
}
},
"membershipList": {
"description": "Memberships List",
@@ -8269,7 +8294,11 @@
"required": [
"total",
"memberships"
]
],
"example": {
"total": 5,
"memberships": ""
}
},
"executionList": {
"description": "Executions List",
@@ -8294,7 +8323,11 @@
"required": [
"total",
"executions"
]
],
"example": {
"total": 5,
"executions": ""
}
},
"countryList": {
"description": "Countries List",
@@ -8319,7 +8352,11 @@
"required": [
"total",
"countries"
]
],
"example": {
"total": 5,
"countries": ""
}
},
"continentList": {
"description": "Continents List",
@@ -8344,7 +8381,11 @@
"required": [
"total",
"continents"
]
],
"example": {
"total": 5,
"continents": ""
}
},
"languageList": {
"description": "Languages List",
@@ -8369,7 +8410,11 @@
"required": [
"total",
"languages"
]
],
"example": {
"total": 5,
"languages": ""
}
},
"currencyList": {
"description": "Currencies List",
@@ -8394,7 +8439,11 @@
"required": [
"total",
"currencies"
]
],
"example": {
"total": 5,
"currencies": ""
}
},
"phoneList": {
"description": "Phones List",
@@ -8419,7 +8468,11 @@
"required": [
"total",
"phones"
]
],
"example": {
"total": 5,
"phones": ""
}
},
"localeCodeList": {
"description": "Locale codes list",
@@ -8444,7 +8497,11 @@
"required": [
"total",
"localeCodes"
]
],
"example": {
"total": 5,
"localeCodes": ""
}
},
"document": {
"description": "Document",
@@ -8501,7 +8558,23 @@
"$createdAt",
"$updatedAt",
"$permissions"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$sequence": 1,
"$collectionId": "5e5ea5c15117e",
"$databaseId": "5e5ea5c15117e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"$permissions": [
"read(\"any\")"
],
"username": "john.doe",
"email": "john.doe@example.com",
"fullName": "John Doe",
"age": 30,
"isAdmin": false
}
},
"log": {
"description": "Log",
@@ -8635,7 +8708,30 @@
"deviceModel",
"countryCode",
"countryName"
]
],
"example": {
"event": "account.sessions.create",
"userId": "610fc2f985ee0",
"userEmail": "john@appwrite.io",
"userName": "John Doe",
"mode": "admin",
"ip": "127.0.0.1",
"time": "2020-10-15T06:38:00.000+00:00",
"osCode": "Mac",
"osName": "Mac",
"osVersion": "Mac",
"clientType": "browser",
"clientCode": "CM",
"clientName": "Chrome Mobile iOS",
"clientVersion": "84.0",
"clientEngine": "WebKit",
"clientEngineVersion": "605.1.15",
"deviceName": "smartphone",
"deviceBrand": "Google",
"deviceModel": "Nexus 5",
"countryCode": "US",
"countryName": "United States"
}
},
"user": {
"description": "User",
@@ -8798,7 +8894,33 @@
"prefs",
"targets",
"accessedAt"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"name": "John Doe",
"password": "$argon2id$v=19$m=2048,t=4,p=3$aUZjLnliVWRINmFNTWMudg$5S+x+7uA31xFnrHFT47yFwcJeaP0w92L\/4LdgrVRXxE",
"hash": "argon2",
"hashOptions": {},
"registration": "2020-10-15T06:38:00.000+00:00",
"status": true,
"labels": [
"vip"
],
"passwordUpdate": "2020-10-15T06:38:00.000+00:00",
"email": "john@appwrite.io",
"phone": "+4930901820",
"emailVerification": true,
"phoneVerification": true,
"mfa": true,
"prefs": {
"theme": "pink",
"timezone": "UTC"
},
"targets": [],
"accessedAt": "2020-10-15T06:38:00.000+00:00"
}
},
"algoMd5": {
"description": "AlgoMD5",
@@ -8812,7 +8934,10 @@
},
"required": [
"type"
]
],
"example": {
"type": "md5"
}
},
"algoSha": {
"description": "AlgoSHA",
@@ -8826,7 +8951,10 @@
},
"required": [
"type"
]
],
"example": {
"type": "sha"
}
},
"algoPhpass": {
"description": "AlgoPHPass",
@@ -8840,7 +8968,10 @@
},
"required": [
"type"
]
],
"example": {
"type": "phpass"
}
},
"algoBcrypt": {
"description": "AlgoBcrypt",
@@ -8854,7 +8985,10 @@
},
"required": [
"type"
]
],
"example": {
"type": "bcrypt"
}
},
"algoScrypt": {
"description": "AlgoScrypt",
@@ -8896,7 +9030,14 @@
"costMemory",
"costParallel",
"length"
]
],
"example": {
"type": "scrypt",
"costCpu": 8,
"costMemory": 14,
"costParallel": 1,
"length": 64
}
},
"algoScryptModified": {
"description": "AlgoScryptModified",
@@ -8928,7 +9069,13 @@
"salt",
"saltSeparator",
"signerKey"
]
],
"example": {
"type": "scryptMod",
"salt": "UxLMreBr6tYyjQ==",
"saltSeparator": "Bw==",
"signerKey": "XyEKE9RcTDeLEsL\/RjwPDBv\/RqDl8fb3gpYEOQaPihbxf1ZAtSOHCjuAAa7Q3oHpCYhXSN9tizHgVOwn6krflQ=="
}
},
"algoArgon2": {
"description": "AlgoArgon2",
@@ -8963,12 +9110,23 @@
"memoryCost",
"timeCost",
"threads"
]
],
"example": {
"type": "argon2",
"memoryCost": 65536,
"timeCost": 4,
"threads": 3
}
},
"preferences": {
"description": "Preferences",
"type": "object",
"additionalProperties": true
"additionalProperties": true,
"example": {
"language": "en",
"timezone": "UTC",
"darkTheme": true
}
},
"session": {
"description": "Session",
@@ -9155,7 +9313,40 @@
"factors",
"secret",
"mfaUpdatedAt"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"userId": "5e5bb8c16897e",
"expire": "2020-10-15T06:38:00.000+00:00",
"provider": "email",
"providerUid": "user@example.com",
"providerAccessToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"providerAccessTokenExpiry": "2020-10-15T06:38:00.000+00:00",
"providerRefreshToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"ip": "127.0.0.1",
"osCode": "Mac",
"osName": "Mac",
"osVersion": "Mac",
"clientType": "browser",
"clientCode": "CM",
"clientName": "Chrome Mobile iOS",
"clientVersion": "84.0",
"clientEngine": "WebKit",
"clientEngineVersion": "605.1.15",
"deviceName": "smartphone",
"deviceBrand": "Google",
"deviceModel": "Nexus 5",
"countryCode": "US",
"countryName": "United States",
"current": true,
"factors": [
"email"
],
"secret": "5e5bb8c16897e",
"mfaUpdatedAt": "2020-10-15T06:38:00.000+00:00"
}
},
"identity": {
"description": "Identity",
@@ -9223,7 +9414,19 @@
"providerAccessToken",
"providerAccessTokenExpiry",
"providerRefreshToken"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"userId": "5e5bb8c16897e",
"provider": "email",
"providerUid": "5e5bb8c16897e",
"providerEmail": "user@example.com",
"providerAccessToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"providerAccessTokenExpiry": "2020-10-15T06:38:00.000+00:00",
"providerRefreshToken": "MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3"
}
},
"token": {
"description": "Token",
@@ -9267,7 +9470,15 @@
"secret",
"expire",
"phrase"
]
],
"example": {
"$id": "bb8ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"userId": "5e5ea5c168bb8",
"secret": "",
"expire": "2020-10-15T06:38:00.000+00:00",
"phrase": "Golden Fox"
}
},
"jwt": {
"description": "JWT",
@@ -9281,7 +9492,10 @@
},
"required": [
"jwt"
]
],
"example": {
"jwt": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
}
},
"locale": {
"description": "Locale",
@@ -9331,7 +9545,16 @@
"continent",
"eu",
"currency"
]
],
"example": {
"ip": "127.0.0.1",
"countryCode": "US",
"country": "United States",
"continentCode": "NA",
"continent": "North America",
"eu": false,
"currency": "USD"
}
},
"localeCode": {
"description": "LocaleCode",
@@ -9351,7 +9574,11 @@
"required": [
"code",
"name"
]
],
"example": {
"code": "en-us",
"name": "US"
}
},
"file": {
"description": "File",
@@ -9433,7 +9660,22 @@
"sizeOriginal",
"chunksTotal",
"chunksUploaded"
]
],
"example": {
"$id": "5e5ea5c16897e",
"bucketId": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"$permissions": [
"read(\"any\")"
],
"name": "Pink.png",
"signature": "5d529fd02b544198ae075bd57c1762bb",
"mimeType": "image\/png",
"sizeOriginal": 17890,
"chunksTotal": 17890,
"chunksUploaded": 17890
}
},
"team": {
"description": "Team",
@@ -9485,7 +9727,18 @@
"name",
"total",
"prefs"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"name": "VIP",
"total": 7,
"prefs": {
"theme": "pink",
"timezone": "UTC"
}
}
},
"membership": {
"description": "Membership",
@@ -9576,7 +9829,24 @@
"confirm",
"mfa",
"roles"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"userId": "5e5ea5c16897e",
"userName": "John Doe",
"userEmail": "john@appwrite.io",
"teamId": "5e5ea5c16897e",
"teamName": "VIP",
"invited": "2020-10-15T06:38:00.000+00:00",
"joined": "2020-10-15T06:38:00.000+00:00",
"confirm": false,
"mfa": false,
"roles": [
"owner"
]
}
},
"execution": {
"description": "Execution",
@@ -9709,7 +9979,36 @@
"logs",
"errors",
"duration"
]
],
"example": {
"$id": "5e5ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"$permissions": [
"any"
],
"functionId": "5e5ea6g16897e",
"trigger": "http",
"status": "processing",
"requestMethod": "GET",
"requestPath": "\/articles?id=5",
"requestHeaders": [
{
"Content-Type": "application\/json"
}
],
"responseStatusCode": 200,
"responseBody": "",
"responseHeaders": [
{
"Content-Type": "application\/json"
}
],
"logs": "",
"errors": "",
"duration": 0.4,
"scheduledAt": "2020-10-15T06:38:00.000+00:00"
}
},
"country": {
"description": "Country",
@@ -9729,7 +10028,11 @@
"required": [
"name",
"code"
]
],
"example": {
"name": "United States",
"code": "US"
}
},
"continent": {
"description": "Continent",
@@ -9749,7 +10052,11 @@
"required": [
"name",
"code"
]
],
"example": {
"name": "Europe",
"code": "EU"
}
},
"language": {
"description": "Language",
@@ -9775,7 +10082,12 @@
"name",
"code",
"nativeName"
]
],
"example": {
"name": "Italian",
"code": "it",
"nativeName": "Italiano"
}
},
"currency": {
"description": "Currency",
@@ -9827,7 +10139,16 @@
"rounding",
"code",
"namePlural"
]
],
"example": {
"symbol": "$",
"name": "US dollar",
"symbolNative": "$",
"decimalDigits": 2,
"rounding": 0,
"code": "USD",
"namePlural": "US dollars"
}
},
"phone": {
"description": "Phone",
@@ -9853,7 +10174,12 @@
"code",
"countryCode",
"countryName"
]
],
"example": {
"code": "+1",
"countryCode": "US",
"countryName": "United States"
}
},
"headers": {
"description": "Headers",
@@ -9873,7 +10199,11 @@
"required": [
"name",
"value"
]
],
"example": {
"name": "Content-Type",
"value": "application\/json"
}
},
"mfaChallenge": {
"description": "MFA Challenge",
@@ -9905,7 +10235,13 @@
"$createdAt",
"userId",
"expire"
]
],
"example": {
"$id": "bb8ea5c16897e",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"userId": "5e5ea5c168bb8",
"expire": "2020-10-15T06:38:00.000+00:00"
}
},
"mfaRecoveryCodes": {
"description": "MFA Recovery Codes",
@@ -9925,7 +10261,13 @@
},
"required": [
"recoveryCodes"
]
],
"example": {
"recoveryCodes": [
"a3kf0-s0cl2",
"s0co1-as98s"
]
}
},
"mfaType": {
"description": "MFAType",
@@ -9945,7 +10287,11 @@
"required": [
"secret",
"uri"
]
],
"example": {
"secret": true,
"uri": true
}
},
"mfaFactors": {
"description": "MFAFactors",
@@ -9977,7 +10323,13 @@
"phone",
"email",
"recoveryCode"
]
],
"example": {
"totp": true,
"phone": true,
"email": true,
"recoveryCode": true
}
},
"subscriber": {
"description": "Subscriber",
@@ -10052,7 +10404,27 @@
"userName",
"topicId",
"providerType"
]
],
"example": {
"$id": "259125845563242502",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"targetId": "259125845563242502",
"target": {
"$id": "259125845563242502",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"providerType": "email",
"providerId": "259125845563242502",
"name": "ageon-app-email",
"identifier": "random-mail@email.org",
"userId": "5e5ea5c16897e"
},
"userId": "5e5ea5c16897e",
"userName": "Aegon Targaryen",
"topicId": "259125845563242502",
"providerType": "email"
}
},
"target": {
"description": "Target",
@@ -10114,7 +10486,18 @@
"providerType",
"identifier",
"expired"
]
],
"example": {
"$id": "259125845563242502",
"$createdAt": "2020-10-15T06:38:00.000+00:00",
"$updatedAt": "2020-10-15T06:38:00.000+00:00",
"name": "Apple iPhone 12",
"userId": "259125845563242502",
"providerId": "259125845563242502",
"providerType": "email",
"identifier": "token",
"expired": false
}
}
},
"externalDocs": {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+33 -9
View File
@@ -10,6 +10,7 @@ use Appwrite\URL\URL as URLParse;
use Appwrite\Utopia\Response;
use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
use enshrined\svgSanitize\Sanitizer as SvgSanitizer;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Database;
@@ -362,7 +363,8 @@ App::get('/v1/avatars/favicon')
$client = new Client();
try {
$res = $client
->setAllowRedirects(false)
->setAllowRedirects(true)
->setMaxRedirects(5)
->setUserAgent(\sprintf(
APP_USERAGENT,
System::getEnv('_APP_VERSION', 'UNKNOWN'),
@@ -373,15 +375,11 @@ App::get('/v1/avatars/favicon')
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
if ($res->getStatusCode() !== 200) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
$doc = new DOMDocument();
$doc->strictErrorChecking = false;
@$doc->loadHTML($res->getBody());
$links = $doc->getElementsByTagName('link');
$links = $doc->getElementsByTagName('link') ?? [];
$outputHref = '';
$outputExt = '';
$space = 0;
@@ -399,6 +397,12 @@ App::get('/v1/avatars/favicon')
$ext = \pathinfo(\parse_url($absolute, PHP_URL_PATH), PATHINFO_EXTENSION);
switch ($ext) {
case 'svg':
// SVG icons are prioritized by assigning the maximum possible value.
$space = PHP_INT_MAX;
$outputHref = $absolute;
$outputExt = $ext;
break;
case 'ico':
case 'png':
case 'jpg':
@@ -437,7 +441,8 @@ App::get('/v1/avatars/favicon')
$client = new Client();
try {
$res = $client
->setAllowRedirects(false)
->setAllowRedirects(true)
->setMaxRedirects(5)
->fetch($outputHref);
} catch (\Throwable) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
@@ -449,14 +454,33 @@ App::get('/v1/avatars/favicon')
$data = $res->getBody();
if ('ico' == $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
if (empty($data) || (\mb_substr($data, 0, 5) === '<html') || \mb_substr($data, 0, 5) === '<!doc') {
if ('ico' === $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
if (
empty($data) ||
stripos($data, '<html') === 0 ||
stripos($data, '<!doc') === 0
) {
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND, 'Favicon not found');
}
$response
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType('image/x-icon')
->file($data);
return;
}
if ('svg' === $outputExt) { // Skip crop, Imagick isn\'t supporting svg files
$sanitizer = new SvgSanitizer();
$sanitizer->minify(true);
$cleanSvg = $sanitizer->sanitize($data);
if ($cleanSvg === false) {
throw new \Exception('SVG sanitization failed');
}
$response
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType('image/svg+xml')
->file($cleanSvg);
return;
}
$image = new Image($data);
+4 -2
View File
@@ -1407,9 +1407,10 @@ App::get('/robots.txt')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
$host = $request->getHostname() ?? '';
$consoleDomain = System::getEnv('_APP_CONSOLE_DOMAIN', '');
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if (($host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
if (($host === $consoleDomain || $host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
@@ -1440,9 +1441,10 @@ App::get('/humans.txt')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
$host = $request->getHostname() ?? '';
$consoleDomain = System::getEnv('_APP_CONSOLE_DOMAIN', '');
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if (($host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
if (($host === $consoleDomain || $host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {
+6 -1
View File
@@ -31,6 +31,7 @@ use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Queue\Publisher;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Validator\WhiteList;
$parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) {
@@ -421,7 +422,8 @@ App::init()
->inject('apiKey')
->inject('plan')
->inject('devKey')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey) use ($usageDatabaseListener, $eventDatabaseListener) {
->inject('telemetry')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry) use ($usageDatabaseListener, $eventDatabaseListener) {
$route = $utopia->getRoute();
@@ -554,6 +556,7 @@ App::init()
));
$useCache = $route->getLabel('cache', false);
$storageCacheOperationsCounter = $telemetry->createCounter('storage.cache.operations.load');
if ($useCache) {
$route = $utopia->match($request);
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
@@ -619,10 +622,12 @@ App::init()
->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp))
->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($cacheLog->getAttribute('mimeType'));
$storageCacheOperationsCounter->add(1, ['result' => 'hit']);
if (!$isImageTransformation || !$isDisabled) {
$response->send($data);
}
} else {
$storageCacheOperationsCounter->add(1, ['result' => 'miss']);
$response
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
->addHeader('Pragma', 'no-cache')
+2 -1
View File
@@ -82,7 +82,8 @@
"adhocore/jwt": "1.1.*",
"spomky-labs/otphp": "^10.0",
"webonyx/graphql-php": "14.11.*",
"league/csv": "9.14.*"
"league/csv": "9.14.*",
"enshrined/svg-sanitize": "0.21.*"
},
"require-dev": {
"ext-fileinfo": "*",
Generated
+47 -2
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "8dd2ec8206f7c2dc31fef4de39569ae8",
"content-hash": "b6ec099ac43f7f5955b3f7d54ef21764",
"packages": [
{
"name": "adhocore/jwt",
@@ -628,6 +628,51 @@
],
"time": "2023-08-10T19:36:49+00:00"
},
{
"name": "enshrined/svg-sanitize",
"version": "0.21.0",
"source": {
"type": "git",
"url": "https://github.com/darylldoyle/svg-sanitizer.git",
"reference": "5e477468fac5c5ce933dce53af3e8e4e58dcccc9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/5e477468fac5c5ce933dce53af3e8e4e58dcccc9",
"reference": "5e477468fac5c5ce933dce53af3e8e4e58dcccc9",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^6.5 || ^8.5"
},
"type": "library",
"autoload": {
"psr-4": {
"enshrined\\svgSanitize\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "Daryll Doyle",
"email": "daryll@enshrined.co.uk"
}
],
"description": "An SVG sanitizer for PHP",
"support": {
"issues": "https://github.com/darylldoyle/svg-sanitizer/issues",
"source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.21.0"
},
"time": "2025-01-13T09:32:25+00:00"
},
{
"name": "giggsey/libphonenumber-for-php-lite",
"version": "8.13.36",
@@ -8263,7 +8308,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": [],
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
+2 -1
View File
@@ -1,10 +1,11 @@
# Change Log
## 0.14.1
## 0.15.0
* Add `incrementDocumentAttribute` and `decrementDocumentAttribute` support to `Databases` service
* Add `encrypt` support to `StringAttribute` model
* Add `sequence` support to `Document` model
* Fix: pass enum value as string in API params
## 0.14.0
@@ -122,6 +122,19 @@ class Maintenance extends Action
Console::info("[{$time}] Found " . \count($certificates) . " certificates for renewal, scheduling jobs.");
foreach ($certificates as $certificate) {
$domain = $certificate->getAttribute('domain');
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = $dbForPlatform->getDocument('rules', md5($domain));
} else {
$rule = $dbForPlatform->findOne('rules', [
Query::equal('domain', [$domain]),
]);
}
if ($rule->isEmpty() || $rule->getAttribute('region') !== System::getEnv('_APP_REGION', 'default')) {
continue;
}
$queueForCertificate
->setDomain(new Document([
'domain' => $certificate->getAttribute('domain')
+1 -1
View File
@@ -119,7 +119,7 @@ class SDKs extends Action
$gettingStarted = ($gettingStarted) ? \file_get_contents($gettingStarted) : '';
$examples = \realpath(__DIR__ . '/../../../../docs/sdks/' . $language['key'] . '/EXAMPLES.md');
$examples = ($examples) ? \file_get_contents($examples) : '';
$changelog = \realpath(__DIR__ . '/../../../../docs/sdks/' . $language['key'] . '/CHANGELOG.md');
$changelog = $language['changelog'] ?? '';
$changelog = ($changelog) ? \file_get_contents($changelog) : '# Change Log';
$warning = '**This SDK is compatible with Appwrite server version ' . $version . '. For older versions, please check [previous releases](' . $language['url'] . '/releases).**';
$license = 'BSD-3-Clause';
@@ -632,6 +632,7 @@ class OpenAPI3 extends Format
$required = $model->getRequired();
$rules = $model->getRules();
$examples = [];
$output['components']['schemas'][$model->getType()] = [
'description' => $model->getName(),
@@ -655,6 +656,8 @@ class OpenAPI3 extends Format
$format = null;
$items = null;
$examples[$name] = $rule['example'] ?? null;
switch ($rule['type']) {
case 'string':
case 'datetime':
@@ -743,6 +746,12 @@ class OpenAPI3 extends Format
$output['components']['schemas'][$model->getType()]['properties'][$name]['nullable'] = true;
}
}
if ($model->isAny() && !empty($model->getSampleData())) {
$examples = array_merge($examples, $model->getSampleData());
}
$output['components']['schemas'][$model->getType()]['example'] = $examples;
}
\ksort($output['paths']);
@@ -642,6 +642,7 @@ class Swagger2 extends Format
$required = $model->getRequired();
$rules = $model->getRules();
$examples = [];
$output['definitions'][$model->getType()] = [
'description' => $model->getName(),
@@ -665,6 +666,8 @@ class Swagger2 extends Format
$format = null;
$items = null;
$examples[$name] = $rule['example'] ?? null;
switch ($rule['type']) {
case 'string':
case 'datetime':
@@ -762,6 +765,12 @@ class Swagger2 extends Format
$output['definitions'][$model->getType()]['properties'][$name]['x-nullable'] = true;
}
}
if ($model->isAny() && !empty($model->getSampleData())) {
$examples = array_merge($examples, $model->getSampleData());
}
$output['definitions'][$model->getType()]['example'] = $examples;
}
\ksort($output['paths']);
@@ -31,4 +31,14 @@ class Any extends Model
{
return Response::MODEL_ANY;
}
/**
* Get sample data
*
* @return array
*/
public function getSampleData(): array
{
return [];
}
}
@@ -95,4 +95,15 @@ class Document extends Any
return $document;
}
public function getSampleData(): array
{
return [
'username' => 'john.doe',
'email' => 'john.doe@example.com',
'fullName' => 'John Doe',
'age' => 30,
'isAdmin' => false,
];
}
}
@@ -25,4 +25,13 @@ class Preferences extends Any
{
return Response::MODEL_PREFERENCES;
}
public function getSampleData(): array
{
return [
'language' => 'en',
'timezone' => 'UTC',
'darkTheme' => true,
];
}
}
+11 -1
View File
@@ -284,7 +284,17 @@ trait AvatarsBase
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('image/x-icon', $response['headers']['content-type']);
$this->assertEquals('image/svg+xml', $response['headers']['content-type']);
$this->assertNotEmpty($response['body']);
$response = $this->client->call(Client::METHOD_GET, '/avatars/favicon', [
'x-appwrite-project' => $this->getProject()['$id'],
], [
'url' => 'https://appwrite.io/',
]);
$this->assertEquals(200, $response['headers']['status-code']);
$this->assertEquals('image/svg+xml', $response['headers']['content-type']);
$this->assertNotEmpty($response['body']);
/**
@@ -5700,6 +5700,26 @@ trait DatabasesBase
]), ['min' => 7]);
$this->assertEquals(400, $err['headers']['status-code']);
// Test min limit exceeded with custom value
$err = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'value' => 3,
'min' => 5,
]);
$this->assertEquals(400, $err['headers']['status-code']);
// Test min limit 0
$err = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
]), [
'value' => 10,
'min' => 0,
]);
$this->assertEquals(400, $err['headers']['status-code']);
// Test type error on non-numeric attribute
$typeErr = $this->client->call(Client::METHOD_PATCH, '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $documentId . '/count/decrement', array_merge([
'content-type' => 'application/json',
@@ -900,9 +900,9 @@ trait MigrationsBase
/**
* Import documents from a CSV file.
*/
public function testCreateCsvMigration(): array
public function testCreateCsvMigration(): void
{
// make a database
// Make a database
$response = $this->client->call(Client::METHOD_POST, '/databases', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -991,6 +991,7 @@ trait MigrationsBase
'missing-row' => $bucketOneId,
'missing-column' => $bucketOneId,
'irrelevant-column' => $bucketOneId,
'documents-internals' => $bucketOneId,
];
$fileIds = [];
@@ -999,7 +1000,8 @@ trait MigrationsBase
$csvFileName = match ($label) {
'missing-row',
'missing-column',
'irrelevant-column' => "{$label}.csv",
'irrelevant-column',
'documents-internals' => "{$label}.csv",
default => 'documents.csv',
};
@@ -1123,7 +1125,7 @@ trait MigrationsBase
);
}, 60000, 500);
// all data exists, pass/
// all data exists, pass.
$migration = $this->performCsvMigration(
[
'endpoint' => 'http://localhost/v1',
@@ -1133,32 +1135,8 @@ trait MigrationsBase
]
);
$this->assertEmpty($migration['body']['statusCounters']);
$this->assertEquals('CSV', $migration['body']['source']);
$this->assertEquals('pending', $migration['body']['status']);
$this->assertEquals('Appwrite', $migration['body']['destination']);
$this->assertContains(Resource::TYPE_ROW, $migration['body']['resources']);
return [
'databaseId' => $databaseId,
'tableId' => $tableId,
'migrationId' => $migration['body']['$id'],
];
}
/**
* @depends testCreateCsvMigration
*/
public function testImportSuccessful(array $response): void
{
$tableId = $response['tableId'];
$databaseId = $response['databaseId'];
$migrationId = $response['migrationId'];
$rowsCountInCSV = 100;
// get migration stats
$this->assertEventually(function () use ($migrationId, $databaseId, $tableId, $rowsCountInCSV) {
$this->assertEventually(function () use ($migration, $databaseId, $collectionId) {
$migrationId = $migration['body']['$id'];
$migration = $this->client->call(Client::METHOD_GET, '/migrations/'.$migrationId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -1171,8 +1149,8 @@ trait MigrationsBase
$this->assertEquals('Appwrite', $migration['body']['destination']);
$this->assertContains(Resource::TYPE_ROW, $migration['body']['resources']);
$this->assertArrayHasKey(Resource::TYPE_ROW, $migration['body']['statusCounters']);
$this->assertEquals($rowsCountInCSV, $migration['body']['statusCounters'][Resource::TYPE_ROW]['success']);
}, 1000, 500);
$this->assertEquals(100, $migration['body']['statusCounters'][Resource::TYPE_ROW]['success']);
}, 10_000, 500);
// get rows count
$rows = $this->client->call(Client::METHOD_GET, '/databases/'.$databaseId.'/grids/tables/'.$tableId.'/rows', array_merge([
@@ -1180,15 +1158,41 @@ trait MigrationsBase
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()), [
'queries' => [
// there should be only 100!
Query::limit(150)->toString()
]
]);
$this->assertEquals(200, $rows['headers']['status-code']);
$this->assertIsArray($rows['body']['rows']);
$this->assertIsNumeric($rows['body']['total']);
$this->assertEquals($rowsCountInCSV, $rows['body']['total']);
$this->assertEquals(200, $documents['headers']['status-code']);
$this->assertIsArray($documents['body']['documents']);
$this->assertIsNumeric($documents['body']['total']);
$this->assertEquals(100, $documents['body']['total']);
// all data exists and includes internals, pass.
$migration = $this->performCsvMigration(
[
'endpoint' => 'http://localhost/v1',
'fileId' => $fileIds['documents-internals'],
'bucketId' => $bucketIds['documents-internals'],
'resourceId' => $databaseId . ':' . $collectionId,
]
);
$this->assertEventually(function () use ($migration, $databaseId, $collectionId) {
$migrationId = $migration['body']['$id'];
$migration = $this->client->call(Client::METHOD_GET, '/migrations/'.$migrationId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $migration['headers']['status-code']);
$this->assertEquals('finished', $migration['body']['stage']);
$this->assertEquals('completed', $migration['body']['status']);
$this->assertEquals('CSV', $migration['body']['source']);
$this->assertEquals('Appwrite', $migration['body']['destination']);
$this->assertContains(Resource::TYPE_ROW, $migration['body']['resources']);
$this->assertArrayHasKey(Resource::TYPE_ROW, $migration['body']['statusCounters']);
$this->assertEquals(25, $migration['body']['statusCounters'][Resource::TYPE_ROW]['success']);
}, 10_000, 500);
}
private function performCsvMigration(array $body): array
@@ -0,0 +1,26 @@
$id,$createdAt,$updatedAt,$permissions,name,age
z1y2x3w4v5u6t7s8,2022-10-23T10:33:01+00:00,2023-03-15T12:00:41+00:00,"read(\"any\"),update(\"user:123\")",Diamond Mendez,56
r9q0p1o2n3m4l5k6,2021-08-11T21:05:13+00:00,2024-01-02T08:45:22+00:00,"read(\"any\"),update(\"user:456\")",Michael Huff,20
j7i8h9g0f1e2d3c4,2020-05-29T14:22:56+00:00,2022-11-30T18:19:33+00:00,"read(\"any\")",Alyssa Rodriguez,37
b5a6z7y8x9w0v1u2,2023-01-18T03:44:09+00:00,2023-09-07T23:50:17+00:00,"read(\"any\")",Barbara Smith,26
t3s4r5q6p7o8n9m0,2020-11-02T09:12:45+00:00,2021-07-21T15:30:55+00:00,"read(\"any\")",Evelyn Edwards,54
l1k2j3i4h5g6f7e8,2022-03-19T19:55:27+00:00,2024-05-14T06:28:11+00:00,"read(\"any\")",Tina Richardson,41
d9c0b1a2z3y4x5w6,2021-04-07T01:18:34+00:00,2023-06-25T11:47:04+00:00,"read(\"any\")",Joel Hernandez,49
v7u8t9s0r1q2p3o4,2023-08-22T16:40:18+00:00,2024-02-19T04:09:58+00:00,"read(\"any\")",Zachary Cooper,59
n5m6l7k8j9i0h1g2,2020-02-12T07:59:01+00:00,2022-09-08T13:21:49+00:00,"read(\"any\")",Brittany Spears,20
f3e4d5c6b7a8z9y0,2021-12-05T22:33:12+00:00,2023-11-11T02:55:37+00:00,"read(\"any\")",Holly White,47
x1w2v3u4t5s6r7q8,2022-07-14T05:01:50+00:00,2024-04-01T20:10:26+00:00,"read(\"any\")",Kimberly Barnes,27
p9o0n1m2l3k4j5i6,2020-09-28T11:27:36+00:00,2021-10-17T09:38:08+00:00,"read(\"any\")",Stephen Miller,53
h7g8f9e0d1c2b3a4,2023-04-04T08:15:59+00:00,2024-06-29T17:03:14+00:00,"read(\"any\")",Yvonne Newman,41
y5x6w7v8u9t0s1r2,2021-01-25T18:09:21+00:00,2022-08-16T22:44:51+00:00,"read(\"any\")",Carol Kane,38
q3p4o5n6m7l8k9j0,2022-06-09T12:53:47+00:00,2023-12-24T01:16:05+00:00,"read(\"any\")",Doris Foster,44
i1h2g3f4e5d6c7b8,2020-07-03T23:37:02+00:00,2021-05-09T05:52:43+00:00,"read(\"any\")",Joseph Stokes,28
a9z0y1x2w3v4u5t6,2023-10-10T02:20:15+00:00,2024-03-28T14:33:29+00:00,"read(\"any\")",Steve Williams,31
s7r8q9p0o1n2m3l4,2021-06-16T13:48:53+00:00,2022-04-22T07:07:19+00:00,"read(\"any\")",James Carey,29
k5j6i7h8g9f0e1d2,2022-12-27T20:06:38+00:00,2023-08-03T10:25:57+00:00,"read(\"any\")",Kathryn Henry,38
c3b4a5z6y7x8w9v0,2020-04-20T04:41:24+00:00,2021-02-13T19:14:06+00:00,"read(\"any\")",Christopher Landry,23
u1t2s3r4q5p6o7n8,2023-05-08T00:58:10+00:00,2024-07-05T03:36:48+00:00,"read(\"any\")",Jennifer Mcgee,62
m9l0k1j2i3h4g5f6,2021-09-01T06:11:42+00:00,2022-01-26T16:59:23+00:00,"read(\"any\")",Cathy Church,35
e7d8c9b0a1z2y3x4,2022-02-18T15:24:07+00:00,2023-04-12T00:40:31+00:00,"read(\"any\")",Jose Lopez,41
w5v6u7t8s9r0q1p2,2020-12-13T09:03:55+00:00,2021-11-06T11:23:16+00:00,"read(\"any\")",William Rose,30
o3n4m5l6k7j8i9h0,2021-12-13T09:03:55+00:00,2022-11-06T11:23:16+00:00,"read(\"any\")",Charles Hammer,61
Can't render this file because it contains an unexpected character in line 2 and column 77.