mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
70
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38ef0ad399 | ||
|
|
5e4c74d501 | ||
|
|
fe8593fecc | ||
|
|
b07674ccd3 | ||
|
|
aa5e3bf9a9 | ||
|
|
c1b50e9941 | ||
|
|
3a76249d80 | ||
|
|
b2991709dc | ||
|
|
fcc07533e1 | ||
|
|
3a6d907b42 | ||
|
|
d411b741b3 | ||
|
|
bc19e66775 | ||
|
|
08c9db7c60 | ||
|
|
a9c9f602ec | ||
|
|
d90b4c2f19 | ||
|
|
79d733486c | ||
|
|
d862245090 | ||
|
|
aacaadcce0 | ||
|
|
69ee6e0659 | ||
|
|
cfd62cf0a1 | ||
|
|
279682941a | ||
|
|
eae26d90af | ||
|
|
b46ebb95a3 | ||
|
|
ffcb6a4677 | ||
|
|
142e359dba | ||
|
|
4130983210 | ||
|
|
e01ac65ce8 | ||
|
|
97454c0768 | ||
|
|
8b4a56cd9b | ||
|
|
da0a6b167f | ||
|
|
27d0c511ac | ||
|
|
8a58f7b6d7 | ||
|
|
cc97d78b5a | ||
|
|
91c24b671b | ||
|
|
b28e536967 | ||
|
|
ce7f40dd34 | ||
|
|
49318b5dac | ||
|
|
6096c42ff9 | ||
|
|
5afae248ae | ||
|
|
98255fe628 | ||
|
|
7c408d675e | ||
|
|
4d47bc2ebf | ||
|
|
119d6c1789 | ||
|
|
1f0e3e0f51 | ||
|
|
fe9d49c346 | ||
|
|
60e57eb9c5 | ||
|
|
10a9845898 | ||
|
|
6a41f8b2b9 | ||
|
|
503145734b | ||
|
|
371cab0658 | ||
|
|
ca8cb6f260 | ||
|
|
fea1630909 | ||
|
|
e40e88c322 | ||
|
|
037d60633b | ||
|
|
218bb4c537 | ||
|
|
c13a70e226 | ||
|
|
5963b2baf7 | ||
|
|
7d4486b9e6 | ||
|
|
1a19e01d69 | ||
|
|
2a145bc284 | ||
|
|
9fe3e94a66 | ||
|
|
8e32fb05d1 | ||
|
|
de8538b622 | ||
|
|
fca2727966 | ||
|
|
cb03bfe74c | ||
|
|
2b1e3bd947 | ||
|
|
4858317891 | ||
|
|
6e16340f16 | ||
|
|
ac604b11e3 | ||
|
|
f68d49c4d6 |
@@ -364,6 +364,61 @@ return [
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('emailCanonical'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 320,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('emailIsFree'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('emailIsDisposable'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('emailIsCorporate'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('emailIsCanonical'),
|
||||
'type' => Database::VAR_BOOLEAN,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
|
||||
@@ -2345,7 +2345,7 @@ return [
|
||||
'$id' => ID::custom('errors'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 65535,
|
||||
'size' => 1_000_000,
|
||||
'signed' => true,
|
||||
'required' => true,
|
||||
'default' => null,
|
||||
|
||||
@@ -273,7 +273,7 @@ return [
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'screenshotSleep' => 5000,
|
||||
'buildRuntime' => 'flutter-3.29',
|
||||
'buildRuntime' => 'flutter-3.35',
|
||||
'runtimes' => getVersions($templateRuntimes['FLUTTER']['versions'], 'flutter'),
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
@@ -282,6 +282,7 @@ return [
|
||||
'installCommand' => 'flutter pub get',
|
||||
'outputDirectory' => './build/web',
|
||||
'startCommand' => 'bash helpers/server.sh',
|
||||
'fallbackFile' => 'index.html'
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
@@ -60,7 +60,7 @@ return [
|
||||
[
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
'version' => '20.3.0',
|
||||
'version' => '20.3.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-flutter',
|
||||
'package' => 'https://pub.dev/packages/appwrite',
|
||||
'enabled' => true,
|
||||
@@ -376,7 +376,7 @@ return [
|
||||
[
|
||||
'key' => 'dart',
|
||||
'name' => 'Dart',
|
||||
'version' => '19.3.0',
|
||||
'version' => '19.4.0',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-dart',
|
||||
'package' => 'https://pub.dev/packages/dart_appwrite',
|
||||
'enabled' => true,
|
||||
|
||||
@@ -13105,6 +13105,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -13131,7 +13132,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -13746,6 +13748,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -13772,7 +13775,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -30954,6 +30958,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -30980,7 +30985,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -31601,6 +31607,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -31627,7 +31634,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
|
||||
@@ -12127,6 +12127,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -12153,7 +12154,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -12529,6 +12531,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -12555,7 +12558,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -21703,6 +21707,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -21729,7 +21734,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -22122,6 +22128,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -22148,7 +22155,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
|
||||
@@ -13105,6 +13105,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -13131,7 +13132,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -13746,6 +13748,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -13772,7 +13775,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -14359,10 +14363,22 @@
|
||||
"description": "Path to function code in the template repo.",
|
||||
"x-example": "<ROOT_DIRECTORY>"
|
||||
},
|
||||
"version": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Version (tag) for the repo linked to the function template.",
|
||||
"x-example": "<VERSION>"
|
||||
"description": "Type for the reference provided. Can be commit, branch, or tag",
|
||||
"x-example": "commit",
|
||||
"enum": [
|
||||
"commit",
|
||||
"branch",
|
||||
"tag"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "Reference value, can be a commit hash, branch name, or release tag",
|
||||
"x-example": "<REFERENCE>"
|
||||
},
|
||||
"activate": {
|
||||
"type": "boolean",
|
||||
@@ -14374,7 +14390,8 @@
|
||||
"repository",
|
||||
"owner",
|
||||
"rootDirectory",
|
||||
"version"
|
||||
"type",
|
||||
"reference"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -30954,6 +30971,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -30980,7 +30998,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -31601,6 +31620,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -31627,7 +31647,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -32176,10 +32197,22 @@
|
||||
"description": "Path to site code in the template repo.",
|
||||
"x-example": "<ROOT_DIRECTORY>"
|
||||
},
|
||||
"version": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Version (tag) for the repo linked to the site template.",
|
||||
"x-example": "<VERSION>"
|
||||
"description": "Type for the reference provided. Can be commit, branch, or tag",
|
||||
"x-example": "branch",
|
||||
"enum": [
|
||||
"branch",
|
||||
"commit",
|
||||
"tag"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "Reference value, can be a commit hash, branch name, or release tag",
|
||||
"x-example": "<REFERENCE>"
|
||||
},
|
||||
"activate": {
|
||||
"type": "boolean",
|
||||
@@ -32191,7 +32224,8 @@
|
||||
"repository",
|
||||
"owner",
|
||||
"rootDirectory",
|
||||
"version"
|
||||
"type",
|
||||
"reference"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12127,6 +12127,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -12153,7 +12154,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -12529,6 +12531,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -12555,7 +12558,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -13148,10 +13152,22 @@
|
||||
"description": "Path to function code in the template repo.",
|
||||
"x-example": "<ROOT_DIRECTORY>"
|
||||
},
|
||||
"version": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Version (tag) for the repo linked to the function template.",
|
||||
"x-example": "<VERSION>"
|
||||
"description": "Type for the reference provided. Can be commit, branch, or tag",
|
||||
"x-example": "commit",
|
||||
"enum": [
|
||||
"commit",
|
||||
"branch",
|
||||
"tag"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "Reference value, can be a commit hash, branch name, or release tag",
|
||||
"x-example": "<REFERENCE>"
|
||||
},
|
||||
"activate": {
|
||||
"type": "boolean",
|
||||
@@ -13163,7 +13179,8 @@
|
||||
"repository",
|
||||
"owner",
|
||||
"rootDirectory",
|
||||
"version"
|
||||
"type",
|
||||
"reference"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -21703,6 +21720,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -21729,7 +21747,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -22122,6 +22141,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -22148,7 +22168,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -22703,10 +22724,22 @@
|
||||
"description": "Path to site code in the template repo.",
|
||||
"x-example": "<ROOT_DIRECTORY>"
|
||||
},
|
||||
"version": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Version (tag) for the repo linked to the site template.",
|
||||
"x-example": "<VERSION>"
|
||||
"description": "Type for the reference provided. Can be commit, branch, or tag",
|
||||
"x-example": "branch",
|
||||
"enum": [
|
||||
"branch",
|
||||
"commit",
|
||||
"tag"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "Reference value, can be a commit hash, branch name, or release tag",
|
||||
"x-example": "<REFERENCE>"
|
||||
},
|
||||
"activate": {
|
||||
"type": "boolean",
|
||||
@@ -22718,7 +22751,8 @@
|
||||
"repository",
|
||||
"owner",
|
||||
"rootDirectory",
|
||||
"version"
|
||||
"type",
|
||||
"reference"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13041,6 +13041,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -13067,7 +13068,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -13683,6 +13685,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -13709,7 +13712,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -31058,6 +31062,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -31084,7 +31089,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -31708,6 +31714,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -31734,7 +31741,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
|
||||
@@ -12078,6 +12078,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -12104,7 +12105,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -12493,6 +12495,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -12519,7 +12522,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -21845,6 +21849,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -21871,7 +21876,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -22277,6 +22283,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -22303,7 +22310,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
|
||||
@@ -13041,6 +13041,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -13067,7 +13068,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -13683,6 +13685,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -13709,7 +13712,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -14302,11 +14306,24 @@
|
||||
"default": null,
|
||||
"x-example": "<ROOT_DIRECTORY>"
|
||||
},
|
||||
"version": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Version (tag) for the repo linked to the function template.",
|
||||
"description": "Type for the reference provided. Can be commit, branch, or tag",
|
||||
"default": null,
|
||||
"x-example": "<VERSION>"
|
||||
"x-example": "commit",
|
||||
"enum": [
|
||||
"commit",
|
||||
"branch",
|
||||
"tag"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "Reference value, can be a commit hash, branch name, or release tag",
|
||||
"default": null,
|
||||
"x-example": "<REFERENCE>"
|
||||
},
|
||||
"activate": {
|
||||
"type": "boolean",
|
||||
@@ -14319,7 +14336,8 @@
|
||||
"repository",
|
||||
"owner",
|
||||
"rootDirectory",
|
||||
"version"
|
||||
"type",
|
||||
"reference"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -31058,6 +31076,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -31084,7 +31103,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -31708,6 +31728,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -31734,7 +31755,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -32284,11 +32306,24 @@
|
||||
"default": null,
|
||||
"x-example": "<ROOT_DIRECTORY>"
|
||||
},
|
||||
"version": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Version (tag) for the repo linked to the site template.",
|
||||
"description": "Type for the reference provided. Can be commit, branch, or tag",
|
||||
"default": null,
|
||||
"x-example": "<VERSION>"
|
||||
"x-example": "branch",
|
||||
"enum": [
|
||||
"branch",
|
||||
"commit",
|
||||
"tag"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "Reference value, can be a commit hash, branch name, or release tag",
|
||||
"default": null,
|
||||
"x-example": "<REFERENCE>"
|
||||
},
|
||||
"activate": {
|
||||
"type": "boolean",
|
||||
@@ -32301,7 +32336,8 @@
|
||||
"repository",
|
||||
"owner",
|
||||
"rootDirectory",
|
||||
"version"
|
||||
"type",
|
||||
"reference"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12078,6 +12078,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -12104,7 +12105,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -12493,6 +12495,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -12519,7 +12522,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -13118,11 +13122,24 @@
|
||||
"default": null,
|
||||
"x-example": "<ROOT_DIRECTORY>"
|
||||
},
|
||||
"version": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Version (tag) for the repo linked to the function template.",
|
||||
"description": "Type for the reference provided. Can be commit, branch, or tag",
|
||||
"default": null,
|
||||
"x-example": "<VERSION>"
|
||||
"x-example": "commit",
|
||||
"enum": [
|
||||
"commit",
|
||||
"branch",
|
||||
"tag"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "Reference value, can be a commit hash, branch name, or release tag",
|
||||
"default": null,
|
||||
"x-example": "<REFERENCE>"
|
||||
},
|
||||
"activate": {
|
||||
"type": "boolean",
|
||||
@@ -13135,7 +13152,8 @@
|
||||
"repository",
|
||||
"owner",
|
||||
"rootDirectory",
|
||||
"version"
|
||||
"type",
|
||||
"reference"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -21845,6 +21863,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -21871,7 +21890,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -22277,6 +22297,7 @@
|
||||
"dart-3.3",
|
||||
"dart-3.5",
|
||||
"dart-3.8",
|
||||
"dart-3.9",
|
||||
"dotnet-6.0",
|
||||
"dotnet-7.0",
|
||||
"dotnet-8.0",
|
||||
@@ -22303,7 +22324,8 @@
|
||||
"flutter-3.24",
|
||||
"flutter-3.27",
|
||||
"flutter-3.29",
|
||||
"flutter-3.32"
|
||||
"flutter-3.32",
|
||||
"flutter-3.35"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
@@ -22859,11 +22881,24 @@
|
||||
"default": null,
|
||||
"x-example": "<ROOT_DIRECTORY>"
|
||||
},
|
||||
"version": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "Version (tag) for the repo linked to the site template.",
|
||||
"description": "Type for the reference provided. Can be commit, branch, or tag",
|
||||
"default": null,
|
||||
"x-example": "<VERSION>"
|
||||
"x-example": "branch",
|
||||
"enum": [
|
||||
"branch",
|
||||
"commit",
|
||||
"tag"
|
||||
],
|
||||
"x-enum-name": null,
|
||||
"x-enum-keys": []
|
||||
},
|
||||
"reference": {
|
||||
"type": "string",
|
||||
"description": "Reference value, can be a commit hash, branch name, or release tag",
|
||||
"default": null,
|
||||
"x-example": "<REFERENCE>"
|
||||
},
|
||||
"activate": {
|
||||
"type": "boolean",
|
||||
@@ -22876,7 +22911,8 @@
|
||||
"repository",
|
||||
"owner",
|
||||
"rootDirectory",
|
||||
"version"
|
||||
"type",
|
||||
"reference"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ return [
|
||||
],
|
||||
'DART' => [
|
||||
'name' => 'dart',
|
||||
'versions' => ['3.8', '3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
|
||||
'versions' => ['3.9', '3.8', '3.5', '3.3', '3.1', '3.0', '2.19', '2.18', '2.17', '2.16']
|
||||
],
|
||||
'GO' => [
|
||||
'name' => 'go',
|
||||
@@ -38,6 +38,6 @@ return [
|
||||
],
|
||||
'FLUTTER' => [
|
||||
'name' => 'flutter',
|
||||
'versions' => ['3.32', '3.24']
|
||||
'versions' => ['3.35', '3.32', '3.24']
|
||||
],
|
||||
];
|
||||
|
||||
@@ -84,7 +84,7 @@ const TEMPLATE_FRAMEWORKS = [
|
||||
'installCommand' => '',
|
||||
'buildCommand' => 'flutter build web',
|
||||
'outputDirectory' => './build/web',
|
||||
'buildRuntime' => 'flutter-3.29',
|
||||
'buildRuntime' => 'flutter-3.35',
|
||||
'adapter' => 'static',
|
||||
'fallbackFile' => '',
|
||||
],
|
||||
|
||||
@@ -20,7 +20,7 @@ use Appwrite\Event\Messaging;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Email as EmailValidator;
|
||||
use Appwrite\Network\Validator\Redirect;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\SDK\AuthType;
|
||||
@@ -57,6 +57,7 @@ use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Emails\Email;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Storage\Validator\FileName;
|
||||
use Utopia\System\System;
|
||||
@@ -337,7 +338,7 @@ App::post('/v1/account')
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'New user password. Must be between 8 and 256 chars.', false, ['project', 'passwordsDictionary'])
|
||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
||||
->inject('request')
|
||||
@@ -394,6 +395,13 @@ App::post('/v1/account')
|
||||
|
||||
$passwordHistory = $project->getAttribute('auths', [])['passwordHistory'] ?? 0;
|
||||
$password = Auth::passwordHash($password, Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS);
|
||||
|
||||
try {
|
||||
$emailCanonical = new Email($email);
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
}
|
||||
|
||||
try {
|
||||
$userId = $userId == 'unique()' ? ID::unique() : $userId;
|
||||
$user->setAttributes([
|
||||
@@ -422,7 +430,13 @@ App::post('/v1/account')
|
||||
'authenticators' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'accessedAt' => DateTime::now(),
|
||||
'emailCanonical' => $emailCanonical?->getCanonical(),
|
||||
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
|
||||
'emailIsCorporate' => $emailCanonical?->isCorporate(),
|
||||
'emailIsDisposable' => $emailCanonical?->isDisposable(),
|
||||
'emailIsFree' => $emailCanonical?->isFree(),
|
||||
]);
|
||||
|
||||
$user->removeAttribute('$sequence');
|
||||
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
|
||||
try {
|
||||
@@ -903,7 +917,7 @@ App::post('/v1/account/sessions/email')
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},email:{param-email}')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -1598,6 +1612,12 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$failureRedirect(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
}
|
||||
|
||||
try {
|
||||
$emailCanonical = new Email($email);
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
}
|
||||
|
||||
try {
|
||||
$userId = ID::unique();
|
||||
$user->setAttributes([
|
||||
@@ -1625,7 +1645,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
'authenticators' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'accessedAt' => DateTime::now(),
|
||||
'emailCanonical' => $emailCanonical?->getCanonical(),
|
||||
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
|
||||
'emailIsCorporate' => $emailCanonical?->isCorporate(),
|
||||
'emailIsDisposable' => $emailCanonical?->isDisposable(),
|
||||
'emailIsFree' => $emailCanonical?->isFree(),
|
||||
]);
|
||||
|
||||
$user->removeAttribute('$sequence');
|
||||
$userDoc = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
|
||||
$dbForProject->createDocument('targets', new Document([
|
||||
@@ -1696,6 +1722,18 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
|
||||
if (empty($user->getAttribute('email'))) {
|
||||
$user->setAttribute('email', $oauth2->getUserEmail($accessToken));
|
||||
|
||||
try {
|
||||
$emailCanonical = new Email($user->getAttribute('email'));
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
}
|
||||
|
||||
$user->setAttribute('emailCanonical', $emailCanonical?->getCanonical());
|
||||
$user->setAttribute('emailIsCanonical', $emailCanonical?->isCanonicalSupported());
|
||||
$user->setAttribute('emailIsCorporate', $emailCanonical?->isCorporate());
|
||||
$user->setAttribute('emailIsDisposable', $emailCanonical?->isDisposable());
|
||||
$user->setAttribute('emailIsFree', $emailCanonical?->isFree());
|
||||
}
|
||||
|
||||
if (empty($user->getAttribute('name'))) {
|
||||
@@ -1944,7 +1982,7 @@ App::post('/v1/account/tokens/magic-url')
|
||||
->label('abuse-limit', 60)
|
||||
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
|
||||
->param('userId', '', new CustomId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['platforms', 'devKey'])
|
||||
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
|
||||
->inject('request')
|
||||
@@ -1990,6 +2028,12 @@ App::post('/v1/account/tokens/magic-url')
|
||||
|
||||
$userId = $userId === 'unique()' ? ID::unique() : $userId;
|
||||
|
||||
try {
|
||||
$emailCanonical = new Email($email);
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
}
|
||||
|
||||
$user->setAttributes([
|
||||
'$id' => $userId,
|
||||
'$permissions' => [
|
||||
@@ -2014,6 +2058,11 @@ App::post('/v1/account/tokens/magic-url')
|
||||
'authenticators' => null,
|
||||
'search' => implode(' ', [$userId, $email]),
|
||||
'accessedAt' => DateTime::now(),
|
||||
'emailCanonical' => $emailCanonical?->getCanonical(),
|
||||
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
|
||||
'emailIsCorporate' => $emailCanonical?->isCorporate(),
|
||||
'emailIsDisposable' => $emailCanonical?->isDisposable(),
|
||||
'emailIsFree' => $emailCanonical?->isFree(),
|
||||
]);
|
||||
|
||||
$user->removeAttribute('$sequence');
|
||||
@@ -2197,7 +2246,7 @@ App::post('/v1/account/tokens/email')
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars. If the email address has never been used, a new account is created using the provided userId. Otherwise, if the email address is already attached to an account, the user ID is ignored.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('phrase', false, new Boolean(), 'Toggle for security phrase. If enabled, email will be send with a randomly generated phrase and the phrase will also be included in the response. Confirming phrases match increases the security of your authentication flow.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -2240,6 +2289,12 @@ App::post('/v1/account/tokens/email')
|
||||
|
||||
$userId = $userId === 'unique()' ? ID::unique() : $userId;
|
||||
|
||||
try {
|
||||
$emailCanonical = new Email($email);
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
}
|
||||
|
||||
$user->setAttributes([
|
||||
'$id' => $userId,
|
||||
'$permissions' => [
|
||||
@@ -2262,6 +2317,11 @@ App::post('/v1/account/tokens/email')
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $email]),
|
||||
'accessedAt' => DateTime::now(),
|
||||
'emailCanonical' => $emailCanonical?->getCanonical(),
|
||||
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
|
||||
'emailIsCorporate' => $emailCanonical?->isCorporate(),
|
||||
'emailIsDisposable' => $emailCanonical?->isDisposable(),
|
||||
'emailIsFree' => $emailCanonical?->isFree(),
|
||||
]);
|
||||
|
||||
$user->removeAttribute('$sequence');
|
||||
@@ -2609,6 +2669,11 @@ App::post('/v1/account/tokens/phone')
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $phone]),
|
||||
'accessedAt' => DateTime::now(),
|
||||
'emailCanonical' => null,
|
||||
'emailIsCanonical' => null,
|
||||
'emailIsCorporate' => null,
|
||||
'emailIsDisposable' => null,
|
||||
'emailIsFree' => null,
|
||||
]);
|
||||
|
||||
$user->removeAttribute('$sequence');
|
||||
@@ -3037,7 +3102,7 @@ App::patch('/v1/account/email')
|
||||
],
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password. Must be at least 8 chars.')
|
||||
->inject('requestTimestamp')
|
||||
->inject('response')
|
||||
@@ -3072,9 +3137,20 @@ App::patch('/v1/account/email')
|
||||
throw new Exception(Exception::GENERAL_BAD_REQUEST); /** Return a generic bad request to prevent exposing existing accounts */
|
||||
}
|
||||
|
||||
try {
|
||||
$emailCanonical = new Email($email);
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
}
|
||||
|
||||
$user
|
||||
->setAttribute('email', $email)
|
||||
->setAttribute('emailVerification', false) // After this user needs to confirm mail again
|
||||
->setAttribute('emailCanonical', $emailCanonical?->getCanonical())
|
||||
->setAttribute('emailIsCanonical', $emailCanonical?->isCanonicalSupported())
|
||||
->setAttribute('emailIsCorporate', $emailCanonical?->isCorporate())
|
||||
->setAttribute('emailIsDisposable', $emailCanonical?->isDisposable())
|
||||
->setAttribute('emailIsFree', $emailCanonical?->isFree())
|
||||
;
|
||||
|
||||
if (empty($passwordUpdate)) {
|
||||
@@ -3311,7 +3387,7 @@ App::post('/v1/account/recovery')
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', ['url:{url},email:{param-email}', 'url:{url},ip:{ip}'])
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('url', '', fn ($platforms, $devKey) => $devKey->isEmpty() ? new Redirect($platforms) : new URL(), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['platforms', 'devKey'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
|
||||
@@ -10,7 +10,7 @@ use Appwrite\Event\Mail;
|
||||
use Appwrite\Event\Messaging;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Email as EmailValidator;
|
||||
use Appwrite\Network\Validator\Redirect;
|
||||
use Appwrite\Platform\Workers\Deletes;
|
||||
use Appwrite\SDK\AuthType;
|
||||
@@ -48,6 +48,7 @@ use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Emails\Email;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\ArrayList;
|
||||
@@ -468,7 +469,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('email', '', new Email(), 'Email of the new team member.', true)
|
||||
->param('email', '', new EmailValidator(), 'Email of the new team member.', true)
|
||||
->param('userId', '', new UID(), 'ID of the user to be added to a team.', true)
|
||||
->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
|
||||
->param('roles', [], function (Document $project) {
|
||||
@@ -567,38 +568,52 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
}
|
||||
|
||||
try {
|
||||
$userId = ID::unique();
|
||||
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::read(Role::user($userId)),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'email' => empty($email) ? null : $email,
|
||||
'phone' => empty($phone) ? null : $phone,
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
// TODO: Set password empty?
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS),
|
||||
'hash' => Auth::DEFAULT_ALGO,
|
||||
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
/**
|
||||
* Set the password update time to 0 for users created using
|
||||
* team invite and OAuth to allow password updates without an
|
||||
* old password
|
||||
*/
|
||||
'passwordUpdate' => null,
|
||||
'registration' => DateTime::now(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
'prefs' => new \stdClass(),
|
||||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
])));
|
||||
$emailCanonical = new Email($email);
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
}
|
||||
|
||||
$userId = ID::unique();
|
||||
|
||||
$userDocument = new Document([
|
||||
'$id' => $userId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::read(Role::user($userId)),
|
||||
Permission::update(Role::user($userId)),
|
||||
Permission::delete(Role::user($userId)),
|
||||
],
|
||||
'email' => empty($email) ? null : $email,
|
||||
'phone' => empty($phone) ? null : $phone,
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
// TODO: Set password empty?
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator(), Auth::DEFAULT_ALGO, Auth::DEFAULT_ALGO_OPTIONS),
|
||||
'hash' => Auth::DEFAULT_ALGO,
|
||||
'hashOptions' => Auth::DEFAULT_ALGO_OPTIONS,
|
||||
/**
|
||||
* Set the password update time to 0 for users created using
|
||||
* team invite and OAuth to allow password updates without an
|
||||
* old password
|
||||
*/
|
||||
'passwordUpdate' => null,
|
||||
'registration' => DateTime::now(),
|
||||
'reset' => false,
|
||||
'name' => $name,
|
||||
'prefs' => new \stdClass(),
|
||||
'sessions' => null,
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $email, $name]),
|
||||
'emailCanonical' => $emailCanonical?->getCanonical(),
|
||||
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
|
||||
'emailIsCorporate' => $emailCanonical?->isCorporate(),
|
||||
'emailIsDisposable' => $emailCanonical?->isDisposable(),
|
||||
'emailIsFree' => $emailCanonical?->isFree(),
|
||||
]);
|
||||
|
||||
try {
|
||||
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', $userDocument));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception(Exception::USER_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Email as EmailValidator;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Deprecated;
|
||||
@@ -49,6 +49,7 @@ use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Emails\Email;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\ArrayList;
|
||||
@@ -97,6 +98,12 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$emailCanonical = new Email($email);
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
}
|
||||
|
||||
$password = (!empty($password)) ? ($hash === 'plaintext' ? Auth::passwordHash($password, $hash, $hashOptionsObject) : $password) : null;
|
||||
$user = new Document([
|
||||
'$id' => $userId,
|
||||
@@ -124,6 +131,11 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
|
||||
'tokens' => null,
|
||||
'memberships' => null,
|
||||
'search' => implode(' ', [$userId, $email, $phone, $name]),
|
||||
'emailCanonical' => $emailCanonical?->getCanonical(),
|
||||
'emailIsCanonical' => $emailCanonical?->isCanonicalSupported(),
|
||||
'emailIsCorporate' => $emailCanonical?->isCorporate(),
|
||||
'emailIsDisposable' => $emailCanonical?->isDisposable(),
|
||||
'emailIsFree' => $emailCanonical?->isFree(),
|
||||
]);
|
||||
|
||||
if ($hash === 'plaintext') {
|
||||
@@ -208,7 +220,7 @@ App::post('/v1/users')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', null, new Email(), 'User email.', true)
|
||||
->param('email', null, new EmailValidator(), 'User email.', true)
|
||||
->param('phone', null, new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true)
|
||||
->param('password', '', fn ($project, $passwordsDictionary) => new PasswordDictionary($passwordsDictionary, $project->getAttribute('auths', [])['passwordDictionary'] ?? false), 'Plain text user password. Must be at least 8 chars.', true, ['project', 'passwordsDictionary'])
|
||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
||||
@@ -243,7 +255,7 @@ App::post('/v1/users/bcrypt')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password hashed using Bcrypt.')
|
||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
@@ -278,7 +290,7 @@ App::post('/v1/users/md5')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password hashed using MD5.')
|
||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
@@ -313,7 +325,7 @@ App::post('/v1/users/argon2')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password hashed using Argon2.')
|
||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
@@ -348,7 +360,7 @@ App::post('/v1/users/sha')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password hashed using SHA.')
|
||||
->param('passwordVersion', '', new WhiteList(['sha1', 'sha224', 'sha256', 'sha384', 'sha512/224', 'sha512/256', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512']), "Optional SHA version used to hash password. Allowed values are: 'sha1', 'sha224', 'sha256', 'sha384', 'sha512/224', 'sha512/256', 'sha512', 'sha3-224', 'sha3-256', 'sha3-384', 'sha3-512'", true)
|
||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
||||
@@ -390,7 +402,7 @@ App::post('/v1/users/phpass')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or pass the string `ID.unique()`to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password hashed using PHPass.')
|
||||
->param('name', '', new Text(128), 'User name. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
@@ -425,7 +437,7 @@ App::post('/v1/users/scrypt')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password hashed using Scrypt.')
|
||||
->param('passwordSalt', '', new Text(128), 'Optional salt used to hash password.')
|
||||
->param('passwordCpu', 8, new Integer(), 'Optional CPU cost used to hash password.')
|
||||
@@ -473,7 +485,7 @@ App::post('/v1/users/scrypt-modified')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new CustomId(), 'User ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('email', '', new EmailValidator(), 'User email.')
|
||||
->param('password', '', new Password(), 'User password hashed using Scrypt Modified.')
|
||||
->param('passwordSalt', '', new Text(128), 'Salt used to hash password.')
|
||||
->param('passwordSaltSeparator', '', new Text(128), 'Salt separator used to hash password.')
|
||||
@@ -527,7 +539,7 @@ App::post('/v1/users/:userId/targets')
|
||||
|
||||
switch ($providerType) {
|
||||
case 'email':
|
||||
$validator = new Email();
|
||||
$validator = new EmailValidator();
|
||||
if (!$validator->isValid($identifier)) {
|
||||
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
|
||||
}
|
||||
@@ -1402,7 +1414,7 @@ App::patch('/v1/users/:userId/email')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('email', '', new Email(allowEmpty: true), 'User email.')
|
||||
->param('email', '', new EmailValidator(allowEmpty: true), 'User email.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
@@ -1437,9 +1449,20 @@ App::patch('/v1/users/:userId/email')
|
||||
|
||||
$oldEmail = $user->getAttribute('email');
|
||||
|
||||
try {
|
||||
$emailCanonical = new Email($email);
|
||||
} catch (Throwable) {
|
||||
$emailCanonical = null;
|
||||
}
|
||||
|
||||
$user
|
||||
->setAttribute('email', $email)
|
||||
->setAttribute('emailVerification', false)
|
||||
->setAttribute('emailCanonical', $emailCanonical?->getCanonical())
|
||||
->setAttribute('emailIsCanonical', $emailCanonical?->isCanonicalSupported())
|
||||
->setAttribute('emailIsCorporate', $emailCanonical?->isCorporate())
|
||||
->setAttribute('emailIsDisposable', $emailCanonical?->isDisposable())
|
||||
->setAttribute('emailIsFree', $emailCanonical?->isFree())
|
||||
;
|
||||
|
||||
try {
|
||||
@@ -1700,7 +1723,7 @@ App::patch('/v1/users/:userId/targets/:targetId')
|
||||
|
||||
switch ($providerType) {
|
||||
case 'email':
|
||||
$validator = new Email();
|
||||
$validator = new EmailValidator();
|
||||
if (!$validator->isValid($identifier)) {
|
||||
throw new Exception(Exception::GENERAL_INVALID_EMAIL);
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
|
||||
use Appwrite\Utopia\Request\Filters\V18 as RequestV18;
|
||||
use Appwrite\Utopia\Request\Filters\V19 as RequestV19;
|
||||
use Appwrite\Utopia\Request\Filters\V20 as RequestV20;
|
||||
use Appwrite\Utopia\Request\Filters\V21 as RequestV21;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
|
||||
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
|
||||
@@ -906,6 +907,9 @@ App::init()
|
||||
$dbForProject = $getProjectDB($project);
|
||||
$request->addFilter(new RequestV20($dbForProject, $route->getPathValues($request)));
|
||||
}
|
||||
if (version_compare($requestFormat, '1.9.0', '<')) {
|
||||
$request->addFilter(new RequestV21());
|
||||
}
|
||||
}
|
||||
|
||||
$domain = $request->getHostname();
|
||||
|
||||
Generated
+24
-24
@@ -161,16 +161,16 @@
|
||||
},
|
||||
{
|
||||
"name": "appwrite/php-runtimes",
|
||||
"version": "0.19.1",
|
||||
"version": "0.19.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/runtimes.git",
|
||||
"reference": "7bd0cc3cb97de625d7b07230bd91b121f88e72ae"
|
||||
"reference": "e5c142519df5aced37de9c302971c29c079ce3d9"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/7bd0cc3cb97de625d7b07230bd91b121f88e72ae",
|
||||
"reference": "7bd0cc3cb97de625d7b07230bd91b121f88e72ae",
|
||||
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/e5c142519df5aced37de9c302971c29c079ce3d9",
|
||||
"reference": "e5c142519df5aced37de9c302971c29c079ce3d9",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -210,9 +210,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/runtimes/issues",
|
||||
"source": "https://github.com/appwrite/runtimes/tree/0.19.1"
|
||||
"source": "https://github.com/appwrite/runtimes/tree/0.19.2"
|
||||
},
|
||||
"time": "2025-05-27T07:12:56+00:00"
|
||||
"time": "2025-11-11T13:44:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "beberlei/assert",
|
||||
@@ -756,16 +756,16 @@
|
||||
},
|
||||
{
|
||||
"name": "google/protobuf",
|
||||
"version": "v4.33.0",
|
||||
"version": "v4.33.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/protocolbuffers/protobuf-php.git",
|
||||
"reference": "b50269e23204e5ae859a326ec3d90f09efe3047d"
|
||||
"reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/b50269e23204e5ae859a326ec3d90f09efe3047d",
|
||||
"reference": "b50269e23204e5ae859a326ec3d90f09efe3047d",
|
||||
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/0cd73ccf0cd26c3e72299cce1ea6144091a57e12",
|
||||
"reference": "0cd73ccf0cd26c3e72299cce1ea6144091a57e12",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -794,9 +794,9 @@
|
||||
"proto"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.0"
|
||||
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.1"
|
||||
},
|
||||
"time": "2025-10-15T20:10:28+00:00"
|
||||
"time": "2025-11-12T21:58:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/csv",
|
||||
@@ -3844,16 +3844,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/database",
|
||||
"version": "3.2.0",
|
||||
"version": "3.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/database.git",
|
||||
"reference": "f2d01b6b38057891184f62107bf70a55bc2ea068"
|
||||
"reference": "e10b4faa4f3a3ef30a5f6d76acdb605469924aec"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/f2d01b6b38057891184f62107bf70a55bc2ea068",
|
||||
"reference": "f2d01b6b38057891184f62107bf70a55bc2ea068",
|
||||
"url": "https://api.github.com/repos/utopia-php/database/zipball/e10b4faa4f3a3ef30a5f6d76acdb605469924aec",
|
||||
"reference": "e10b4faa4f3a3ef30a5f6d76acdb605469924aec",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -3896,9 +3896,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/database/issues",
|
||||
"source": "https://github.com/utopia-php/database/tree/3.2.0"
|
||||
"source": "https://github.com/utopia-php/database/tree/3.4.0"
|
||||
},
|
||||
"time": "2025-11-06T05:41:54+00:00"
|
||||
"time": "2025-11-13T06:34:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/detector",
|
||||
@@ -5383,16 +5383,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "1.5.3",
|
||||
"version": "1.5.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "1a7a3b89147aa8c1bde5247f8eeb7e4832c6016d"
|
||||
"reference": "958947b6483a79e11c3812f23bb3056199fa4105"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/1a7a3b89147aa8c1bde5247f8eeb7e4832c6016d",
|
||||
"reference": "1a7a3b89147aa8c1bde5247f8eeb7e4832c6016d",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/958947b6483a79e11c3812f23bb3056199fa4105",
|
||||
"reference": "958947b6483a79e11c3812f23bb3056199fa4105",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5428,9 +5428,9 @@
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.5.3"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/1.5.4"
|
||||
},
|
||||
"time": "2025-11-10T09:50:41+00:00"
|
||||
"time": "2025-11-12T12:43:42+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Avatars;
|
||||
|
||||
Client client = new Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getScreenshot(
|
||||
"https://example.com", // url
|
||||
mapOf( "a" to "b" ), // headers (optional)
|
||||
1, // viewportWidth (optional)
|
||||
1, // viewportHeight (optional)
|
||||
0.1, // scale (optional)
|
||||
theme.LIGHT, // theme (optional)
|
||||
"<USER_AGENT>", // userAgent (optional)
|
||||
false, // fullpage (optional)
|
||||
"<LOCALE>", // locale (optional)
|
||||
timezone.AFRICA_ABIDJAN, // timezone (optional)
|
||||
-90, // latitude (optional)
|
||||
-180, // longitude (optional)
|
||||
0, // accuracy (optional)
|
||||
false, // touch (optional)
|
||||
listOf(), // permissions (optional)
|
||||
0, // sleep (optional)
|
||||
0, // width (optional)
|
||||
0, // height (optional)
|
||||
-1, // quality (optional)
|
||||
output.JPG, // output (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
Log.d("Appwrite", result.toString());
|
||||
})
|
||||
);
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.coroutines.CoroutineCallback
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
val client = Client(context)
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
|
||||
val avatars = Avatars(client)
|
||||
|
||||
val result = avatars.getScreenshot(
|
||||
url = "https://example.com",
|
||||
headers = mapOf( "a" to "b" ), // (optional)
|
||||
viewportWidth = 1, // (optional)
|
||||
viewportHeight = 1, // (optional)
|
||||
scale = 0.1, // (optional)
|
||||
theme = theme.LIGHT, // (optional)
|
||||
userAgent = "<USER_AGENT>", // (optional)
|
||||
fullpage = false, // (optional)
|
||||
locale = "<LOCALE>", // (optional)
|
||||
timezone = timezone.AFRICA_ABIDJAN, // (optional)
|
||||
latitude = -90, // (optional)
|
||||
longitude = -180, // (optional)
|
||||
accuracy = 0, // (optional)
|
||||
touch = false, // (optional)
|
||||
permissions = listOf(), // (optional)
|
||||
sleep = 0, // (optional)
|
||||
width = 0, // (optional)
|
||||
height = 0, // (optional)
|
||||
quality = -1, // (optional)
|
||||
output = output.JPG, // (optional)
|
||||
)
|
||||
@@ -0,0 +1,32 @@
|
||||
import Appwrite
|
||||
import AppwriteEnums
|
||||
|
||||
let client = Client()
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
|
||||
let avatars = Avatars(client)
|
||||
|
||||
let bytes = try await avatars.getScreenshot(
|
||||
url: "https://example.com",
|
||||
headers: [:], // optional
|
||||
viewportWidth: 1, // optional
|
||||
viewportHeight: 1, // optional
|
||||
scale: 0.1, // optional
|
||||
theme: .light, // optional
|
||||
userAgent: "<USER_AGENT>", // optional
|
||||
fullpage: false, // optional
|
||||
locale: "<LOCALE>", // optional
|
||||
timezone: .africaAbidjan, // optional
|
||||
latitude: -90, // optional
|
||||
longitude: -180, // optional
|
||||
accuracy: 0, // optional
|
||||
touch: false, // optional
|
||||
permissions: [], // optional
|
||||
sleep: 0, // optional
|
||||
width: 0, // optional
|
||||
height: 0, // optional
|
||||
quality: -1, // optional
|
||||
output: .jpg // optional
|
||||
)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import 'package:appwrite/appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
Avatars avatars = Avatars(client);
|
||||
|
||||
// Downloading file
|
||||
UInt8List bytes = await avatars.getScreenshot(
|
||||
url: 'https://example.com',
|
||||
headers: {}, // optional
|
||||
viewportWidth: 1, // optional
|
||||
viewportHeight: 1, // optional
|
||||
scale: 0.1, // optional
|
||||
theme: .light, // optional
|
||||
userAgent: '<USER_AGENT>', // optional
|
||||
fullpage: false, // optional
|
||||
locale: '<LOCALE>', // optional
|
||||
timezone: .africaAbidjan, // optional
|
||||
latitude: -90, // optional
|
||||
longitude: -180, // optional
|
||||
accuracy: 0, // optional
|
||||
touch: false, // optional
|
||||
permissions: [], // optional
|
||||
sleep: 0, // optional
|
||||
width: 0, // optional
|
||||
height: 0, // optional
|
||||
quality: -1, // optional
|
||||
output: .jpg, // optional
|
||||
)
|
||||
|
||||
final file = File('path_to_file/filename.ext');
|
||||
file.writeAsBytesSync(bytes);
|
||||
|
||||
// Displaying image preview
|
||||
FutureBuilder(
|
||||
future: avatars.getScreenshot(
|
||||
url:'https://example.com' ,
|
||||
headers:{} , // optional
|
||||
viewportWidth:1 , // optional
|
||||
viewportHeight:1 , // optional
|
||||
scale:0.1 , // optional
|
||||
theme: .light, // optional
|
||||
userAgent:'<USER_AGENT>' , // optional
|
||||
fullpage:false , // optional
|
||||
locale:'<LOCALE>' , // optional
|
||||
timezone: .africaAbidjan, // optional
|
||||
latitude:-90 , // optional
|
||||
longitude:-180 , // optional
|
||||
accuracy:0 , // optional
|
||||
touch:false , // optional
|
||||
permissions:[] , // optional
|
||||
sleep:0 , // optional
|
||||
width:0 , // optional
|
||||
height:0 , // optional
|
||||
quality:-1 , // optional
|
||||
output: .jpg, // optional
|
||||
), // Works for both public file and private file, for private files you need to be logged in
|
||||
builder: (context, snapshot) {
|
||||
return snapshot.hasData && snapshot.data != null
|
||||
? Image.memory(snapshot.data)
|
||||
: CircularProgressIndicator();
|
||||
}
|
||||
);
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Client, Avatars, , , } from "react-native-appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const avatars = new Avatars(client);
|
||||
|
||||
const result = avatars.getScreenshot({
|
||||
url: 'https://example.com',
|
||||
headers: {}, // optional
|
||||
viewportWidth: 1, // optional
|
||||
viewportHeight: 1, // optional
|
||||
scale: 0.1, // optional
|
||||
theme: .Light, // optional
|
||||
userAgent: '<USER_AGENT>', // optional
|
||||
fullpage: false, // optional
|
||||
locale: '<LOCALE>', // optional
|
||||
timezone: .AfricaAbidjan, // optional
|
||||
latitude: -90, // optional
|
||||
longitude: -180, // optional
|
||||
accuracy: 0, // optional
|
||||
touch: false, // optional
|
||||
permissions: [], // optional
|
||||
sleep: 0, // optional
|
||||
width: 0, // optional
|
||||
height: 0, // optional
|
||||
quality: -1, // optional
|
||||
output: .Jpg // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
@@ -0,0 +1,6 @@
|
||||
GET /v1/avatars/screenshots HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
X-Appwrite-Response-Format: 1.8.0
|
||||
X-Appwrite-Project: <YOUR_PROJECT_ID>
|
||||
X-Appwrite-Session:
|
||||
X-Appwrite-JWT: <YOUR_JWT>
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Client, Avatars, , , } from "appwrite";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const avatars = new Avatars(client);
|
||||
|
||||
const result = avatars.getScreenshot({
|
||||
url: 'https://example.com',
|
||||
headers: {}, // optional
|
||||
viewportWidth: 1, // optional
|
||||
viewportHeight: 1, // optional
|
||||
scale: 0.1, // optional
|
||||
theme: .Light, // optional
|
||||
userAgent: '<USER_AGENT>', // optional
|
||||
fullpage: false, // optional
|
||||
locale: '<LOCALE>', // optional
|
||||
timezone: .AfricaAbidjan, // optional
|
||||
latitude: -90, // optional
|
||||
longitude: -180, // optional
|
||||
accuracy: 0, // optional
|
||||
touch: false, // optional
|
||||
permissions: [], // optional
|
||||
sleep: 0, // optional
|
||||
width: 0, // optional
|
||||
height: 0, // optional
|
||||
quality: -1, // optional
|
||||
output: .Jpg // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
@@ -0,0 +1,32 @@
|
||||
import { Client, Avatars, , , } from "@appwrite.io/console";
|
||||
|
||||
const client = new Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
|
||||
|
||||
const avatars = new Avatars(client);
|
||||
|
||||
const result = avatars.getScreenshot({
|
||||
url: 'https://example.com',
|
||||
headers: {}, // optional
|
||||
viewportWidth: 1, // optional
|
||||
viewportHeight: 1, // optional
|
||||
scale: 0.1, // optional
|
||||
theme: .Light, // optional
|
||||
userAgent: '<USER_AGENT>', // optional
|
||||
fullpage: false, // optional
|
||||
locale: '<LOCALE>', // optional
|
||||
timezone: .AfricaAbidjan, // optional
|
||||
latitude: -90, // optional
|
||||
longitude: -180, // optional
|
||||
accuracy: 0, // optional
|
||||
touch: false, // optional
|
||||
permissions: [], // optional
|
||||
sleep: 0, // optional
|
||||
width: 0, // optional
|
||||
height: 0, // optional
|
||||
quality: -1, // optional
|
||||
output: .Jpg // optional
|
||||
});
|
||||
|
||||
console.log(result);
|
||||
@@ -0,0 +1,31 @@
|
||||
import 'package:dart_appwrite/dart_appwrite.dart';
|
||||
|
||||
Client client = Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setSession(''); // The user session to authenticate with
|
||||
|
||||
Avatars avatars = Avatars(client);
|
||||
|
||||
UInt8List result = await avatars.getScreenshot(
|
||||
url: 'https://example.com',
|
||||
headers: {}, // (optional)
|
||||
viewportWidth: 1, // (optional)
|
||||
viewportHeight: 1, // (optional)
|
||||
scale: 0.1, // (optional)
|
||||
theme: .light, // (optional)
|
||||
userAgent: '<USER_AGENT>', // (optional)
|
||||
fullpage: false, // (optional)
|
||||
locale: '<LOCALE>', // (optional)
|
||||
timezone: .africaAbidjan, // (optional)
|
||||
latitude: -90, // (optional)
|
||||
longitude: -180, // (optional)
|
||||
accuracy: 0, // (optional)
|
||||
touch: false, // (optional)
|
||||
permissions: [], // (optional)
|
||||
sleep: 0, // (optional)
|
||||
width: 0, // (optional)
|
||||
height: 0, // (optional)
|
||||
quality: -1, // (optional)
|
||||
output: .jpg, // (optional)
|
||||
);
|
||||
@@ -0,0 +1,34 @@
|
||||
using Appwrite;
|
||||
using Appwrite.Enums;
|
||||
using Appwrite.Models;
|
||||
using Appwrite.Services;
|
||||
|
||||
Client client = new Client()
|
||||
.SetEndPoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.SetProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
.SetSession(""); // The user session to authenticate with
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
byte[] result = await avatars.GetScreenshot(
|
||||
url: "https://example.com",
|
||||
headers: [object], // optional
|
||||
viewportWidth: 1, // optional
|
||||
viewportHeight: 1, // optional
|
||||
scale: 0.1, // optional
|
||||
theme: .Light, // optional
|
||||
userAgent: "<USER_AGENT>", // optional
|
||||
fullpage: false, // optional
|
||||
locale: "<LOCALE>", // optional
|
||||
timezone: .AfricaAbidjan, // optional
|
||||
latitude: -90, // optional
|
||||
longitude: -180, // optional
|
||||
accuracy: 0, // optional
|
||||
touch: false, // optional
|
||||
permissions: new List<string>(), // optional
|
||||
sleep: 0, // optional
|
||||
width: 0, // optional
|
||||
height: 0, // optional
|
||||
quality: -1, // optional
|
||||
output: .Jpg // optional
|
||||
);
|
||||
@@ -0,0 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/appwrite/sdk-for-go/client"
|
||||
"github.com/appwrite/sdk-for-go/avatars"
|
||||
)
|
||||
|
||||
client := client.New(
|
||||
client.WithEndpoint("https://<REGION>.cloud.appwrite.io/v1")
|
||||
client.WithProject("<YOUR_PROJECT_ID>")
|
||||
client.WithSession("")
|
||||
)
|
||||
|
||||
service := avatars.New(client)
|
||||
|
||||
response, error := service.GetScreenshot(
|
||||
"https://example.com",
|
||||
avatars.WithGetScreenshotHeaders(map[string]interface{}{}),
|
||||
avatars.WithGetScreenshotViewportWidth(1),
|
||||
avatars.WithGetScreenshotViewportHeight(1),
|
||||
avatars.WithGetScreenshotScale(0.1),
|
||||
avatars.WithGetScreenshotTheme("light"),
|
||||
avatars.WithGetScreenshotUserAgent("<USER_AGENT>"),
|
||||
avatars.WithGetScreenshotFullpage(false),
|
||||
avatars.WithGetScreenshotLocale("<LOCALE>"),
|
||||
avatars.WithGetScreenshotTimezone("africa/abidjan"),
|
||||
avatars.WithGetScreenshotLatitude(-90),
|
||||
avatars.WithGetScreenshotLongitude(-180),
|
||||
avatars.WithGetScreenshotAccuracy(0),
|
||||
avatars.WithGetScreenshotTouch(false),
|
||||
avatars.WithGetScreenshotPermissions([]interface{}{}),
|
||||
avatars.WithGetScreenshotSleep(0),
|
||||
avatars.WithGetScreenshotWidth(0),
|
||||
avatars.WithGetScreenshotHeight(0),
|
||||
avatars.WithGetScreenshotQuality(-1),
|
||||
avatars.WithGetScreenshotOutput("jpg"),
|
||||
)
|
||||
@@ -0,0 +1,42 @@
|
||||
import io.appwrite.Client;
|
||||
import io.appwrite.coroutines.CoroutineCallback;
|
||||
import io.appwrite.services.Avatars;
|
||||
|
||||
Client client = new Client()
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
.setSession(""); // The user session to authenticate with
|
||||
|
||||
Avatars avatars = new Avatars(client);
|
||||
|
||||
avatars.getScreenshot(
|
||||
"https://example.com", // url
|
||||
mapOf( "a" to "b" ), // headers (optional)
|
||||
1, // viewportWidth (optional)
|
||||
1, // viewportHeight (optional)
|
||||
0.1, // scale (optional)
|
||||
.LIGHT, // theme (optional)
|
||||
"<USER_AGENT>", // userAgent (optional)
|
||||
false, // fullpage (optional)
|
||||
"<LOCALE>", // locale (optional)
|
||||
.AFRICA_ABIDJAN, // timezone (optional)
|
||||
-90, // latitude (optional)
|
||||
-180, // longitude (optional)
|
||||
0, // accuracy (optional)
|
||||
false, // touch (optional)
|
||||
listOf(), // permissions (optional)
|
||||
0, // sleep (optional)
|
||||
0, // width (optional)
|
||||
0, // height (optional)
|
||||
-1, // quality (optional)
|
||||
.JPG, // output (optional)
|
||||
new CoroutineCallback<>((result, error) -> {
|
||||
if (error != null) {
|
||||
error.printStackTrace();
|
||||
return;
|
||||
}
|
||||
|
||||
System.out.println(result);
|
||||
})
|
||||
);
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import io.appwrite.Client
|
||||
import io.appwrite.coroutines.CoroutineCallback
|
||||
import io.appwrite.services.Avatars
|
||||
|
||||
val client = Client()
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
.setSession("") // The user session to authenticate with
|
||||
|
||||
val avatars = Avatars(client)
|
||||
|
||||
val result = avatars.getScreenshot(
|
||||
url = "https://example.com",
|
||||
headers = mapOf( "a" to "b" ), // optional
|
||||
viewportWidth = 1, // optional
|
||||
viewportHeight = 1, // optional
|
||||
scale = 0.1, // optional
|
||||
theme = "light", // optional
|
||||
userAgent = "<USER_AGENT>", // optional
|
||||
fullpage = false, // optional
|
||||
locale = "<LOCALE>", // optional
|
||||
timezone = "africa/abidjan", // optional
|
||||
latitude = -90, // optional
|
||||
longitude = -180, // optional
|
||||
accuracy = 0, // optional
|
||||
touch = false, // optional
|
||||
permissions = listOf(), // optional
|
||||
sleep = 0, // optional
|
||||
width = 0, // optional
|
||||
height = 0, // optional
|
||||
quality = -1, // optional
|
||||
output = "jpg" // optional
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
const sdk = require('node-appwrite');
|
||||
|
||||
const client = new sdk.Client()
|
||||
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
|
||||
.setProject('<YOUR_PROJECT_ID>') // Your project ID
|
||||
.setSession(''); // The user session to authenticate with
|
||||
|
||||
const avatars = new sdk.Avatars(client);
|
||||
|
||||
const result = await avatars.getScreenshot({
|
||||
url: 'https://example.com',
|
||||
headers: {}, // optional
|
||||
viewportWidth: 1, // optional
|
||||
viewportHeight: 1, // optional
|
||||
scale: 0.1, // optional
|
||||
theme: sdk..Light, // optional
|
||||
userAgent: '<USER_AGENT>', // optional
|
||||
fullpage: false, // optional
|
||||
locale: '<LOCALE>', // optional
|
||||
timezone: sdk..AfricaAbidjan, // optional
|
||||
latitude: -90, // optional
|
||||
longitude: -180, // optional
|
||||
accuracy: 0, // optional
|
||||
touch: false, // optional
|
||||
permissions: [], // optional
|
||||
sleep: 0, // optional
|
||||
width: 0, // optional
|
||||
height: 0, // optional
|
||||
quality: -1, // optional
|
||||
output: sdk..Jpg // optional
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
from appwrite.client import Client
|
||||
from appwrite.services.avatars import Avatars
|
||||
|
||||
client = Client()
|
||||
client.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint
|
||||
client.set_project('<YOUR_PROJECT_ID>') # Your project ID
|
||||
client.set_session('') # The user session to authenticate with
|
||||
|
||||
avatars = Avatars(client)
|
||||
|
||||
result = avatars.get_screenshot(
|
||||
url = 'https://example.com',
|
||||
headers = {}, # optional
|
||||
viewport_width = 1, # optional
|
||||
viewport_height = 1, # optional
|
||||
scale = 0.1, # optional
|
||||
theme = .LIGHT, # optional
|
||||
user_agent = '<USER_AGENT>', # optional
|
||||
fullpage = False, # optional
|
||||
locale = '<LOCALE>', # optional
|
||||
timezone = .AFRICA_ABIDJAN, # optional
|
||||
latitude = -90, # optional
|
||||
longitude = -180, # optional
|
||||
accuracy = 0, # optional
|
||||
touch = False, # optional
|
||||
permissions = [], # optional
|
||||
sleep = 0, # optional
|
||||
width = 0, # optional
|
||||
height = 0, # optional
|
||||
quality = -1, # optional
|
||||
output = .JPG # optional
|
||||
)
|
||||
@@ -0,0 +1,7 @@
|
||||
GET /v1/avatars/screenshots HTTP/1.1
|
||||
Host: cloud.appwrite.io
|
||||
X-Appwrite-Response-Format: 1.8.0
|
||||
X-Appwrite-Project: <YOUR_PROJECT_ID>
|
||||
X-Appwrite-Session:
|
||||
X-Appwrite-Key: <YOUR_API_KEY>
|
||||
X-Appwrite-JWT: <YOUR_JWT>
|
||||
@@ -0,0 +1,33 @@
|
||||
require 'appwrite'
|
||||
|
||||
include Appwrite
|
||||
|
||||
client = Client.new
|
||||
.set_endpoint('https://<REGION>.cloud.appwrite.io/v1') # Your API Endpoint
|
||||
.set_project('<YOUR_PROJECT_ID>') # Your project ID
|
||||
.set_session('') # The user session to authenticate with
|
||||
|
||||
avatars = Avatars.new(client)
|
||||
|
||||
result = avatars.get_screenshot(
|
||||
url: 'https://example.com',
|
||||
headers: {}, # optional
|
||||
viewport_width: 1, # optional
|
||||
viewport_height: 1, # optional
|
||||
scale: 0.1, # optional
|
||||
theme: ::LIGHT, # optional
|
||||
user_agent: '<USER_AGENT>', # optional
|
||||
fullpage: false, # optional
|
||||
locale: '<LOCALE>', # optional
|
||||
timezone: ::AFRICA_ABIDJAN, # optional
|
||||
latitude: -90, # optional
|
||||
longitude: -180, # optional
|
||||
accuracy: 0, # optional
|
||||
touch: false, # optional
|
||||
permissions: [], # optional
|
||||
sleep: 0, # optional
|
||||
width: 0, # optional
|
||||
height: 0, # optional
|
||||
quality: -1, # optional
|
||||
output: ::JPG # optional
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
import Appwrite
|
||||
import AppwriteEnums
|
||||
|
||||
let client = Client()
|
||||
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
|
||||
.setProject("<YOUR_PROJECT_ID>") // Your project ID
|
||||
.setSession("") // The user session to authenticate with
|
||||
|
||||
let avatars = Avatars(client)
|
||||
|
||||
let bytes = try await avatars.getScreenshot(
|
||||
url: "https://example.com",
|
||||
headers: [:], // optional
|
||||
viewportWidth: 1, // optional
|
||||
viewportHeight: 1, // optional
|
||||
scale: 0.1, // optional
|
||||
theme: .light, // optional
|
||||
userAgent: "<USER_AGENT>", // optional
|
||||
fullpage: false, // optional
|
||||
locale: "<LOCALE>", // optional
|
||||
timezone: .africaAbidjan, // optional
|
||||
latitude: -90, // optional
|
||||
longitude: -180, // optional
|
||||
accuracy: 0, // optional
|
||||
touch: false, // optional
|
||||
permissions: [], // optional
|
||||
sleep: 0, // optional
|
||||
width: 0, // optional
|
||||
height: 0, // optional
|
||||
quality: -1, // optional
|
||||
output: .jpg // optional
|
||||
)
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
# Change Log
|
||||
|
||||
## 19.4.0
|
||||
|
||||
* Add `getScreenshot` method to `Avatars` service
|
||||
* Add enums `Theme`, `Output` and `Timezone`
|
||||
* Update runtime enums to add support for `dart39` and `flutter335` runtimes
|
||||
* Fix passing of `null` values and stripping only non-nullable optional parameters from the request body
|
||||
|
||||
## 19.3.0
|
||||
|
||||
* Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# Change Log
|
||||
|
||||
## 20.3.1
|
||||
|
||||
* Fix passing of `null` values and stripping only non-nullable optional parameters from the request body
|
||||
|
||||
## 20.3.0
|
||||
|
||||
* Add `total` parameter to list queries allowing skipping counting rows in a table for improved performance
|
||||
|
||||
@@ -26,27 +26,34 @@ Before releasing SDKs, you need to:
|
||||
|
||||
To enable SDK releases via GitHub, you need to mount SSH keys and configure GitHub authentication in your Docker environment.
|
||||
|
||||
#### Update docker-compose.override.yml
|
||||
#### Update Dockerfile
|
||||
|
||||
Update `docker-compose.override.yml` to mount SSH keys and set environment variables for the `appwrite` service:
|
||||
Add the following configuration to your `Dockerfile`:
|
||||
|
||||
```dockerfile
|
||||
ARG GH_TOKEN
|
||||
ENV GH_TOKEN=your_github_token_here
|
||||
RUN git config --global user.email "your-email@example.com"
|
||||
RUN apk add --update --no-cache openssh-client github-cli
|
||||
```
|
||||
|
||||
Replace:
|
||||
- `your_github_token_here` with your GitHub personal access token (with appropriate permissions)
|
||||
- `your-email@example.com` with your Git email address
|
||||
|
||||
#### Update docker-compose.yml
|
||||
|
||||
Add the SSH key volume mount to the `appwrite` service in `docker-compose.yml`:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
appwrite:
|
||||
volumes:
|
||||
- ~/.ssh:/root/.ssh
|
||||
environment:
|
||||
- GH_TOKEN=your_github_token_here
|
||||
- GIT_EMAIL=your-email@example.com
|
||||
# ... other volumes
|
||||
```
|
||||
|
||||
Uncomment the volumes section.
|
||||
|
||||
Replace:
|
||||
- `your_github_token_here` with your GitHub personal access token (with appropriate permissions)
|
||||
- `your-email@example.com` with your Git email address
|
||||
|
||||
This mounts your SSH keys from the host machine and sets the GitHub token and email as environment variables, allowing the container to authenticate with GitHub. The git configuration is handled automatically at runtime.
|
||||
This mounts your SSH keys from the host machine, allowing the container to authenticate with GitHub.
|
||||
|
||||
### Updating Specs
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
<directory>./tests/e2e/Services/Locale</directory>
|
||||
<directory>./tests/e2e/Services/Projects</directory>
|
||||
<directory>./tests/e2e/Services/Storage</directory>
|
||||
<directory>./tests/e2e/Services/Tokens</directory>
|
||||
<directory>./tests/e2e/Services/Webhooks</directory>
|
||||
<directory>./tests/e2e/Services/Messaging</directory>
|
||||
<directory>./tests/e2e/Services/Migrations</directory>
|
||||
|
||||
@@ -6,6 +6,7 @@ use Appwrite\Migration\Migration;
|
||||
use Exception;
|
||||
use Throwable;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Conflict;
|
||||
@@ -132,6 +133,13 @@ class V23 extends Migration
|
||||
}
|
||||
$this->dbForProject->purgeCachedCollection($id);
|
||||
break;
|
||||
case 'migrations':
|
||||
try {
|
||||
$this->updateMigrateErrorSize();
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning("Failed to migration error attribute size in collection {$id}: {$th->getMessage()}");
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -201,4 +209,46 @@ class V23 extends Migration
|
||||
}
|
||||
return $document;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update migration attribute size
|
||||
* @return void
|
||||
*/
|
||||
private function updateMigrateErrorSize(): void
|
||||
{
|
||||
|
||||
if ($this->project->getId() === 'console') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Read-modify-write from the live schema to avoid overwriting unrelated changes.
|
||||
$migration = $this->dbForProject->getCollection('migrations');
|
||||
$attributes = $migration->getAttribute('attributes', []);
|
||||
$attrsArray = \array_map(fn (Document $doc) => $doc->getArrayCopy(), $attributes);
|
||||
$errorsIdx = \array_search('errors', \array_column($attrsArray, '$id'));
|
||||
|
||||
if ($errorsIdx === false) {
|
||||
Console::warning("Skipping: 'errors' attribute not found in migrations collection for project {$this->project->getId()}");
|
||||
return;
|
||||
}
|
||||
|
||||
$desiredSize = 1_000_000;
|
||||
$migrationAttributes = Config::getParam('collections', [])['projects']['migrations']['attributes'] ?? [];
|
||||
$migrationIndex = \array_search('errors', \array_column($migrationAttributes, '$id'));
|
||||
|
||||
if ($migrationIndex !== false && isset($migrationAttributes[$migrationIndex]['size'])) {
|
||||
$desiredSize = (int) $migrationAttributes[$migrationIndex]['size'];
|
||||
}
|
||||
|
||||
$currentSize = (int) ($attributes[$errorsIdx]['size'] ?? 0);
|
||||
|
||||
if ($currentSize === $desiredSize) {
|
||||
Console::warning("Skipping: 'errors' attribute already of desired size {$desiredSize} in migrations collection for project {$this->project->getId()}");
|
||||
return;
|
||||
}
|
||||
$attributes[$errorsIdx]['size'] = $desiredSize;
|
||||
$migration->setAttribute('attributes', $attributes);
|
||||
$this->dbForProject->updateDocument($migration->getCollection(), $migration->getId(), $migration);
|
||||
$this->dbForProject->purgeCachedCollection('migrations');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Swoole\Request;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
class Create extends Base
|
||||
@@ -65,7 +66,8 @@ class Create extends Base
|
||||
->param('repository', '', new Text(128, 0), 'Repository name of the template.')
|
||||
->param('owner', '', new Text(128, 0), 'The name of the owner of the template.')
|
||||
->param('rootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.')
|
||||
->param('version', '', new Text(128, 0), 'Version (tag) for the repo linked to the function template.')
|
||||
->param('type', '', new WhiteList(['commit', 'branch', 'tag']), 'Type for the reference provided. Can be commit, branch, or tag')
|
||||
->param('reference', '', new Text(128, 0), 'Reference value, can be a commit hash, branch name, or release tag')
|
||||
->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -83,7 +85,8 @@ class Create extends Base
|
||||
string $repository,
|
||||
string $owner,
|
||||
string $rootDirectory,
|
||||
string $version,
|
||||
string $type,
|
||||
string $reference,
|
||||
bool $activate,
|
||||
Request $request,
|
||||
Response $response,
|
||||
@@ -100,11 +103,16 @@ class Create extends Base
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$branchUrl = "https://github.com/$owner/$repository/blob/$reference";
|
||||
|
||||
$repositoryUrl = "https://github.com/$owner/$repository";
|
||||
|
||||
$template = new Document([
|
||||
'repositoryName' => $repository,
|
||||
'ownerName' => $owner,
|
||||
'rootDirectory' => $rootDirectory,
|
||||
'version' => $version
|
||||
'referenceType' => $type,
|
||||
'referenceValue' => $reference,
|
||||
]);
|
||||
|
||||
if (!empty($function->getAttribute('providerRepositoryId'))) {
|
||||
@@ -146,7 +154,12 @@ class Create extends Base
|
||||
'resourceType' => 'functions',
|
||||
'entrypoint' => $function->getAttribute('entrypoint', ''),
|
||||
'buildCommands' => $function->getAttribute('commands', ''),
|
||||
'type' => 'manual',
|
||||
'providerRepositoryName' => $repository,
|
||||
'providerRepositoryOwner' => $owner,
|
||||
'providerRepositoryUrl' => $repositoryUrl,
|
||||
'providerBranchUrl' => $branchUrl,
|
||||
'providerBranch' => $type == GitHub::CLONE_TYPE_BRANCH ? $reference : '',
|
||||
'type' => 'vcs',
|
||||
'activate' => $activate,
|
||||
]));
|
||||
|
||||
|
||||
@@ -310,20 +310,23 @@ class Builds extends Action
|
||||
// Non-VCS + Template
|
||||
$templateRepositoryName = $template->getAttribute('repositoryName', '');
|
||||
$templateOwnerName = $template->getAttribute('ownerName', '');
|
||||
$templateVersion = $template->getAttribute('version', '');
|
||||
$templateReferenceType = $template->getAttribute('referenceType', '');
|
||||
$templateReferenceValue = $template->getAttribute('referenceValue', '');
|
||||
|
||||
$templateRootDirectory = $template->getAttribute('rootDirectory', '');
|
||||
$templateRootDirectory = \rtrim($templateRootDirectory, '/');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '.');
|
||||
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
|
||||
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateVersion)) {
|
||||
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateReferenceType) && !empty($templateReferenceValue)) {
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
|
||||
// Clone template repo
|
||||
$tmpTemplateDirectory = '/tmp/builds/' . $deploymentId . '-template';
|
||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
|
||||
|
||||
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateReferenceValue, $templateReferenceType, $tmpTemplateDirectory, $templateRootDirectory);
|
||||
|
||||
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
|
||||
|
||||
if ($exit !== 0) {
|
||||
|
||||
@@ -23,6 +23,7 @@ use Utopia\Swoole\Request;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
class Create extends Base
|
||||
@@ -67,7 +68,8 @@ class Create extends Base
|
||||
->param('repository', '', new Text(128, 0), 'Repository name of the template.')
|
||||
->param('owner', '', new Text(128, 0), 'The name of the owner of the template.')
|
||||
->param('rootDirectory', '', new Text(128, 0), 'Path to site code in the template repo.')
|
||||
->param('version', '', new Text(128, 0), 'Version (tag) for the repo linked to the site template.')
|
||||
->param('type', '', new WhiteList(['branch', 'commit', 'tag']), 'Type for the reference provided. Can be commit, branch, or tag')
|
||||
->param('reference', '', new Text(128, 0), 'Reference value, can be a commit hash, branch name, or release tag')
|
||||
->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -85,7 +87,8 @@ class Create extends Base
|
||||
string $repository,
|
||||
string $owner,
|
||||
string $rootDirectory,
|
||||
string $version,
|
||||
string $type,
|
||||
string $reference,
|
||||
bool $activate,
|
||||
Request $request,
|
||||
Response $response,
|
||||
@@ -102,11 +105,15 @@ class Create extends Base
|
||||
throw new Exception(Exception::SITE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$branchUrl = "https://github.com/$owner/$repository/blob/$reference";
|
||||
$repositoryUrl = "https://github.com/$owner/$repository";
|
||||
|
||||
$template = new Document([
|
||||
'repositoryName' => $repository,
|
||||
'ownerName' => $owner,
|
||||
'rootDirectory' => $rootDirectory,
|
||||
'version' => $version
|
||||
'referenceType' => $type,
|
||||
'referenceValue' => $reference
|
||||
]);
|
||||
|
||||
if (!empty($site->getAttribute('providerRepositoryId'))) {
|
||||
@@ -157,9 +164,14 @@ class Create extends Base
|
||||
'resourceType' => 'sites',
|
||||
'buildCommands' => \implode(' && ', $commands),
|
||||
'buildOutput' => $site->getAttribute('outputDirectory', ''),
|
||||
'providerRepositoryName' => $repository,
|
||||
'providerRepositoryOwner' => $owner,
|
||||
'providerRepositoryUrl' => $repositoryUrl,
|
||||
'providerBranchUrl' => $branchUrl,
|
||||
'providerBranch' => $type == GitHub::CLONE_TYPE_BRANCH ? $reference : '',
|
||||
'adapter' => $site->getAttribute('adapter', ''),
|
||||
'fallbackFile' => $site->getAttribute('fallbackFile', ''),
|
||||
'type' => 'manual',
|
||||
'type' => 'vcs',
|
||||
'activate' => $activate,
|
||||
]));
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ class Create extends Action
|
||||
))
|
||||
->param('bucketId', '', new UID(), 'Storage bucket unique ID. You can create a new storage bucket using the Storage service [server integration](https://appwrite.io/docs/server/storage#createBucket).')
|
||||
->param('fileId', '', new UID(), 'File unique ID.')
|
||||
->param('expire', null, new Nullable(new DatetimeValidator()), 'Token expiry date', true)
|
||||
->param('expire', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'Token expiry date', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
@@ -70,7 +70,6 @@ class Create extends Action
|
||||
|
||||
public function action(string $bucketId, string $fileId, ?string $expire, Response $response, Database $dbForProject, Event $queueForEvents): void
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Document $bucket
|
||||
* @var Document $file
|
||||
|
||||
@@ -57,7 +57,7 @@ class Update extends Action
|
||||
contentType: ContentType::JSON
|
||||
))
|
||||
->param('tokenId', '', new UID(), 'Token unique ID.')
|
||||
->param('expire', null, new Nullable(new DatetimeValidator()), 'File token expiry date', true)
|
||||
->param('expire', null, new Nullable(new DatetimeValidator(requireDateInFuture: true)), 'File token expiry date', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForEvents')
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Utopia\Request\Filters;
|
||||
|
||||
use Appwrite\Utopia\Request\Filter;
|
||||
|
||||
class V21 extends Filter
|
||||
{
|
||||
// Convert 1.8.0 params to 1.8.1
|
||||
public function parse(array $content, string $model): array
|
||||
{
|
||||
switch ($model) {
|
||||
case 'functions.createTemplateDeployment':
|
||||
case 'sites.createTemplateDeployment':
|
||||
$content = $this->convertVersionToTypeAndReference($content);
|
||||
break;
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert version parameter to type and reference for backwards compatibility
|
||||
* with 1.8.0 template deployment endpoints
|
||||
*/
|
||||
protected function convertVersionToTypeAndReference(array $content): array
|
||||
{
|
||||
if (!empty($content['version'])) {
|
||||
$content['type'] = 'tag';
|
||||
$content['reference'] = $content['version'];
|
||||
unset($content['version']);
|
||||
}
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ class UsageTest extends Scope
|
||||
FunctionsBase::createVariable insteadof SitesBase;
|
||||
FunctionsBase::getVariable insteadof SitesBase;
|
||||
FunctionsBase::listVariables insteadof SitesBase;
|
||||
FunctionsBase::helperGetLatestCommit insteadof SitesBase;
|
||||
FunctionsBase::updateVariable insteadof SitesBase;
|
||||
FunctionsBase::deleteVariable insteadof SitesBase;
|
||||
FunctionsBase::getDeployment insteadof SitesBase;
|
||||
|
||||
@@ -41,6 +41,11 @@ trait AccountBase
|
||||
$this->assertNotEmpty($response['body']['accessedAt']);
|
||||
$this->assertArrayHasKey('targets', $response['body']);
|
||||
$this->assertEquals($email, $response['body']['targets'][0]['identifier']);
|
||||
$this->assertArrayNotHasKey('emailCanonical', $response['body']);
|
||||
$this->assertArrayNotHasKey('emailIsFree', $response['body']);
|
||||
$this->assertArrayNotHasKey('emailIsDisposable', $response['body']);
|
||||
$this->assertArrayNotHasKey('emailIsCorporate', $response['body']);
|
||||
$this->assertArrayNotHasKey('emailIsCanonical', $response['body']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
||||
@@ -271,6 +271,29 @@ trait FunctionsBase
|
||||
return $template;
|
||||
}
|
||||
|
||||
protected function helperGetLatestCommit(string $owner, string $repository): ?string
|
||||
{
|
||||
$ch = curl_init("https://api.github.com/repos/{$owner}/{$repository}/commits/main");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'User-Agent: Appwrite',
|
||||
'Accept: application/vnd.github.v3+json'
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200) {
|
||||
$commitData = json_decode($response, true);
|
||||
if (isset($commitData['sha'])) {
|
||||
return $commitData['sha'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function createExecution(string $functionId, mixed $params = []): mixed
|
||||
{
|
||||
$execution = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/executions', array_merge([
|
||||
|
||||
@@ -361,7 +361,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$starterTemplate = $this->getTemplate('starter');
|
||||
$this->assertEquals(200, $starterTemplate['headers']['status-code']);
|
||||
|
||||
$phpRuntime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
|
||||
$runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
|
||||
return $runtime['name'] === 'node-22';
|
||||
}))[0];
|
||||
|
||||
@@ -374,15 +374,15 @@ class FunctionsCustomServerTest extends Scope
|
||||
'name' => $starterTemplate['body']['name'],
|
||||
'runtime' => 'node-22',
|
||||
'execute' => $starterTemplate['body']['permissions'],
|
||||
'entrypoint' => $phpRuntime['entrypoint'],
|
||||
'entrypoint' => $runtime['entrypoint'],
|
||||
'events' => $starterTemplate['body']['events'],
|
||||
'schedule' => $starterTemplate['body']['cron'],
|
||||
'timeout' => $starterTemplate['body']['timeout'],
|
||||
'commands' => $phpRuntime['commands'],
|
||||
'commands' => $runtime['commands'],
|
||||
'scopes' => $starterTemplate['body']['scopes'],
|
||||
'templateRepository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'templateOwner' => $starterTemplate['body']['providerOwner'],
|
||||
'templateRootDirectory' => $phpRuntime['providerRootDirectory'],
|
||||
'templateRootDirectory' => $runtime['providerRootDirectory'],
|
||||
'templateVersion' => $starterTemplate['body']['providerVersion'],
|
||||
]
|
||||
);
|
||||
@@ -399,19 +399,29 @@ class FunctionsCustomServerTest extends Scope
|
||||
'activate' => true,
|
||||
'repository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'owner' => $starterTemplate['body']['providerOwner'],
|
||||
'rootDirectory' => $phpRuntime['providerRootDirectory'],
|
||||
'version' => $starterTemplate['body']['providerVersion'],
|
||||
'rootDirectory' => $runtime['providerRootDirectory'],
|
||||
'type' => 'tag',
|
||||
'reference' => $starterTemplate['body']['providerVersion'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deployment = $this->getDeployment($functionId, $deployment['body']['$id']);
|
||||
// Wait for deployment to be ready
|
||||
$deploymentId = $deployment['body']['$id'];
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals('ready', $deployment['body']['status']);
|
||||
}, 50000, 500);
|
||||
|
||||
// Verify deployment sizes
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertEquals(0, $deployment['body']['sourceSize']);
|
||||
$this->assertEquals(0, $deployment['body']['buildSize']);
|
||||
$this->assertEquals(0, $deployment['body']['totalSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
|
||||
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
|
||||
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
|
||||
|
||||
$deployments = $this->listDeployments($functionId);
|
||||
|
||||
@@ -433,16 +443,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$lastDeployment = $deployments['body']['deployments'][0];
|
||||
|
||||
$this->assertNotEmpty($lastDeployment['$id']);
|
||||
$this->assertEquals(0, $lastDeployment['sourceSize']);
|
||||
|
||||
$deploymentId = $lastDeployment['$id'];
|
||||
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertEquals('ready', $deployment['body']['status']);
|
||||
}, 50000, 1000);
|
||||
$this->assertGreaterThan(0, $lastDeployment['sourceSize']);
|
||||
|
||||
$function = $this->getFunction($functionId);
|
||||
|
||||
@@ -511,7 +512,144 @@ class FunctionsCustomServerTest extends Scope
|
||||
$this->assertEquals($deployment['body']['$id'], $function['body']['deploymentId']);
|
||||
$this->assertEquals($deployment['body']['$createdAt'], $function['body']['deploymentCreatedAt']);
|
||||
|
||||
$function = $this->cleanupFunction($functionId);
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
public function testCreateFunctionAndDeploymentFromTemplateBranch()
|
||||
{
|
||||
$starterTemplate = $this->getTemplate('starter');
|
||||
$this->assertEquals(200, $starterTemplate['headers']['status-code']);
|
||||
|
||||
$runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
|
||||
return $runtime['name'] === 'node-22';
|
||||
}))[0];
|
||||
|
||||
// If this fails, the template has variables, and this test needs to be updated
|
||||
$this->assertEmpty($starterTemplate['body']['variables']);
|
||||
|
||||
$function = $this->createFunction(
|
||||
[
|
||||
'functionId' => ID::unique(),
|
||||
'name' => $starterTemplate['body']['name'] . ' - Branch Test',
|
||||
'runtime' => 'node-22',
|
||||
'execute' => $starterTemplate['body']['permissions'],
|
||||
'entrypoint' => $runtime['entrypoint'],
|
||||
'events' => $starterTemplate['body']['events'],
|
||||
'schedule' => $starterTemplate['body']['cron'],
|
||||
'timeout' => $starterTemplate['body']['timeout'],
|
||||
'commands' => $runtime['commands'],
|
||||
'scopes' => $starterTemplate['body']['scopes'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
// Deploy using branch
|
||||
$deployment = $this->createTemplateDeployment(
|
||||
$functionId,
|
||||
[
|
||||
'resourceId' => ID::unique(),
|
||||
'activate' => true,
|
||||
'repository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'owner' => $starterTemplate['body']['providerOwner'],
|
||||
'rootDirectory' => $runtime['providerRootDirectory'],
|
||||
'type' => 'branch',
|
||||
'reference' => 'main',
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'];
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals('ready', $deployment['body']['status']);
|
||||
}, 50000, 500);
|
||||
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
|
||||
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
|
||||
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
public function testCreateFunctionAndDeploymentFromTemplateCommit()
|
||||
{
|
||||
$starterTemplate = $this->getTemplate('starter');
|
||||
$this->assertEquals(200, $starterTemplate['headers']['status-code']);
|
||||
|
||||
// Get latest commit using helper function
|
||||
$latestCommit = $this->helperGetLatestCommit(
|
||||
$starterTemplate['body']['providerOwner'],
|
||||
$starterTemplate['body']['providerRepositoryId']
|
||||
);
|
||||
$this->assertNotNull($latestCommit);
|
||||
|
||||
$runtime = array_values(array_filter($starterTemplate['body']['runtimes'], function ($runtime) {
|
||||
return $runtime['name'] === 'node-22';
|
||||
}))[0];
|
||||
|
||||
// If this fails, the template has variables, and this test needs to be updated
|
||||
$this->assertEmpty($starterTemplate['body']['variables']);
|
||||
|
||||
$function = $this->createFunction(
|
||||
[
|
||||
'functionId' => ID::unique(),
|
||||
'name' => $starterTemplate['body']['name'] . ' - Commit Test',
|
||||
'runtime' => 'node-22',
|
||||
'execute' => $starterTemplate['body']['permissions'],
|
||||
'entrypoint' => $runtime['entrypoint'],
|
||||
'events' => $starterTemplate['body']['events'],
|
||||
'schedule' => $starterTemplate['body']['cron'],
|
||||
'timeout' => $starterTemplate['body']['timeout'],
|
||||
'commands' => $runtime['commands'],
|
||||
'scopes' => $starterTemplate['body']['scopes'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
// Deploy using commit
|
||||
$deployment = $this->createTemplateDeployment(
|
||||
$functionId,
|
||||
[
|
||||
'resourceId' => ID::unique(),
|
||||
'activate' => true,
|
||||
'repository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'owner' => $starterTemplate['body']['providerOwner'],
|
||||
'rootDirectory' => $runtime['providerRootDirectory'],
|
||||
'type' => 'commit',
|
||||
'reference' => $latestCommit,
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'];
|
||||
$this->assertEventually(function () use ($functionId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals('ready', $deployment['body']['status']);
|
||||
}, 50000, 500);
|
||||
|
||||
$deployment = $this->getDeployment($functionId, $deploymentId);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
|
||||
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
|
||||
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -329,9 +329,33 @@ trait SitesBase
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
protected function helperGetLatestCommit(string $owner, string $repository): ?string
|
||||
{
|
||||
$ch = curl_init("https://api.github.com/repos/{$owner}/{$repository}/commits/main");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'User-Agent: Appwrite',
|
||||
'Accept: application/vnd.github.v3+json'
|
||||
]);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($httpCode === 200) {
|
||||
$commitData = json_decode($response, true);
|
||||
if (isset($commitData['sha'])) {
|
||||
return $commitData['sha'];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function deleteSite(string $siteId): mixed
|
||||
{
|
||||
$site = $this->client->call(Client::METHOD_DELETE, '/sites/' . $siteId, array_merge([
|
||||
|
||||
@@ -1567,7 +1567,157 @@ class SitesCustomServerTest extends Scope
|
||||
'repository' => $template['providerRepositoryId'],
|
||||
'owner' => $template['providerOwner'],
|
||||
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
|
||||
'version' => $template['providerVersion'],
|
||||
'type' => 'tag',
|
||||
'reference' => $template['providerVersion'],
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertEquals(0, $deployment['body']['sourceSize']);
|
||||
$this->assertEquals(0, $deployment['body']['buildSize']);
|
||||
$this->assertEquals(0, $deployment['body']['totalSize']);
|
||||
|
||||
$this->assertEventually(function () use ($siteId) {
|
||||
$site = $this->getSite($siteId);
|
||||
$this->assertNotEmpty($site['body']['deploymentId']);
|
||||
}, 50000, 500);
|
||||
|
||||
$domain = $this->setupSiteDomain($siteId);
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/');
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Astro Blog", $response['body']);
|
||||
$this->assertStringContainsString("Hello, Astronaut!", $response['body']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/about');
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Astro Blog", $response['body']);
|
||||
$this->assertStringContainsString("About Me", $response['body']);
|
||||
|
||||
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
|
||||
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
|
||||
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testCreateSiteFromTemplateBranch()
|
||||
{
|
||||
$template = $this->getTemplate('playground-for-astro');
|
||||
$this->assertEquals(200, $template['headers']['status-code']);
|
||||
|
||||
$template = $template['body'];
|
||||
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Astro Blog - Branch Test',
|
||||
'framework' => $template['frameworks'][0]['key'],
|
||||
'adapter' => $template['frameworks'][0]['adapter'],
|
||||
'buildRuntime' => $template['frameworks'][0]['buildRuntime'],
|
||||
'outputDirectory' => $template['frameworks'][0]['outputDirectory'],
|
||||
'buildCommand' => $template['frameworks'][0]['buildCommand'],
|
||||
'installCommand' => $template['frameworks'][0]['installCommand'],
|
||||
'fallbackFile' => $template['frameworks'][0]['fallbackFile'],
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
// Deploy using branch
|
||||
$deployment = $this->createTemplateDeployment($siteId, [
|
||||
'repository' => $template['providerRepositoryId'],
|
||||
'owner' => $template['providerOwner'],
|
||||
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
|
||||
'type' => 'branch',
|
||||
'reference' => 'main',
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertEquals(0, $deployment['body']['sourceSize']);
|
||||
$this->assertEquals(0, $deployment['body']['buildSize']);
|
||||
$this->assertEquals(0, $deployment['body']['totalSize']);
|
||||
|
||||
$this->assertEventually(function () use ($siteId) {
|
||||
$site = $this->getSite($siteId);
|
||||
$this->assertNotEmpty($site['body']['deploymentId']);
|
||||
}, 50000, 500);
|
||||
|
||||
$domain = $this->setupSiteDomain($siteId);
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/');
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Astro Blog", $response['body']);
|
||||
$this->assertStringContainsString("Hello, Astronaut!", $response['body']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/about');
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Astro Blog", $response['body']);
|
||||
$this->assertStringContainsString("About Me", $response['body']);
|
||||
|
||||
$deployment = $this->getDeployment($siteId, $deployment['body']['$id']);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['sourceSize']);
|
||||
$this->assertGreaterThan(0, $deployment['body']['buildSize']);
|
||||
$totalSize = $deployment['body']['sourceSize'] + $deployment['body']['buildSize'];
|
||||
$this->assertEquals($totalSize, $deployment['body']['totalSize']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testCreateSiteFromTemplateCommit()
|
||||
{
|
||||
$template = $this->getTemplate('playground-for-astro');
|
||||
$this->assertEquals(200, $template['headers']['status-code']);
|
||||
|
||||
// Get latest commit using helper function
|
||||
$latestCommit = $this->helperGetLatestCommit(
|
||||
$template['body']['providerOwner'],
|
||||
$template['body']['providerRepositoryId']
|
||||
);
|
||||
$this->assertNotNull($latestCommit);
|
||||
|
||||
$template = $template['body'];
|
||||
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Astro Blog - Commit Test',
|
||||
'framework' => $template['frameworks'][0]['key'],
|
||||
'adapter' => $template['frameworks'][0]['adapter'],
|
||||
'buildRuntime' => $template['frameworks'][0]['buildRuntime'],
|
||||
'outputDirectory' => $template['frameworks'][0]['outputDirectory'],
|
||||
'buildCommand' => $template['frameworks'][0]['buildCommand'],
|
||||
'installCommand' => $template['frameworks'][0]['installCommand'],
|
||||
'fallbackFile' => $template['frameworks'][0]['fallbackFile'],
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
// Deploy using commit
|
||||
$deployment = $this->createTemplateDeployment($siteId, [
|
||||
'repository' => $template['providerRepositoryId'],
|
||||
'owner' => $template['providerOwner'],
|
||||
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
|
||||
'type' => 'commit',
|
||||
'reference' => $latestCommit,
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
@@ -63,10 +62,23 @@ class TokensConsoleClientTest extends Scope
|
||||
|
||||
$fileId = $file['body']['$id'];
|
||||
|
||||
// Failure case: Expire date is in the past
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()));
|
||||
], $this->getHeaders()), [
|
||||
'expire' => '2022-11-02',
|
||||
]);
|
||||
$this->assertEquals(400, $token['headers']['status-code']);
|
||||
$this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
|
||||
|
||||
// Success case: No expire date
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'expire' => null,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $token['headers']['status-code']);
|
||||
$this->assertEquals('files', $token['body']['resourceType']);
|
||||
@@ -107,8 +119,19 @@ class TokensConsoleClientTest extends Scope
|
||||
{
|
||||
$tokenId = $data['tokenId'];
|
||||
|
||||
// Failure case: Expire date is in the past
|
||||
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'expire' => '2022-11-02',
|
||||
]);
|
||||
$this->assertEquals(400, $token['headers']['status-code']);
|
||||
$this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
|
||||
|
||||
// Finite expiry
|
||||
$expiry = DateTime::addSeconds(new \DateTime(), 3600);
|
||||
$expiry = date('Y-m-d', strtotime("tomorrow"));
|
||||
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
|
||||
@@ -7,7 +7,6 @@ use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
@@ -61,6 +60,17 @@ class TokensCustomServerTest extends Scope
|
||||
|
||||
$fileId = $file['body']['$id'];
|
||||
|
||||
// Failure case: Expire date is in the past
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
], $this->getHeaders()), [
|
||||
'expire' => '2022-11-02',
|
||||
]);
|
||||
$this->assertEquals(400, $token['headers']['status-code']);
|
||||
$this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
|
||||
|
||||
// Success case: No expire date
|
||||
$token = $this->client->call(Client::METHOD_POST, '/tokens/buckets/' . $bucketId . '/files/' . $fileId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id']
|
||||
@@ -83,8 +93,19 @@ class TokensCustomServerTest extends Scope
|
||||
{
|
||||
$tokenId = $data['tokenId'];
|
||||
|
||||
// Finite expiry
|
||||
$expiry = DateTime::addSeconds(new \DateTime(), 3600);
|
||||
// Failure case: Expire date is in the past
|
||||
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'expire' => '2022-11-02',
|
||||
]);
|
||||
$this->assertEquals(400, $token['headers']['status-code']);
|
||||
$this->assertStringContainsString('Value must be valid date in the future', $token['body']['message']);
|
||||
|
||||
// Success case: Finite expiry
|
||||
$expiry = date('Y-m-d', strtotime("tomorrow"));
|
||||
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
@@ -94,9 +115,10 @@ class TokensCustomServerTest extends Scope
|
||||
]);
|
||||
|
||||
$dateValidator = new DatetimeValidator();
|
||||
$this->assertEquals(200, $token['headers']['status-code']);
|
||||
$this->assertTrue($dateValidator->isValid($token['body']['expire']));
|
||||
|
||||
// Infinite expiry
|
||||
// Success case: Infinite expiry
|
||||
$token = $this->client->call(Client::METHOD_PATCH, '/tokens/' . $tokenId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
|
||||
Reference in New Issue
Block a user