mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Compare commits
158
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a369d49b2c | ||
|
|
22691bee66 | ||
|
|
51f036491a | ||
|
|
4df0934023 | ||
|
|
5e974713b7 | ||
|
|
e2071bd5dd | ||
|
|
0621a32aa6 | ||
|
|
e1cbbe3943 | ||
|
|
226e36db95 | ||
|
|
faedf49488 | ||
|
|
b432918385 | ||
|
|
a87263a571 | ||
|
|
dafa97879c | ||
|
|
a263afeff1 | ||
|
|
29915ddd3b | ||
|
|
c0f5fa90cb | ||
|
|
e666dc9504 | ||
|
|
3dc69ba62a | ||
|
|
7bf5f2d360 | ||
|
|
6df5556473 | ||
|
|
96e85c0bab | ||
|
|
40ab50ec9d | ||
|
|
9b762dde40 | ||
|
|
615aff0714 | ||
|
|
994850c5a6 | ||
|
|
525b929e54 | ||
|
|
074ffad826 | ||
|
|
36c87d109a | ||
|
|
e09dd98f36 | ||
|
|
96e1221a5f | ||
|
|
e13f4c8545 | ||
|
|
353b7f2a49 | ||
|
|
f7ab8e46d3 | ||
|
|
bec6725900 | ||
|
|
494ce5fc6a | ||
|
|
bae7b92dd9 | ||
|
|
801707c407 | ||
|
|
94581adfcb | ||
|
|
fd323feae8 | ||
|
|
3a0dc60a4c | ||
|
|
bed46a6bef | ||
|
|
ba5273872b | ||
|
|
f1639e0b67 | ||
|
|
e8fe9222b2 | ||
|
|
a520a67da8 | ||
|
|
9c03c5fab3 | ||
|
|
d864a61f93 | ||
|
|
fb1cbdca6c | ||
|
|
ab35806372 | ||
|
|
9612ed338f | ||
|
|
1219391c76 | ||
|
|
da7f5b7336 | ||
|
|
ec418e60b7 | ||
|
|
455004a536 | ||
|
|
2ad0135890 | ||
|
|
6414c0ebda | ||
|
|
801befeacd | ||
|
|
941d40e704 | ||
|
|
093bc7bf3d | ||
|
|
90f10ce685 | ||
|
|
45e74d7b3d | ||
|
|
4090ce853e | ||
|
|
af5c0da1d9 | ||
|
|
b5bb4c2e07 | ||
|
|
c492cfe9ab | ||
|
|
a866f0a69f | ||
|
|
3b2c05eb8d | ||
|
|
e28ba64dbf | ||
|
|
747b400616 | ||
|
|
cb58da7279 | ||
|
|
106458a79d | ||
|
|
d40b1707ba | ||
|
|
8600d0e64f | ||
|
|
2bc8d80273 | ||
|
|
1d94e60a37 | ||
|
|
4f7337759f | ||
|
|
657f16031b | ||
|
|
fed8354c52 | ||
|
|
cbfef909ee | ||
|
|
107fadfe37 | ||
|
|
e21614088f | ||
|
|
1c6880a78e | ||
|
|
18125156d2 | ||
|
|
765d334674 | ||
|
|
a46fa50c51 | ||
|
|
cb81939604 | ||
|
|
3e31094e1e | ||
|
|
074470c2c2 | ||
|
|
f8f2f9ca25 | ||
|
|
aff180c517 | ||
|
|
5d1b63fd35 | ||
|
|
aa30db2136 | ||
|
|
64f8b9195b | ||
|
|
12b7f94c1f | ||
|
|
4aaa3bfd9f | ||
|
|
6dd928b73c | ||
|
|
e490f375ee | ||
|
|
96ea3391d3 | ||
|
|
7f824785d5 | ||
|
|
8c191fca92 | ||
|
|
85ea4c8904 | ||
|
|
7e385fe87f | ||
|
|
d06759e33e | ||
|
|
ecf0a1ec67 | ||
|
|
ead2afb9d4 | ||
|
|
26b0447657 | ||
|
|
f7f6b5187c | ||
|
|
c3fbb83ed6 | ||
|
|
a860975b0e | ||
|
|
ce99d80c2f | ||
|
|
a30f12cd66 | ||
|
|
0003443c77 | ||
|
|
227d61e194 | ||
|
|
4cf6f4e066 | ||
|
|
b1409e5e8e | ||
|
|
a744cb0843 | ||
|
|
186ed5b61f | ||
|
|
522c9d3272 | ||
|
|
720ceabbbc | ||
|
|
5ff31e92d7 | ||
|
|
4ef43aacd9 | ||
|
|
ce49b8ecb8 | ||
|
|
1db42c6fc7 | ||
|
|
9bd2966222 | ||
|
|
579f07f8bb | ||
|
|
febb7dc10a | ||
|
|
4b02bf1aa8 | ||
|
|
d45893d123 | ||
|
|
ffd568aecd | ||
|
|
d811d1c534 | ||
|
|
c2b87cff66 | ||
|
|
ca03176ad6 | ||
|
|
946a1c8f34 | ||
|
|
91c1572318 | ||
|
|
05a3417297 | ||
|
|
5e495aa2da | ||
|
|
49195e5d6f | ||
|
|
1eaf5d5ffa | ||
|
|
94d733ee63 | ||
|
|
17be0eb634 | ||
|
|
a419501ea7 | ||
|
|
9bafa90ac8 | ||
|
|
736b72164e | ||
|
|
f82662e84e | ||
|
|
7d193d9c36 | ||
|
|
8b4f4d2fea | ||
|
|
3da3b6a324 | ||
|
|
20e209038d | ||
|
|
c0cbf991ab | ||
|
|
91f3bf0244 | ||
|
|
f1a2e869a2 | ||
|
|
a987195f6f | ||
|
|
2144f27059 | ||
|
|
cb0f2299fb | ||
|
|
01cc9bcd1f | ||
|
|
95ce53b66e | ||
|
|
f35aad0816 | ||
|
|
69e6c0afc0 |
@@ -10,6 +10,7 @@ _APP_CONSOLE_SESSION_ALERTS=enabled
|
||||
_APP_CONSOLE_WHITELIST_IPS=
|
||||
_APP_CONSOLE_COUNTRIES_DENYLIST=AQ
|
||||
_APP_CONSOLE_HOSTNAMES=localhost,appwrite.io,*.appwrite.io
|
||||
_APP_MIGRATION_HOST=appwrite
|
||||
_APP_SYSTEM_EMAIL_NAME=Appwrite
|
||||
_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io
|
||||
_APP_SYSTEM_TEAM_EMAIL=team@appwrite.io
|
||||
@@ -25,6 +26,7 @@ _APP_OPENSSL_KEY_V1=your-secret-key
|
||||
_APP_DNS=172.16.238.100 # CoreDNS
|
||||
_APP_DOMAIN=appwrite.test
|
||||
_APP_CONSOLE_DOMAIN=localhost
|
||||
_APP_CONSOLE_TRUSTED_PROJECTS=trusted-project,another-trusted-project
|
||||
_APP_DOMAIN_FUNCTIONS=functions.localhost
|
||||
_APP_DOMAIN_SITES=sites.localhost,rebranded.localhost
|
||||
_APP_DOMAIN_TARGET_CNAME=cname.localhost
|
||||
|
||||
@@ -81,6 +81,7 @@ RUN chmod +x /usr/local/bin/doctor && \
|
||||
chmod +x /usr/local/bin/worker-certificates && \
|
||||
chmod +x /usr/local/bin/worker-databases && \
|
||||
chmod +x /usr/local/bin/worker-deletes && \
|
||||
chmod +x /usr/local/bin/worker-executions && \
|
||||
chmod +x /usr/local/bin/worker-functions && \
|
||||
chmod +x /usr/local/bin/worker-mails && \
|
||||
chmod +x /usr/local/bin/worker-messaging && \
|
||||
|
||||
@@ -1139,6 +1139,11 @@ return [
|
||||
'description' => 'Key with the requested ID could not be found.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::KEY_ALREADY_EXISTS => [
|
||||
'name' => Exception::KEY_ALREADY_EXISTS,
|
||||
'description' => 'Key with the same ID already exists. Try again with a different ID.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::PLATFORM_NOT_FOUND => [
|
||||
'name' => Exception::PLATFORM_NOT_FOUND,
|
||||
'description' => 'Platform with the requested ID could not be found.',
|
||||
|
||||
@@ -22,6 +22,7 @@ return [
|
||||
'hostnames' => array_filter(array_unique([
|
||||
System::getEnv('_APP_DOMAIN', 'localhost'),
|
||||
System::getEnv('_APP_CONSOLE_DOMAIN', 'localhost'),
|
||||
System::getEnv('_APP_MIGRATION_HOST'),
|
||||
])),
|
||||
'platformName' => APP_EMAIL_PLATFORM_NAME,
|
||||
'logoUrl' => APP_EMAIL_LOGO_URL,
|
||||
|
||||
+1
-1
@@ -227,7 +227,7 @@ return [
|
||||
[
|
||||
'key' => 'cli',
|
||||
'name' => 'Command Line',
|
||||
'version' => '13.2.0',
|
||||
'version' => '13.3.1',
|
||||
'url' => 'https://github.com/appwrite/sdk-for-cli',
|
||||
'package' => 'https://www.npmjs.com/package/appwrite-cli',
|
||||
'enabled' => true,
|
||||
|
||||
@@ -555,7 +555,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMFA",
|
||||
"group": "mfa",
|
||||
"weight": 253,
|
||||
"weight": 250,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa.md",
|
||||
@@ -627,7 +627,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 255,
|
||||
"weight": 252,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-authenticator.md",
|
||||
@@ -751,7 +751,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 256,
|
||||
"weight": 253,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-authenticator.md",
|
||||
@@ -891,7 +891,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 257,
|
||||
"weight": 254,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-mfa-authenticator.md",
|
||||
@@ -1015,7 +1015,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 261,
|
||||
"weight": 258,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-challenge.md",
|
||||
@@ -1149,7 +1149,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 262,
|
||||
"weight": 259,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-challenge.md",
|
||||
@@ -1287,7 +1287,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMfaFactors",
|
||||
"group": "mfa",
|
||||
"weight": 254,
|
||||
"weight": 251,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-mfa-factors.md",
|
||||
@@ -1388,7 +1388,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 260,
|
||||
"weight": 257,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/get-mfa-recovery-codes.md",
|
||||
@@ -1487,7 +1487,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 258,
|
||||
"weight": 255,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-recovery-codes.md",
|
||||
@@ -1586,7 +1586,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 259,
|
||||
"weight": 256,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-recovery-codes.md",
|
||||
@@ -4051,7 +4051,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getBrowser",
|
||||
"group": null,
|
||||
"weight": 264,
|
||||
"weight": 261,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-browser.md",
|
||||
@@ -4179,7 +4179,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getCreditCard",
|
||||
"group": null,
|
||||
"weight": 263,
|
||||
"weight": 260,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-credit-card.md",
|
||||
@@ -4313,7 +4313,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFavicon",
|
||||
"group": null,
|
||||
"weight": 267,
|
||||
"weight": 264,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-favicon.md",
|
||||
@@ -4373,7 +4373,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFlag",
|
||||
"group": null,
|
||||
"weight": 265,
|
||||
"weight": 262,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-flag.md",
|
||||
@@ -4863,7 +4863,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getImage",
|
||||
"group": null,
|
||||
"weight": 266,
|
||||
"weight": 263,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-image.md",
|
||||
@@ -4947,7 +4947,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getInitials",
|
||||
"group": null,
|
||||
"weight": 269,
|
||||
"weight": 266,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-initials.md",
|
||||
@@ -5041,7 +5041,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getQR",
|
||||
"group": null,
|
||||
"weight": 268,
|
||||
"weight": 265,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-qr.md",
|
||||
@@ -5135,7 +5135,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getScreenshot",
|
||||
"group": null,
|
||||
"weight": 270,
|
||||
"weight": 267,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-screenshot.md",
|
||||
@@ -5888,7 +5888,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listTransactions",
|
||||
"group": "transactions",
|
||||
"weight": 346,
|
||||
"weight": 343,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/list-transactions.md",
|
||||
@@ -5955,7 +5955,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 342,
|
||||
"weight": 339,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-transaction.md",
|
||||
@@ -6026,7 +6026,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 343,
|
||||
"weight": 340,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/get-transaction.md",
|
||||
@@ -6090,7 +6090,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 344,
|
||||
"weight": 341,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/update-transaction.md",
|
||||
@@ -6168,7 +6168,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 345,
|
||||
"weight": 342,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/delete-transaction.md",
|
||||
@@ -6234,7 +6234,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createOperations",
|
||||
"group": "transactions",
|
||||
"weight": 347,
|
||||
"weight": 344,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-operations.md",
|
||||
@@ -6319,7 +6319,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listDocuments",
|
||||
"group": "documents",
|
||||
"weight": 297,
|
||||
"weight": 294,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/list-documents.md",
|
||||
@@ -6431,7 +6431,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createDocument",
|
||||
"group": "documents",
|
||||
"weight": 289,
|
||||
"weight": 286,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-document.md",
|
||||
@@ -6592,7 +6592,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getDocument",
|
||||
"group": "documents",
|
||||
"weight": 290,
|
||||
"weight": 287,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/get-document.md",
|
||||
@@ -6703,7 +6703,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "upsertDocument",
|
||||
"group": "documents",
|
||||
"weight": 293,
|
||||
"weight": 290,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/upsert-document.md",
|
||||
@@ -6858,7 +6858,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateDocument",
|
||||
"group": "documents",
|
||||
"weight": 291,
|
||||
"weight": 288,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/update-document.md",
|
||||
@@ -6970,7 +6970,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteDocument",
|
||||
"group": "documents",
|
||||
"weight": 295,
|
||||
"weight": 292,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/delete-document.md",
|
||||
@@ -7077,7 +7077,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "decrementDocumentAttribute",
|
||||
"group": "documents",
|
||||
"weight": 300,
|
||||
"weight": 297,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/decrement-document-attribute.md",
|
||||
@@ -7206,7 +7206,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "incrementDocumentAttribute",
|
||||
"group": "documents",
|
||||
"weight": 299,
|
||||
"weight": 296,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/increment-document-attribute.md",
|
||||
@@ -7335,7 +7335,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"group": "executions",
|
||||
"weight": 447,
|
||||
"weight": 444,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/list-executions.md",
|
||||
@@ -7422,7 +7422,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"group": "executions",
|
||||
"weight": 445,
|
||||
"weight": 442,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/create-execution.md",
|
||||
@@ -7540,7 +7540,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"group": "executions",
|
||||
"weight": 446,
|
||||
"weight": 443,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/get-execution.md",
|
||||
@@ -7615,7 +7615,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 190,
|
||||
"weight": 187,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/query.md",
|
||||
@@ -7669,7 +7669,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 189,
|
||||
"weight": 186,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/mutation.md",
|
||||
@@ -8155,7 +8155,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 237,
|
||||
"weight": 234,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/create-subscriber.md",
|
||||
@@ -8239,7 +8239,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 241,
|
||||
"weight": 238,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/delete-subscriber.md",
|
||||
@@ -8315,7 +8315,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 543,
|
||||
"weight": 540,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/list-files.md",
|
||||
@@ -8414,7 +8414,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 541,
|
||||
"weight": 538,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"demo": "storage\/create-file.md",
|
||||
@@ -8516,7 +8516,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 542,
|
||||
"weight": 539,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/get-file.md",
|
||||
@@ -8590,7 +8590,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 544,
|
||||
"weight": 541,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/update-file.md",
|
||||
@@ -8682,7 +8682,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 545,
|
||||
"weight": 542,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/delete-file.md",
|
||||
@@ -8751,7 +8751,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 547,
|
||||
"weight": 544,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-download.md",
|
||||
@@ -8831,7 +8831,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 546,
|
||||
"weight": 543,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-preview.md",
|
||||
@@ -9061,7 +9061,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 548,
|
||||
"weight": 545,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-view.md",
|
||||
@@ -9148,7 +9148,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listTransactions",
|
||||
"group": "transactions",
|
||||
"weight": 419,
|
||||
"weight": 416,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/list-transactions.md",
|
||||
@@ -9218,7 +9218,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 415,
|
||||
"weight": 412,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-transaction.md",
|
||||
@@ -9292,7 +9292,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 416,
|
||||
"weight": 413,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/get-transaction.md",
|
||||
@@ -9359,7 +9359,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 417,
|
||||
"weight": 414,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/update-transaction.md",
|
||||
@@ -9440,7 +9440,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 418,
|
||||
"weight": 415,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/delete-transaction.md",
|
||||
@@ -9509,7 +9509,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createOperations",
|
||||
"group": "transactions",
|
||||
"weight": 420,
|
||||
"weight": 417,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-operations.md",
|
||||
@@ -9597,7 +9597,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listRows",
|
||||
"group": "rows",
|
||||
"weight": 411,
|
||||
"weight": 408,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/list-rows.md",
|
||||
@@ -9708,7 +9708,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createRow",
|
||||
"group": "rows",
|
||||
"weight": 403,
|
||||
"weight": 400,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-row.md",
|
||||
@@ -9864,7 +9864,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getRow",
|
||||
"group": "rows",
|
||||
"weight": 404,
|
||||
"weight": 401,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/get-row.md",
|
||||
@@ -9974,7 +9974,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "upsertRow",
|
||||
"group": "rows",
|
||||
"weight": 407,
|
||||
"weight": 404,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/upsert-row.md",
|
||||
@@ -10124,7 +10124,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateRow",
|
||||
"group": "rows",
|
||||
"weight": 405,
|
||||
"weight": 402,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/update-row.md",
|
||||
@@ -10235,7 +10235,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteRow",
|
||||
"group": "rows",
|
||||
"weight": 409,
|
||||
"weight": 406,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/delete-row.md",
|
||||
@@ -10341,7 +10341,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "decrementRowColumn",
|
||||
"group": "rows",
|
||||
"weight": 414,
|
||||
"weight": 411,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/decrement-row-column.md",
|
||||
@@ -10469,7 +10469,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "incrementRowColumn",
|
||||
"group": "rows",
|
||||
"weight": 413,
|
||||
"weight": 410,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/increment-row-column.md",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -555,7 +555,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMFA",
|
||||
"group": "mfa",
|
||||
"weight": 253,
|
||||
"weight": 250,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa.md",
|
||||
@@ -627,7 +627,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 255,
|
||||
"weight": 252,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-authenticator.md",
|
||||
@@ -751,7 +751,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 256,
|
||||
"weight": 253,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-authenticator.md",
|
||||
@@ -891,7 +891,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 257,
|
||||
"weight": 254,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-mfa-authenticator.md",
|
||||
@@ -1015,7 +1015,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 261,
|
||||
"weight": 258,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-challenge.md",
|
||||
@@ -1149,7 +1149,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 262,
|
||||
"weight": 259,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-challenge.md",
|
||||
@@ -1287,7 +1287,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMfaFactors",
|
||||
"group": "mfa",
|
||||
"weight": 254,
|
||||
"weight": 251,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-mfa-factors.md",
|
||||
@@ -1388,7 +1388,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 260,
|
||||
"weight": 257,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/get-mfa-recovery-codes.md",
|
||||
@@ -1487,7 +1487,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 258,
|
||||
"weight": 255,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-recovery-codes.md",
|
||||
@@ -1586,7 +1586,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 259,
|
||||
"weight": 256,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-recovery-codes.md",
|
||||
@@ -4051,7 +4051,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getBrowser",
|
||||
"group": null,
|
||||
"weight": 264,
|
||||
"weight": 261,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-browser.md",
|
||||
@@ -4179,7 +4179,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getCreditCard",
|
||||
"group": null,
|
||||
"weight": 263,
|
||||
"weight": 260,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-credit-card.md",
|
||||
@@ -4313,7 +4313,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFavicon",
|
||||
"group": null,
|
||||
"weight": 267,
|
||||
"weight": 264,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-favicon.md",
|
||||
@@ -4373,7 +4373,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFlag",
|
||||
"group": null,
|
||||
"weight": 265,
|
||||
"weight": 262,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-flag.md",
|
||||
@@ -4863,7 +4863,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getImage",
|
||||
"group": null,
|
||||
"weight": 266,
|
||||
"weight": 263,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-image.md",
|
||||
@@ -4947,7 +4947,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getInitials",
|
||||
"group": null,
|
||||
"weight": 269,
|
||||
"weight": 266,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-initials.md",
|
||||
@@ -5041,7 +5041,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getQR",
|
||||
"group": null,
|
||||
"weight": 268,
|
||||
"weight": 265,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-qr.md",
|
||||
@@ -5135,7 +5135,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getScreenshot",
|
||||
"group": null,
|
||||
"weight": 270,
|
||||
"weight": 267,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-screenshot.md",
|
||||
@@ -5888,7 +5888,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listTransactions",
|
||||
"group": "transactions",
|
||||
"weight": 346,
|
||||
"weight": 343,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/list-transactions.md",
|
||||
@@ -5955,7 +5955,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 342,
|
||||
"weight": 339,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-transaction.md",
|
||||
@@ -6026,7 +6026,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 343,
|
||||
"weight": 340,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/get-transaction.md",
|
||||
@@ -6090,7 +6090,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 344,
|
||||
"weight": 341,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/update-transaction.md",
|
||||
@@ -6168,7 +6168,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 345,
|
||||
"weight": 342,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/delete-transaction.md",
|
||||
@@ -6234,7 +6234,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createOperations",
|
||||
"group": "transactions",
|
||||
"weight": 347,
|
||||
"weight": 344,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-operations.md",
|
||||
@@ -6319,7 +6319,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listDocuments",
|
||||
"group": "documents",
|
||||
"weight": 297,
|
||||
"weight": 294,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/list-documents.md",
|
||||
@@ -6431,7 +6431,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createDocument",
|
||||
"group": "documents",
|
||||
"weight": 289,
|
||||
"weight": 286,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-document.md",
|
||||
@@ -6592,7 +6592,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getDocument",
|
||||
"group": "documents",
|
||||
"weight": 290,
|
||||
"weight": 287,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/get-document.md",
|
||||
@@ -6703,7 +6703,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "upsertDocument",
|
||||
"group": "documents",
|
||||
"weight": 293,
|
||||
"weight": 290,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/upsert-document.md",
|
||||
@@ -6858,7 +6858,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateDocument",
|
||||
"group": "documents",
|
||||
"weight": 291,
|
||||
"weight": 288,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/update-document.md",
|
||||
@@ -6970,7 +6970,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteDocument",
|
||||
"group": "documents",
|
||||
"weight": 295,
|
||||
"weight": 292,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/delete-document.md",
|
||||
@@ -7077,7 +7077,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "decrementDocumentAttribute",
|
||||
"group": "documents",
|
||||
"weight": 300,
|
||||
"weight": 297,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/decrement-document-attribute.md",
|
||||
@@ -7206,7 +7206,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "incrementDocumentAttribute",
|
||||
"group": "documents",
|
||||
"weight": 299,
|
||||
"weight": 296,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/increment-document-attribute.md",
|
||||
@@ -7335,7 +7335,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"group": "executions",
|
||||
"weight": 447,
|
||||
"weight": 444,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/list-executions.md",
|
||||
@@ -7422,7 +7422,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"group": "executions",
|
||||
"weight": 445,
|
||||
"weight": 442,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/create-execution.md",
|
||||
@@ -7540,7 +7540,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"group": "executions",
|
||||
"weight": 446,
|
||||
"weight": 443,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/get-execution.md",
|
||||
@@ -7615,7 +7615,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 190,
|
||||
"weight": 187,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/query.md",
|
||||
@@ -7669,7 +7669,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 189,
|
||||
"weight": 186,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/mutation.md",
|
||||
@@ -8155,7 +8155,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 237,
|
||||
"weight": 234,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/create-subscriber.md",
|
||||
@@ -8239,7 +8239,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 241,
|
||||
"weight": 238,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/delete-subscriber.md",
|
||||
@@ -8315,7 +8315,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 543,
|
||||
"weight": 540,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/list-files.md",
|
||||
@@ -8414,7 +8414,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 541,
|
||||
"weight": 538,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"demo": "storage\/create-file.md",
|
||||
@@ -8516,7 +8516,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 542,
|
||||
"weight": 539,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/get-file.md",
|
||||
@@ -8590,7 +8590,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 544,
|
||||
"weight": 541,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/update-file.md",
|
||||
@@ -8682,7 +8682,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 545,
|
||||
"weight": 542,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/delete-file.md",
|
||||
@@ -8751,7 +8751,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 547,
|
||||
"weight": 544,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-download.md",
|
||||
@@ -8831,7 +8831,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 546,
|
||||
"weight": 543,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-preview.md",
|
||||
@@ -9061,7 +9061,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 548,
|
||||
"weight": 545,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-view.md",
|
||||
@@ -9148,7 +9148,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listTransactions",
|
||||
"group": "transactions",
|
||||
"weight": 419,
|
||||
"weight": 416,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/list-transactions.md",
|
||||
@@ -9218,7 +9218,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 415,
|
||||
"weight": 412,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-transaction.md",
|
||||
@@ -9292,7 +9292,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 416,
|
||||
"weight": 413,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/get-transaction.md",
|
||||
@@ -9359,7 +9359,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 417,
|
||||
"weight": 414,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/update-transaction.md",
|
||||
@@ -9440,7 +9440,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 418,
|
||||
"weight": 415,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/delete-transaction.md",
|
||||
@@ -9509,7 +9509,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createOperations",
|
||||
"group": "transactions",
|
||||
"weight": 420,
|
||||
"weight": 417,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-operations.md",
|
||||
@@ -9597,7 +9597,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listRows",
|
||||
"group": "rows",
|
||||
"weight": 411,
|
||||
"weight": 408,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/list-rows.md",
|
||||
@@ -9708,7 +9708,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createRow",
|
||||
"group": "rows",
|
||||
"weight": 403,
|
||||
"weight": 400,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-row.md",
|
||||
@@ -9864,7 +9864,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getRow",
|
||||
"group": "rows",
|
||||
"weight": 404,
|
||||
"weight": 401,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/get-row.md",
|
||||
@@ -9974,7 +9974,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "upsertRow",
|
||||
"group": "rows",
|
||||
"weight": 407,
|
||||
"weight": 404,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/upsert-row.md",
|
||||
@@ -10124,7 +10124,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateRow",
|
||||
"group": "rows",
|
||||
"weight": 405,
|
||||
"weight": 402,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/update-row.md",
|
||||
@@ -10235,7 +10235,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteRow",
|
||||
"group": "rows",
|
||||
"weight": 409,
|
||||
"weight": 406,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/delete-row.md",
|
||||
@@ -10341,7 +10341,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "decrementRowColumn",
|
||||
"group": "rows",
|
||||
"weight": 414,
|
||||
"weight": 411,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/decrement-row-column.md",
|
||||
@@ -10469,7 +10469,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "incrementRowColumn",
|
||||
"group": "rows",
|
||||
"weight": 413,
|
||||
"weight": 410,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/increment-row-column.md",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -612,7 +612,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMFA",
|
||||
"group": "mfa",
|
||||
"weight": 253,
|
||||
"weight": 250,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa.md",
|
||||
@@ -687,7 +687,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 255,
|
||||
"weight": 252,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-authenticator.md",
|
||||
@@ -811,7 +811,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 256,
|
||||
"weight": 253,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-authenticator.md",
|
||||
@@ -952,7 +952,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 257,
|
||||
"weight": 254,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-mfa-authenticator.md",
|
||||
@@ -1076,7 +1076,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 261,
|
||||
"weight": 258,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-challenge.md",
|
||||
@@ -1213,7 +1213,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 262,
|
||||
"weight": 259,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-challenge.md",
|
||||
@@ -1353,7 +1353,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMfaFactors",
|
||||
"group": "mfa",
|
||||
"weight": 254,
|
||||
"weight": 251,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-mfa-factors.md",
|
||||
@@ -1454,7 +1454,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 260,
|
||||
"weight": 257,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/get-mfa-recovery-codes.md",
|
||||
@@ -1555,7 +1555,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 258,
|
||||
"weight": 255,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-recovery-codes.md",
|
||||
@@ -1656,7 +1656,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 259,
|
||||
"weight": 256,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-recovery-codes.md",
|
||||
@@ -4203,7 +4203,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getBrowser",
|
||||
"group": null,
|
||||
"weight": 264,
|
||||
"weight": 261,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-browser.md",
|
||||
@@ -4329,7 +4329,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getCreditCard",
|
||||
"group": null,
|
||||
"weight": 263,
|
||||
"weight": 260,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-credit-card.md",
|
||||
@@ -4461,7 +4461,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFavicon",
|
||||
"group": null,
|
||||
"weight": 267,
|
||||
"weight": 264,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-favicon.md",
|
||||
@@ -4525,7 +4525,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFlag",
|
||||
"group": null,
|
||||
"weight": 265,
|
||||
"weight": 262,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-flag.md",
|
||||
@@ -5013,7 +5013,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getImage",
|
||||
"group": null,
|
||||
"weight": 266,
|
||||
"weight": 263,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-image.md",
|
||||
@@ -5097,7 +5097,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getInitials",
|
||||
"group": null,
|
||||
"weight": 269,
|
||||
"weight": 266,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-initials.md",
|
||||
@@ -5189,7 +5189,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getQR",
|
||||
"group": null,
|
||||
"weight": 268,
|
||||
"weight": 265,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-qr.md",
|
||||
@@ -5281,7 +5281,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getScreenshot",
|
||||
"group": null,
|
||||
"weight": 270,
|
||||
"weight": 267,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-screenshot.md",
|
||||
@@ -5994,7 +5994,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listTransactions",
|
||||
"group": "transactions",
|
||||
"weight": 346,
|
||||
"weight": 343,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/list-transactions.md",
|
||||
@@ -6061,7 +6061,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 342,
|
||||
"weight": 339,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-transaction.md",
|
||||
@@ -6132,7 +6132,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 343,
|
||||
"weight": 340,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/get-transaction.md",
|
||||
@@ -6195,7 +6195,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 344,
|
||||
"weight": 341,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/update-transaction.md",
|
||||
@@ -6274,7 +6274,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 345,
|
||||
"weight": 342,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/delete-transaction.md",
|
||||
@@ -6339,7 +6339,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createOperations",
|
||||
"group": "transactions",
|
||||
"weight": 347,
|
||||
"weight": 344,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-operations.md",
|
||||
@@ -6420,7 +6420,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listDocuments",
|
||||
"group": "documents",
|
||||
"weight": 297,
|
||||
"weight": 294,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/list-documents.md",
|
||||
@@ -6524,7 +6524,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createDocument",
|
||||
"group": "documents",
|
||||
"weight": 289,
|
||||
"weight": 286,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-document.md",
|
||||
@@ -6683,7 +6683,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getDocument",
|
||||
"group": "documents",
|
||||
"weight": 290,
|
||||
"weight": 287,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/get-document.md",
|
||||
@@ -6786,7 +6786,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "upsertDocument",
|
||||
"group": "documents",
|
||||
"weight": 293,
|
||||
"weight": 290,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/upsert-document.md",
|
||||
@@ -6937,7 +6937,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateDocument",
|
||||
"group": "documents",
|
||||
"weight": 291,
|
||||
"weight": 288,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/update-document.md",
|
||||
@@ -7047,7 +7047,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteDocument",
|
||||
"group": "documents",
|
||||
"weight": 295,
|
||||
"weight": 292,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/delete-document.md",
|
||||
@@ -7148,7 +7148,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "decrementDocumentAttribute",
|
||||
"group": "documents",
|
||||
"weight": 300,
|
||||
"weight": 297,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/decrement-document-attribute.md",
|
||||
@@ -7271,7 +7271,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "incrementDocumentAttribute",
|
||||
"group": "documents",
|
||||
"weight": 299,
|
||||
"weight": 296,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/increment-document-attribute.md",
|
||||
@@ -7392,7 +7392,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"group": "executions",
|
||||
"weight": 447,
|
||||
"weight": 444,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/list-executions.md",
|
||||
@@ -7475,7 +7475,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"group": "executions",
|
||||
"weight": 445,
|
||||
"weight": 442,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/create-execution.md",
|
||||
@@ -7594,7 +7594,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"group": "executions",
|
||||
"weight": 446,
|
||||
"weight": 443,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/get-execution.md",
|
||||
@@ -7666,7 +7666,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 190,
|
||||
"weight": 187,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/query.md",
|
||||
@@ -7741,7 +7741,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 189,
|
||||
"weight": 186,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/mutation.md",
|
||||
@@ -8240,7 +8240,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 237,
|
||||
"weight": 234,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/create-subscriber.md",
|
||||
@@ -8325,7 +8325,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 241,
|
||||
"weight": 238,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/delete-subscriber.md",
|
||||
@@ -8396,7 +8396,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 543,
|
||||
"weight": 540,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/list-files.md",
|
||||
@@ -8489,7 +8489,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 541,
|
||||
"weight": 538,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"demo": "storage\/create-file.md",
|
||||
@@ -8580,7 +8580,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 542,
|
||||
"weight": 539,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/get-file.md",
|
||||
@@ -8651,7 +8651,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 544,
|
||||
"weight": 541,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/update-file.md",
|
||||
@@ -8742,7 +8742,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 545,
|
||||
"weight": 542,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/delete-file.md",
|
||||
@@ -8813,7 +8813,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 547,
|
||||
"weight": 544,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-download.md",
|
||||
@@ -8893,7 +8893,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 546,
|
||||
"weight": 543,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-preview.md",
|
||||
@@ -9101,7 +9101,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 548,
|
||||
"weight": 545,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-view.md",
|
||||
@@ -9181,7 +9181,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listTransactions",
|
||||
"group": "transactions",
|
||||
"weight": 419,
|
||||
"weight": 416,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/list-transactions.md",
|
||||
@@ -9251,7 +9251,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 415,
|
||||
"weight": 412,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-transaction.md",
|
||||
@@ -9325,7 +9325,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 416,
|
||||
"weight": 413,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/get-transaction.md",
|
||||
@@ -9391,7 +9391,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 417,
|
||||
"weight": 414,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/update-transaction.md",
|
||||
@@ -9473,7 +9473,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 418,
|
||||
"weight": 415,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/delete-transaction.md",
|
||||
@@ -9541,7 +9541,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createOperations",
|
||||
"group": "transactions",
|
||||
"weight": 420,
|
||||
"weight": 417,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-operations.md",
|
||||
@@ -9625,7 +9625,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listRows",
|
||||
"group": "rows",
|
||||
"weight": 411,
|
||||
"weight": 408,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/list-rows.md",
|
||||
@@ -9728,7 +9728,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createRow",
|
||||
"group": "rows",
|
||||
"weight": 403,
|
||||
"weight": 400,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-row.md",
|
||||
@@ -9882,7 +9882,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getRow",
|
||||
"group": "rows",
|
||||
"weight": 404,
|
||||
"weight": 401,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/get-row.md",
|
||||
@@ -9984,7 +9984,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "upsertRow",
|
||||
"group": "rows",
|
||||
"weight": 407,
|
||||
"weight": 404,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/upsert-row.md",
|
||||
@@ -10130,7 +10130,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateRow",
|
||||
"group": "rows",
|
||||
"weight": 405,
|
||||
"weight": 402,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/update-row.md",
|
||||
@@ -10239,7 +10239,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteRow",
|
||||
"group": "rows",
|
||||
"weight": 409,
|
||||
"weight": 406,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/delete-row.md",
|
||||
@@ -10339,7 +10339,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "decrementRowColumn",
|
||||
"group": "rows",
|
||||
"weight": 414,
|
||||
"weight": 411,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/decrement-row-column.md",
|
||||
@@ -10461,7 +10461,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "incrementRowColumn",
|
||||
"group": "rows",
|
||||
"weight": 413,
|
||||
"weight": 410,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/increment-row-column.md",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -612,7 +612,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMFA",
|
||||
"group": "mfa",
|
||||
"weight": 253,
|
||||
"weight": 250,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa.md",
|
||||
@@ -687,7 +687,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 255,
|
||||
"weight": 252,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-authenticator.md",
|
||||
@@ -811,7 +811,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 256,
|
||||
"weight": 253,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-authenticator.md",
|
||||
@@ -952,7 +952,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteMfaAuthenticator",
|
||||
"group": "mfa",
|
||||
"weight": 257,
|
||||
"weight": 254,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/delete-mfa-authenticator.md",
|
||||
@@ -1076,7 +1076,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 261,
|
||||
"weight": 258,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-challenge.md",
|
||||
@@ -1213,7 +1213,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaChallenge",
|
||||
"group": "mfa",
|
||||
"weight": 262,
|
||||
"weight": 259,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-challenge.md",
|
||||
@@ -1353,7 +1353,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listMfaFactors",
|
||||
"group": "mfa",
|
||||
"weight": 254,
|
||||
"weight": 251,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/list-mfa-factors.md",
|
||||
@@ -1454,7 +1454,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 260,
|
||||
"weight": 257,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/get-mfa-recovery-codes.md",
|
||||
@@ -1555,7 +1555,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 258,
|
||||
"weight": 255,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/create-mfa-recovery-codes.md",
|
||||
@@ -1656,7 +1656,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateMfaRecoveryCodes",
|
||||
"group": "mfa",
|
||||
"weight": 259,
|
||||
"weight": 256,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "account\/update-mfa-recovery-codes.md",
|
||||
@@ -4203,7 +4203,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getBrowser",
|
||||
"group": null,
|
||||
"weight": 264,
|
||||
"weight": 261,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-browser.md",
|
||||
@@ -4329,7 +4329,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getCreditCard",
|
||||
"group": null,
|
||||
"weight": 263,
|
||||
"weight": 260,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-credit-card.md",
|
||||
@@ -4461,7 +4461,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFavicon",
|
||||
"group": null,
|
||||
"weight": 267,
|
||||
"weight": 264,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-favicon.md",
|
||||
@@ -4525,7 +4525,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFlag",
|
||||
"group": null,
|
||||
"weight": 265,
|
||||
"weight": 262,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-flag.md",
|
||||
@@ -5013,7 +5013,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getImage",
|
||||
"group": null,
|
||||
"weight": 266,
|
||||
"weight": 263,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-image.md",
|
||||
@@ -5097,7 +5097,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getInitials",
|
||||
"group": null,
|
||||
"weight": 269,
|
||||
"weight": 266,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-initials.md",
|
||||
@@ -5189,7 +5189,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getQR",
|
||||
"group": null,
|
||||
"weight": 268,
|
||||
"weight": 265,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-qr.md",
|
||||
@@ -5281,7 +5281,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getScreenshot",
|
||||
"group": null,
|
||||
"weight": 270,
|
||||
"weight": 267,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "avatars\/get-screenshot.md",
|
||||
@@ -5994,7 +5994,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listTransactions",
|
||||
"group": "transactions",
|
||||
"weight": 346,
|
||||
"weight": 343,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/list-transactions.md",
|
||||
@@ -6061,7 +6061,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 342,
|
||||
"weight": 339,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-transaction.md",
|
||||
@@ -6132,7 +6132,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 343,
|
||||
"weight": 340,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/get-transaction.md",
|
||||
@@ -6195,7 +6195,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 344,
|
||||
"weight": 341,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/update-transaction.md",
|
||||
@@ -6274,7 +6274,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 345,
|
||||
"weight": 342,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/delete-transaction.md",
|
||||
@@ -6339,7 +6339,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createOperations",
|
||||
"group": "transactions",
|
||||
"weight": 347,
|
||||
"weight": 344,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-operations.md",
|
||||
@@ -6420,7 +6420,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listDocuments",
|
||||
"group": "documents",
|
||||
"weight": 297,
|
||||
"weight": 294,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/list-documents.md",
|
||||
@@ -6524,7 +6524,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createDocument",
|
||||
"group": "documents",
|
||||
"weight": 289,
|
||||
"weight": 286,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/create-document.md",
|
||||
@@ -6683,7 +6683,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getDocument",
|
||||
"group": "documents",
|
||||
"weight": 290,
|
||||
"weight": 287,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/get-document.md",
|
||||
@@ -6786,7 +6786,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "upsertDocument",
|
||||
"group": "documents",
|
||||
"weight": 293,
|
||||
"weight": 290,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/upsert-document.md",
|
||||
@@ -6937,7 +6937,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateDocument",
|
||||
"group": "documents",
|
||||
"weight": 291,
|
||||
"weight": 288,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/update-document.md",
|
||||
@@ -7047,7 +7047,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteDocument",
|
||||
"group": "documents",
|
||||
"weight": 295,
|
||||
"weight": 292,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/delete-document.md",
|
||||
@@ -7148,7 +7148,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "decrementDocumentAttribute",
|
||||
"group": "documents",
|
||||
"weight": 300,
|
||||
"weight": 297,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/decrement-document-attribute.md",
|
||||
@@ -7271,7 +7271,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "incrementDocumentAttribute",
|
||||
"group": "documents",
|
||||
"weight": 299,
|
||||
"weight": 296,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "databases\/increment-document-attribute.md",
|
||||
@@ -7392,7 +7392,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"group": "executions",
|
||||
"weight": 447,
|
||||
"weight": 444,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/list-executions.md",
|
||||
@@ -7475,7 +7475,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"group": "executions",
|
||||
"weight": 445,
|
||||
"weight": 442,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/create-execution.md",
|
||||
@@ -7594,7 +7594,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"group": "executions",
|
||||
"weight": 446,
|
||||
"weight": 443,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "functions\/get-execution.md",
|
||||
@@ -7666,7 +7666,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"group": "graphql",
|
||||
"weight": 190,
|
||||
"weight": 187,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/query.md",
|
||||
@@ -7741,7 +7741,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"group": "graphql",
|
||||
"weight": 189,
|
||||
"weight": 186,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"demo": "graphql\/mutation.md",
|
||||
@@ -8240,7 +8240,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 237,
|
||||
"weight": 234,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/create-subscriber.md",
|
||||
@@ -8325,7 +8325,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"group": "subscribers",
|
||||
"weight": 241,
|
||||
"weight": 238,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "messaging\/delete-subscriber.md",
|
||||
@@ -8396,7 +8396,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"group": "files",
|
||||
"weight": 543,
|
||||
"weight": 540,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/list-files.md",
|
||||
@@ -8489,7 +8489,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"group": "files",
|
||||
"weight": 541,
|
||||
"weight": 538,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"demo": "storage\/create-file.md",
|
||||
@@ -8580,7 +8580,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"group": "files",
|
||||
"weight": 542,
|
||||
"weight": 539,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/get-file.md",
|
||||
@@ -8651,7 +8651,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"group": "files",
|
||||
"weight": 544,
|
||||
"weight": 541,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/update-file.md",
|
||||
@@ -8742,7 +8742,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"group": "files",
|
||||
"weight": 545,
|
||||
"weight": 542,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "storage\/delete-file.md",
|
||||
@@ -8813,7 +8813,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"group": "files",
|
||||
"weight": 547,
|
||||
"weight": 544,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-download.md",
|
||||
@@ -8893,7 +8893,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"group": "files",
|
||||
"weight": 546,
|
||||
"weight": 543,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-preview.md",
|
||||
@@ -9101,7 +9101,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"group": "files",
|
||||
"weight": 548,
|
||||
"weight": 545,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"demo": "storage\/get-file-view.md",
|
||||
@@ -9181,7 +9181,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listTransactions",
|
||||
"group": "transactions",
|
||||
"weight": 419,
|
||||
"weight": 416,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/list-transactions.md",
|
||||
@@ -9251,7 +9251,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 415,
|
||||
"weight": 412,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-transaction.md",
|
||||
@@ -9325,7 +9325,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 416,
|
||||
"weight": 413,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/get-transaction.md",
|
||||
@@ -9391,7 +9391,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 417,
|
||||
"weight": 414,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/update-transaction.md",
|
||||
@@ -9473,7 +9473,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteTransaction",
|
||||
"group": "transactions",
|
||||
"weight": 418,
|
||||
"weight": 415,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/delete-transaction.md",
|
||||
@@ -9541,7 +9541,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createOperations",
|
||||
"group": "transactions",
|
||||
"weight": 420,
|
||||
"weight": 417,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-operations.md",
|
||||
@@ -9625,7 +9625,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "listRows",
|
||||
"group": "rows",
|
||||
"weight": 411,
|
||||
"weight": 408,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/list-rows.md",
|
||||
@@ -9728,7 +9728,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "createRow",
|
||||
"group": "rows",
|
||||
"weight": 403,
|
||||
"weight": 400,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/create-row.md",
|
||||
@@ -9882,7 +9882,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "getRow",
|
||||
"group": "rows",
|
||||
"weight": 404,
|
||||
"weight": 401,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/get-row.md",
|
||||
@@ -9984,7 +9984,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "upsertRow",
|
||||
"group": "rows",
|
||||
"weight": 407,
|
||||
"weight": 404,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/upsert-row.md",
|
||||
@@ -10130,7 +10130,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "updateRow",
|
||||
"group": "rows",
|
||||
"weight": 405,
|
||||
"weight": 402,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/update-row.md",
|
||||
@@ -10239,7 +10239,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "deleteRow",
|
||||
"group": "rows",
|
||||
"weight": 409,
|
||||
"weight": 406,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/delete-row.md",
|
||||
@@ -10339,7 +10339,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "decrementRowColumn",
|
||||
"group": "rows",
|
||||
"weight": 414,
|
||||
"weight": 411,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/decrement-row-column.md",
|
||||
@@ -10461,7 +10461,7 @@
|
||||
"x-appwrite": {
|
||||
"method": "incrementRowColumn",
|
||||
"group": "rows",
|
||||
"weight": 413,
|
||||
"weight": 410,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"demo": "tablesdb\/increment-row-column.md",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -36,7 +36,6 @@ use Appwrite\Utopia\Response;
|
||||
use libphonenumber\NumberParseException;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Auth\Hashes\Sha;
|
||||
use Utopia\Auth\Proofs\Code as ProofsCode;
|
||||
@@ -61,6 +60,7 @@ use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Emails\Email;
|
||||
use Utopia\Http;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Storage\Validator\FileName;
|
||||
use Utopia\System\System;
|
||||
@@ -344,7 +344,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
||||
$response->dynamic($session, Response::MODEL_SESSION);
|
||||
};
|
||||
|
||||
App::post('/v1/account')
|
||||
Http::post('/v1/account')
|
||||
->desc('Create account')
|
||||
->groups(['api', 'account', 'auth'])
|
||||
->label('scope', 'sessions.write')
|
||||
@@ -507,7 +507,7 @@ App::post('/v1/account')
|
||||
->dynamic($user, Response::MODEL_ACCOUNT);
|
||||
});
|
||||
|
||||
App::get('/v1/account')
|
||||
Http::get('/v1/account')
|
||||
->desc('Get account')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
@@ -535,7 +535,7 @@ App::get('/v1/account')
|
||||
$response->dynamic($user, Response::MODEL_ACCOUNT);
|
||||
});
|
||||
|
||||
App::delete('/v1/account')
|
||||
Http::delete('/v1/account')
|
||||
->desc('Delete account')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
@@ -590,7 +590,7 @@ App::delete('/v1/account')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::get('/v1/account/sessions')
|
||||
Http::get('/v1/account/sessions')
|
||||
->desc('List sessions')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
@@ -635,7 +635,7 @@ App::get('/v1/account/sessions')
|
||||
]), Response::MODEL_SESSION_LIST);
|
||||
});
|
||||
|
||||
App::delete('/v1/account/sessions')
|
||||
Http::delete('/v1/account/sessions')
|
||||
->desc('Delete sessions')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
@@ -710,7 +710,7 @@ App::delete('/v1/account/sessions')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::get('/v1/account/sessions/:sessionId')
|
||||
Http::get('/v1/account/sessions/:sessionId')
|
||||
->desc('Get session')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
@@ -758,7 +758,7 @@ App::get('/v1/account/sessions/:sessionId')
|
||||
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
|
||||
});
|
||||
|
||||
App::delete('/v1/account/sessions/:sessionId')
|
||||
Http::delete('/v1/account/sessions/:sessionId')
|
||||
->desc('Delete session')
|
||||
->groups(['api', 'account', 'mfa'])
|
||||
->label('scope', 'account')
|
||||
@@ -845,7 +845,7 @@ App::delete('/v1/account/sessions/:sessionId')
|
||||
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
|
||||
});
|
||||
|
||||
App::patch('/v1/account/sessions/:sessionId')
|
||||
Http::patch('/v1/account/sessions/:sessionId')
|
||||
->desc('Update session')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
@@ -934,7 +934,7 @@ App::patch('/v1/account/sessions/:sessionId')
|
||||
return $response->dynamic($session, Response::MODEL_SESSION);
|
||||
});
|
||||
|
||||
App::post('/v1/account/sessions/email')
|
||||
Http::post('/v1/account/sessions/email')
|
||||
->alias('/v1/account/sessions')
|
||||
->desc('Create email password session')
|
||||
->groups(['api', 'account', 'auth', 'session'])
|
||||
@@ -1086,7 +1086,7 @@ App::post('/v1/account/sessions/email')
|
||||
$response->dynamic($session, Response::MODEL_SESSION);
|
||||
});
|
||||
|
||||
App::post('/v1/account/sessions/anonymous')
|
||||
Http::post('/v1/account/sessions/anonymous')
|
||||
->desc('Create anonymous session')
|
||||
->groups(['api', 'account', 'auth', 'session'])
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
@@ -1237,7 +1237,7 @@ App::post('/v1/account/sessions/anonymous')
|
||||
$response->dynamic($session, Response::MODEL_SESSION);
|
||||
});
|
||||
|
||||
App::post('/v1/account/sessions/token')
|
||||
Http::post('/v1/account/sessions/token')
|
||||
->desc('Create session')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
->groups(['api', 'account', 'session'])
|
||||
@@ -1280,7 +1280,7 @@ App::post('/v1/account/sessions/token')
|
||||
->inject('authorization')
|
||||
->action($createSession);
|
||||
|
||||
App::get('/v1/account/sessions/oauth2/:provider')
|
||||
Http::get('/v1/account/sessions/oauth2/:provider')
|
||||
->desc('Create OAuth2 session')
|
||||
->groups(['api', 'account'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
@@ -1374,7 +1374,7 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
||||
->redirect($oauth2->getLoginURL());
|
||||
});
|
||||
|
||||
App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
Http::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
->desc('Get OAuth2 callback')
|
||||
->groups(['account'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
@@ -1409,7 +1409,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
. \http_build_query($params));
|
||||
});
|
||||
|
||||
App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
Http::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
->desc('Create OAuth2 callback')
|
||||
->groups(['account'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
@@ -1445,7 +1445,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
||||
. \http_build_query($params));
|
||||
});
|
||||
|
||||
App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
Http::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->desc('Get OAuth2 redirect')
|
||||
->groups(['api', 'account', 'session'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
@@ -1469,13 +1469,14 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
->inject('devKey')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->inject('geodb')
|
||||
->inject('queueForEvents')
|
||||
->inject('store')
|
||||
->inject('proofForPassword')
|
||||
->inject('proofForToken')
|
||||
->inject('authorization')
|
||||
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Validator $redirectValidator, Document $devKey, User $user, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) use ($oauthDefaultSuccess) {
|
||||
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Validator $redirectValidator, Document $devKey, User $user, Database $dbForProject, Database $dbForPlatform, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) use ($oauthDefaultSuccess) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$port = $request->getPort();
|
||||
$callbackBase = $protocol . '://' . $request->getHostname();
|
||||
@@ -1512,6 +1513,25 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
$state = $defaultState;
|
||||
}
|
||||
|
||||
// Allow redirect to rule URL if related to project
|
||||
//Check if $redirectValidator is instance of Redirect class
|
||||
if ($redirectValidator instanceof Redirect) {
|
||||
$rules = $authorization->skip(fn () => $dbForPlatform->find('rules', [
|
||||
Query::equal('domain', [
|
||||
parse_url($state['success'], PHP_URL_HOST),
|
||||
parse_url($state['failure'], PHP_URL_HOST)
|
||||
]),
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
Query::limit(2)
|
||||
]));
|
||||
|
||||
foreach ($rules as $rule) {
|
||||
$allowedHostnames = $redirectValidator->getAllowedHostnames();
|
||||
$allowedHostnames[] = $rule->getAttribute('domain', '');
|
||||
$redirectValidator->setAllowedHostnames($allowedHostnames);
|
||||
}
|
||||
}
|
||||
|
||||
if ($devKey->isEmpty() && !$redirectValidator->isValid($state['success'])) {
|
||||
throw new Exception(Exception::PROJECT_INVALID_SUCCESS_URL);
|
||||
}
|
||||
@@ -1948,7 +1968,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
||||
;
|
||||
});
|
||||
|
||||
App::get('/v1/account/tokens/oauth2/:provider')
|
||||
Http::get('/v1/account/tokens/oauth2/:provider')
|
||||
->desc('Create OAuth2 token')
|
||||
->groups(['api', 'account'])
|
||||
->label('error', __DIR__ . '/../../views/general/error.phtml')
|
||||
@@ -2043,7 +2063,7 @@ App::get('/v1/account/tokens/oauth2/:provider')
|
||||
->redirect($oauth2->getLoginURL());
|
||||
});
|
||||
|
||||
App::post('/v1/account/tokens/magic-url')
|
||||
Http::post('/v1/account/tokens/magic-url')
|
||||
->alias('/v1/account/sessions/magic-url')
|
||||
->desc('Create magic URL token')
|
||||
->groups(['api', 'account', 'auth'])
|
||||
@@ -2324,7 +2344,7 @@ App::post('/v1/account/tokens/magic-url')
|
||||
->dynamic($token, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::post('/v1/account/tokens/email')
|
||||
Http::post('/v1/account/tokens/email')
|
||||
->desc('Create email token (OTP)')
|
||||
->groups(['api', 'account', 'auth'])
|
||||
->label('scope', 'sessions.write')
|
||||
@@ -2626,7 +2646,7 @@ App::post('/v1/account/tokens/email')
|
||||
->dynamic($token, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::put('/v1/account/sessions/magic-url')
|
||||
Http::put('/v1/account/sessions/magic-url')
|
||||
->desc('Update magic URL session')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
->groups(['api', 'account', 'session'])
|
||||
@@ -2676,7 +2696,7 @@ App::put('/v1/account/sessions/magic-url')
|
||||
$createSession($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForToken, $proofForCode, $authorization);
|
||||
});
|
||||
|
||||
App::put('/v1/account/sessions/phone')
|
||||
Http::put('/v1/account/sessions/phone')
|
||||
->desc('Update phone session')
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
->groups(['api', 'account', 'session'])
|
||||
@@ -2722,7 +2742,7 @@ App::put('/v1/account/sessions/phone')
|
||||
->inject('authorization')
|
||||
->action($createSession);
|
||||
|
||||
App::post('/v1/account/tokens/phone')
|
||||
Http::post('/v1/account/tokens/phone')
|
||||
->alias('/v1/account/sessions/phone')
|
||||
->desc('Create phone token')
|
||||
->groups(['api', 'account', 'auth'])
|
||||
@@ -2942,7 +2962,7 @@ App::post('/v1/account/tokens/phone')
|
||||
->dynamic($token, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::post('/v1/account/jwts')
|
||||
Http::post('/v1/account/jwts')
|
||||
->alias('/v1/account/jwt')
|
||||
->desc('Create JWT')
|
||||
->groups(['api', 'account', 'auth'])
|
||||
@@ -2989,7 +3009,7 @@ App::post('/v1/account/jwts')
|
||||
]), Response::MODEL_JWT);
|
||||
});
|
||||
|
||||
App::get('/v1/account/prefs')
|
||||
Http::get('/v1/account/prefs')
|
||||
->desc('Get account preferences')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
@@ -3016,7 +3036,7 @@ App::get('/v1/account/prefs')
|
||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||
});
|
||||
|
||||
App::get('/v1/account/logs')
|
||||
Http::get('/v1/account/logs')
|
||||
->desc('List logs')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
@@ -3087,7 +3107,7 @@ App::get('/v1/account/logs')
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::patch('/v1/account/name')
|
||||
Http::patch('/v1/account/name')
|
||||
->desc('Update name')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.name')
|
||||
@@ -3124,7 +3144,7 @@ App::patch('/v1/account/name')
|
||||
$response->dynamic($user, Response::MODEL_ACCOUNT);
|
||||
});
|
||||
|
||||
App::patch('/v1/account/password')
|
||||
Http::patch('/v1/account/password')
|
||||
->desc('Update password')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.password')
|
||||
@@ -3217,7 +3237,7 @@ App::patch('/v1/account/password')
|
||||
$response->dynamic($user, Response::MODEL_ACCOUNT);
|
||||
});
|
||||
|
||||
App::patch('/v1/account/email')
|
||||
Http::patch('/v1/account/email')
|
||||
->desc('Update email')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.email')
|
||||
@@ -3329,7 +3349,7 @@ App::patch('/v1/account/email')
|
||||
$response->dynamic($user, Response::MODEL_ACCOUNT);
|
||||
});
|
||||
|
||||
App::patch('/v1/account/phone')
|
||||
Http::patch('/v1/account/phone')
|
||||
->desc('Update phone')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.phone')
|
||||
@@ -3418,7 +3438,7 @@ App::patch('/v1/account/phone')
|
||||
$response->dynamic($user, Response::MODEL_ACCOUNT);
|
||||
});
|
||||
|
||||
App::patch('/v1/account/prefs')
|
||||
Http::patch('/v1/account/prefs')
|
||||
->desc('Update preferences')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.prefs')
|
||||
@@ -3456,7 +3476,7 @@ App::patch('/v1/account/prefs')
|
||||
$response->dynamic($user, Response::MODEL_ACCOUNT);
|
||||
});
|
||||
|
||||
App::patch('/v1/account/status')
|
||||
Http::patch('/v1/account/status')
|
||||
->desc('Update status')
|
||||
->groups(['api', 'account'])
|
||||
->label('event', 'users.[userId].update.status')
|
||||
@@ -3506,7 +3526,7 @@ App::patch('/v1/account/status')
|
||||
$response->dynamic($user, Response::MODEL_ACCOUNT);
|
||||
});
|
||||
|
||||
App::post('/v1/account/recovery')
|
||||
Http::post('/v1/account/recovery')
|
||||
->desc('Create password recovery')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'sessions.write')
|
||||
@@ -3701,7 +3721,7 @@ App::post('/v1/account/recovery')
|
||||
->dynamic($recovery, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::put('/v1/account/recovery')
|
||||
Http::put('/v1/account/recovery')
|
||||
->desc('Update password recovery (confirmation)')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'sessions.write')
|
||||
@@ -3798,7 +3818,7 @@ App::put('/v1/account/recovery')
|
||||
$response->dynamic($recoveryDocument, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::post('/v1/account/verifications/email')
|
||||
Http::post('/v1/account/verifications/email')
|
||||
->alias('/v1/account/verification')
|
||||
->desc('Create email verification')
|
||||
->groups(['api', 'account'])
|
||||
@@ -4031,7 +4051,7 @@ App::post('/v1/account/verifications/email')
|
||||
->dynamic($verification, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::put('/v1/account/verifications/email')
|
||||
Http::put('/v1/account/verifications/email')
|
||||
->alias('/v1/account/verification')
|
||||
->desc('Update email verification (confirmation)')
|
||||
->groups(['api', 'account'])
|
||||
@@ -4121,7 +4141,7 @@ App::put('/v1/account/verifications/email')
|
||||
$response->dynamic($verification, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::post('/v1/account/verifications/phone')
|
||||
Http::post('/v1/account/verifications/phone')
|
||||
->alias('/v1/account/verification/phone')
|
||||
->desc('Create phone verification')
|
||||
->groups(['api', 'account', 'auth'])
|
||||
@@ -4268,7 +4288,7 @@ App::post('/v1/account/verifications/phone')
|
||||
->dynamic($verification, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::put('/v1/account/verifications/phone')
|
||||
Http::put('/v1/account/verifications/phone')
|
||||
->alias('/v1/account/verification/phone')
|
||||
->desc('Update phone verification (confirmation)')
|
||||
->groups(['api', 'account'])
|
||||
@@ -4336,7 +4356,7 @@ App::put('/v1/account/verifications/phone')
|
||||
$response->dynamic($verificationDocument, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::post('/v1/account/targets/push')
|
||||
Http::post('/v1/account/targets/push')
|
||||
->desc('Create push target')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'targets.write')
|
||||
@@ -4420,7 +4440,7 @@ App::post('/v1/account/targets/push')
|
||||
->dynamic($target, Response::MODEL_TARGET);
|
||||
});
|
||||
|
||||
App::put('/v1/account/targets/:targetId/push')
|
||||
Http::put('/v1/account/targets/:targetId/push')
|
||||
->desc('Update push target')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'targets.write')
|
||||
@@ -4486,7 +4506,7 @@ App::put('/v1/account/targets/:targetId/push')
|
||||
->dynamic($target, Response::MODEL_TARGET);
|
||||
});
|
||||
|
||||
App::delete('/v1/account/targets/:targetId/push')
|
||||
Http::delete('/v1/account/targets/:targetId/push')
|
||||
->desc('Delete push target')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'targets.write')
|
||||
@@ -4541,7 +4561,7 @@ App::delete('/v1/account/targets/:targetId/push')
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
App::get('/v1/account/identities')
|
||||
Http::get('/v1/account/identities')
|
||||
->desc('List identities')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
@@ -4574,16 +4594,10 @@ App::get('/v1/account/identities')
|
||||
|
||||
$queries[] = Query::equal('userInternalId', [$user->getSequence()]);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -4613,7 +4627,7 @@ App::get('/v1/account/identities')
|
||||
]), Response::MODEL_IDENTITY_LIST);
|
||||
});
|
||||
|
||||
App::delete('/v1/account/identities/:identityId')
|
||||
Http::delete('/v1/account/identities/:identityId')
|
||||
->desc('Delete identity')
|
||||
->groups(['api', 'account'])
|
||||
->label('scope', 'account')
|
||||
|
||||
@@ -18,14 +18,14 @@ use GraphQL\Validator\Rules\DisableIntrospection;
|
||||
use GraphQL\Validator\Rules\QueryComplexity;
|
||||
use GraphQL\Validator\Rules\QueryDepth;
|
||||
use Swoole\Coroutine\WaitGroup;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Http;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\JSON;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['graphql'])
|
||||
->inject('project')
|
||||
->inject('authorization')
|
||||
@@ -39,7 +39,7 @@ App::init()
|
||||
}
|
||||
});
|
||||
|
||||
App::get('/v1/graphql')
|
||||
Http::get('/v1/graphql')
|
||||
->desc('GraphQL endpoint')
|
||||
->groups(['graphql'])
|
||||
->label('scope', 'graphql')
|
||||
@@ -86,7 +86,7 @@ App::get('/v1/graphql')
|
||||
->json($output);
|
||||
});
|
||||
|
||||
App::post('/v1/graphql/mutation')
|
||||
Http::post('/v1/graphql/mutation')
|
||||
->desc('GraphQL endpoint')
|
||||
->groups(['graphql'])
|
||||
->label('scope', 'graphql')
|
||||
@@ -137,7 +137,7 @@ App::post('/v1/graphql/mutation')
|
||||
->json($output);
|
||||
});
|
||||
|
||||
App::post('/v1/graphql')
|
||||
Http::post('/v1/graphql')
|
||||
->desc('GraphQL endpoint')
|
||||
->groups(['graphql'])
|
||||
->label('scope', 'graphql')
|
||||
@@ -232,7 +232,7 @@ function execute(
|
||||
$validations[] = new QueryComplexity($maxComplexity);
|
||||
$validations[] = new QueryDepth($maxDepth);
|
||||
}
|
||||
if (App::getMode() === App::MODE_TYPE_PRODUCTION) {
|
||||
if (Http::getMode() === Http::MODE_TYPE_PRODUCTION) {
|
||||
$flags = DebugFlag::NONE;
|
||||
}
|
||||
|
||||
@@ -333,7 +333,7 @@ function processResult($result, $debugFlags): array
|
||||
);
|
||||
}
|
||||
|
||||
App::shutdown()
|
||||
Http::shutdown()
|
||||
->groups(['schema'])
|
||||
->inject('project')
|
||||
->action(function (Document $project) {
|
||||
|
||||
@@ -6,12 +6,12 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Http;
|
||||
use Utopia\Locale\Locale;
|
||||
|
||||
App::get('/v1/locale')
|
||||
Http::get('/v1/locale')
|
||||
->desc('Get user locale')
|
||||
->groups(['api', 'locale'])
|
||||
->label('scope', 'locale.read')
|
||||
@@ -70,7 +70,7 @@ App::get('/v1/locale')
|
||||
$response->dynamic(new Document($output), Response::MODEL_LOCALE);
|
||||
});
|
||||
|
||||
App::get('/v1/locale/codes')
|
||||
Http::get('/v1/locale/codes')
|
||||
->desc('List locale codes')
|
||||
->groups(['api', 'locale'])
|
||||
->label('scope', 'locale.read')
|
||||
@@ -96,7 +96,7 @@ App::get('/v1/locale/codes')
|
||||
]), Response::MODEL_LOCALE_CODE_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/locale/countries')
|
||||
Http::get('/v1/locale/countries')
|
||||
->desc('List countries')
|
||||
->groups(['api', 'locale'])
|
||||
->label('scope', 'locale.read')
|
||||
@@ -133,7 +133,7 @@ App::get('/v1/locale/countries')
|
||||
$response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/locale/countries/eu')
|
||||
Http::get('/v1/locale/countries/eu')
|
||||
->desc('List EU countries')
|
||||
->groups(['api', 'locale'])
|
||||
->label('scope', 'locale.read')
|
||||
@@ -172,7 +172,7 @@ App::get('/v1/locale/countries/eu')
|
||||
$response->dynamic(new Document(['countries' => $output, 'total' => \count($output)]), Response::MODEL_COUNTRY_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/locale/countries/phones')
|
||||
Http::get('/v1/locale/countries/phones')
|
||||
->desc('List countries phone codes')
|
||||
->groups(['api', 'locale'])
|
||||
->label('scope', 'locale.read')
|
||||
@@ -210,7 +210,7 @@ App::get('/v1/locale/countries/phones')
|
||||
$response->dynamic(new Document(['phones' => $output, 'total' => \count($output)]), Response::MODEL_PHONE_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/locale/continents')
|
||||
Http::get('/v1/locale/continents')
|
||||
->desc('List continents')
|
||||
->groups(['api', 'locale'])
|
||||
->label('scope', 'locale.read')
|
||||
@@ -246,7 +246,7 @@ App::get('/v1/locale/continents')
|
||||
$response->dynamic(new Document(['continents' => $output, 'total' => \count($output)]), Response::MODEL_CONTINENT_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/locale/currencies')
|
||||
Http::get('/v1/locale/currencies')
|
||||
->desc('List currencies')
|
||||
->groups(['api', 'locale'])
|
||||
->label('scope', 'locale.read')
|
||||
@@ -273,7 +273,7 @@ App::get('/v1/locale/currencies')
|
||||
});
|
||||
|
||||
|
||||
App::get('/v1/locale/languages')
|
||||
Http::get('/v1/locale/languages')
|
||||
->desc('List languages')
|
||||
->groups(['api', 'locale'])
|
||||
->label('scope', 'locale.read')
|
||||
|
||||
@@ -25,7 +25,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Targets;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Topics;
|
||||
use Appwrite\Utopia\Response;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
@@ -44,6 +43,7 @@ use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\Roles;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\ArrayList;
|
||||
@@ -57,7 +57,7 @@ use Utopia\Validator\WhiteList;
|
||||
|
||||
use function Swoole\Coroutine\batch;
|
||||
|
||||
App::post('/v1/messaging/providers/mailgun')
|
||||
Http::post('/v1/messaging/providers/mailgun')
|
||||
->desc('Create Mailgun provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -151,7 +151,7 @@ App::post('/v1/messaging/providers/mailgun')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/providers/sendgrid')
|
||||
Http::post('/v1/messaging/providers/sendgrid')
|
||||
->desc('Create Sendgrid provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -233,7 +233,7 @@ App::post('/v1/messaging/providers/sendgrid')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/providers/resend')
|
||||
Http::post('/v1/messaging/providers/resend')
|
||||
->desc('Create Resend provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -315,7 +315,7 @@ App::post('/v1/messaging/providers/resend')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/providers/smtp')
|
||||
Http::post('/v1/messaging/providers/smtp')
|
||||
->desc('Create SMTP provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -430,7 +430,7 @@ App::post('/v1/messaging/providers/smtp')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/providers/msg91')
|
||||
Http::post('/v1/messaging/providers/msg91')
|
||||
->desc('Create Msg91 provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -513,7 +513,7 @@ App::post('/v1/messaging/providers/msg91')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/providers/telesign')
|
||||
Http::post('/v1/messaging/providers/telesign')
|
||||
->desc('Create Telesign provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -597,7 +597,7 @@ App::post('/v1/messaging/providers/telesign')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/providers/textmagic')
|
||||
Http::post('/v1/messaging/providers/textmagic')
|
||||
->desc('Create Textmagic provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -681,7 +681,7 @@ App::post('/v1/messaging/providers/textmagic')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/providers/twilio')
|
||||
Http::post('/v1/messaging/providers/twilio')
|
||||
->desc('Create Twilio provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -765,7 +765,7 @@ App::post('/v1/messaging/providers/twilio')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/providers/vonage')
|
||||
Http::post('/v1/messaging/providers/vonage')
|
||||
->desc('Create Vonage provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -849,7 +849,7 @@ App::post('/v1/messaging/providers/vonage')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/providers/fcm')
|
||||
Http::post('/v1/messaging/providers/fcm')
|
||||
->desc('Create FCM provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -939,7 +939,7 @@ App::post('/v1/messaging/providers/fcm')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/providers/apns')
|
||||
Http::post('/v1/messaging/providers/apns')
|
||||
->desc('Create APNS provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.create')
|
||||
@@ -1052,7 +1052,7 @@ App::post('/v1/messaging/providers/apns')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/providers')
|
||||
Http::get('/v1/messaging/providers')
|
||||
->desc('List providers')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'providers.read')
|
||||
@@ -1087,15 +1087,10 @@ App::get('/v1/messaging/providers')
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -1122,7 +1117,7 @@ App::get('/v1/messaging/providers')
|
||||
]), Response::MODEL_PROVIDER_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/providers/:providerId/logs')
|
||||
Http::get('/v1/messaging/providers/:providerId/logs')
|
||||
->desc('List provider logs')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'providers.read')
|
||||
@@ -1218,7 +1213,7 @@ App::get('/v1/messaging/providers/:providerId/logs')
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/providers/:providerId')
|
||||
Http::get('/v1/messaging/providers/:providerId')
|
||||
->desc('Get provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'providers.read')
|
||||
@@ -1249,7 +1244,7 @@ App::get('/v1/messaging/providers/:providerId')
|
||||
$response->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/providers/mailgun/:providerId')
|
||||
Http::patch('/v1/messaging/providers/mailgun/:providerId')
|
||||
->desc('Update Mailgun provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -1362,7 +1357,7 @@ App::patch('/v1/messaging/providers/mailgun/:providerId')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
||||
Http::patch('/v1/messaging/providers/sendgrid/:providerId')
|
||||
->desc('Update Sendgrid provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -1460,7 +1455,7 @@ App::patch('/v1/messaging/providers/sendgrid/:providerId')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/providers/resend/:providerId')
|
||||
Http::patch('/v1/messaging/providers/resend/:providerId')
|
||||
->desc('Update Resend provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -1558,7 +1553,7 @@ App::patch('/v1/messaging/providers/resend/:providerId')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/providers/smtp/:providerId')
|
||||
Http::patch('/v1/messaging/providers/smtp/:providerId')
|
||||
->desc('Update SMTP provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -1707,7 +1702,7 @@ App::patch('/v1/messaging/providers/smtp/:providerId')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/providers/msg91/:providerId')
|
||||
Http::patch('/v1/messaging/providers/msg91/:providerId')
|
||||
->desc('Update Msg91 provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -1794,7 +1789,7 @@ App::patch('/v1/messaging/providers/msg91/:providerId')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/providers/telesign/:providerId')
|
||||
Http::patch('/v1/messaging/providers/telesign/:providerId')
|
||||
->desc('Update Telesign provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -1883,7 +1878,7 @@ App::patch('/v1/messaging/providers/telesign/:providerId')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/providers/textmagic/:providerId')
|
||||
Http::patch('/v1/messaging/providers/textmagic/:providerId')
|
||||
->desc('Update Textmagic provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -1972,7 +1967,7 @@ App::patch('/v1/messaging/providers/textmagic/:providerId')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/providers/twilio/:providerId')
|
||||
Http::patch('/v1/messaging/providers/twilio/:providerId')
|
||||
->desc('Update Twilio provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -2061,7 +2056,7 @@ App::patch('/v1/messaging/providers/twilio/:providerId')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/providers/vonage/:providerId')
|
||||
Http::patch('/v1/messaging/providers/vonage/:providerId')
|
||||
->desc('Update Vonage provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -2150,7 +2145,7 @@ App::patch('/v1/messaging/providers/vonage/:providerId')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/providers/fcm/:providerId')
|
||||
Http::patch('/v1/messaging/providers/fcm/:providerId')
|
||||
->desc('Update FCM provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -2246,7 +2241,7 @@ App::patch('/v1/messaging/providers/fcm/:providerId')
|
||||
});
|
||||
|
||||
|
||||
App::patch('/v1/messaging/providers/apns/:providerId')
|
||||
Http::patch('/v1/messaging/providers/apns/:providerId')
|
||||
->desc('Update APNS provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.update')
|
||||
@@ -2368,7 +2363,7 @@ App::patch('/v1/messaging/providers/apns/:providerId')
|
||||
->dynamic($provider, Response::MODEL_PROVIDER);
|
||||
});
|
||||
|
||||
App::delete('/v1/messaging/providers/:providerId')
|
||||
Http::delete('/v1/messaging/providers/:providerId')
|
||||
->desc('Delete provider')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'provider.delete')
|
||||
@@ -2411,7 +2406,7 @@ App::delete('/v1/messaging/providers/:providerId')
|
||||
->noContent();
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/topics')
|
||||
Http::post('/v1/messaging/topics')
|
||||
->desc('Create topic')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'topic.create')
|
||||
@@ -2461,7 +2456,7 @@ App::post('/v1/messaging/topics')
|
||||
->dynamic($topic, Response::MODEL_TOPIC);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/topics')
|
||||
Http::get('/v1/messaging/topics')
|
||||
->desc('List topics')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'topics.read')
|
||||
@@ -2496,15 +2491,10 @@ App::get('/v1/messaging/topics')
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -2531,7 +2521,7 @@ App::get('/v1/messaging/topics')
|
||||
]), Response::MODEL_TOPIC_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/topics/:topicId/logs')
|
||||
Http::get('/v1/messaging/topics/:topicId/logs')
|
||||
->desc('List topic logs')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'topics.read')
|
||||
@@ -2628,7 +2618,7 @@ App::get('/v1/messaging/topics/:topicId/logs')
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/topics/:topicId')
|
||||
Http::get('/v1/messaging/topics/:topicId')
|
||||
->desc('Get topic')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'topics.read')
|
||||
@@ -2660,7 +2650,7 @@ App::get('/v1/messaging/topics/:topicId')
|
||||
->dynamic($topic, Response::MODEL_TOPIC);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/topics/:topicId')
|
||||
Http::patch('/v1/messaging/topics/:topicId')
|
||||
->desc('Update topic')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'topic.update')
|
||||
@@ -2711,7 +2701,7 @@ App::patch('/v1/messaging/topics/:topicId')
|
||||
->dynamic($topic, Response::MODEL_TOPIC);
|
||||
});
|
||||
|
||||
App::delete('/v1/messaging/topics/:topicId')
|
||||
Http::delete('/v1/messaging/topics/:topicId')
|
||||
->desc('Delete topic')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'topic.delete')
|
||||
@@ -2759,7 +2749,7 @@ App::delete('/v1/messaging/topics/:topicId')
|
||||
->noContent();
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
Http::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
->desc('Create subscriber')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'subscriber.create')
|
||||
@@ -2860,7 +2850,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
|
||||
->dynamic($subscriber, Response::MODEL_SUBSCRIBER);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
Http::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
->desc('List subscribers')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'subscribers.read')
|
||||
@@ -2904,15 +2894,10 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
|
||||
$queries[] = Query::equal('topicInternalId', [$topic->getSequence()]);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -2951,7 +2936,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
|
||||
]), Response::MODEL_SUBSCRIBER_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||
Http::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||
->desc('List subscriber logs')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'subscribers.read')
|
||||
@@ -3048,7 +3033,7 @@ App::get('/v1/messaging/subscribers/:subscriberId/logs')
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
Http::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->desc('Get subscriber')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'subscribers.read')
|
||||
@@ -3095,7 +3080,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->dynamic($subscriber, Response::MODEL_SUBSCRIBER);
|
||||
});
|
||||
|
||||
App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
Http::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->desc('Delete subscriber')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'subscriber.delete')
|
||||
@@ -3163,7 +3148,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
|
||||
->noContent();
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/messages/email')
|
||||
Http::post('/v1/messaging/messages/email')
|
||||
->desc('Create email')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.create')
|
||||
@@ -3322,7 +3307,7 @@ App::post('/v1/messaging/messages/email')
|
||||
->dynamic($message, Response::MODEL_MESSAGE);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/messages/sms')
|
||||
Http::post('/v1/messaging/messages/sms')
|
||||
->desc('Create SMS')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.create')
|
||||
@@ -3465,7 +3450,7 @@ App::post('/v1/messaging/messages/sms')
|
||||
->dynamic($message, Response::MODEL_MESSAGE);
|
||||
});
|
||||
|
||||
App::post('/v1/messaging/messages/push')
|
||||
Http::post('/v1/messaging/messages/push')
|
||||
->desc('Create push notification')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.create')
|
||||
@@ -3684,7 +3669,7 @@ App::post('/v1/messaging/messages/push')
|
||||
->dynamic($message, Response::MODEL_MESSAGE);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/messages')
|
||||
Http::get('/v1/messaging/messages')
|
||||
->desc('List messages')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'messages.read')
|
||||
@@ -3719,15 +3704,10 @@ App::get('/v1/messaging/messages')
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -3754,7 +3734,7 @@ App::get('/v1/messaging/messages')
|
||||
]), Response::MODEL_MESSAGE_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/messages/:messageId/logs')
|
||||
Http::get('/v1/messaging/messages/:messageId/logs')
|
||||
->desc('List message logs')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'messages.read')
|
||||
@@ -3851,7 +3831,7 @@ App::get('/v1/messaging/messages/:messageId/logs')
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/messages/:messageId/targets')
|
||||
Http::get('/v1/messaging/messages/:messageId/targets')
|
||||
->desc('List message targets')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'messages.read')
|
||||
@@ -3899,15 +3879,10 @@ App::get('/v1/messaging/messages/:messageId/targets')
|
||||
|
||||
$queries[] = Query::equal('$id', $targetIDs);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -3934,7 +3909,7 @@ App::get('/v1/messaging/messages/:messageId/targets')
|
||||
]), Response::MODEL_TARGET_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/messaging/messages/:messageId')
|
||||
Http::get('/v1/messaging/messages/:messageId')
|
||||
->desc('Get message')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('scope', 'messages.read')
|
||||
@@ -3965,7 +3940,7 @@ App::get('/v1/messaging/messages/:messageId')
|
||||
$response->dynamic($message, Response::MODEL_MESSAGE);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/messages/email/:messageId')
|
||||
Http::patch('/v1/messaging/messages/email/:messageId')
|
||||
->desc('Update email')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.update')
|
||||
@@ -4172,7 +4147,7 @@ App::patch('/v1/messaging/messages/email/:messageId')
|
||||
->dynamic($message, Response::MODEL_MESSAGE);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/messages/sms/:messageId')
|
||||
Http::patch('/v1/messaging/messages/sms/:messageId')
|
||||
->desc('Update SMS')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.update')
|
||||
@@ -4354,7 +4329,7 @@ App::patch('/v1/messaging/messages/sms/:messageId')
|
||||
->dynamic($message, Response::MODEL_MESSAGE);
|
||||
});
|
||||
|
||||
App::patch('/v1/messaging/messages/push/:messageId')
|
||||
Http::patch('/v1/messaging/messages/push/:messageId')
|
||||
->desc('Update push notification')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.update')
|
||||
@@ -4615,7 +4590,7 @@ App::patch('/v1/messaging/messages/push/:messageId')
|
||||
->dynamic($message, Response::MODEL_MESSAGE);
|
||||
});
|
||||
|
||||
App::delete('/v1/messaging/messages/:messageId')
|
||||
Http::delete('/v1/messaging/messages/:messageId')
|
||||
->desc('Delete message')
|
||||
->groups(['api', 'messaging'])
|
||||
->label('audits.event', 'message.delete')
|
||||
|
||||
@@ -11,7 +11,6 @@ use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\CompoundUID;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Migrations;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
@@ -22,6 +21,7 @@ use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Queries\Documents;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http;
|
||||
use Utopia\Migration\Resource;
|
||||
use Utopia\Migration\Sources\Appwrite;
|
||||
use Utopia\Migration\Sources\CSV;
|
||||
@@ -43,7 +43,7 @@ use Utopia\Validator\WhiteList;
|
||||
|
||||
include_once __DIR__ . '/../shared/api.php';
|
||||
|
||||
App::post('/v1/migrations/appwrite')
|
||||
Http::post('/v1/migrations/appwrite')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Create Appwrite migration')
|
||||
->label('scope', 'migrations.write')
|
||||
@@ -106,7 +106,7 @@ App::post('/v1/migrations/appwrite')
|
||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||
});
|
||||
|
||||
App::post('/v1/migrations/firebase')
|
||||
Http::post('/v1/migrations/firebase')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Create Firebase migration')
|
||||
->label('scope', 'migrations.write')
|
||||
@@ -175,7 +175,7 @@ App::post('/v1/migrations/firebase')
|
||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||
});
|
||||
|
||||
App::post('/v1/migrations/supabase')
|
||||
Http::post('/v1/migrations/supabase')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Create Supabase migration')
|
||||
->label('scope', 'migrations.write')
|
||||
@@ -244,7 +244,7 @@ App::post('/v1/migrations/supabase')
|
||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||
});
|
||||
|
||||
App::post('/v1/migrations/nhost')
|
||||
Http::post('/v1/migrations/nhost')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Create NHost migration')
|
||||
->label('scope', 'migrations.write')
|
||||
@@ -315,7 +315,7 @@ App::post('/v1/migrations/nhost')
|
||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||
});
|
||||
|
||||
App::post('/v1/migrations/csv/imports')
|
||||
Http::post('/v1/migrations/csv/imports')
|
||||
->alias('/v1/migrations/csv')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Import documents from a CSV')
|
||||
@@ -461,7 +461,7 @@ App::post('/v1/migrations/csv/imports')
|
||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||
});
|
||||
|
||||
App::post('/v1/migrations/csv/exports')
|
||||
Http::post('/v1/migrations/csv/exports')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Export documents to CSV')
|
||||
->label('scope', 'migrations.write')
|
||||
@@ -596,7 +596,7 @@ App::post('/v1/migrations/csv/exports')
|
||||
->dynamic($migration, Response::MODEL_MIGRATION);
|
||||
});
|
||||
|
||||
App::get('/v1/migrations')
|
||||
Http::get('/v1/migrations')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('List migrations')
|
||||
->label('scope', 'migrations.read')
|
||||
@@ -629,16 +629,10 @@ App::get('/v1/migrations')
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -667,7 +661,7 @@ App::get('/v1/migrations')
|
||||
]), Response::MODEL_MIGRATION_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/migrations/:migrationId')
|
||||
Http::get('/v1/migrations/:migrationId')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Get migration')
|
||||
->label('scope', 'migrations.read')
|
||||
@@ -697,7 +691,7 @@ App::get('/v1/migrations/:migrationId')
|
||||
$response->dynamic($migration, Response::MODEL_MIGRATION);
|
||||
});
|
||||
|
||||
App::get('/v1/migrations/appwrite/report')
|
||||
Http::get('/v1/migrations/appwrite/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Get Appwrite migration report')
|
||||
->label('scope', 'migrations.write')
|
||||
@@ -746,7 +740,7 @@ App::get('/v1/migrations/appwrite/report')
|
||||
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
||||
});
|
||||
|
||||
App::get('/v1/migrations/firebase/report')
|
||||
Http::get('/v1/migrations/firebase/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Get Firebase migration report')
|
||||
->label('scope', 'migrations.write')
|
||||
@@ -799,7 +793,7 @@ App::get('/v1/migrations/firebase/report')
|
||||
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
||||
});
|
||||
|
||||
App::get('/v1/migrations/supabase/report')
|
||||
Http::get('/v1/migrations/supabase/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Get Supabase migration report')
|
||||
->label('scope', 'migrations.write')
|
||||
@@ -848,7 +842,7 @@ App::get('/v1/migrations/supabase/report')
|
||||
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
||||
});
|
||||
|
||||
App::get('/v1/migrations/nhost/report')
|
||||
Http::get('/v1/migrations/nhost/report')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Get NHost migration report')
|
||||
->label('scope', 'migrations.write')
|
||||
@@ -897,7 +891,7 @@ App::get('/v1/migrations/nhost/report')
|
||||
->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT);
|
||||
});
|
||||
|
||||
App::patch('/v1/migrations/:migrationId')
|
||||
Http::patch('/v1/migrations/:migrationId')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Update retry migration')
|
||||
->label('scope', 'migrations.write')
|
||||
@@ -950,7 +944,7 @@ App::patch('/v1/migrations/:migrationId')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::delete('/v1/migrations/:migrationId')
|
||||
Http::delete('/v1/migrations/:migrationId')
|
||||
->groups(['api', 'migrations'])
|
||||
->desc('Delete migration')
|
||||
->label('scope', 'migrations.write')
|
||||
|
||||
@@ -6,7 +6,6 @@ use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
@@ -17,12 +16,13 @@ use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Datetime as DateTimeValidator;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Nullable;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::get('/v1/project/usage')
|
||||
Http::get('/v1/project/usage')
|
||||
->desc('Get project usage stats')
|
||||
->groups(['api', 'usage'])
|
||||
->label('scope', 'projects.read')
|
||||
@@ -385,7 +385,7 @@ App::get('/v1/project/usage')
|
||||
|
||||
|
||||
// Variables
|
||||
App::post('/v1/project/variables')
|
||||
Http::post('/v1/project/variables')
|
||||
->desc('Create variable')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -448,7 +448,7 @@ App::post('/v1/project/variables')
|
||||
->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
});
|
||||
|
||||
App::get('/v1/project/variables')
|
||||
Http::get('/v1/project/variables')
|
||||
->desc('List variables')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.read')
|
||||
@@ -479,7 +479,7 @@ App::get('/v1/project/variables')
|
||||
]), Response::MODEL_VARIABLE_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/project/variables/:variableId')
|
||||
Http::get('/v1/project/variables/:variableId')
|
||||
->desc('Get variable')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.read')
|
||||
@@ -509,7 +509,7 @@ App::get('/v1/project/variables/:variableId')
|
||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
});
|
||||
|
||||
App::put('/v1/project/variables/:variableId')
|
||||
Http::put('/v1/project/variables/:variableId')
|
||||
->desc('Update variable')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -567,7 +567,7 @@ App::put('/v1/project/variables/:variableId')
|
||||
$response->dynamic($variable, Response::MODEL_VARIABLE);
|
||||
});
|
||||
|
||||
App::delete('/v1/project/variables/:variableId')
|
||||
Http::delete('/v1/project/variables/:variableId')
|
||||
->desc('Delete variable')
|
||||
->groups(['api'])
|
||||
->label('scope', 'projects.write')
|
||||
|
||||
@@ -6,7 +6,6 @@ use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Mail;
|
||||
use Appwrite\Event\Validator\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
use Appwrite\Network\Platform;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\SDK\AuthType;
|
||||
@@ -15,31 +14,25 @@ use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\Utopia\Database\Validator\ProjectId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Keys;
|
||||
use Appwrite\Utopia\Response;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Adapter\Database as AdapterDatabase;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Datetime as DatetimeValidator;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Domains\Validator\PublicDomain;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Http;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Boolean;
|
||||
@@ -52,7 +45,7 @@ use Utopia\Validator\Text;
|
||||
use Utopia\Validator\URL;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['projects'])
|
||||
->inject('project')
|
||||
->action(function (Document $project) {
|
||||
@@ -61,252 +54,7 @@ App::init()
|
||||
}
|
||||
});
|
||||
|
||||
App::post('/v1/projects')
|
||||
->desc('Create project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'projects.create')
|
||||
->label('audits.resource', 'project/{response.$id}')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'create',
|
||||
description: '/docs/references/projects/create.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
model: Response::MODEL_PROJECT,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('projectId', '', new ProjectId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, and hyphen. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('region', System::getEnv('_APP_REGION', 'default'), new Whitelist(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true)
|
||||
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
|
||||
->param('logo', '', new Text(1024), 'Project logo.', true)
|
||||
->param('url', '', new URL(), 'Project URL.', true)
|
||||
->param('legalName', '', new Text(256), 'Project legal Name. Max length: 256 chars.', true)
|
||||
->param('legalCountry', '', new Text(256), 'Project legal Country. Max length: 256 chars.', true)
|
||||
->param('legalState', '', new Text(256), 'Project legal State. Max length: 256 chars.', true)
|
||||
->param('legalCity', '', new Text(256), 'Project legal City. Max length: 256 chars.', true)
|
||||
->param('legalAddress', '', new Text(256), 'Project legal Address. Max length: 256 chars.', true)
|
||||
->param('legalTaxId', '', new Text(256), 'Project legal Tax ID. Max length: 256 chars.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('cache')
|
||||
->inject('pools')
|
||||
->inject('hooks')
|
||||
->action(function (string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Request $request, Response $response, Database $dbForPlatform, Cache $cache, Group $pools, Hooks $hooks) {
|
||||
|
||||
$team = $dbForPlatform->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$allowList = \array_filter(\explode(',', System::getEnv('_APP_PROJECT_REGIONS', '')));
|
||||
|
||||
if (!empty($allowList) && !\in_array($region, $allowList)) {
|
||||
throw new Exception(Exception::PROJECT_REGION_UNSUPPORTED, 'Region "' . $region . '" is not supported');
|
||||
}
|
||||
|
||||
$auth = Config::getParam('auth', []);
|
||||
$auths = [
|
||||
'limit' => 0,
|
||||
'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT,
|
||||
'passwordHistory' => 0,
|
||||
'passwordDictionary' => false,
|
||||
'duration' => TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'personalDataCheck' => false,
|
||||
'mockNumbers' => [],
|
||||
'sessionAlerts' => false,
|
||||
'membershipsUserName' => false,
|
||||
'membershipsUserEmail' => false,
|
||||
'membershipsMfa' => false,
|
||||
'invalidateSessions' => true
|
||||
];
|
||||
|
||||
foreach ($auth as $method) {
|
||||
$auths[$method['key'] ?? ''] = true;
|
||||
}
|
||||
|
||||
$projectId = ($projectId == 'unique()') ? ID::unique() : $projectId;
|
||||
|
||||
if ($projectId === 'console') {
|
||||
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
|
||||
}
|
||||
|
||||
$databases = Config::getParam('pools-database', []);
|
||||
|
||||
if ($region !== 'default') {
|
||||
$databaseKeys = System::getEnv('_APP_DATABASE_KEYS', '');
|
||||
$keys = explode(',', $databaseKeys);
|
||||
$databases = array_filter($keys, function ($value) use ($region) {
|
||||
return str_contains($value, $region);
|
||||
});
|
||||
}
|
||||
|
||||
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
|
||||
$index = \array_search($databaseOverride, $databases);
|
||||
if ($index !== false) {
|
||||
$dsn = $databases[$index];
|
||||
} else {
|
||||
$dsn = $databases[array_rand($databases)];
|
||||
}
|
||||
|
||||
// TODO: Temporary until all projects are using shared tables.
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn, $sharedTables)) {
|
||||
$schema = 'appwrite';
|
||||
$database = 'appwrite';
|
||||
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
|
||||
$dsn = $schema . '://' . $dsn . '?database=' . $database;
|
||||
|
||||
if (!empty($namespace)) {
|
||||
$dsn .= '&namespace=' . $namespace;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$project = $dbForPlatform->createDocument('projects', new Document([
|
||||
'$id' => $projectId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'name' => $name,
|
||||
'teamInternalId' => $team->getSequence(),
|
||||
'teamId' => $team->getId(),
|
||||
'region' => $region,
|
||||
'description' => $description,
|
||||
'logo' => $logo,
|
||||
'url' => $url,
|
||||
'version' => APP_VERSION_STABLE,
|
||||
'legalName' => $legalName,
|
||||
'legalCountry' => $legalCountry,
|
||||
'legalState' => $legalState,
|
||||
'legalCity' => $legalCity,
|
||||
'legalAddress' => $legalAddress,
|
||||
'legalTaxId' => ID::custom($legalTaxId),
|
||||
'services' => new stdClass(),
|
||||
'platforms' => null,
|
||||
'oAuthProviders' => [],
|
||||
'webhooks' => null,
|
||||
'keys' => null,
|
||||
'auths' => $auths,
|
||||
'accessedAt' => DateTime::now(),
|
||||
'search' => implode(' ', [$projectId, $name]),
|
||||
'database' => $dsn,
|
||||
'labels' => [],
|
||||
]));
|
||||
} catch (Duplicate) {
|
||||
throw new Exception(Exception::PROJECT_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
try {
|
||||
$dsn = new DSN($dsn);
|
||||
} catch (\InvalidArgumentException) {
|
||||
// TODO: Temporary until all projects are using shared tables
|
||||
$dsn = new DSN('mysql://' . $dsn);
|
||||
}
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
|
||||
$projectTables = !\in_array($dsn->getHost(), $sharedTables);
|
||||
$sharedTablesV1 = \in_array($dsn->getHost(), $sharedTablesV1);
|
||||
$sharedTablesV2 = !$projectTables && !$sharedTablesV1;
|
||||
$sharedTables = $sharedTablesV1 || $sharedTablesV2;
|
||||
|
||||
if (!$sharedTablesV2) {
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$dbForProject = new Database($adapter, $cache);
|
||||
$dbForProject->setDatabase(APP_DATABASE);
|
||||
|
||||
if ($sharedTables) {
|
||||
$dbForProject
|
||||
->setSharedTables(true)
|
||||
->setTenant($sharedTablesV1 ? (int)$project->getSequence() : null)
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$dbForProject
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$create = true;
|
||||
|
||||
try {
|
||||
$dbForProject->create();
|
||||
} catch (Duplicate) {
|
||||
$create = false;
|
||||
}
|
||||
|
||||
if ($create || $projectTables) {
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
$audit = new Audit($adapter);
|
||||
$audit->setup();
|
||||
}
|
||||
|
||||
if (!$create && $sharedTablesV1) {
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
$attributes = $adapter->getAttributeDocuments();
|
||||
$indexes = $adapter->getIndexDocuments();
|
||||
$dbForProject->createDocument(Database::METADATA, new Document([
|
||||
'$id' => ID::custom('audit'),
|
||||
'$permissions' => [Permission::create(Role::any())],
|
||||
'name' => 'audit',
|
||||
'attributes' => $attributes,
|
||||
'indexes' => $indexes,
|
||||
'documentSecurity' => true
|
||||
]));
|
||||
}
|
||||
|
||||
if ($create || $sharedTablesV1) {
|
||||
/** @var array $collections */
|
||||
$collections = Config::getParam('collections', [])['projects'] ?? [];
|
||||
|
||||
foreach ($collections as $key => $collection) {
|
||||
if (($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']);
|
||||
$indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']);
|
||||
|
||||
try {
|
||||
$dbForProject->createCollection($key, $attributes, $indexes);
|
||||
} catch (Duplicate) {
|
||||
$dbForProject->createDocument(Database::METADATA, new Document([
|
||||
'$id' => ID::custom($key),
|
||||
'$permissions' => [Permission::create(Role::any())],
|
||||
'name' => $key,
|
||||
'attributes' => $attributes,
|
||||
'indexes' => $indexes,
|
||||
'documentSecurity' => true
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hook allowing instant project mirroring during migration
|
||||
// Outside of migration, hook is not registered and has no effect
|
||||
$hooks->trigger('afterProjectCreation', [$project, $pools, $cache]);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::get('/v1/projects/:projectId')
|
||||
Http::get('/v1/projects/:projectId')
|
||||
->desc('Get project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
@@ -337,138 +85,7 @@ App::get('/v1/projects/:projectId')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId')
|
||||
->desc('Update project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('audits.event', 'projects.update')
|
||||
->label('audits.resource', 'project/{request.projectId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'update',
|
||||
description: '/docs/references/projects/update.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROJECT,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
|
||||
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
|
||||
->param('logo', '', new Text(1024), 'Project logo.', true)
|
||||
->param('url', '', new URL(), 'Project URL.', true)
|
||||
->param('legalName', '', new Text(256), 'Project legal name. Max length: 256 chars.', true)
|
||||
->param('legalCountry', '', new Text(256), 'Project legal country. Max length: 256 chars.', true)
|
||||
->param('legalState', '', new Text(256), 'Project legal state. Max length: 256 chars.', true)
|
||||
->param('legalCity', '', new Text(256), 'Project legal city. Max length: 256 chars.', true)
|
||||
->param('legalAddress', '', new Text(256), 'Project legal address. Max length: 256 chars.', true)
|
||||
->param('legalTaxId', '', new Text(256), 'Project legal tax ID. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $projectId, string $name, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForPlatform) {
|
||||
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('description', $description)
|
||||
->setAttribute('logo', $logo)
|
||||
->setAttribute('url', $url)
|
||||
->setAttribute('legalName', $legalName)
|
||||
->setAttribute('legalCountry', $legalCountry)
|
||||
->setAttribute('legalState', $legalState)
|
||||
->setAttribute('legalCity', $legalCity)
|
||||
->setAttribute('legalAddress', $legalAddress)
|
||||
->setAttribute('legalTaxId', $legalTaxId)
|
||||
->setAttribute('search', implode(' ', [$projectId, $name])));
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/team')
|
||||
->desc('Update project team')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateTeam',
|
||||
description: '/docs/references/projects/update-team.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROJECT,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('teamId', '', new UID(), 'Team ID of the team to transfer project to.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $projectId, string $teamId, Response $response, Database $dbForPlatform) {
|
||||
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
$team = $dbForPlatform->getDocument('teams', $teamId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$permissions = [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
];
|
||||
|
||||
$project
|
||||
->setAttribute('teamId', $teamId)
|
||||
->setAttribute('teamInternalId', $team->getSequence())
|
||||
->setAttribute('$permissions', $permissions);
|
||||
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project);
|
||||
|
||||
$installations = $dbForPlatform->find('installations', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($installations as $installation) {
|
||||
$installation->getAttribute('$permissions', $permissions);
|
||||
$dbForPlatform->updateDocument('installations', $installation->getId(), $installation);
|
||||
}
|
||||
|
||||
$repositories = $dbForPlatform->find('repositories', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($repositories as $repository) {
|
||||
$repository->getAttribute('$permissions', $permissions);
|
||||
$dbForPlatform->updateDocument('repositories', $repository->getId(), $repository);
|
||||
}
|
||||
|
||||
$vcsComments = $dbForPlatform->find('vcsComments', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($vcsComments as $vcsComment) {
|
||||
$vcsComment->getAttribute('$permissions', $permissions);
|
||||
$dbForPlatform->updateDocument('vcsComments', $vcsComment->getId(), $vcsComment);
|
||||
}
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/service')
|
||||
Http::patch('/v1/projects/:projectId/service')
|
||||
->desc('Update service status')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -506,7 +123,7 @@ App::patch('/v1/projects/:projectId/service')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/service/all')
|
||||
Http::patch('/v1/projects/:projectId/service/all')
|
||||
->desc('Update all service status')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -547,7 +164,7 @@ App::patch('/v1/projects/:projectId/service/all')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/api')
|
||||
Http::patch('/v1/projects/:projectId/api')
|
||||
->desc('Update API status')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -605,7 +222,7 @@ App::patch('/v1/projects/:projectId/api')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/api/all')
|
||||
Http::patch('/v1/projects/:projectId/api/all')
|
||||
->desc('Update all API status')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -666,7 +283,7 @@ App::patch('/v1/projects/:projectId/api/all')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/oauth2')
|
||||
Http::patch('/v1/projects/:projectId/oauth2')
|
||||
->desc('Update project OAuth2')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -717,7 +334,7 @@ App::patch('/v1/projects/:projectId/oauth2')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/session-alerts')
|
||||
Http::patch('/v1/projects/:projectId/auth/session-alerts')
|
||||
->desc('Update project sessions emails')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -755,7 +372,7 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/memberships-privacy')
|
||||
Http::patch('/v1/projects/:projectId/auth/memberships-privacy')
|
||||
->desc('Update project memberships privacy attributes')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -797,7 +414,7 @@ App::patch('/v1/projects/:projectId/auth/memberships-privacy')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/limit')
|
||||
Http::patch('/v1/projects/:projectId/auth/limit')
|
||||
->desc('Update project users limit')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -835,7 +452,7 @@ App::patch('/v1/projects/:projectId/auth/limit')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/duration')
|
||||
Http::patch('/v1/projects/:projectId/auth/duration')
|
||||
->desc('Update project authentication duration')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -873,7 +490,7 @@ App::patch('/v1/projects/:projectId/auth/duration')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/:method')
|
||||
Http::patch('/v1/projects/:projectId/auth/:method')
|
||||
->desc('Update project auth method status. Use this endpoint to enable or disable a given auth method for this project.')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -914,7 +531,7 @@ App::patch('/v1/projects/:projectId/auth/:method')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/password-history')
|
||||
Http::patch('/v1/projects/:projectId/auth/password-history')
|
||||
->desc('Update authentication password history. Use this endpoint to set the number of password history to save and 0 to disable password history.')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -952,7 +569,7 @@ App::patch('/v1/projects/:projectId/auth/password-history')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||
Http::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||
->desc('Update authentication password dictionary status. Use this endpoint to enable or disable the dicitonary check for user password')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -990,7 +607,7 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/personal-data')
|
||||
Http::patch('/v1/projects/:projectId/auth/personal-data')
|
||||
->desc('Update personal data check')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -1028,7 +645,7 @@ App::patch('/v1/projects/:projectId/auth/personal-data')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/max-sessions')
|
||||
Http::patch('/v1/projects/:projectId/auth/max-sessions')
|
||||
->desc('Update project user sessions limit')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -1066,7 +683,7 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/mock-numbers')
|
||||
Http::patch('/v1/projects/:projectId/auth/mock-numbers')
|
||||
->desc('Update the mock numbers for the project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -1112,7 +729,7 @@ App::patch('/v1/projects/:projectId/auth/mock-numbers')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::delete('/v1/projects/:projectId')
|
||||
Http::delete('/v1/projects/:projectId')
|
||||
->desc('Delete project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'projects.delete')
|
||||
@@ -1158,7 +775,7 @@ App::delete('/v1/projects/:projectId')
|
||||
|
||||
// Webhooks
|
||||
|
||||
App::post('/v1/projects/:projectId/webhooks')
|
||||
Http::post('/v1/projects/:projectId/webhooks')
|
||||
->desc('Create webhook')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -1223,7 +840,7 @@ App::post('/v1/projects/:projectId/webhooks')
|
||||
->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||
});
|
||||
|
||||
App::get('/v1/projects/:projectId/webhooks')
|
||||
Http::get('/v1/projects/:projectId/webhooks')
|
||||
->desc('List webhooks')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
@@ -1263,7 +880,7 @@ App::get('/v1/projects/:projectId/webhooks')
|
||||
]), Response::MODEL_WEBHOOK_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
Http::get('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->desc('Get webhook')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.read')
|
||||
@@ -1304,7 +921,7 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||
});
|
||||
|
||||
App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
Http::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->desc('Update webhook')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -1370,7 +987,7 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||
Http::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||
->desc('Update webhook signature key')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -1416,7 +1033,7 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
|
||||
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||
});
|
||||
|
||||
App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
Http::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
->desc('Delete webhook')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -1464,7 +1081,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
||||
|
||||
// Keys
|
||||
|
||||
App::post('/v1/projects/:projectId/keys')
|
||||
Http::post('/v1/projects/:projectId/keys')
|
||||
->desc('Create key')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'keys.write')
|
||||
@@ -1482,12 +1099,15 @@ App::post('/v1/projects/:projectId/keys')
|
||||
]
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
// TODO: When migrating to Platform API, mark keyId required for consistency
|
||||
->param('keyId', 'unique()', new CustomId(), 'Key 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.', true)
|
||||
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
|
||||
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
|
||||
->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $projectId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForPlatform) {
|
||||
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForPlatform) {
|
||||
$keyId = $keyId == 'unique()' ? ID::unique() : $keyId;
|
||||
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
|
||||
@@ -1496,7 +1116,7 @@ App::post('/v1/projects/:projectId/keys')
|
||||
}
|
||||
|
||||
$key = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'$id' => $keyId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
@@ -1513,7 +1133,11 @@ App::post('/v1/projects/:projectId/keys')
|
||||
'secret' => API_KEY_STANDARD . '_' . \bin2hex(\random_bytes(128)),
|
||||
]);
|
||||
|
||||
$key = $dbForPlatform->createDocument('keys', $key);
|
||||
try {
|
||||
$key = $dbForPlatform->createDocument('keys', $key);
|
||||
} catch (Duplicate) {
|
||||
throw new Exception(Exception::KEY_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
|
||||
|
||||
@@ -1522,7 +1146,7 @@ App::post('/v1/projects/:projectId/keys')
|
||||
->dynamic($key, Response::MODEL_KEY);
|
||||
});
|
||||
|
||||
App::get('/v1/projects/:projectId/keys')
|
||||
Http::get('/v1/projects/:projectId/keys')
|
||||
->desc('List keys')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'keys.read')
|
||||
@@ -1540,10 +1164,11 @@ App::get('/v1/projects/:projectId/keys')
|
||||
]
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('queries', [], new Keys(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Keys::ALLOWED_ATTRIBUTES), true)
|
||||
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $projectId, bool $includeTotal, Response $response, Database $dbForPlatform) {
|
||||
->action(function (string $projectId, array $queries, bool $includeTotal, Response $response, Database $dbForPlatform) {
|
||||
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
|
||||
@@ -1551,19 +1176,50 @@ App::get('/v1/projects/:projectId/keys')
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$keys = $dbForPlatform->find('keys', [
|
||||
Query::equal('resourceType', ['projects']),
|
||||
Query::equal('resourceInternalId', [$project->getSequence()]),
|
||||
Query::limit(5000),
|
||||
]);
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
// Backwards compatibility
|
||||
if (\count(Query::getByType($queries, [Query::TYPE_LIMIT])) === 0) {
|
||||
$queries[] = Query::limit(5000);
|
||||
}
|
||||
|
||||
$queries[] = Query::equal('resourceType', ['projects']);
|
||||
$queries[] = Query::equal('resourceInternalId', [$project->getSequence()]);
|
||||
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$keyId = $cursor->getValue();
|
||||
$cursorDocument = $dbForPlatform->getDocument('keys', $keyId);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Key '{$keyId}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
|
||||
$keys = $dbForPlatform->find('keys', $queries);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'keys' => $keys,
|
||||
'total' => $includeTotal ? count($keys) : 0,
|
||||
'total' => $includeTotal ? $dbForPlatform->count('keys', $filterQueries, APP_LIMIT_COUNT) : 0,
|
||||
]), Response::MODEL_KEY_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/projects/:projectId/keys/:keyId')
|
||||
Http::get('/v1/projects/:projectId/keys/:keyId')
|
||||
->desc('Get key')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'keys.read')
|
||||
@@ -1605,7 +1261,7 @@ App::get('/v1/projects/:projectId/keys/:keyId')
|
||||
$response->dynamic($key, Response::MODEL_KEY);
|
||||
});
|
||||
|
||||
App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
Http::put('/v1/projects/:projectId/keys/:keyId')
|
||||
->desc('Update key')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'keys.write')
|
||||
@@ -1659,7 +1315,7 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
||||
$response->dynamic($key, Response::MODEL_KEY);
|
||||
});
|
||||
|
||||
App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||
Http::delete('/v1/projects/:projectId/keys/:keyId')
|
||||
->desc('Delete key')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'keys.write')
|
||||
@@ -1708,7 +1364,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
||||
|
||||
// JWT Keys
|
||||
|
||||
App::post('/v1/projects/:projectId/jwts')
|
||||
Http::post('/v1/projects/:projectId/jwts')
|
||||
->groups(['api', 'projects'])
|
||||
->desc('Create JWT')
|
||||
->label('scope', 'projects.write')
|
||||
@@ -1750,7 +1406,7 @@ App::post('/v1/projects/:projectId/jwts')
|
||||
|
||||
// Platforms
|
||||
|
||||
App::post('/v1/projects/:projectId/platforms')
|
||||
Http::post('/v1/projects/:projectId/platforms')
|
||||
->desc('Create platform')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'platforms.create')
|
||||
@@ -1830,7 +1486,7 @@ App::post('/v1/projects/:projectId/platforms')
|
||||
->dynamic($platform, Response::MODEL_PLATFORM);
|
||||
});
|
||||
|
||||
App::get('/v1/projects/:projectId/platforms')
|
||||
Http::get('/v1/projects/:projectId/platforms')
|
||||
->desc('List platforms')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'platforms.read')
|
||||
@@ -1870,7 +1526,7 @@ App::get('/v1/projects/:projectId/platforms')
|
||||
]), Response::MODEL_PLATFORM_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/projects/:projectId/platforms/:platformId')
|
||||
Http::get('/v1/projects/:projectId/platforms/:platformId')
|
||||
->desc('Get platform')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'platforms.read')
|
||||
@@ -1911,7 +1567,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
|
||||
$response->dynamic($platform, Response::MODEL_PLATFORM);
|
||||
});
|
||||
|
||||
App::put('/v1/projects/:projectId/platforms/:platformId')
|
||||
Http::put('/v1/projects/:projectId/platforms/:platformId')
|
||||
->desc('Update platform')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'platforms.write')
|
||||
@@ -1965,7 +1621,7 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
||||
$response->dynamic($platform, Response::MODEL_PLATFORM);
|
||||
});
|
||||
|
||||
App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||
Http::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||
->desc('Delete platform')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'platforms.delete')
|
||||
@@ -2015,7 +1671,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
|
||||
|
||||
|
||||
// CUSTOM SMTP and Templates
|
||||
App::patch('/v1/projects/:projectId/smtp')
|
||||
Http::patch('/v1/projects/:projectId/smtp')
|
||||
->desc('Update SMTP')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -2133,7 +1789,7 @@ App::patch('/v1/projects/:projectId/smtp')
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
||||
App::post('/v1/projects/:projectId/smtp/tests')
|
||||
Http::post('/v1/projects/:projectId/smtp/tests')
|
||||
->desc('Create SMTP test')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -2228,7 +1884,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
|
||||
return $response->noContent();
|
||||
});
|
||||
|
||||
App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
Http::get('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
->desc('Get custom SMS template')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -2296,7 +1952,7 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
});
|
||||
|
||||
|
||||
App::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
Http::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->desc('Get custom email template')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -2395,7 +2051,7 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
$response->dynamic(new Document($template), Response::MODEL_EMAIL_TEMPLATE);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
->desc('Update custom SMS template')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -2462,7 +2118,7 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
]), Response::MODEL_SMS_TEMPLATE);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
Http::patch('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->desc('Update custom email templates')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -2519,7 +2175,7 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
]), Response::MODEL_EMAIL_TEMPLATE);
|
||||
});
|
||||
|
||||
App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
->desc('Reset custom SMS template')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -2591,7 +2247,7 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
|
||||
]), Response::MODEL_SMS_TEMPLATE);
|
||||
});
|
||||
|
||||
App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
Http::delete('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
->desc('Delete custom email template')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
@@ -2644,7 +2300,7 @@ App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
|
||||
]), Response::MODEL_EMAIL_TEMPLATE);
|
||||
});
|
||||
|
||||
App::patch('/v1/projects/:projectId/auth/session-invalidation')
|
||||
Http::patch('/v1/projects/:projectId/auth/session-invalidation')
|
||||
->desc('Update invalidate session option of the project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
|
||||
@@ -26,7 +26,6 @@ use Appwrite\Utopia\Response;
|
||||
use libphonenumber\NumberParseException;
|
||||
use libphonenumber\PhoneNumberUtil;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Auth\Proofs\Password;
|
||||
use Utopia\Auth\Proofs\Token;
|
||||
@@ -52,15 +51,15 @@ use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Emails\Email;
|
||||
use Utopia\Http;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::post('/v1/teams')
|
||||
Http::post('/v1/teams')
|
||||
->desc('Create team')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].create')
|
||||
@@ -156,7 +155,7 @@ App::post('/v1/teams')
|
||||
->dynamic($team, Response::MODEL_TEAM);
|
||||
});
|
||||
|
||||
App::get('/v1/teams')
|
||||
Http::get('/v1/teams')
|
||||
->desc('List teams')
|
||||
->groups(['api', 'teams'])
|
||||
->label('scope', 'teams.read')
|
||||
@@ -191,16 +190,10 @@ App::get('/v1/teams')
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -230,7 +223,7 @@ App::get('/v1/teams')
|
||||
]), Response::MODEL_TEAM_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/teams/:teamId')
|
||||
Http::get('/v1/teams/:teamId')
|
||||
->desc('Get team')
|
||||
->groups(['api', 'teams'])
|
||||
->label('scope', 'teams.read')
|
||||
@@ -261,7 +254,7 @@ App::get('/v1/teams/:teamId')
|
||||
$response->dynamic($team, Response::MODEL_TEAM);
|
||||
});
|
||||
|
||||
App::get('/v1/teams/:teamId/prefs')
|
||||
Http::get('/v1/teams/:teamId/prefs')
|
||||
->desc('Get team preferences')
|
||||
->groups(['api', 'teams'])
|
||||
->label('scope', 'teams.read')
|
||||
@@ -300,7 +293,7 @@ App::get('/v1/teams/:teamId/prefs')
|
||||
$response->dynamic($prefs, Response::MODEL_PREFERENCES);
|
||||
});
|
||||
|
||||
App::put('/v1/teams/:teamId')
|
||||
Http::put('/v1/teams/:teamId')
|
||||
->desc('Update name')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].update')
|
||||
@@ -345,7 +338,7 @@ App::put('/v1/teams/:teamId')
|
||||
$response->dynamic($team, Response::MODEL_TEAM);
|
||||
});
|
||||
|
||||
App::put('/v1/teams/:teamId/prefs')
|
||||
Http::put('/v1/teams/:teamId/prefs')
|
||||
->desc('Update preferences')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].update.prefs')
|
||||
@@ -393,7 +386,7 @@ App::put('/v1/teams/:teamId/prefs')
|
||||
$response->dynamic($prefs, Response::MODEL_PREFERENCES);
|
||||
});
|
||||
|
||||
App::delete('/v1/teams/:teamId')
|
||||
Http::delete('/v1/teams/:teamId')
|
||||
->desc('Delete team')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].delete')
|
||||
@@ -457,7 +450,7 @@ App::delete('/v1/teams/:teamId')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::post('/v1/teams/:teamId/memberships')
|
||||
Http::post('/v1/teams/:teamId/memberships')
|
||||
->desc('Create team membership')
|
||||
->groups(['api', 'teams', 'auth'])
|
||||
->label('event', 'teams.[teamId].memberships.[membershipId].create')
|
||||
@@ -484,16 +477,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
->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) {
|
||||
if ($project->getId() === 'console') {
|
||||
$roles = array_keys(Config::getParam('roles', []));
|
||||
$roles = array_filter($roles, function ($role) {
|
||||
return !in_array($role, [User::ROLE_APPS, User::ROLE_GUESTS, User::ROLE_USERS]);
|
||||
});
|
||||
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}
|
||||
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
|
||||
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
|
||||
->param('url', '', fn ($redirectValidator) => $redirectValidator, 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. 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, ['redirectValidator']) // TODO add our own built-in confirm page
|
||||
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
@@ -844,7 +828,7 @@ App::post('/v1/teams/:teamId/memberships')
|
||||
);
|
||||
});
|
||||
|
||||
App::get('/v1/teams/:teamId/memberships')
|
||||
Http::get('/v1/teams/:teamId/memberships')
|
||||
->desc('List team memberships')
|
||||
->groups(['api', 'teams'])
|
||||
->label('scope', 'teams.read')
|
||||
@@ -889,22 +873,15 @@ App::get('/v1/teams/:teamId/memberships')
|
||||
// Set internal queries
|
||||
$queries[] = Query::equal('teamInternalId', [$team->getSequence()]);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
|
||||
$membershipId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
@@ -988,7 +965,7 @@ App::get('/v1/teams/:teamId/memberships')
|
||||
]), Response::MODEL_MEMBERSHIP_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||
Http::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->desc('Get team membership')
|
||||
->groups(['api', 'teams'])
|
||||
->label('scope', 'teams.read')
|
||||
@@ -1073,7 +1050,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
||||
$response->dynamic($membership, Response::MODEL_MEMBERSHIP);
|
||||
});
|
||||
|
||||
App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
Http::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->desc('Update membership')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].memberships.[membershipId].update')
|
||||
@@ -1095,16 +1072,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
))
|
||||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('membershipId', '', new UID(), 'Membership ID.')
|
||||
->param('roles', [], function (Document $project) {
|
||||
if ($project->getId() === 'console') {
|
||||
$roles = array_keys(Config::getParam('roles', []));
|
||||
$roles = array_filter($roles, function ($role) {
|
||||
return !in_array($role, [User::ROLE_APPS, User::ROLE_GUESTS, User::ROLE_USERS]);
|
||||
});
|
||||
return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}
|
||||
return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE);
|
||||
}, 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
|
||||
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('user')
|
||||
@@ -1183,7 +1151,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
);
|
||||
});
|
||||
|
||||
App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
Http::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
->desc('Update team membership status')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].memberships.[membershipId].update.status')
|
||||
@@ -1350,7 +1318,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
||||
);
|
||||
});
|
||||
|
||||
App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
Http::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
->desc('Delete team membership')
|
||||
->groups(['api', 'teams'])
|
||||
->label('event', 'teams.[teamId].memberships.[membershipId].delete')
|
||||
@@ -1450,7 +1418,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::get('/v1/teams/:teamId/logs')
|
||||
Http::get('/v1/teams/:teamId/logs')
|
||||
->desc('List team logs')
|
||||
->groups(['api', 'teams'])
|
||||
->label('scope', 'teams.read')
|
||||
|
||||
@@ -29,7 +29,6 @@ use Appwrite\Utopia\Database\Validator\Queries\Users;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Auth\Hash;
|
||||
use Utopia\Auth\Hashes\Argon2;
|
||||
@@ -61,6 +60,7 @@ use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Emails\Email;
|
||||
use Utopia\Http;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\ArrayList;
|
||||
@@ -227,7 +227,7 @@ function createUser(Hash $hash, string $userId, ?string $email, ?string $passwor
|
||||
return $user;
|
||||
}
|
||||
|
||||
App::post('/v1/users')
|
||||
Http::post('/v1/users')
|
||||
->desc('Create user')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.write')
|
||||
@@ -264,7 +264,7 @@ App::post('/v1/users')
|
||||
->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::post('/v1/users/bcrypt')
|
||||
Http::post('/v1/users/bcrypt')
|
||||
->desc('Create user with bcrypt password')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.write')
|
||||
@@ -302,7 +302,7 @@ App::post('/v1/users/bcrypt')
|
||||
->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::post('/v1/users/md5')
|
||||
Http::post('/v1/users/md5')
|
||||
->desc('Create user with MD5 password')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.write')
|
||||
@@ -339,7 +339,7 @@ App::post('/v1/users/md5')
|
||||
->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::post('/v1/users/argon2')
|
||||
Http::post('/v1/users/argon2')
|
||||
->desc('Create user with Argon2 password')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.write')
|
||||
@@ -376,7 +376,7 @@ App::post('/v1/users/argon2')
|
||||
->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::post('/v1/users/sha')
|
||||
Http::post('/v1/users/sha')
|
||||
->desc('Create user with SHA password')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.write')
|
||||
@@ -417,7 +417,7 @@ App::post('/v1/users/sha')
|
||||
->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::post('/v1/users/phpass')
|
||||
Http::post('/v1/users/phpass')
|
||||
->desc('Create user with PHPass password')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.write')
|
||||
@@ -454,7 +454,7 @@ App::post('/v1/users/phpass')
|
||||
->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::post('/v1/users/scrypt')
|
||||
Http::post('/v1/users/scrypt')
|
||||
->desc('Create user with Scrypt password')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.write')
|
||||
@@ -502,7 +502,7 @@ App::post('/v1/users/scrypt')
|
||||
->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::post('/v1/users/scrypt-modified')
|
||||
Http::post('/v1/users/scrypt-modified')
|
||||
->desc('Create user with Scrypt modified password')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.write')
|
||||
@@ -546,7 +546,7 @@ App::post('/v1/users/scrypt-modified')
|
||||
->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::post('/v1/users/:userId/targets')
|
||||
Http::post('/v1/users/:userId/targets')
|
||||
->desc('Create user target')
|
||||
->groups(['api', 'users'])
|
||||
->label('audits.event', 'target.create')
|
||||
@@ -641,7 +641,7 @@ App::post('/v1/users/:userId/targets')
|
||||
->dynamic($target, Response::MODEL_TARGET);
|
||||
});
|
||||
|
||||
App::get('/v1/users')
|
||||
Http::get('/v1/users')
|
||||
->desc('List users')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
@@ -675,16 +675,10 @@ App::get('/v1/users')
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -720,7 +714,7 @@ App::get('/v1/users')
|
||||
]), Response::MODEL_USER_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId')
|
||||
Http::get('/v1/users/:userId')
|
||||
->desc('Get user')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
@@ -751,7 +745,7 @@ App::get('/v1/users/:userId')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/prefs')
|
||||
Http::get('/v1/users/:userId/prefs')
|
||||
->desc('Get user preferences')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
@@ -784,7 +778,7 @@ App::get('/v1/users/:userId/prefs')
|
||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/targets/:targetId')
|
||||
Http::get('/v1/users/:userId/targets/:targetId')
|
||||
->desc('Get user target')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'targets.read')
|
||||
@@ -822,7 +816,7 @@ App::get('/v1/users/:userId/targets/:targetId')
|
||||
$response->dynamic($target, Response::MODEL_TARGET);
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/sessions')
|
||||
Http::get('/v1/users/:userId/sessions')
|
||||
->desc('List user sessions')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
@@ -866,7 +860,7 @@ App::get('/v1/users/:userId/sessions')
|
||||
]), Response::MODEL_SESSION_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/memberships')
|
||||
Http::get('/v1/users/:userId/memberships')
|
||||
->desc('List user memberships')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
@@ -921,7 +915,7 @@ App::get('/v1/users/:userId/memberships')
|
||||
]), Response::MODEL_MEMBERSHIP_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/logs')
|
||||
Http::get('/v1/users/:userId/logs')
|
||||
->desc('List user logs')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
@@ -1008,7 +1002,7 @@ App::get('/v1/users/:userId/logs')
|
||||
]), Response::MODEL_LOG_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/targets')
|
||||
Http::get('/v1/users/:userId/targets')
|
||||
->desc('List user targets')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'targets.read')
|
||||
@@ -1042,14 +1036,11 @@ App::get('/v1/users/:userId/targets')
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
$queries[] = Query::equal('userId', [$userId]);
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -1073,7 +1064,7 @@ App::get('/v1/users/:userId/targets')
|
||||
]), Response::MODEL_TARGET_LIST);
|
||||
});
|
||||
|
||||
App::get('/v1/users/identities')
|
||||
Http::get('/v1/users/identities')
|
||||
->desc('List identities')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
@@ -1106,15 +1097,11 @@ App::get('/v1/users/identities')
|
||||
if (!empty($search)) {
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -1139,7 +1126,7 @@ App::get('/v1/users/identities')
|
||||
]), Response::MODEL_IDENTITY_LIST);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/status')
|
||||
Http::patch('/v1/users/:userId/status')
|
||||
->desc('Update user status')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.status')
|
||||
@@ -1181,7 +1168,7 @@ App::patch('/v1/users/:userId/status')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::put('/v1/users/:userId/labels')
|
||||
Http::put('/v1/users/:userId/labels')
|
||||
->desc('Update user labels')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.labels')
|
||||
@@ -1224,7 +1211,7 @@ App::put('/v1/users/:userId/labels')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/verification/phone')
|
||||
Http::patch('/v1/users/:userId/verification/phone')
|
||||
->desc('Update phone verification')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.verification')
|
||||
@@ -1265,7 +1252,7 @@ App::patch('/v1/users/:userId/verification/phone')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/name')
|
||||
Http::patch('/v1/users/:userId/name')
|
||||
->desc('Update name')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.name')
|
||||
@@ -1308,7 +1295,7 @@ App::patch('/v1/users/:userId/name')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/password')
|
||||
Http::patch('/v1/users/:userId/password')
|
||||
->desc('Update password')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.password')
|
||||
@@ -1407,7 +1394,7 @@ App::patch('/v1/users/:userId/password')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/email')
|
||||
Http::patch('/v1/users/:userId/email')
|
||||
->desc('Update email')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.email')
|
||||
@@ -1519,7 +1506,7 @@ App::patch('/v1/users/:userId/email')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/phone')
|
||||
Http::patch('/v1/users/:userId/phone')
|
||||
->desc('Update phone')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.phone')
|
||||
@@ -1608,7 +1595,7 @@ App::patch('/v1/users/:userId/phone')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/verification')
|
||||
Http::patch('/v1/users/:userId/verification')
|
||||
->desc('Update email verification')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.verification')
|
||||
@@ -1649,7 +1636,7 @@ App::patch('/v1/users/:userId/verification')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/prefs')
|
||||
Http::patch('/v1/users/:userId/prefs')
|
||||
->desc('Update user preferences')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.prefs')
|
||||
@@ -1688,7 +1675,7 @@ App::patch('/v1/users/:userId/prefs')
|
||||
$response->dynamic(new Document($prefs), Response::MODEL_PREFERENCES);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/targets/:targetId')
|
||||
Http::patch('/v1/users/:userId/targets/:targetId')
|
||||
->desc('Update user target')
|
||||
->groups(['api', 'users'])
|
||||
->label('audits.event', 'target.update')
|
||||
@@ -1791,7 +1778,7 @@ App::patch('/v1/users/:userId/targets/:targetId')
|
||||
->dynamic($target, Response::MODEL_TARGET);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/mfa')
|
||||
Http::patch('/v1/users/:userId/mfa')
|
||||
->desc('Update MFA')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.mfa')
|
||||
@@ -1855,7 +1842,7 @@ App::patch('/v1/users/:userId/mfa')
|
||||
$response->dynamic($user, Response::MODEL_USER);
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/mfa/factors')
|
||||
Http::get('/v1/users/:userId/mfa/factors')
|
||||
->desc('List factors')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
@@ -1914,7 +1901,7 @@ App::get('/v1/users/:userId/mfa/factors')
|
||||
$response->dynamic($factors, Response::MODEL_MFA_FACTORS);
|
||||
});
|
||||
|
||||
App::get('/v1/users/:userId/mfa/recovery-codes')
|
||||
Http::get('/v1/users/:userId/mfa/recovery-codes')
|
||||
->desc('Get MFA recovery codes')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
@@ -1975,7 +1962,7 @@ App::get('/v1/users/:userId/mfa/recovery-codes')
|
||||
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
|
||||
});
|
||||
|
||||
App::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||
Http::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||
->desc('Create MFA recovery codes')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].create.mfa.recovery-codes')
|
||||
@@ -2047,7 +2034,7 @@ App::patch('/v1/users/:userId/mfa/recovery-codes')
|
||||
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
|
||||
});
|
||||
|
||||
App::put('/v1/users/:userId/mfa/recovery-codes')
|
||||
Http::put('/v1/users/:userId/mfa/recovery-codes')
|
||||
->desc('Update MFA recovery codes (regenerate)')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].update.mfa.recovery-codes')
|
||||
@@ -2119,7 +2106,7 @@ App::put('/v1/users/:userId/mfa/recovery-codes')
|
||||
$response->dynamic($document, Response::MODEL_MFA_RECOVERY_CODES);
|
||||
});
|
||||
|
||||
App::delete('/v1/users/:userId/mfa/authenticators/:type')
|
||||
Http::delete('/v1/users/:userId/mfa/authenticators/:type')
|
||||
->desc('Delete authenticator')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].delete.mfa')
|
||||
@@ -2189,7 +2176,7 @@ App::delete('/v1/users/:userId/mfa/authenticators/:type')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::post('/v1/users/:userId/sessions')
|
||||
Http::post('/v1/users/:userId/sessions')
|
||||
->desc('Create session')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].sessions.[sessionId].create')
|
||||
@@ -2282,7 +2269,7 @@ App::post('/v1/users/:userId/sessions')
|
||||
->dynamic($session, Response::MODEL_SESSION);
|
||||
});
|
||||
|
||||
App::post('/v1/users/:userId/tokens')
|
||||
Http::post('/v1/users/:userId/tokens')
|
||||
->desc('Create token')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].tokens.[tokenId].create')
|
||||
@@ -2347,7 +2334,7 @@ App::post('/v1/users/:userId/tokens')
|
||||
->dynamic($token, Response::MODEL_TOKEN);
|
||||
});
|
||||
|
||||
App::delete('/v1/users/:userId/sessions/:sessionId')
|
||||
Http::delete('/v1/users/:userId/sessions/:sessionId')
|
||||
->desc('Delete user session')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].sessions.[sessionId].delete')
|
||||
@@ -2398,7 +2385,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::delete('/v1/users/:userId/sessions')
|
||||
Http::delete('/v1/users/:userId/sessions')
|
||||
->desc('Delete user sessions')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].sessions.delete')
|
||||
@@ -2448,7 +2435,7 @@ App::delete('/v1/users/:userId/sessions')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::delete('/v1/users/:userId')
|
||||
Http::delete('/v1/users/:userId')
|
||||
->desc('Delete user')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].delete')
|
||||
@@ -2500,7 +2487,7 @@ App::delete('/v1/users/:userId')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::delete('/v1/users/:userId/targets/:targetId')
|
||||
Http::delete('/v1/users/:userId/targets/:targetId')
|
||||
->desc('Delete user target')
|
||||
->groups(['api', 'users'])
|
||||
->label('audits.event', 'target.delete')
|
||||
@@ -2558,7 +2545,7 @@ App::delete('/v1/users/:userId/targets/:targetId')
|
||||
$response->noContent();
|
||||
});
|
||||
|
||||
App::delete('/v1/users/identities/:identityId')
|
||||
Http::delete('/v1/users/identities/:identityId')
|
||||
->desc('Delete identity')
|
||||
->groups(['api', 'users'])
|
||||
->label('event', 'users.[userId].identities.[identityId].delete')
|
||||
@@ -2601,7 +2588,7 @@ App::delete('/v1/users/identities/:identityId')
|
||||
return $response->noContent();
|
||||
});
|
||||
|
||||
App::post('/v1/users/:userId/jwts')
|
||||
Http::post('/v1/users/:userId/jwts')
|
||||
->desc('Create user JWT')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.write')
|
||||
@@ -2658,7 +2645,7 @@ App::post('/v1/users/:userId/jwts')
|
||||
])]), Response::MODEL_JWT);
|
||||
});
|
||||
|
||||
App::get('/v1/users/usage')
|
||||
Http::get('/v1/users/usage')
|
||||
->desc('Get users usage stats')
|
||||
->groups(['api', 'users'])
|
||||
->label('scope', 'users.read')
|
||||
|
||||
+3
-1156
File diff suppressed because it is too large
Load Diff
+43
-38
@@ -8,7 +8,7 @@ use Appwrite\Auth\Key;
|
||||
use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Delete as DeleteEvent;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Execution;
|
||||
use Appwrite\Event\StatsUsage;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Appwrite\Network\Cors;
|
||||
@@ -34,7 +34,6 @@ use Appwrite\Utopia\View;
|
||||
use Executor\Executor;
|
||||
use MaxMind\Db\Reader;
|
||||
use Swoole\Http\Request as SwooleRequest;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
@@ -46,6 +45,7 @@ use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Http;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Logger\Adapter\Sentry;
|
||||
use Utopia\Logger\Log;
|
||||
@@ -60,7 +60,7 @@ Config::setParam('domainVerification', false);
|
||||
Config::setParam('cookieDomain', 'localhost');
|
||||
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
|
||||
|
||||
function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Authorization $authorization, ?Key $apiKey, DeleteEvent $queueForDeletes, int $executionsRetentionCount)
|
||||
function router(Http $utopia, Database $dbForPlatform, callable $getProjectDB, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Event $queueForEvents, StatsUsage $queueForStatsUsage, Execution $queueForExecutions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Authorization $authorization, ?Key $apiKey, DeleteEvent $queueForDeletes, int $executionsRetentionCount)
|
||||
{
|
||||
$host = $request->getHostname() ?? '';
|
||||
if (!empty($previewHostname)) {
|
||||
@@ -696,14 +696,11 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
throw $th;
|
||||
}
|
||||
} finally {
|
||||
if ($type === 'function') {
|
||||
$queueForFunctions
|
||||
->setType(Func::TYPE_ASYNC_WRITE)
|
||||
if ($type === 'function' || $type === 'site') {
|
||||
$queueForExecutions
|
||||
->setExecution($execution)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
} elseif ($type === 'site') { // TODO: Move it to logs worker later
|
||||
$dbForProject->createDocument('executions', $execution);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -839,7 +836,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
}
|
||||
}
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['api'])
|
||||
->inject('project')
|
||||
->inject('mode')
|
||||
@@ -849,7 +846,7 @@ App::init()
|
||||
}
|
||||
});
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['database', 'functions', 'sites', 'messaging'])
|
||||
->inject('project')
|
||||
->inject('request')
|
||||
@@ -862,7 +859,7 @@ App::init()
|
||||
}
|
||||
});
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['api', 'web'])
|
||||
->inject('utopia')
|
||||
->inject('swooleRequest')
|
||||
@@ -877,7 +874,7 @@ App::init()
|
||||
->inject('geodb')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForFunctions')
|
||||
->inject('queueForExecutions')
|
||||
->inject('executor')
|
||||
->inject('platform')
|
||||
->inject('isResourceBlocked')
|
||||
@@ -888,7 +885,7 @@ App::init()
|
||||
->inject('authorization')
|
||||
->inject('queueForDeletes')
|
||||
->inject('executionsRetentionCount')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Func $queueForFunctions, Executor $executor, array $platform, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
|
||||
->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Document $project, Database $dbForPlatform, callable $getProjectDB, Locale $locale, array $localeCodes, Reader $geodb, StatsUsage $queueForStatsUsage, Event $queueForEvents, Execution $queueForExecutions, Executor $executor, array $platform, callable $isResourceBlocked, string $previewHostname, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
@@ -896,7 +893,7 @@ App::init()
|
||||
$platformHostnames = $platform['hostnames'] ?? [];
|
||||
// Only run Router when external domain
|
||||
if (!\in_array($hostname, $platformHostnames) || !empty($previewHostname)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForExecutions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
@@ -950,7 +947,15 @@ App::init()
|
||||
$endDomain->getRegisterable() !== ''
|
||||
);
|
||||
|
||||
$isLocalHost = $request->getHostname() === 'localhost' || $request->getHostname() === 'localhost:' . $request->getPort();
|
||||
$localHosts = ['localhost','localhost:'.$request->getPort()];
|
||||
|
||||
$migrationHost = System::getEnv('_APP_MIGRATION_HOST');
|
||||
if (!empty($migrationHost)) {
|
||||
$localHosts[] = $migrationHost;
|
||||
$localHosts[] = $migrationHost.':'.$request->getPort();
|
||||
}
|
||||
|
||||
$isLocalHost = in_array($request->getHostname(), $localHosts);
|
||||
$isIpAddress = filter_var($request->getHostname(), FILTER_VALIDATE_IP) !== false;
|
||||
|
||||
$isConsoleProject = $project->getAttribute('$id', '') === 'console';
|
||||
@@ -997,7 +1002,7 @@ App::init()
|
||||
}
|
||||
|
||||
if (System::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
|
||||
if ($request->getProtocol() !== 'https' && ($swooleRequest->header['host'] ?? '') !== 'localhost') { // localhost allowed for proxy, APP_HOSTNAME_INTERNAL allowed for migrations
|
||||
if ($request->getProtocol() !== 'https' && !in_array(($swooleRequest->header['host'] ?? ''), $localHosts)) { // localhost allowed for proxy
|
||||
if ($request->getMethod() !== Request::METHOD_GET) {
|
||||
throw new AppwriteException(AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED, 'Method unsupported over HTTP. Please use HTTPS instead.');
|
||||
}
|
||||
@@ -1012,7 +1017,7 @@ App::init()
|
||||
*
|
||||
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
|
||||
*/
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['api', 'web'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
@@ -1052,7 +1057,7 @@ App::init()
|
||||
/**
|
||||
* Automatic certificate generation
|
||||
*/
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['api', 'web'])
|
||||
->inject('request')
|
||||
->inject('console')
|
||||
@@ -1157,7 +1162,7 @@ App::init()
|
||||
});
|
||||
});
|
||||
|
||||
App::options()
|
||||
Http::options()
|
||||
->inject('utopia')
|
||||
->inject('swooleRequest')
|
||||
->inject('request')
|
||||
@@ -1167,7 +1172,7 @@ App::options()
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('queueForExecutions')
|
||||
->inject('executor')
|
||||
->inject('geodb')
|
||||
->inject('isResourceBlocked')
|
||||
@@ -1180,14 +1185,14 @@ App::options()
|
||||
->inject('authorization')
|
||||
->inject('queueForDeletes')
|
||||
->inject('executionsRetentionCount')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
|
||||
->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Execution $queueForExecutions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, Document $project, Document $devKey, ?Key $apiKey, Cors $cors, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
|
||||
/*
|
||||
* Appwrite Router
|
||||
*/
|
||||
$platformHostnames = $platform['hostnames'] ?? [];
|
||||
// Only run Router when external domain
|
||||
if (!in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForExecutions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
@@ -1211,7 +1216,7 @@ App::options()
|
||||
->trigger();
|
||||
});
|
||||
|
||||
App::error()
|
||||
Http::error()
|
||||
->inject('error')
|
||||
->inject('utopia')
|
||||
->inject('request')
|
||||
@@ -1222,7 +1227,7 @@ App::error()
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('devKey')
|
||||
->inject('authorization')
|
||||
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage, Document $devKey, Authorization $authorization) {
|
||||
->action(function (Throwable $error, Http $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage, Document $devKey, Authorization $authorization) {
|
||||
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
$route = $utopia->getRoute();
|
||||
$class = \get_class($error);
|
||||
@@ -1498,7 +1503,7 @@ App::error()
|
||||
|
||||
$type = $error->getType();
|
||||
|
||||
$output = App::isDevelopment() ? [
|
||||
$output = Http::isDevelopment() ? [
|
||||
'message' => $message,
|
||||
'code' => $code,
|
||||
'file' => $file,
|
||||
@@ -1531,7 +1536,7 @@ App::error()
|
||||
|
||||
$layout
|
||||
->setParam('title', $project->getAttribute('name') . ' - Error')
|
||||
->setParam('development', App::isDevelopment())
|
||||
->setParam('development', Http::isDevelopment())
|
||||
->setParam('projectName', $project->getAttribute('name'))
|
||||
->setParam('projectURL', $project->getAttribute('url'))
|
||||
->setParam('message', $output['message'] ?? '')
|
||||
@@ -1550,7 +1555,7 @@ App::error()
|
||||
);
|
||||
});
|
||||
|
||||
App::get('/robots.txt')
|
||||
Http::get('/robots.txt')
|
||||
->desc('Robots.txt File')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
@@ -1563,7 +1568,7 @@ App::get('/robots.txt')
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('queueForExecutions')
|
||||
->inject('executor')
|
||||
->inject('geodb')
|
||||
->inject('isResourceBlocked')
|
||||
@@ -1573,19 +1578,19 @@ App::get('/robots.txt')
|
||||
->inject('authorization')
|
||||
->inject('queueForDeletes')
|
||||
->inject('executionsRetentionCount')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
|
||||
->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Execution $queueForExecutions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
|
||||
$platformHostnames = $platform['hostnames'] ?? [];
|
||||
if (in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
|
||||
$template = new View(__DIR__ . '/../views/general/robots.phtml');
|
||||
$response->text($template->render(false));
|
||||
} else {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForExecutions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App::get('/humans.txt')
|
||||
Http::get('/humans.txt')
|
||||
->desc('Humans.txt File')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
@@ -1598,7 +1603,7 @@ App::get('/humans.txt')
|
||||
->inject('getProjectDB')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForStatsUsage')
|
||||
->inject('queueForFunctions')
|
||||
->inject('queueForExecutions')
|
||||
->inject('executor')
|
||||
->inject('geodb')
|
||||
->inject('isResourceBlocked')
|
||||
@@ -1608,19 +1613,19 @@ App::get('/humans.txt')
|
||||
->inject('authorization')
|
||||
->inject('queueForDeletes')
|
||||
->inject('executionsRetentionCount')
|
||||
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
|
||||
->action(function (Http $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Execution $queueForExecutions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey, Authorization $authorization, DeleteEvent $queueForDeletes, int $executionsRetentionCount) {
|
||||
$platformHostnames = $platform['hostnames'] ?? [];
|
||||
if (in_array($request->getHostname(), $platformHostnames) || !empty($previewHostname)) {
|
||||
$template = new View(__DIR__ . '/../views/general/humans.phtml');
|
||||
$response->text($template->render(false));
|
||||
} else {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
|
||||
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForExecutions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey, $queueForDeletes, $executionsRetentionCount)) {
|
||||
$utopia->getRoute()?->label('router', true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
App::get('/.well-known/acme-challenge/*')
|
||||
Http::get('/.well-known/acme-challenge/*')
|
||||
->desc('SSL Verification')
|
||||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
@@ -1673,7 +1678,7 @@ App::get('/.well-known/acme-challenge/*')
|
||||
include_once __DIR__ . '/shared/api.php';
|
||||
include_once __DIR__ . '/shared/api/auth.php';
|
||||
|
||||
App::get('/v1/ping')
|
||||
Http::get('/v1/ping')
|
||||
->groups(['api', 'general'])
|
||||
->desc('Test the connection between the Appwrite and the SDK.')
|
||||
->label('scope', 'global')
|
||||
@@ -1723,7 +1728,7 @@ App::get('/v1/ping')
|
||||
});
|
||||
|
||||
// Preview authorization
|
||||
App::get('/_appwrite/authorize')
|
||||
Http::get('/_appwrite/authorize')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('previewHostname')
|
||||
@@ -1750,7 +1755,7 @@ App::get('/_appwrite/authorize')
|
||||
->redirect($protocol . '://' . $host . $path);
|
||||
});
|
||||
|
||||
App::wildcard()
|
||||
Http::wildcard()
|
||||
->groups(['api'])
|
||||
->label('scope', 'global')
|
||||
->action(function () {
|
||||
|
||||
+12
-12
@@ -5,7 +5,6 @@ global $utopia, $request, $response;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
@@ -13,13 +12,14 @@ use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
App::get('/v1/mock/tests/general/oauth2')
|
||||
Http::get('/v1/mock/tests/general/oauth2')
|
||||
->desc('OAuth Login')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
@@ -35,7 +35,7 @@ App::get('/v1/mock/tests/general/oauth2')
|
||||
$response->redirect($redirectURI . '?' . \http_build_query(['code' => 'abcdef', 'state' => $state]));
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/locale')
|
||||
Http::get('/v1/mock/tests/locale')
|
||||
->desc('Mock locale translation key')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
@@ -54,7 +54,7 @@ App::get('/v1/mock/tests/locale')
|
||||
$response->send($locale->getText('mock'));
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/oauth2/token')
|
||||
Http::get('/v1/mock/tests/general/oauth2/token')
|
||||
->desc('OAuth2 Token')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
@@ -100,7 +100,7 @@ App::get('/v1/mock/tests/general/oauth2/token')
|
||||
}
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/oauth2/user')
|
||||
Http::get('/v1/mock/tests/general/oauth2/user')
|
||||
->desc('OAuth2 User')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
@@ -121,7 +121,7 @@ App::get('/v1/mock/tests/general/oauth2/user')
|
||||
]);
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/oauth2/user-unverified')
|
||||
Http::get('/v1/mock/tests/general/oauth2/user-unverified')
|
||||
->desc('OAuth2 User Unverified')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
@@ -142,7 +142,7 @@ App::get('/v1/mock/tests/general/oauth2/user-unverified')
|
||||
]);
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/oauth2/success')
|
||||
Http::get('/v1/mock/tests/general/oauth2/success')
|
||||
->desc('OAuth2 Success')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
@@ -155,7 +155,7 @@ App::get('/v1/mock/tests/general/oauth2/success')
|
||||
]);
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/oauth2/failure')
|
||||
Http::get('/v1/mock/tests/general/oauth2/failure')
|
||||
->desc('OAuth2 Failure')
|
||||
->groups(['mock'])
|
||||
->label('scope', 'public')
|
||||
@@ -170,7 +170,7 @@ App::get('/v1/mock/tests/general/oauth2/failure')
|
||||
]);
|
||||
});
|
||||
|
||||
App::post('/v1/mock/api-key-unprefixed')
|
||||
Http::post('/v1/mock/api-key-unprefixed')
|
||||
->desc('Create API Key (without standard prefix)')
|
||||
->groups(['mock', 'api', 'projects'])
|
||||
->label('scope', 'public')
|
||||
@@ -220,7 +220,7 @@ App::post('/v1/mock/api-key-unprefixed')
|
||||
->dynamic($key, Response::MODEL_KEY);
|
||||
});
|
||||
|
||||
App::get('/v1/mock/github/callback')
|
||||
Http::get('/v1/mock/github/callback')
|
||||
->desc('Create installation document using GitHub installation id')
|
||||
->groups(['mock', 'api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
@@ -280,12 +280,12 @@ App::get('/v1/mock/github/callback')
|
||||
]);
|
||||
});
|
||||
|
||||
App::shutdown()
|
||||
Http::shutdown()
|
||||
->groups(['mock'])
|
||||
->inject('utopia')
|
||||
->inject('response')
|
||||
->inject('request')
|
||||
->action(function (App $utopia, Response $response, Request $request) {
|
||||
->action(function (Http $utopia, Response $response, Request $request) {
|
||||
|
||||
$result = [];
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
@@ -21,7 +21,6 @@ use Appwrite\Utopia\Database\Documents\User;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Filesystem;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Config\Config;
|
||||
@@ -31,6 +30,7 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Authorization\Input;
|
||||
use Utopia\Http;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Telemetry\Adapter as Telemetry;
|
||||
use Utopia\Validator\WhiteList;
|
||||
@@ -74,7 +74,7 @@ $parseLabel = function (string $label, array $responsePayload, array $requestPar
|
||||
return $label;
|
||||
};
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['api'])
|
||||
->inject('utopia')
|
||||
->inject('request')
|
||||
@@ -89,7 +89,7 @@ App::init()
|
||||
->inject('team')
|
||||
->inject('apiKey')
|
||||
->inject('authorization')
|
||||
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey, Authorization $authorization) {
|
||||
->action(function (Http $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $apiKey, Authorization $authorization) {
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
/**
|
||||
@@ -356,7 +356,7 @@ App::init()
|
||||
}
|
||||
});
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['api'])
|
||||
->inject('utopia')
|
||||
->inject('request')
|
||||
@@ -382,7 +382,7 @@ App::init()
|
||||
->inject('telemetry')
|
||||
->inject('platform')
|
||||
->inject('authorization')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Mail $queueForMails, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization) {
|
||||
->action(function (Http $utopia, Request $request, Response $response, Document $project, Document $user, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Mail $queueForMails, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization) {
|
||||
|
||||
$route = $utopia->getRoute();
|
||||
|
||||
@@ -592,7 +592,7 @@ App::init()
|
||||
}
|
||||
});
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['session'])
|
||||
->inject('user')
|
||||
->inject('request')
|
||||
@@ -612,14 +612,14 @@ App::init()
|
||||
* Delete older sessions if the number of sessions have crossed
|
||||
* the session limit set for the project
|
||||
*/
|
||||
App::shutdown()
|
||||
Http::shutdown()
|
||||
->groups(['session'])
|
||||
->inject('utopia')
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForProject')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, Database $dbForProject) {
|
||||
->action(function (Http $utopia, Request $request, Response $response, Document $project, Database $dbForProject) {
|
||||
$sessionLimit = $project->getAttribute('auths', [])['maxSessions'] ?? APP_LIMIT_USER_SESSIONS_DEFAULT;
|
||||
$session = $response->getPayload();
|
||||
$userId = $session['userId'] ?? '';
|
||||
@@ -646,7 +646,7 @@ App::shutdown()
|
||||
$dbForProject->purgeCachedDocument('users', $userId);
|
||||
});
|
||||
|
||||
App::shutdown()
|
||||
Http::shutdown()
|
||||
->groups(['api'])
|
||||
->inject('utopia')
|
||||
->inject('request')
|
||||
@@ -667,7 +667,7 @@ App::shutdown()
|
||||
->inject('authorization')
|
||||
->inject('timelimit')
|
||||
->inject('eventProcessor')
|
||||
->action(function (App $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor) use ($parseLabel) {
|
||||
->action(function (Http $utopia, Request $request, Response $response, Document $project, User $user, Event $queueForEvents, Audit $queueForAudits, StatsUsage $queueForStatsUsage, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, Messaging $queueForMessaging, Func $queueForFunctions, Event $queueForWebhooks, Realtime $queueForRealtime, Database $dbForProject, Authorization $authorization, callable $timelimit, EventProcessor $eventProcessor) use ($parseLabel) {
|
||||
|
||||
$responsePayload = $response->getPayload();
|
||||
|
||||
@@ -892,7 +892,7 @@ App::shutdown()
|
||||
}
|
||||
});
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['usage'])
|
||||
->action(function () {
|
||||
if (System::getEnv('_APP_USAGE_STATS', 'enabled') !== 'enabled') {
|
||||
|
||||
@@ -4,14 +4,14 @@ use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Database\Documents\User;
|
||||
use Appwrite\Utopia\Request;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Http;
|
||||
use Utopia\System\System;
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['mfaProtected'])
|
||||
->inject('session')
|
||||
->action(function (Document $session) {
|
||||
@@ -30,14 +30,14 @@ App::init()
|
||||
}
|
||||
});
|
||||
|
||||
App::init()
|
||||
Http::init()
|
||||
->groups(['auth'])
|
||||
->inject('utopia')
|
||||
->inject('request')
|
||||
->inject('project')
|
||||
->inject('geodb')
|
||||
->inject('authorization')
|
||||
->action(function (App $utopia, Request $request, Document $project, Reader $geodb, Authorization $authorization) {
|
||||
->action(function (Http $utopia, Request $request, Document $project, Reader $geodb, Authorization $authorization) {
|
||||
$denylist = System::getEnv('_APP_CONSOLE_COUNTRIES_DENYLIST', '');
|
||||
if (!empty($denylist && $project->getId() === 'console')) {
|
||||
$countries = explode(',', $denylist);
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Http;
|
||||
|
||||
App::get('/versions')
|
||||
Http::get('/versions')
|
||||
->desc('Get Version')
|
||||
->groups(['home', 'web'])
|
||||
->label('scope', 'public')
|
||||
|
||||
+11
-11
@@ -11,7 +11,6 @@ use Swoole\Http\Server;
|
||||
use Swoole\Process;
|
||||
use Swoole\Table;
|
||||
use Swoole\Timer;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Adapter\Database as AdapterDatabase;
|
||||
use Utopia\Audit\Adapter\SQL as AuditAdapterSQL;
|
||||
use Utopia\Audit\Audit;
|
||||
@@ -27,6 +26,7 @@ use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Http;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Logger\Log\User;
|
||||
use Utopia\Pools\Group;
|
||||
@@ -181,7 +181,7 @@ $http->on(Constant::EVENT_AFTER_RELOAD, function ($server) {
|
||||
|
||||
include __DIR__ . '/controllers/general.php';
|
||||
|
||||
function createDatabase(App $app, string $resourceKey, string $dbName, array $collections, mixed $pools, callable $extraSetup = null): void
|
||||
function createDatabase(Http $app, string $resourceKey, string $dbName, array $collections, mixed $pools, ?callable $extraSetup = null): void
|
||||
{
|
||||
$max = 10;
|
||||
$sleep = 1;
|
||||
@@ -254,12 +254,12 @@ function createDatabase(App $app, string $resourceKey, string $dbName, array $co
|
||||
}
|
||||
|
||||
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) {
|
||||
$app = new App('UTC');
|
||||
$app = new Http('UTC');
|
||||
|
||||
go(function () use ($register, $app) {
|
||||
$pools = $register->get('pools');
|
||||
/** @var Group $pools */
|
||||
App::setResource('pools', fn () => $pools);
|
||||
Http::setResource('pools', fn () => $pools);
|
||||
|
||||
/** @var array $collections */
|
||||
$collections = Config::getParam('collections', []);
|
||||
@@ -441,8 +441,8 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
|
||||
});
|
||||
|
||||
$http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register) {
|
||||
App::setResource('swooleRequest', fn () => $swooleRequest);
|
||||
App::setResource('swooleResponse', fn () => $swooleResponse);
|
||||
Http::setResource('swooleRequest', fn () => $swooleRequest);
|
||||
Http::setResource('swooleResponse', fn () => $swooleResponse);
|
||||
|
||||
$request = new Request($swooleRequest);
|
||||
$response = new Response($swooleResponse);
|
||||
@@ -459,12 +459,12 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
|
||||
return;
|
||||
}
|
||||
|
||||
$app = new App('UTC');
|
||||
$app = new Http('UTC');
|
||||
$app->setCompression(System::getEnv('_APP_COMPRESSION_ENABLED', 'enabled') === 'enabled');
|
||||
$app->setCompressionMinSize(intval(System::getEnv('_APP_COMPRESSION_MIN_SIZE_BYTES', '1024'))); // 1KB
|
||||
|
||||
$pools = $register->get('pools');
|
||||
App::setResource('pools', fn () => $pools);
|
||||
Http::setResource('pools', fn () => $pools);
|
||||
|
||||
try {
|
||||
$authorization = $app->getResource('authorization');
|
||||
@@ -546,7 +546,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
|
||||
|
||||
$swooleResponse->setStatusCode(500);
|
||||
|
||||
$output = ((App::isDevelopment())) ? [
|
||||
$output = ((Http::isDevelopment())) ? [
|
||||
'message' => 'Error: ' . $th->getMessage(),
|
||||
'code' => 500,
|
||||
'file' => $th->getFile(),
|
||||
@@ -567,8 +567,8 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
|
||||
$http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
|
||||
$lastSyncUpdate = null;
|
||||
$pools = $register->get('pools');
|
||||
App::setResource('pools', fn () => $pools);
|
||||
$app = new App('UTC');
|
||||
Http::setResource('pools', fn () => $pools);
|
||||
$app = new Http('UTC');
|
||||
|
||||
/** @var Utopia\Database\Database $dbForPlatform */
|
||||
$dbForPlatform = $app->getResource('dbForPlatform');
|
||||
|
||||
@@ -5,6 +5,8 @@ use Appwrite\Platform\Modules\Compute\Specification;
|
||||
const APP_NAME = 'Appwrite';
|
||||
const APP_DOMAIN = 'appwrite.io';
|
||||
|
||||
const APP_VIEWS_DIR = __DIR__ . '/../views';
|
||||
|
||||
// Email
|
||||
const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address
|
||||
const APP_EMAIL_SECURITY = ''; // Default security email address
|
||||
@@ -369,6 +371,7 @@ const RESOURCE_TYPE_TOPICS = 'topics';
|
||||
const RESOURCE_TYPE_SUBSCRIBERS = 'subscribers';
|
||||
const RESOURCE_TYPE_MESSAGES = 'messages';
|
||||
const RESOURCE_TYPE_EXECUTIONS = 'executions';
|
||||
const RESOURCE_TYPE_VCS = 'vcs';
|
||||
|
||||
// Resource types for Tokens
|
||||
const TOKENS_RESOURCE_TYPE_FILES = 'files';
|
||||
|
||||
@@ -8,7 +8,6 @@ use Appwrite\URL\URL as AppwriteURL;
|
||||
use MaxMind\Db\Reader;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use Swoole\Database\PDOProxy;
|
||||
use Utopia\App;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Config\Config;
|
||||
@@ -18,6 +17,7 @@ use Utopia\Database\Adapter\SQL;
|
||||
use Utopia\Database\PDO;
|
||||
use Utopia\Domains\Validator\PublicDomain;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Http;
|
||||
use Utopia\Logger\Adapter\AppSignal;
|
||||
use Utopia\Logger\Adapter\LogOwl;
|
||||
use Utopia\Logger\Adapter\Raygun;
|
||||
@@ -31,11 +31,12 @@ use Utopia\Queue;
|
||||
use Utopia\Registry\Registry;
|
||||
use Utopia\System\System;
|
||||
|
||||
global $register;
|
||||
$register = new Registry();
|
||||
|
||||
App::setMode(System::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
|
||||
Http::setMode(System::getEnv('_APP_ENV', Http::MODE_TYPE_PRODUCTION));
|
||||
|
||||
if (!App::isProduction()) {
|
||||
if (!Http::isProduction()) {
|
||||
// Allow specific domains to skip public domain validation in dev environment
|
||||
// Useful for existing tests involving webhooks
|
||||
PublicDomain::allow(['request-catcher-sms']);
|
||||
|
||||
+126
-88
@@ -10,6 +10,7 @@ use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Execution;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Mail;
|
||||
use Appwrite\Event\Messaging;
|
||||
@@ -31,7 +32,6 @@ use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Executor\Executor;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Adapter\Database as AdapterDatabase;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Auth\Hashes\Argon2;
|
||||
@@ -52,6 +52,7 @@ use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Http;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Pools\Group;
|
||||
@@ -74,114 +75,118 @@ use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub as VcsGitHub;
|
||||
|
||||
// Runtime Execution
|
||||
App::setResource('log', fn () => new Log());
|
||||
App::setResource('logger', function ($register) {
|
||||
Http::setResource('log', fn () => new Log());
|
||||
Http::setResource('logger', function ($register) {
|
||||
return $register->get('logger');
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('hooks', function ($register) {
|
||||
Http::setResource('hooks', function ($register) {
|
||||
return $register->get('hooks');
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('register', fn () => $register);
|
||||
App::setResource('locale', function () {
|
||||
global $register;
|
||||
Http::setResource('register', fn () => $register);
|
||||
Http::setResource('locale', function () {
|
||||
$locale = new Locale(System::getEnv('_APP_LOCALE', 'en'));
|
||||
$locale->setFallback(System::getEnv('_APP_LOCALE', 'en'));
|
||||
return $locale;
|
||||
});
|
||||
|
||||
App::setResource('localeCodes', function () {
|
||||
Http::setResource('localeCodes', function () {
|
||||
return array_map(fn ($locale) => $locale['code'], Config::getParam('locale-codes', []));
|
||||
});
|
||||
|
||||
// Queues
|
||||
App::setResource('publisher', function (Group $pools) {
|
||||
Http::setResource('publisher', function (Group $pools) {
|
||||
return new BrokerPool(publisher: $pools->get('publisher'));
|
||||
}, ['pools']);
|
||||
App::setResource('publisherDatabases', function (Publisher $publisher) {
|
||||
Http::setResource('publisherDatabases', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherFunctions', function (Publisher $publisher) {
|
||||
Http::setResource('publisherFunctions', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherMigrations', function (Publisher $publisher) {
|
||||
Http::setResource('publisherMigrations', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherStatsUsage', function (Publisher $publisher) {
|
||||
Http::setResource('publisherStatsUsage', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherMails', function (Publisher $publisher) {
|
||||
Http::setResource('publisherMails', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherDeletes', function (Publisher $publisher) {
|
||||
Http::setResource('publisherDeletes', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherMessaging', function (Publisher $publisher) {
|
||||
Http::setResource('publisherMessaging', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('publisherWebhooks', function (Publisher $publisher) {
|
||||
Http::setResource('publisherWebhooks', function (Publisher $publisher) {
|
||||
return $publisher;
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForMessaging', function (Publisher $publisher) {
|
||||
Http::setResource('queueForMessaging', function (Publisher $publisher) {
|
||||
return new Messaging($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForMails', function (Publisher $publisher) {
|
||||
Http::setResource('queueForMails', function (Publisher $publisher) {
|
||||
return new Mail($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForBuilds', function (Publisher $publisher) {
|
||||
Http::setResource('queueForBuilds', function (Publisher $publisher) {
|
||||
return new Build($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForScreenshots', function (Publisher $publisher) {
|
||||
Http::setResource('queueForScreenshots', function (Publisher $publisher) {
|
||||
return new Screenshot($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForDatabase', function (Publisher $publisher) {
|
||||
Http::setResource('queueForDatabase', function (Publisher $publisher) {
|
||||
return new EventDatabase($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForDeletes', function (Publisher $publisher) {
|
||||
Http::setResource('queueForDeletes', function (Publisher $publisher) {
|
||||
return new Delete($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForEvents', function (Publisher $publisher) {
|
||||
Http::setResource('queueForEvents', function (Publisher $publisher) {
|
||||
return new Event($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForWebhooks', function (Publisher $publisher) {
|
||||
Http::setResource('queueForWebhooks', function (Publisher $publisher) {
|
||||
return new Webhook($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForRealtime', function () {
|
||||
Http::setResource('queueForRealtime', function () {
|
||||
return new Realtime();
|
||||
}, []);
|
||||
App::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
Http::setResource('queueForStatsUsage', function (Publisher $publisher) {
|
||||
return new StatsUsage($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForAudits', function (Publisher $publisher) {
|
||||
Http::setResource('queueForAudits', function (Publisher $publisher) {
|
||||
return new AuditEvent($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
Http::setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('eventProcessor', function () {
|
||||
Http::setResource('queueForExecutions', function (Publisher $publisher) {
|
||||
return new Execution($publisher);
|
||||
}, ['publisher']);
|
||||
Http::setResource('eventProcessor', function () {
|
||||
return new EventProcessor();
|
||||
}, []);
|
||||
App::setResource('queueForCertificates', function (Publisher $publisher) {
|
||||
Http::setResource('queueForCertificates', function (Publisher $publisher) {
|
||||
return new Certificate($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForMigrations', function (Publisher $publisher) {
|
||||
Http::setResource('queueForMigrations', function (Publisher $publisher) {
|
||||
return new Migration($publisher);
|
||||
}, ['publisher']);
|
||||
App::setResource('queueForStatsResources', function (Publisher $publisher) {
|
||||
Http::setResource('queueForStatsResources', function (Publisher $publisher) {
|
||||
return new StatsResources($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
/**
|
||||
* Platform configuration
|
||||
*/
|
||||
App::setResource('platform', function () {
|
||||
Http::setResource('platform', function () {
|
||||
return Config::getParam('platform', []);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* List of allowed request hostnames for the request.
|
||||
*/
|
||||
App::setResource('allowedHostnames', function (array $platform, Document $project, Document $rule, Document $devKey, Request $request) {
|
||||
Http::setResource('allowedHostnames', function (array $platform, Document $project, Document $rule, Document $devKey, Request $request) {
|
||||
$allowed = [...($platform['hostnames'] ?? [])];
|
||||
|
||||
/* Add platform configured hostnames */
|
||||
@@ -197,15 +202,26 @@ App::setResource('allowedHostnames', function (array $platform, Document $projec
|
||||
}
|
||||
|
||||
$originHostname = parse_url($request->getOrigin(), PHP_URL_HOST);
|
||||
$refererHostname = parse_url($request->getReferer(), PHP_URL_HOST);
|
||||
|
||||
$hostname = $originHostname;
|
||||
if (empty($hostname)) {
|
||||
$hostname = $refererHostname;
|
||||
}
|
||||
|
||||
/* Add request hostname for preflight requests */
|
||||
if ($request->getMethod() === 'OPTIONS') {
|
||||
$allowed[] = $originHostname;
|
||||
$allowed[] = $hostname;
|
||||
}
|
||||
|
||||
/* Allow the request origin if a dev key or rule is found */
|
||||
if ((!$rule->isEmpty() || !$devKey->isEmpty()) && !empty($originHostname)) {
|
||||
$allowed[] = $originHostname;
|
||||
/* Allow the request origin of rule */
|
||||
if (!$rule->isEmpty() && !empty($rule->getAttribute('domain', ''))) {
|
||||
$allowed[] = $rule->getAttribute('domain', '');
|
||||
}
|
||||
|
||||
/* Allow the request origin if a dev key is found */
|
||||
if (!$devKey->isEmpty() && !empty($hostname)) {
|
||||
$allowed[] = $hostname;
|
||||
}
|
||||
|
||||
return array_unique($allowed);
|
||||
@@ -214,7 +230,7 @@ App::setResource('allowedHostnames', function (array $platform, Document $projec
|
||||
/**
|
||||
* List of allowed request schemes for the request.
|
||||
*/
|
||||
App::setResource('allowedSchemes', function (Document $project) {
|
||||
Http::setResource('allowedSchemes', function (Document $project) {
|
||||
$allowed = [];
|
||||
|
||||
if (!$project->isEmpty() && $project->getId() !== 'console') {
|
||||
@@ -234,8 +250,13 @@ App::setResource('allowedSchemes', function (Document $project) {
|
||||
/**
|
||||
* Rule associated with a request origin.
|
||||
*/
|
||||
App::setResource('rule', function (Request $request, Database $dbForPlatform, Document $project, Authorization $authorization) {
|
||||
Http::setResource('rule', function (Request $request, Database $dbForPlatform, Document $project, Authorization $authorization) {
|
||||
$domain = \parse_url($request->getOrigin(), PHP_URL_HOST);
|
||||
|
||||
if (empty($domain)) {
|
||||
$domain = \parse_url($request->getReferer(), PHP_URL_HOST);
|
||||
}
|
||||
|
||||
if (empty($domain)) {
|
||||
return new Document();
|
||||
}
|
||||
@@ -252,7 +273,24 @@ App::setResource('rule', function (Request $request, Database $dbForPlatform, Do
|
||||
]) ?? new Document();
|
||||
});
|
||||
|
||||
if ($rule->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
$permitsCurrentProject = $rule->getAttribute('projectInternalId', '') === $project->getSequence();
|
||||
|
||||
// Temporary implementation until custom wildcard domains are an official feature
|
||||
// Allow trusted projects; Used for Console (website) previews
|
||||
if (!$permitsCurrentProject && !$rule->isEmpty() && !empty($rule->getAttribute('projectId', ''))) {
|
||||
$trustedProjects = [];
|
||||
foreach (\explode(',', System::getEnv('_APP_CONSOLE_TRUSTED_PROJECTS', '')) as $trustedProject) {
|
||||
if (empty($trustedProject)) {
|
||||
continue;
|
||||
}
|
||||
$trustedProjects[] = $trustedProject;
|
||||
}
|
||||
if (\in_array($rule->getAttribute('projectId', ''), $trustedProjects)) {
|
||||
$permitsCurrentProject = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$permitsCurrentProject) {
|
||||
return new Document();
|
||||
}
|
||||
|
||||
@@ -262,7 +300,7 @@ App::setResource('rule', function (Request $request, Database $dbForPlatform, Do
|
||||
/**
|
||||
* CORS service
|
||||
*/
|
||||
App::setResource('cors', fn (array $allowedHostnames) => new Cors(
|
||||
Http::setResource('cors', fn (array $allowedHostnames) => new Cors(
|
||||
$allowedHostnames,
|
||||
allowedMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
|
||||
allowedHeaders: [
|
||||
@@ -311,21 +349,21 @@ App::setResource('cors', fn (array $allowedHostnames) => new Cors(
|
||||
],
|
||||
), ['allowedHostnames']);
|
||||
|
||||
App::setResource('originValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) {
|
||||
Http::setResource('originValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) {
|
||||
if (!$devKey->isEmpty()) {
|
||||
return new URL();
|
||||
}
|
||||
return new Origin($allowedHostnames, $allowedSchemes);
|
||||
}, ['devKey', 'allowedHostnames', 'allowedSchemes']);
|
||||
|
||||
App::setResource('redirectValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) {
|
||||
Http::setResource('redirectValidator', function (Document $devKey, array $allowedHostnames, array $allowedSchemes) {
|
||||
if (!$devKey->isEmpty()) {
|
||||
return new URL();
|
||||
}
|
||||
return new Redirect($allowedHostnames, $allowedSchemes);
|
||||
}, ['devKey', 'allowedHostnames', 'allowedSchemes']);
|
||||
|
||||
App::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Store $store, Token $proofForToken, $authorization) {
|
||||
Http::setResource('user', function (string $mode, Document $project, Document $console, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Store $store, Token $proofForToken, $authorization) {
|
||||
/**
|
||||
* Handles user authentication and session validation.
|
||||
*
|
||||
@@ -480,7 +518,7 @@ App::setResource('user', function (string $mode, Document $project, Document $co
|
||||
return $user;
|
||||
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'store', 'proofForToken', 'authorization']);
|
||||
|
||||
App::setResource('project', function ($dbForPlatform, $request, $console, $authorization) {
|
||||
Http::setResource('project', function ($dbForPlatform, $request, $console, $authorization) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Utopia\Database\Database $dbForPlatform */
|
||||
/** @var Utopia\Database\Document $console */
|
||||
@@ -496,7 +534,7 @@ App::setResource('project', function ($dbForPlatform, $request, $console, $autho
|
||||
return $project;
|
||||
}, ['dbForPlatform', 'request', 'console', 'authorization']);
|
||||
|
||||
App::setResource('session', function (User $user, Store $store, Token $proofForToken) {
|
||||
Http::setResource('session', function (User $user, Store $store, Token $proofForToken) {
|
||||
if ($user->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
@@ -517,11 +555,11 @@ App::setResource('session', function (User $user, Store $store, Token $proofForT
|
||||
return;
|
||||
}, ['user', 'store', 'proofForToken']);
|
||||
|
||||
App::setResource('store', function (): Store {
|
||||
Http::setResource('store', function (): Store {
|
||||
return new Store();
|
||||
});
|
||||
|
||||
App::setResource('proofForPassword', function (): Password {
|
||||
Http::setResource('proofForPassword', function (): Password {
|
||||
$hash = new Argon2();
|
||||
$hash
|
||||
->setMemoryCost(7168)
|
||||
@@ -535,27 +573,27 @@ App::setResource('proofForPassword', function (): Password {
|
||||
return $password;
|
||||
});
|
||||
|
||||
App::setResource('proofForToken', function (): Token {
|
||||
Http::setResource('proofForToken', function (): Token {
|
||||
$token = new Token();
|
||||
$token->setHash(new Sha());
|
||||
return $token;
|
||||
});
|
||||
|
||||
App::setResource('proofForCode', function (): Code {
|
||||
Http::setResource('proofForCode', function (): Code {
|
||||
$code = new Code();
|
||||
$code->setHash(new Sha());
|
||||
return $code;
|
||||
});
|
||||
|
||||
App::setResource('console', function () {
|
||||
Http::setResource('console', function () {
|
||||
return new Document(Config::getParam('console'));
|
||||
}, []);
|
||||
|
||||
App::setResource('authorization', function () {
|
||||
Http::setResource('authorization', function () {
|
||||
return new Authorization();
|
||||
}, []);
|
||||
|
||||
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project, Response $response, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Func $queueForFunctions, Webhook $queueForWebhooks, Realtime $queueForRealtime, StatsUsage $queueForStatsUsage, Authorization $authorization) {
|
||||
Http::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project, Response $response, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Func $queueForFunctions, Webhook $queueForWebhooks, Realtime $queueForRealtime, StatsUsage $queueForStatsUsage, Authorization $authorization) {
|
||||
if ($project->isEmpty() || $project->getId() === 'console') {
|
||||
return $dbForPlatform;
|
||||
}
|
||||
@@ -797,7 +835,7 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
|
||||
return $database;
|
||||
}, ['pools', 'dbForPlatform', 'cache', 'project', 'response', 'publisher', 'publisherFunctions', 'publisherWebhooks', 'queueForEvents', 'queueForFunctions', 'queueForWebhooks', 'queueForRealtime', 'queueForStatsUsage', 'authorization']);
|
||||
|
||||
App::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
Http::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
|
||||
$adapter = new DatabasePool($pools->get('console'));
|
||||
$database = new Database($adapter, $cache);
|
||||
@@ -816,7 +854,7 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authoriz
|
||||
return $database;
|
||||
}, ['pools', 'cache', 'authorization']);
|
||||
|
||||
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
|
||||
Http::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
|
||||
$databases = [];
|
||||
|
||||
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases) {
|
||||
@@ -872,7 +910,7 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
|
||||
};
|
||||
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
|
||||
|
||||
App::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
Http::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
|
||||
$database = null;
|
||||
|
||||
return function (?Document $project = null) use ($pools, $cache, $authorization, &$database) {
|
||||
@@ -901,14 +939,14 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorizatio
|
||||
};
|
||||
}, ['pools', 'cache', 'authorization']);
|
||||
|
||||
App::setResource('audit', function ($dbForProject) {
|
||||
Http::setResource('audit', function ($dbForProject) {
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
return new Audit($adapter);
|
||||
}, ['dbForProject']);
|
||||
|
||||
App::setResource('telemetry', fn () => new NoTelemetry());
|
||||
Http::setResource('telemetry', fn () => new NoTelemetry());
|
||||
|
||||
App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
|
||||
Http::setResource('cache', function (Group $pools, Telemetry $telemetry) {
|
||||
$list = Config::getParam('pools-cache', []);
|
||||
$adapters = [];
|
||||
|
||||
@@ -921,7 +959,7 @@ App::setResource('cache', function (Group $pools, Telemetry $telemetry) {
|
||||
return $cache;
|
||||
}, ['pools', 'telemetry']);
|
||||
|
||||
App::setResource('redis', function () {
|
||||
Http::setResource('redis', function () {
|
||||
$host = System::getEnv('_APP_REDIS_HOST', 'localhost');
|
||||
$port = System::getEnv('_APP_REDIS_PORT', 6379);
|
||||
$pass = System::getEnv('_APP_REDIS_PASS', '');
|
||||
@@ -936,28 +974,28 @@ App::setResource('redis', function () {
|
||||
return $redis;
|
||||
});
|
||||
|
||||
App::setResource('timelimit', function (\Redis $redis) {
|
||||
Http::setResource('timelimit', function (\Redis $redis) {
|
||||
return function (string $key, int $limit, int $time) use ($redis) {
|
||||
return new TimeLimitRedis($key, $limit, $time, $redis);
|
||||
};
|
||||
}, ['redis']);
|
||||
|
||||
App::setResource('deviceForLocal', function (Telemetry $telemetry) {
|
||||
Http::setResource('deviceForLocal', function (Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, new Local());
|
||||
}, ['telemetry']);
|
||||
App::setResource('deviceForFiles', function ($project, Telemetry $telemetry) {
|
||||
Http::setResource('deviceForFiles', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
App::setResource('deviceForSites', function ($project, Telemetry $telemetry) {
|
||||
Http::setResource('deviceForSites', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_SITES . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
App::setResource('deviceForMigrations', function ($project, Telemetry $telemetry) {
|
||||
Http::setResource('deviceForMigrations', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_IMPORTS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
App::setResource('deviceForFunctions', function ($project, Telemetry $telemetry) {
|
||||
Http::setResource('deviceForFunctions', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
App::setResource('deviceForBuilds', function ($project, Telemetry $telemetry) {
|
||||
Http::setResource('deviceForBuilds', function ($project, Telemetry $telemetry) {
|
||||
return new Device\Telemetry($telemetry, getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId()));
|
||||
}, ['project', 'telemetry']);
|
||||
|
||||
@@ -1061,7 +1099,7 @@ function getDevice(string $root, string $connection = ''): Device
|
||||
}
|
||||
}
|
||||
|
||||
App::setResource('mode', function ($request) {
|
||||
Http::setResource('mode', function ($request) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
|
||||
/**
|
||||
@@ -1072,18 +1110,18 @@ App::setResource('mode', function ($request) {
|
||||
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT));
|
||||
}, ['request']);
|
||||
|
||||
App::setResource('geodb', function ($register) {
|
||||
Http::setResource('geodb', function ($register) {
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
return $register->get('geodb');
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('passwordsDictionary', function ($register) {
|
||||
Http::setResource('passwordsDictionary', function ($register) {
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
return $register->get('passwordsDictionary');
|
||||
}, ['register']);
|
||||
|
||||
|
||||
App::setResource('servers', function () {
|
||||
Http::setResource('servers', function () {
|
||||
$platforms = Config::getParam('sdks');
|
||||
$server = $platforms[APP_SDK_PLATFORM_SERVER];
|
||||
|
||||
@@ -1094,11 +1132,11 @@ App::setResource('servers', function () {
|
||||
return $languages;
|
||||
});
|
||||
|
||||
App::setResource('promiseAdapter', function ($register) {
|
||||
Http::setResource('promiseAdapter', function ($register) {
|
||||
return $register->get('promiseAdapter');
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('schema', function ($utopia, $dbForProject, $authorization) {
|
||||
Http::setResource('schema', function ($utopia, $dbForProject, $authorization) {
|
||||
|
||||
$complexity = function (int $complexity, array $args) {
|
||||
$queries = Query::parseQueries($args['queries'] ?? []);
|
||||
@@ -1185,11 +1223,11 @@ App::setResource('schema', function ($utopia, $dbForProject, $authorization) {
|
||||
);
|
||||
}, ['utopia', 'dbForProject', 'authorization']);
|
||||
|
||||
App::setResource('gitHub', function (Cache $cache) {
|
||||
Http::setResource('gitHub', function (Cache $cache) {
|
||||
return new VcsGitHub($cache);
|
||||
}, ['cache']);
|
||||
|
||||
App::setResource('requestTimestamp', function ($request) {
|
||||
Http::setResource('requestTimestamp', function ($request) {
|
||||
//TODO: Move this to the Request class itself
|
||||
$timestampHeader = $request->getHeader('x-appwrite-timestamp');
|
||||
$requestTimestamp = null;
|
||||
@@ -1203,15 +1241,15 @@ App::setResource('requestTimestamp', function ($request) {
|
||||
return $requestTimestamp;
|
||||
}, ['request']);
|
||||
|
||||
App::setResource('plan', function (array $plan = []) {
|
||||
Http::setResource('plan', function (array $plan = []) {
|
||||
return [];
|
||||
});
|
||||
|
||||
App::setResource('smsRates', function () {
|
||||
Http::setResource('smsRates', function () {
|
||||
return [];
|
||||
});
|
||||
|
||||
App::setResource('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform, Authorization $authorization) {
|
||||
Http::setResource('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform, Authorization $authorization) {
|
||||
$devKey = $request->getHeader('x-appwrite-dev-key', $request->getParam('devKey', ''));
|
||||
|
||||
// Check if given key match project's development keys
|
||||
@@ -1255,7 +1293,7 @@ App::setResource('devKey', function (Request $request, Document $project, array
|
||||
return $key;
|
||||
}, ['request', 'project', 'servers', 'dbForPlatform', 'authorization']);
|
||||
|
||||
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request, Authorization $authorization) {
|
||||
Http::setResource('team', function (Document $project, Database $dbForPlatform, Http $utopia, Request $request, Authorization $authorization) {
|
||||
$teamInternalId = '';
|
||||
if ($project->getId() !== 'console') {
|
||||
$teamInternalId = $project->getAttribute('teamInternalId', '');
|
||||
@@ -1292,15 +1330,15 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
|
||||
return $team;
|
||||
}, ['project', 'dbForPlatform', 'utopia', 'request', 'authorization']);
|
||||
|
||||
App::setResource(
|
||||
Http::setResource(
|
||||
'isResourceBlocked',
|
||||
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
|
||||
);
|
||||
|
||||
App::setResource('previewHostname', function (Request $request, ?Key $apiKey) {
|
||||
Http::setResource('previewHostname', function (Request $request, ?Key $apiKey) {
|
||||
$allowed = false;
|
||||
|
||||
if (App::isDevelopment()) {
|
||||
if (Http::isDevelopment()) {
|
||||
$allowed = true;
|
||||
} elseif (!\is_null($apiKey) && $apiKey->getHostnameOverride() === true) {
|
||||
$allowed = true;
|
||||
@@ -1316,7 +1354,7 @@ App::setResource('previewHostname', function (Request $request, ?Key $apiKey) {
|
||||
return '';
|
||||
}, ['request', 'apiKey']);
|
||||
|
||||
App::setResource('apiKey', function (Request $request, Document $project, Document $team, Document $user): ?Key {
|
||||
Http::setResource('apiKey', function (Request $request, Document $project, Document $team, Document $user): ?Key {
|
||||
$key = $request->getHeader('x-appwrite-key');
|
||||
|
||||
if (empty($key)) {
|
||||
@@ -1350,9 +1388,9 @@ App::setResource('apiKey', function (Request $request, Document $project, Docume
|
||||
return $key;
|
||||
}, ['request', 'project', 'team', 'user']);
|
||||
|
||||
App::setResource('executor', fn () => new Executor());
|
||||
Http::setResource('executor', fn () => new Executor());
|
||||
|
||||
App::setResource('resourceToken', function ($project, $dbForProject, $request, Authorization $authorization) {
|
||||
Http::setResource('resourceToken', function ($project, $dbForProject, $request, Authorization $authorization) {
|
||||
$tokenJWT = $request->getParam('token');
|
||||
|
||||
if (!empty($tokenJWT) && !$project->isEmpty()) { // JWT authentication
|
||||
@@ -1416,11 +1454,11 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request, A
|
||||
return new Document([]);
|
||||
}, ['project', 'dbForProject', 'request', 'authorization']);
|
||||
|
||||
App::setResource('transactionState', function (Database $dbForProject, Authorization $authorization) {
|
||||
Http::setResource('transactionState', function (Database $dbForProject, Authorization $authorization) {
|
||||
return new TransactionState($dbForProject, $authorization);
|
||||
}, ['dbForProject', 'authorization']);
|
||||
|
||||
App::setResource('executionsRetentionCount', function (Document $project, array $plan) {
|
||||
Http::setResource('executionsRetentionCount', function (Document $project, array $plan) {
|
||||
if ($project->getId() === 'console' || empty($plan)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
+61
-58
@@ -16,7 +16,6 @@ use Swoole\Table;
|
||||
use Swoole\Timer;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\TimeLimit\Redis as TimeLimitRedis;
|
||||
use Utopia\App;
|
||||
use Utopia\Auth\Hashes\Sha;
|
||||
use Utopia\Auth\Proofs\Token;
|
||||
use Utopia\Auth\Store;
|
||||
@@ -34,6 +33,7 @@ use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Http;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\Registry\Registry;
|
||||
@@ -431,15 +431,20 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
]
|
||||
];
|
||||
|
||||
$subscribers = $realtime->getSubscribers($event); // [connectionId => [subId => queries]]
|
||||
$subscribers = $realtime->getSubscribers($event);
|
||||
|
||||
// For test events, send to all connections with their matched subscription queries
|
||||
foreach ($subscribers as $connectionId => $matchedSubscriptions) {
|
||||
$groups = [];
|
||||
foreach ($subscribers as $id => $matched) {
|
||||
$key = implode(',', array_keys($matched));
|
||||
$groups[$key]['ids'][] = $id;
|
||||
$groups[$key]['subscriptions'] = array_keys($matched);
|
||||
}
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$data = $event['data'];
|
||||
// Send matched subscription IDs
|
||||
$data['subscriptions'] = array_keys($matchedSubscriptions);
|
||||
$data['subscriptions'] = $group['subscriptions'];
|
||||
|
||||
$server->send([$connectionId], json_encode([
|
||||
$server->send($group['ids'], json_encode([
|
||||
'type' => 'event',
|
||||
'data' => $data
|
||||
]));
|
||||
@@ -484,18 +489,18 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
$roles = $user->getRoles($database->getAuthorization());
|
||||
$authorization = $realtime->connections[$connection]['authorization'] ?? null;
|
||||
|
||||
$subscriptionMetadata = $realtime->getSubscriptionMetadata($connection);
|
||||
$meta = $realtime->getSubscriptionMetadata($connection);
|
||||
|
||||
$realtime->unsubscribe($connection);
|
||||
|
||||
foreach ($subscriptionMetadata as $subscriptionId => $metadata) {
|
||||
$queries = Query::parseQueries($metadata['queries'] ?? []);
|
||||
foreach ($meta as $subscriptionId => $subscription) {
|
||||
$queries = Query::parseQueries($subscription['queries'] ?? []);
|
||||
$realtime->subscribe(
|
||||
$projectId,
|
||||
$connection,
|
||||
$subscriptionId,
|
||||
$roles,
|
||||
$metadata['channels'] ?? [],
|
||||
$subscription['channels'] ?? [],
|
||||
$queries
|
||||
);
|
||||
}
|
||||
@@ -507,35 +512,38 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
}
|
||||
}
|
||||
|
||||
$receivers = $realtime->getSubscribers($event); // [connectionId => [subId => queries]]
|
||||
$receivers = $realtime->getSubscribers($event);
|
||||
|
||||
if (App::isDevelopment() && !empty($receivers)) {
|
||||
if (Http::isDevelopment() && !empty($receivers)) {
|
||||
Console::log("[Debug][Worker {$workerId}] Receivers: " . count($receivers));
|
||||
Console::log("[Debug][Worker {$workerId}] Receivers Connection IDs: " . json_encode(array_keys($receivers)));
|
||||
Console::log("[Debug][Worker {$workerId}] Event Query: " . json_encode(array_values($receivers)));
|
||||
Console::log("[Debug][Worker {$workerId}] Connection IDs: " . json_encode(array_keys($receivers)));
|
||||
Console::log("[Debug][Worker {$workerId}] Matched: " . json_encode(array_values($receivers)));
|
||||
Console::log("[Debug][Worker {$workerId}] Event: " . $payload);
|
||||
}
|
||||
|
||||
$totalMessages = 0;
|
||||
|
||||
foreach ($receivers as $connectionId => $matchedSubscriptions) {
|
||||
$data = $event['data'];
|
||||
// Send matched subscription IDs
|
||||
$data['subscriptions'] = array_keys($matchedSubscriptions);
|
||||
|
||||
$server->send(
|
||||
[$connectionId],
|
||||
json_encode([
|
||||
'type' => 'event',
|
||||
'data' => $data
|
||||
])
|
||||
);
|
||||
$totalMessages++;
|
||||
// Group connections by matched subscription IDs for batch sending
|
||||
$groups = [];
|
||||
foreach ($receivers as $id => $matched) {
|
||||
$key = implode(',', array_keys($matched));
|
||||
$groups[$key]['ids'][] = $id;
|
||||
$groups[$key]['subscriptions'] = array_keys($matched);
|
||||
}
|
||||
|
||||
if ($totalMessages > 0) {
|
||||
$register->get('telemetry.messageSentCounter')->add($totalMessages);
|
||||
$stats->incr($event['project'], 'messages', $totalMessages);
|
||||
$total = 0;
|
||||
foreach ($groups as $group) {
|
||||
$data = $event['data'];
|
||||
$data['subscriptions'] = $group['subscriptions'];
|
||||
|
||||
$server->send($group['ids'], json_encode([
|
||||
'type' => 'event',
|
||||
'data' => $data
|
||||
]));
|
||||
$total += count($group['ids']);
|
||||
}
|
||||
|
||||
if ($total > 0) {
|
||||
$register->get('telemetry.messageSentCounter')->add($total);
|
||||
$stats->incr($event['project'], 'messages', $total);
|
||||
}
|
||||
});
|
||||
} catch (Throwable $th) {
|
||||
@@ -552,15 +560,15 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
||||
});
|
||||
|
||||
$server->onOpen(function (int $connection, SwooleRequest $request) use ($server, $register, $stats, &$realtime, $logError) {
|
||||
$app = new App('UTC');
|
||||
$app = new Http('UTC');
|
||||
$request = new Request($request);
|
||||
$response = new Response(new SwooleResponse());
|
||||
|
||||
Console::info("Connection open (user: {$connection})");
|
||||
|
||||
App::setResource('pools', fn () => $register->get('pools'));
|
||||
App::setResource('request', fn () => $request);
|
||||
App::setResource('response', fn () => $response);
|
||||
Http::setResource('pools', fn () => $register->get('pools'));
|
||||
Http::setResource('request', fn () => $request);
|
||||
Http::setResource('response', fn () => $response);
|
||||
|
||||
try {
|
||||
/** @var Document $project */
|
||||
@@ -624,21 +632,19 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing channels');
|
||||
}
|
||||
|
||||
// Reconstruct subscriptions from query params using helper method
|
||||
$channelNames = array_keys($channels);
|
||||
$names = array_keys($channels);
|
||||
|
||||
try {
|
||||
$subscriptionsByIndex = Realtime::constructSubscriptions(
|
||||
$channelNames,
|
||||
$subscriptions = Realtime::constructSubscriptions(
|
||||
$names,
|
||||
fn ($channel) => $request->getQuery($channel, null)
|
||||
);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $e->getMessage());
|
||||
}
|
||||
|
||||
// Generate subscription IDs and subscribe
|
||||
$subscriptionMapping = [];
|
||||
foreach ($subscriptionsByIndex as $index => $subscription) {
|
||||
$mapping = [];
|
||||
foreach ($subscriptions as $index => $subscription) {
|
||||
$subscriptionId = ID::unique();
|
||||
|
||||
$realtime->subscribe(
|
||||
@@ -647,10 +653,10 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
$subscriptionId,
|
||||
$roles,
|
||||
$subscription['channels'],
|
||||
$subscription['queries'] // Query objects
|
||||
$subscription['queries']
|
||||
);
|
||||
|
||||
$subscriptionMapping[$index] = $subscriptionId;
|
||||
$mapping[$index] = $subscriptionId;
|
||||
}
|
||||
|
||||
$realtime->connections[$connection]['authorization'] = $authorization;
|
||||
@@ -660,8 +666,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
$server->send([$connection], json_encode([
|
||||
'type' => 'connected',
|
||||
'data' => [
|
||||
'channels' => $channelNames,
|
||||
'subscriptions' => $subscriptionMapping,
|
||||
'channels' => $names,
|
||||
'subscriptions' => $mapping,
|
||||
'user' => $user
|
||||
]
|
||||
]));
|
||||
@@ -688,7 +694,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
$message = $th->getMessage();
|
||||
|
||||
// sanitize 0 && 5xx errors
|
||||
if (($code === 0 || $code >= 500) && !App::isDevelopment()) {
|
||||
if (($code === 0 || $code >= 500) && !Http::isDevelopment()) {
|
||||
$message = 'Error: Server Error';
|
||||
}
|
||||
|
||||
@@ -703,7 +709,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
||||
$server->send([$connection], json_encode($response));
|
||||
$server->close($connection, $code);
|
||||
|
||||
if (App::isDevelopment()) {
|
||||
if (Http::isDevelopment()) {
|
||||
Console::error('[Error] Connection Error');
|
||||
Console::error('[Error] Code: ' . $response['data']['code']);
|
||||
Console::error('[Error] Message: ' . $response['data']['message']);
|
||||
@@ -791,32 +797,29 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
||||
}
|
||||
|
||||
$roles = $user->getRoles($database->getAuthorization());
|
||||
$channelNames = $realtime->connections[$connection]['channels'] ?? [];
|
||||
$channels = Realtime::convertChannels(array_flip($channelNames), $user->getId());
|
||||
|
||||
$authorization = $realtime->connections[$connection]['authorization'] ?? null;
|
||||
$projectId = $realtime->connections[$connection]['projectId'] ?? null;
|
||||
|
||||
$subscriptionMetadata = $realtime->getSubscriptionMetadata($connection);
|
||||
$meta = $realtime->getSubscriptionMetadata($connection);
|
||||
|
||||
$realtime->unsubscribe($connection);
|
||||
|
||||
if (!empty($projectId)) {
|
||||
foreach ($subscriptionMetadata as $subscriptionId => $metadata) {
|
||||
$queries = Query::parseQueries($metadata['queries'] ?? []);
|
||||
foreach ($meta as $subscriptionId => $subscription) {
|
||||
$queries = Query::parseQueries($subscription['queries'] ?? []);
|
||||
|
||||
$realtime->subscribe(
|
||||
$projectId,
|
||||
$connection,
|
||||
$subscriptionId,
|
||||
$roles,
|
||||
$metadata['channels'] ?? [],
|
||||
$subscription['channels'] ?? [],
|
||||
$queries
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Restore authorization after subscribe
|
||||
if ($authorization !== null) {
|
||||
$realtime->connections[$connection]['authorization'] = $authorization;
|
||||
}
|
||||
@@ -845,7 +848,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
||||
$message = $th->getMessage();
|
||||
|
||||
// sanitize 0 && 5xx errors
|
||||
if (($code === 0 || $code >= 500) && !App::isDevelopment()) {
|
||||
if (($code === 0 || $code >= 500) && !Http::isDevelopment()) {
|
||||
$message = 'Error: Server Error';
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ $image = $this->getParam('image', '');
|
||||
$enableAssistant = $this->getParam('enableAssistant', false);
|
||||
?>services:
|
||||
traefik:
|
||||
image: traefik:2.11
|
||||
image: traefik:3.6
|
||||
container_name: appwrite-traefik
|
||||
<<: *x-logging
|
||||
command:
|
||||
|
||||
@@ -9,6 +9,7 @@ use Appwrite\Event\Certificate;
|
||||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Execution;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Mail;
|
||||
use Appwrite\Event\Messaging;
|
||||
@@ -358,6 +359,10 @@ Server::setResource('queueForFunctions', function (Publisher $publisher) {
|
||||
return new Func($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForExecutions', function (Publisher $publisher) {
|
||||
return new Execution($publisher);
|
||||
}, ['publisher']);
|
||||
|
||||
Server::setResource('queueForRealtime', function () {
|
||||
return new Realtime();
|
||||
}, []);
|
||||
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
exec php /usr/src/code/app/worker.php executions "$@"
|
||||
+3
-3
@@ -73,8 +73,8 @@
|
||||
"utopia-php/registry": "0.5.*",
|
||||
"utopia-php/storage": "0.18.*",
|
||||
"utopia-php/swoole": "1.*",
|
||||
"utopia-php/system": "0.9.*",
|
||||
"utopia-php/telemetry": "0.1.*",
|
||||
"utopia-php/system": "0.10.*",
|
||||
"utopia-php/telemetry": "0.2.*",
|
||||
"utopia-php/vcs": "1.*",
|
||||
"utopia-php/websocket": "0.3.*",
|
||||
"matomo/device-detector": "6.4.*",
|
||||
@@ -82,7 +82,7 @@
|
||||
"phpmailer/phpmailer": "6.9.*",
|
||||
"chillerlan/php-qrcode": "4.4.*",
|
||||
"adhocore/jwt": "1.1.*",
|
||||
"spomky-labs/otphp": "10.0.*",
|
||||
"spomky-labs/otphp": "^11.4.2",
|
||||
"webonyx/graphql-php": "14.11.*",
|
||||
"league/csv": "9.24.*",
|
||||
"enshrined/svg-sanitize": "0.22.*"
|
||||
|
||||
Generated
+322
-380
File diff suppressed because it is too large
Load Diff
+36
-1
@@ -12,7 +12,7 @@ x-logging: &x-logging
|
||||
|
||||
services:
|
||||
traefik:
|
||||
image: traefik:2.11
|
||||
image: traefik:3.6
|
||||
<<: *x-logging
|
||||
container_name: appwrite-traefik
|
||||
command:
|
||||
@@ -133,6 +133,7 @@ services:
|
||||
- _APP_OPENSSL_KEY_V1
|
||||
- _APP_DOMAIN
|
||||
- _APP_CONSOLE_DOMAIN
|
||||
- _APP_CONSOLE_TRUSTED_PROJECTS
|
||||
- _APP_DOMAIN_TARGET_CNAME
|
||||
- _APP_DOMAIN_TARGET_AAAA
|
||||
- _APP_DOMAIN_TARGET_A
|
||||
@@ -228,6 +229,7 @@ services:
|
||||
- _APP_FUNCTIONS_CREATION_ABUSE_LIMIT
|
||||
- _APP_CUSTOM_DOMAIN_DENY_LIST
|
||||
- _APP_TRUSTED_HEADERS
|
||||
- _APP_MIGRATION_HOST
|
||||
extra_hosts:
|
||||
- "host.docker.internal:host-gateway"
|
||||
|
||||
@@ -510,6 +512,7 @@ services:
|
||||
- _APP_OPTIONS_ROUTER_FORCE_HTTPS
|
||||
- _APP_DOMAIN
|
||||
- _APP_CONSOLE_DOMAIN
|
||||
- _APP_CONSOLE_TRUSTED_PROJECTS
|
||||
- _APP_STORAGE_DEVICE
|
||||
- _APP_STORAGE_S3_ACCESS_KEY
|
||||
- _APP_STORAGE_S3_SECRET
|
||||
@@ -638,6 +641,37 @@ services:
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-worker-executions:
|
||||
entrypoint: worker-executions
|
||||
<<: *x-logging
|
||||
container_name: appwrite-worker-executions
|
||||
image: appwrite-dev
|
||||
networks:
|
||||
- appwrite
|
||||
volumes:
|
||||
- ./app:/usr/src/code/app
|
||||
- ./src:/usr/src/code/src
|
||||
depends_on:
|
||||
redis:
|
||||
condition: service_started
|
||||
mariadb:
|
||||
condition: service_started
|
||||
environment:
|
||||
- _APP_ENV
|
||||
- _APP_WORKER_PER_CORE
|
||||
- _APP_POOL_ADAPTER
|
||||
- _APP_REDIS_HOST
|
||||
- _APP_REDIS_PORT
|
||||
- _APP_REDIS_USER
|
||||
- _APP_REDIS_PASS
|
||||
- _APP_DB_HOST
|
||||
- _APP_DB_PORT
|
||||
- _APP_DB_SCHEMA
|
||||
- _APP_DB_USER
|
||||
- _APP_DB_PASS
|
||||
- _APP_LOGGING_CONFIG
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
|
||||
appwrite-worker-functions:
|
||||
entrypoint: worker-functions
|
||||
<<: *x-logging
|
||||
@@ -818,6 +852,7 @@ services:
|
||||
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
|
||||
- _APP_DATABASE_SHARED_TABLES
|
||||
- _APP_OPTIONS_FORCE_HTTPS
|
||||
- _APP_MIGRATION_HOST
|
||||
|
||||
appwrite-task-maintenance:
|
||||
entrypoint: maintenance
|
||||
|
||||
@@ -1,5 +1,18 @@
|
||||
# Change Log
|
||||
|
||||
## 13.3.1
|
||||
|
||||
- Fix generated TS imports to auto-detect ESM vs non-ESM
|
||||
|
||||
## 13.3.0
|
||||
|
||||
- Support type generation for text/varchar/mediumtext/longtext attributes
|
||||
- Improve CLI session switch and logout UX
|
||||
|
||||
## 13.2.1
|
||||
|
||||
- Fix site domain construction
|
||||
|
||||
## 13.2.0
|
||||
|
||||
- Feat: Add dedicated commands for text-based attribute management:
|
||||
|
||||
@@ -4,9 +4,9 @@ namespace Appwrite\Certificates;
|
||||
|
||||
use Appwrite\Certificates\Exception\CertificateStatus as CertificateStatusException;
|
||||
use Exception;
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Http;
|
||||
use Utopia\Logger\Log;
|
||||
|
||||
class LetsEncrypt implements Adapter
|
||||
@@ -24,7 +24,7 @@ class LetsEncrypt implements Adapter
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
|
||||
$staging = (App::isProduction()) ? '' : ' --dry-run';
|
||||
$staging = (Http::isProduction()) ? '' : ' --dry-run';
|
||||
$exit = Console::execute(
|
||||
"certbot certonly -v --webroot --noninteractive --agree-tos{$staging}"
|
||||
. " --email " . $this->email
|
||||
|
||||
@@ -46,6 +46,9 @@ class Event
|
||||
public const MESSAGING_QUEUE_NAME = 'v1-messaging';
|
||||
public const MESSAGING_CLASS_NAME = 'MessagingV1';
|
||||
|
||||
public const EXECUTIONS_QUEUE_NAME = 'v1-executions';
|
||||
public const EXECUTIONS_CLASS_NAME = 'ExecutionsV1';
|
||||
|
||||
public const MIGRATIONS_QUEUE_NAME = 'v1-migrations';
|
||||
public const MIGRATIONS_CLASS_NAME = 'MigrationsV1';
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Event;
|
||||
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Queue\Publisher;
|
||||
|
||||
class Execution extends Event
|
||||
{
|
||||
protected ?Document $execution = null;
|
||||
|
||||
public function __construct(protected Publisher $publisher)
|
||||
{
|
||||
parent::__construct($publisher);
|
||||
|
||||
$this
|
||||
->setQueue(Event::EXECUTIONS_QUEUE_NAME)
|
||||
->setClass(Event::EXECUTIONS_CLASS_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets execution document for the execution event.
|
||||
*
|
||||
* @param Document $execution
|
||||
* @return self
|
||||
*/
|
||||
public function setExecution(Document $execution): self
|
||||
{
|
||||
$this->execution = $execution;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns set execution document for the execution event.
|
||||
*
|
||||
* @return null|Document
|
||||
*/
|
||||
public function getExecution(): ?Document
|
||||
{
|
||||
return $this->execution;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare payload for the execution event.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function preparePayload(): array
|
||||
{
|
||||
return [
|
||||
'project' => $this->project,
|
||||
'execution' => $this->execution,
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -9,8 +9,6 @@ use Utopia\System\System;
|
||||
|
||||
class Func extends Event
|
||||
{
|
||||
public const TYPE_ASYNC_WRITE = 'async_write';
|
||||
|
||||
protected string $jwt = '';
|
||||
protected string $type = '';
|
||||
protected string $body = '';
|
||||
|
||||
@@ -318,6 +318,7 @@ class Exception extends \Exception
|
||||
|
||||
/** Keys */
|
||||
public const string KEY_NOT_FOUND = 'key_not_found';
|
||||
public const string KEY_ALREADY_EXISTS = 'key_already_exists';
|
||||
|
||||
/** Variables */
|
||||
public const string VARIABLE_NOT_FOUND = 'variable_not_found';
|
||||
|
||||
@@ -6,8 +6,8 @@ use Appwrite\GraphQL\Exception as GQLException;
|
||||
use Appwrite\Promises\Swoole;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Http;
|
||||
use Utopia\Route;
|
||||
use Utopia\System\System;
|
||||
|
||||
@@ -16,17 +16,17 @@ class Resolvers
|
||||
/**
|
||||
* Create a resolver for a given API {@see Route}.
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param ?Route $route
|
||||
* @return callable
|
||||
*/
|
||||
public static function api(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
?Route $route,
|
||||
): callable {
|
||||
return static fn ($type, $args, $context, $info) => new Swoole(
|
||||
function (callable $resolve, callable $reject) use ($utopia, $route, $args, $context, $info) {
|
||||
/** @var App $utopia */
|
||||
/** @var Http $utopia */
|
||||
/** @var Response $response */
|
||||
/** @var Request $request */
|
||||
|
||||
@@ -61,14 +61,14 @@ class Resolvers
|
||||
/**
|
||||
* Create a resolver for a document in a specified database and collection with a specific method type.
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param string $databaseId
|
||||
* @param string $collectionId
|
||||
* @param string $methodType
|
||||
* @return callable
|
||||
*/
|
||||
public static function document(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
string $databaseId,
|
||||
string $collectionId,
|
||||
string $methodType,
|
||||
@@ -83,14 +83,14 @@ class Resolvers
|
||||
/**
|
||||
* Create a resolver for getting a document in a specified database and collection.
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param string $databaseId
|
||||
* @param string $collectionId
|
||||
* @param callable $url
|
||||
* @return callable
|
||||
*/
|
||||
public static function documentGet(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
string $databaseId,
|
||||
string $collectionId,
|
||||
callable $url,
|
||||
@@ -112,7 +112,7 @@ class Resolvers
|
||||
/**
|
||||
* Create a resolver for listing documents in a specified database and collection.
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param string $databaseId
|
||||
* @param string $collectionId
|
||||
* @param callable $url
|
||||
@@ -120,7 +120,7 @@ class Resolvers
|
||||
* @return callable
|
||||
*/
|
||||
public static function documentList(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
string $databaseId,
|
||||
string $collectionId,
|
||||
callable $url,
|
||||
@@ -148,7 +148,7 @@ class Resolvers
|
||||
/**
|
||||
* Create a resolver for creating a document in a specified database and collection.
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param string $databaseId
|
||||
* @param string $collectionId
|
||||
* @param callable $url
|
||||
@@ -156,7 +156,7 @@ class Resolvers
|
||||
* @return callable
|
||||
*/
|
||||
public static function documentCreate(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
string $databaseId,
|
||||
string $collectionId,
|
||||
callable $url,
|
||||
@@ -180,7 +180,7 @@ class Resolvers
|
||||
/**
|
||||
* Create a resolver for updating a document in a specified database and collection.
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param string $databaseId
|
||||
* @param string $collectionId
|
||||
* @param callable $url
|
||||
@@ -188,7 +188,7 @@ class Resolvers
|
||||
* @return callable
|
||||
*/
|
||||
public static function documentUpdate(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
string $databaseId,
|
||||
string $collectionId,
|
||||
callable $url,
|
||||
@@ -212,14 +212,14 @@ class Resolvers
|
||||
/**
|
||||
* Create a resolver for deleting a document in a specified database and collection.
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param string $databaseId
|
||||
* @param string $collectionId
|
||||
* @param callable $url
|
||||
* @return callable
|
||||
*/
|
||||
public static function documentDelete(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
string $databaseId,
|
||||
string $collectionId,
|
||||
callable $url,
|
||||
@@ -239,7 +239,7 @@ class Resolvers
|
||||
}
|
||||
|
||||
/**
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param Request $request
|
||||
* @param Response $response
|
||||
* @param callable $resolve
|
||||
@@ -250,7 +250,7 @@ class Resolvers
|
||||
* @throws Exception
|
||||
*/
|
||||
private static function resolve(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
Request $request,
|
||||
Response $response,
|
||||
callable $resolve,
|
||||
|
||||
@@ -6,8 +6,8 @@ use Appwrite\GraphQL\Types\Mapper;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Schema as GQLSchema;
|
||||
use Utopia\App;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Http;
|
||||
use Utopia\Route;
|
||||
|
||||
class Schema
|
||||
@@ -17,7 +17,7 @@ class Schema
|
||||
|
||||
/**
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param callable $complexity Function to calculate complexity
|
||||
* @param callable $attributes Function to get attributes
|
||||
* @param array $urls Array of functions to get urls for specific method types
|
||||
@@ -26,13 +26,13 @@ class Schema
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function build(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
callable $complexity,
|
||||
callable $attributes,
|
||||
array $urls,
|
||||
array $params,
|
||||
): GQLSchema {
|
||||
App::setResource('utopia:graphql', static function () use ($utopia) {
|
||||
Http::setResource('utopia:graphql', static function () use ($utopia) {
|
||||
return $utopia;
|
||||
});
|
||||
|
||||
@@ -80,12 +80,12 @@ class Schema
|
||||
* This function iterates all API routes and builds a GraphQL
|
||||
* schema defining types and resolvers for all response models.
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param callable $complexity
|
||||
* @return array
|
||||
* @throws Exception
|
||||
*/
|
||||
protected static function api(App $utopia, callable $complexity): array
|
||||
protected static function api(Http $utopia, callable $complexity): array
|
||||
{
|
||||
Mapper::init($utopia
|
||||
->getResource('response')
|
||||
@@ -143,7 +143,7 @@ class Schema
|
||||
* Iterates all of a projects attributes and builds GraphQL
|
||||
* queries and mutations for the collections they make up.
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param callable $complexity
|
||||
* @param callable $attributes
|
||||
* @param array $urls
|
||||
@@ -152,7 +152,7 @@ class Schema
|
||||
* @throws \Exception
|
||||
*/
|
||||
protected static function collections(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
callable $complexity,
|
||||
callable $attributes,
|
||||
array $urls,
|
||||
|
||||
@@ -9,7 +9,7 @@ use Exception;
|
||||
use GraphQL\Type\Definition\ObjectType;
|
||||
use GraphQL\Type\Definition\Type;
|
||||
use GraphQL\Type\Definition\UnionType;
|
||||
use Utopia\App;
|
||||
use Utopia\Http;
|
||||
use Utopia\Route;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\Nullable;
|
||||
@@ -79,7 +79,7 @@ class Mapper
|
||||
}
|
||||
|
||||
public static function route(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
Route $route,
|
||||
Method $method,
|
||||
callable $complexity
|
||||
@@ -245,7 +245,7 @@ class Mapper
|
||||
/**
|
||||
* Map a {@see Route} parameter to a GraphQL Type
|
||||
*
|
||||
* @param App $utopia
|
||||
* @param Http $utopia
|
||||
* @param Validator|callable $validator
|
||||
* @param bool $required
|
||||
* @param array $injections
|
||||
@@ -253,7 +253,7 @@ class Mapper
|
||||
* @throws Exception
|
||||
*/
|
||||
public static function param(
|
||||
App $utopia,
|
||||
Http $utopia,
|
||||
Validator|callable $validator,
|
||||
bool $required,
|
||||
array $injections
|
||||
|
||||
@@ -31,20 +31,29 @@ class Realtime extends MessagingAdapter
|
||||
* [ROLE_X] ->
|
||||
* [CHANNEL_NAME_X] ->
|
||||
* [CONNECTION_ID] ->
|
||||
* [SUB_ID] -> ['strings' => [...], 'parsed' => [...]]
|
||||
* [SUB_ID] -> ['strings' => [...], 'compiled' => [...]]
|
||||
*
|
||||
* Each subscription ID maps to query strings (for metadata) and pre-parsed Query objects (for filtering).
|
||||
* Each subscription ID maps to query strings (for metadata) and pre-compiled query filters.
|
||||
* Within a subscription: AND logic (all queries must match)
|
||||
* Across subscriptions: OR logic (any subscription matching = send event)
|
||||
*/
|
||||
public array $subscriptions = [];
|
||||
|
||||
private PubSubPool $pubSubPool;
|
||||
private ?PubSubPool $pubSubPool = null;
|
||||
|
||||
public function __construct()
|
||||
/**
|
||||
* Get the PubSubPool instance, initializing it lazily if needed.
|
||||
* This allows unit tests to work without requiring the global $register.
|
||||
*
|
||||
* @return PubSubPool
|
||||
*/
|
||||
private function getPubSubPool(): PubSubPool
|
||||
{
|
||||
global $register;
|
||||
$this->pubSubPool = new PubSubPool($register->get('pools')->get('pubsub'));
|
||||
if ($this->pubSubPool === null) {
|
||||
global $register;
|
||||
$this->pubSubPool = new PubSubPool($register->get('pools')->get('pubsub'));
|
||||
}
|
||||
return $this->pubSubPool;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -64,25 +73,18 @@ class Realtime extends MessagingAdapter
|
||||
$this->subscriptions[$projectId] = [];
|
||||
}
|
||||
|
||||
// Convert Query objects to strings and store both for this subscription
|
||||
$queryStrings = [];
|
||||
$parsedQueries = [];
|
||||
$strings = [];
|
||||
if (empty($queryGroup)) {
|
||||
// No queries means "listen to all events" - use select("*")
|
||||
$selectAll = Query::select(['*']);
|
||||
$queryStrings[] = $selectAll->toString();
|
||||
$parsedQueries[] = $selectAll;
|
||||
$strings[] = Query::select(['*'])->toString();
|
||||
} else {
|
||||
foreach ($queryGroup as $query) {
|
||||
/** @var Query $query */
|
||||
$queryStrings[] = $query->toString();
|
||||
$parsedQueries[] = $query;
|
||||
$strings[] = $query->toString();
|
||||
}
|
||||
}
|
||||
|
||||
$subscriptionData = [
|
||||
'strings' => $queryStrings,
|
||||
'parsed' => $parsedQueries,
|
||||
$data = [
|
||||
'strings' => $strings,
|
||||
'compiled' => RuntimeQuery::compile($queryGroup),
|
||||
];
|
||||
|
||||
foreach ($roles as $role) {
|
||||
@@ -97,7 +99,7 @@ class Realtime extends MessagingAdapter
|
||||
if (!isset($this->subscriptions[$projectId][$role][$channel][$identifier])) {
|
||||
$this->subscriptions[$projectId][$role][$channel][$identifier] = [];
|
||||
}
|
||||
$this->subscriptions[$projectId][$role][$channel][$identifier][$subscriptionId] = $subscriptionData;
|
||||
$this->subscriptions[$projectId][$role][$channel][$identifier][$subscriptionId] = $data;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,15 +141,15 @@ class Realtime extends MessagingAdapter
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($this->subscriptions[$projectId][$role][$channel][$connection] as $subId => $subscriptionData) {
|
||||
if (!isset($subscriptions[$subId])) {
|
||||
$subscriptions[$subId] = [
|
||||
foreach ($this->subscriptions[$projectId][$role][$channel][$connection] as $subscriptionId => $data) {
|
||||
if (!isset($subscriptions[$subscriptionId])) {
|
||||
$subscriptions[$subscriptionId] = [
|
||||
'channels' => [],
|
||||
'queries' => $subscriptionData['strings'] ?? []
|
||||
'queries' => $data['strings'] ?? []
|
||||
];
|
||||
}
|
||||
if (!\in_array($channel, $subscriptions[$subId]['channels'])) {
|
||||
$subscriptions[$subId]['channels'][] = $channel;
|
||||
if (!\in_array($channel, $subscriptions[$subscriptionId]['channels'])) {
|
||||
$subscriptions[$subscriptionId]['channels'][] = $channel;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -232,7 +234,7 @@ class Realtime extends MessagingAdapter
|
||||
$permissionsChanged = array_key_exists('permissionsChanged', $options) && $options['permissionsChanged'];
|
||||
$userId = array_key_exists('userId', $options) ? $options['userId'] : null;
|
||||
|
||||
$this->pubSubPool->publish('realtime', json_encode([
|
||||
$this->getPubSubPool()->publish('realtime', json_encode([
|
||||
'project' => $projectId,
|
||||
'roles' => $roles,
|
||||
'permissionsChanged' => $permissionsChanged,
|
||||
@@ -250,12 +252,10 @@ class Realtime extends MessagingAdapter
|
||||
* Identifies the receivers of all subscriptions, based on the permissions and event.
|
||||
*
|
||||
* Example of performance with an event with user:XXX permissions and with X users spread across 10 different channels:
|
||||
* - 0.014 ms (±6.88%) | 10 Connections / 100 Subscriptions
|
||||
* - 0.070 ms (±3.71%) | 100 Connections / 1,000 Subscriptions
|
||||
* - 0.846 ms (±2.74%) | 1,000 Connections / 10,000 Subscriptions
|
||||
* - 10.866 ms (±1.01%) | 10,000 Connections / 100,000 Subscriptions
|
||||
* - 110.201 ms (±2.32%) | 100,000 Connections / 1,000,000 Subscriptions
|
||||
* - 1,121.328 ms (±0.84%) | 1,000,000 Connections / 10,000,000 Subscriptions
|
||||
* - 0.013 ms | 10 Connections / 100 Subscriptions
|
||||
* - 0.14 ms | 100 Connections / 1,000 Subscriptions
|
||||
* - 1.5 ms | 1,000 Connections / 10,000 Subscriptions
|
||||
* - 15 ms | 10,000 Connections / 100,000 Subscriptions
|
||||
*
|
||||
* @param array $event
|
||||
* @return array<int|string, array> Map of connection IDs to matched query groups
|
||||
@@ -263,55 +263,39 @@ class Realtime extends MessagingAdapter
|
||||
public function getSubscribers(array $event): array
|
||||
{
|
||||
$receivers = [];
|
||||
/**
|
||||
* Check if project has subscriber.
|
||||
*/
|
||||
if (isset($this->subscriptions[$event['project']])) {
|
||||
/**
|
||||
* Iterate through each role.
|
||||
*/
|
||||
foreach ($this->subscriptions[$event['project']] as $role => $subscription) {
|
||||
/**
|
||||
* Iterate through each channel.
|
||||
*/
|
||||
foreach ($event['data']['channels'] as $channel) {
|
||||
/**
|
||||
* Check if channel has subscriber. Also taking care of the role in the event and the wildcard role.
|
||||
*/
|
||||
if (
|
||||
\array_key_exists($channel, $this->subscriptions[$event['project']][$role])
|
||||
&& (\in_array($role, $event['roles']) || \in_array(Role::any()->toString(), $event['roles']))
|
||||
) {
|
||||
/**
|
||||
* Saving all connections that are allowed to receive this event.
|
||||
*/
|
||||
$payload = $event['data']['payload'] ?? [];
|
||||
foreach ($this->subscriptions[$event['project']][$role][$channel] as $id => $subscriptions) {
|
||||
$matchedSubscriptions = [];
|
||||
|
||||
// Process each subscription (OR logic across subscriptions)
|
||||
foreach ($subscriptions as $subId => $subscriptionData) {
|
||||
// Use pre-parsed queries instead of re-parsing on every event
|
||||
$parsedQueries = $subscriptionData['parsed'] ?? [];
|
||||
$queryStrings = $subscriptionData['strings'] ?? [];
|
||||
if (!isset($this->subscriptions[$event['project']])) {
|
||||
return $receivers;
|
||||
}
|
||||
|
||||
// Check if this subscription matches (AND logic within subscription)
|
||||
// Or if empty payload and select all as filter will return empty payload out of it even if it passed
|
||||
$isEmptyPayloadAndSelectAll = !empty($parsedQueries) && RuntimeQuery::isSelectAll($parsedQueries[0]) && empty($payload);
|
||||
if ($isEmptyPayloadAndSelectAll || !empty(RuntimeQuery::filter($parsedQueries, $payload))) {
|
||||
$matchedSubscriptions[$subId] = $queryStrings;
|
||||
}
|
||||
}
|
||||
$payload = $event['data']['payload'] ?? [];
|
||||
|
||||
// Only add connection to receivers if at least one subscription matched
|
||||
if (!empty($matchedSubscriptions)) {
|
||||
if (!isset($receivers[$id])) {
|
||||
$receivers[$id] = [];
|
||||
}
|
||||
$receivers[$id] += $matchedSubscriptions;
|
||||
}
|
||||
foreach ($this->subscriptions[$event['project']] as $role => $subscriptionsByChannel) {
|
||||
foreach ($event['data']['channels'] as $channel) {
|
||||
if (
|
||||
!\array_key_exists($channel, $subscriptionsByChannel)
|
||||
|| (!\in_array($role, $event['roles']) && !\in_array(Role::any()->toString(), $event['roles']))
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($subscriptionsByChannel[$channel] as $id => $subscriptions) {
|
||||
$matched = [];
|
||||
|
||||
foreach ($subscriptions as $subscriptionId => $data) {
|
||||
$compiled = $data['compiled'] ?? ['type' => 'selectAll'];
|
||||
$strings = $data['strings'] ?? [];
|
||||
|
||||
if (RuntimeQuery::filter($compiled, $payload) !== null) {
|
||||
$matched[$subscriptionId] = $strings;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if (!empty($matched)) {
|
||||
if (!isset($receivers[$id])) {
|
||||
$receivers[$id] = [];
|
||||
}
|
||||
$receivers[$id] += $matched;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -351,65 +335,50 @@ class Realtime extends MessagingAdapter
|
||||
/**
|
||||
* Constructs subscriptions from query parameters.
|
||||
*
|
||||
* Reconstructs subscription structure from query params where subscription indices can span multiple channels.
|
||||
* Format: {channel}[subscriptionIndex][]=query1&{channel}[subscriptionIndex][]=query2
|
||||
*
|
||||
* Example:
|
||||
* - tests[0][]=select(*) → subscription 0: channels=["tests"]
|
||||
* - tests[1][]=equal(...) & prod[1][]=equal(...) → subscription 1: channels=["tests", "prod"]
|
||||
*
|
||||
* @param array $channelNames Array of channel names
|
||||
* @param callable $getQueryParam Callable that takes a channel name and returns its query param value (null if not present)
|
||||
* @return array Array indexed by subscription index: [index => ['channels' => string[], 'queries' => Query[]]]
|
||||
* @param array $channelNames
|
||||
* @param callable $getQueryParam
|
||||
* @return array [index => ['channels' => string[], 'queries' => Query[]]]
|
||||
* @throws QueryException
|
||||
*/
|
||||
public static function constructSubscriptions(array $channelNames, callable $getQueryParam): array
|
||||
{
|
||||
$subscriptionsByIndex = [];
|
||||
$subscriptions = [];
|
||||
|
||||
foreach ($channelNames as $channel) {
|
||||
$channelSubscriptions = $getQueryParam($channel);
|
||||
$params = $getQueryParam(\str_replace('.', '_', $channel));
|
||||
|
||||
// Backward compatibility: if no channel-specific query params, treat as subscription 0 with select("*")
|
||||
if ($channelSubscriptions === null) {
|
||||
if (!isset($subscriptionsByIndex[0])) {
|
||||
$subscriptionsByIndex[0] = [
|
||||
'channels' => [],
|
||||
'queries' => []
|
||||
];
|
||||
if ($params === null) {
|
||||
if (!isset($subscriptions[0])) {
|
||||
$subscriptions[0] = ['channels' => [], 'queries' => []];
|
||||
}
|
||||
$subscriptionsByIndex[0]['channels'][] = $channel;
|
||||
if (empty($subscriptionsByIndex[0]['queries'])) {
|
||||
$subscriptionsByIndex[0]['queries'] = [Query::select(['*'])];
|
||||
$subscriptions[0]['channels'][] = $channel;
|
||||
if (empty($subscriptions[0]['queries'])) {
|
||||
$subscriptions[0]['queries'] = [Query::select(['*'])];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_array($channelSubscriptions)) {
|
||||
$channelSubscriptions = [$channelSubscriptions];
|
||||
if (!is_array($params)) {
|
||||
$params = [$params];
|
||||
}
|
||||
|
||||
foreach ($channelSubscriptions as $subscriptionIndex => $subscription) {
|
||||
if (!isset($subscriptionsByIndex[$subscriptionIndex])) {
|
||||
$subscriptionsByIndex[$subscriptionIndex] = [
|
||||
'channels' => [],
|
||||
'queries' => []
|
||||
];
|
||||
foreach ($params as $index => $slot) {
|
||||
if (!isset($subscriptions[$index])) {
|
||||
$subscriptions[$index] = ['channels' => [], 'queries' => []];
|
||||
}
|
||||
|
||||
if (!in_array($channel, $subscriptionsByIndex[$subscriptionIndex]['channels'])) {
|
||||
$subscriptionsByIndex[$subscriptionIndex]['channels'][] = $channel;
|
||||
if (!in_array($channel, $subscriptions[$index]['channels'])) {
|
||||
$subscriptions[$index]['channels'][] = $channel;
|
||||
}
|
||||
|
||||
if (empty($subscriptionsByIndex[$subscriptionIndex]['queries'])) {
|
||||
$queriesToParse = is_array($subscription) ? $subscription : [$subscription];
|
||||
$parsedQueries = self::convertQueries($queriesToParse);
|
||||
$subscriptionsByIndex[$subscriptionIndex]['queries'] = $parsedQueries;
|
||||
if (empty($subscriptions[$index]['queries'])) {
|
||||
$raw = is_array($slot) ? $slot : [$slot];
|
||||
$subscriptions[$index]['queries'] = self::convertQueries($raw);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $subscriptionsByIndex;
|
||||
return $subscriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -422,19 +391,18 @@ class Realtime extends MessagingAdapter
|
||||
{
|
||||
$queries = Query::parseQueries($queries);
|
||||
$stack = $queries;
|
||||
$allowedMethods = implode(', ', RuntimeQuery::ALLOWED_QUERIES);
|
||||
$allowed = implode(', ', RuntimeQuery::ALLOWED_QUERIES);
|
||||
|
||||
while (!empty($stack)) {
|
||||
/** @var Query $query */
|
||||
$query = array_pop($stack);
|
||||
$method = $query->getMethod();
|
||||
|
||||
if (!in_array($method, RuntimeQuery::ALLOWED_QUERIES, true)) {
|
||||
$unsupportedMethod = $method;
|
||||
throw new QueryException(
|
||||
"Query method '{$unsupportedMethod}' is not supported in Realtime queries. Allowed query methods are: {$allowedMethods}"
|
||||
"Query method '{$method}' is not supported in Realtime queries. Allowed: {$allowed}"
|
||||
);
|
||||
}
|
||||
|
||||
// Validate select queries - only select("*") is allowed
|
||||
if ($method === Query::TYPE_SELECT) {
|
||||
RuntimeQuery::validateSelectQuery($query);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,27 @@ class Origin extends Validator
|
||||
{
|
||||
}
|
||||
|
||||
public function setAllowedHostnames(array $allowedHostnames): self
|
||||
{
|
||||
$this->allowedHostnames = $allowedHostnames;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setAllowedSchemes(array $allowedSchemes): self
|
||||
{
|
||||
$this->allowedSchemes = $allowedSchemes;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAllowedHostnames(): array
|
||||
{
|
||||
return $this->allowedHostnames;
|
||||
}
|
||||
|
||||
public function getAllowedSchemes(): array
|
||||
{
|
||||
return $this->allowedSchemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Origin is valid.
|
||||
|
||||
@@ -14,6 +14,7 @@ use Appwrite\Platform\Modules\Proxy;
|
||||
use Appwrite\Platform\Modules\Sites;
|
||||
use Appwrite\Platform\Modules\Storage;
|
||||
use Appwrite\Platform\Modules\Tokens;
|
||||
use Appwrite\Platform\Modules\VCS;
|
||||
use Utopia\Platform\Platform;
|
||||
|
||||
class Appwrite extends Platform
|
||||
@@ -32,5 +33,6 @@ class Appwrite extends Platform
|
||||
$this->addModule(new Proxy\Module());
|
||||
$this->addModule(new Tokens\Module());
|
||||
$this->addModule(new Storage\Module());
|
||||
$this->addModule(new VCS\Module());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,24 @@ use Utopia\VCS\Exception\RepositoryNotFound;
|
||||
|
||||
class Base extends Action
|
||||
{
|
||||
/**
|
||||
* Permissions for resources in this project.
|
||||
*
|
||||
* @param string $teamId
|
||||
* @param string $projectId
|
||||
* @return string[]
|
||||
*/
|
||||
protected function getPermissions(string $teamId, string $projectId): array
|
||||
{
|
||||
return [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default specification based on plan and available specifications.
|
||||
*
|
||||
@@ -91,6 +109,12 @@ class Base extends Action
|
||||
} catch (\Throwable $error) {
|
||||
// Ignore; deployment can continue
|
||||
}
|
||||
} else {
|
||||
// Fallback till we have tag support here
|
||||
// Goal is to set providerBranch, so build worker knows what to clone as base
|
||||
// Without this, clone command would be cloning empty branch, and failing
|
||||
$providerBranch = $function->getAttribute('providerBranch', 'main');
|
||||
$branchUrl = "https://github.com/$owner/$repositoryName/tree/$providerBranch";
|
||||
}
|
||||
|
||||
$repositoryUrl = "https://github.com/$owner/$repositoryName";
|
||||
@@ -181,6 +205,12 @@ class Base extends Action
|
||||
} catch (\Throwable $error) {
|
||||
// Ignore; deployment can continue
|
||||
}
|
||||
} else {
|
||||
// Fallback till we have tag support here
|
||||
// Goal is to set providerBranch, so build worker knows what to clone as base
|
||||
// Without this, clone command would be cloning empty branch, and failing
|
||||
$providerBranch = $site->getAttribute('providerBranch', 'main');
|
||||
$branchUrl = "https://github.com/$owner/$repositoryName/tree/$providerBranch";
|
||||
}
|
||||
|
||||
$repositoryUrl = "https://github.com/$owner/$repositoryName";
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Platform\Action as UtopiaAction;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
|
||||
@@ -165,4 +167,15 @@ abstract class Action extends UtopiaAction
|
||||
? Exception::ATTRIBUTE_TYPE_INVALID
|
||||
: Exception::COLUMN_TYPE_INVALID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add row bytes information to a collection/table document.
|
||||
*/
|
||||
protected function addRowBytesInfo(Document $document, Database $dbForProject): Document
|
||||
{
|
||||
$adapter = $dbForProject->getAdapter();
|
||||
$document->setAttribute('bytesMax', $adapter->getDocumentSizeLimit());
|
||||
$document->setAttribute('bytesUsed', $adapter->getAttributeWidth($document));
|
||||
return $document;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -11,12 +11,12 @@ use Appwrite\SDK\Deprecated;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response as UtopiaResponse;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Key;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Http;
|
||||
use Utopia\Swoole\Response as SwooleResponse;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\Boolean;
|
||||
@@ -98,7 +98,7 @@ class Create extends Action
|
||||
array $plan,
|
||||
Authorization $authorization
|
||||
): void {
|
||||
if (!App::isDevelopment() && $encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) {
|
||||
if (!Http::isDevelopment() && $encrypt && !empty($plan) && !($plan['databasesAllowEncrypt'] ?? false)) {
|
||||
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Encrypted string ' . $this->getSDKGroup() . ' are not available on your plan. Please upgrade to create encrypted string ' . $this->getSDKGroup() . '.');
|
||||
}
|
||||
|
||||
|
||||
@@ -248,6 +248,8 @@ class Create extends Action
|
||||
->setParam('databaseId', $databaseId)
|
||||
->setParam($this->getEventsParamKey(), $collection->getId());
|
||||
|
||||
$this->addRowBytesInfo($collection, $dbForProject);
|
||||
|
||||
$response
|
||||
->setStatusCode(SwooleResponse::STATUS_CODE_CREATED)
|
||||
->dynamic($collection, $this->getResponseModel());
|
||||
|
||||
+2
-8
@@ -99,16 +99,10 @@ class XList extends Action
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -75,6 +75,8 @@ class Get extends Action
|
||||
throw new Exception($this->getNotFoundException(), params: [$collectionId]);
|
||||
}
|
||||
|
||||
$this->addRowBytesInfo($collection, $dbForProject);
|
||||
|
||||
$response->dynamic($collection, $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-8
@@ -98,15 +98,10 @@ class XList extends Action
|
||||
Query::equal('collectionId', [$collectionId]),
|
||||
);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -117,6 +117,8 @@ class Update extends Action
|
||||
->setParam('databaseId', $databaseId)
|
||||
->setParam($this->getEventsParamKey(), $collection->getId());
|
||||
|
||||
$this->addRowBytesInfo($collection, $dbForProject);
|
||||
|
||||
$response->dynamic($collection, $this->getResponseModel());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,15 +89,10 @@ class XList extends Action
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor) {
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
@@ -122,6 +117,10 @@ class XList extends Action
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID);
|
||||
}
|
||||
|
||||
foreach ($collections as $collection) {
|
||||
$this->addRowBytesInfo($collection, $dbForProject);
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'total' => $total,
|
||||
$this->getSDKGroup() => $collections,
|
||||
|
||||
@@ -73,16 +73,10 @@ class XList extends Action
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
namespace Appwrite\Platform\Modules\Databases\Http\Init;
|
||||
|
||||
use Appwrite\Utopia\Request;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Http;
|
||||
use Utopia\Platform\Action;
|
||||
|
||||
/**
|
||||
@@ -27,7 +27,7 @@ class Timeout extends Action
|
||||
->callback(function (Request $request, Database $dbForProject) {
|
||||
$timeout = \intval($request->getHeader('x-appwrite-timeout'));
|
||||
|
||||
if (!empty($timeout) && App::isDevelopment()) {
|
||||
if (!empty($timeout) && Http::isDevelopment()) {
|
||||
$dbForProject->setTimeout($timeout);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -95,16 +95,10 @@ class XList extends Base
|
||||
$queries[] = Query::equal('resourceInternalId', [$function->getSequence()]);
|
||||
$queries[] = Query::equal('resourceType', ['functions']);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -301,7 +301,9 @@ class Create extends Base
|
||||
|
||||
if ($async) {
|
||||
if (is_null($scheduledAt)) {
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
$queueForFunctions
|
||||
->setType('http')
|
||||
->setExecution($execution)
|
||||
@@ -342,7 +344,9 @@ class Create extends Base
|
||||
->setAttribute('scheduleInternalId', $schedule->getSequence())
|
||||
->setAttribute('scheduledAt', $scheduledAt);
|
||||
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
}
|
||||
|
||||
$this->enqueueDeletes(
|
||||
@@ -501,7 +505,9 @@ class Create extends Base
|
||||
->addMetric(str_replace(['{resourceType}', '{resourceInternalId}'], [RESOURCE_TYPE_FUNCTIONS, $function->getSequence()], METRIC_RESOURCE_TYPE_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT)))
|
||||
;
|
||||
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
if (System::getEnv('_APP_REGION') !== 'nyc') { // TODO: Remove region check
|
||||
$execution = $authorization->skip(fn () => $dbForProject->createDocument('executions', $execution));
|
||||
}
|
||||
}
|
||||
|
||||
$executionResponse['headers']['x-appwrite-execution-id'] = $execution->getId();
|
||||
|
||||
@@ -91,16 +91,10 @@ class XList extends Base
|
||||
$queries[] = Query::equal('resourceInternalId', [$function->getSequence()]);
|
||||
$queries[] = Query::equal('resourceType', ['functions']);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -267,13 +267,7 @@ class Create extends Base
|
||||
|
||||
$repository = $dbForPlatform->createDocument('repositories', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'$permissions' => $this->getPermissions($teamId, $project->getId()),
|
||||
'installationId' => $installation->getId(),
|
||||
'installationInternalId' => $installation->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
|
||||
@@ -19,8 +19,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Roles;
|
||||
@@ -202,13 +200,7 @@ class Update extends Base
|
||||
|
||||
$repository = $dbForPlatform->createDocument('repositories', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'$permissions' => $this->getPermissions($teamId, $project->getId()),
|
||||
'installationId' => $installation->getId(),
|
||||
'installationInternalId' => $installation->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
|
||||
@@ -78,16 +78,10 @@ class XList extends Base
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -13,8 +13,6 @@ use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
@@ -91,13 +89,7 @@ class Create extends Base
|
||||
$teamId = $project->getAttribute('teamId', '');
|
||||
$variable = new Document([
|
||||
'$id' => $variableId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'$permissions' => $this->getPermissions($teamId, $project->getId()),
|
||||
'resourceInternalId' => $function->getSequence(),
|
||||
'resourceId' => $function->getId(),
|
||||
'resourceType' => 'function',
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Projects\Http\Projects;
|
||||
|
||||
use Appwrite\Platform\Action as AppwriteAction;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
|
||||
class Action extends AppwriteAction
|
||||
{
|
||||
protected function getPermissions(string $teamId, string $projectId): array
|
||||
{
|
||||
return [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Projects\Http\Projects;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Hooks\Hooks;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\ProjectId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Audit\Adapter\Database as AdapterDatabase;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Adapter\Pool as DatabasePool;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\DSN\DSN;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Pools\Group;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\URL;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
class Create extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'createProject';
|
||||
}
|
||||
|
||||
protected function getQueriesValidator(): Validator
|
||||
{
|
||||
return new Projects();
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/projects')
|
||||
->desc('Create project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('audits.event', 'projects.create')
|
||||
->label('audits.resource', 'project/{response.$id}')
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'create',
|
||||
description: '/docs/references/projects/create.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_CREATED,
|
||||
model: Response::MODEL_PROJECT,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('projectId', '', new ProjectId(), 'Unique Id. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, and hyphen. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
|
||||
->param('teamId', '', new UID(), 'Team unique ID.')
|
||||
->param('region', System::getEnv('_APP_REGION', 'default'), new WhiteList(array_keys(array_filter(Config::getParam('regions'), fn ($config) => !$config['disabled']))), 'Project Region.', true)
|
||||
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
|
||||
->param('logo', '', new Text(1024), 'Project logo.', true)
|
||||
->param('url', '', new URL(), 'Project URL.', true)
|
||||
->param('legalName', '', new Text(256), 'Project legal Name. Max length: 256 chars.', true)
|
||||
->param('legalCountry', '', new Text(256), 'Project legal Country. Max length: 256 chars.', true)
|
||||
->param('legalState', '', new Text(256), 'Project legal State. Max length: 256 chars.', true)
|
||||
->param('legalCity', '', new Text(256), 'Project legal City. Max length: 256 chars.', true)
|
||||
->param('legalAddress', '', new Text(256), 'Project legal Address. Max length: 256 chars.', true)
|
||||
->param('legalTaxId', '', new Text(256), 'Project legal Tax ID. Max length: 256 chars.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('cache')
|
||||
->inject('pools')
|
||||
->inject('hooks')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $projectId, string $name, string $teamId, string $region, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Request $request, Response $response, Database $dbForPlatform, Cache $cache, Group $pools, Hooks $hooks)
|
||||
{
|
||||
$team = $dbForPlatform->getDocument('teams', $teamId);
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$allowList = \array_filter(\explode(',', System::getEnv('_APP_PROJECT_REGIONS', '')));
|
||||
|
||||
if (!empty($allowList) && !\in_array($region, $allowList)) {
|
||||
throw new Exception(Exception::PROJECT_REGION_UNSUPPORTED, 'Region "' . $region . '" is not supported');
|
||||
}
|
||||
|
||||
$auth = Config::getParam('auth', []);
|
||||
$auths = [
|
||||
'limit' => 0,
|
||||
'maxSessions' => APP_LIMIT_USER_SESSIONS_DEFAULT,
|
||||
'passwordHistory' => 0,
|
||||
'passwordDictionary' => false,
|
||||
'duration' => TOKEN_EXPIRATION_LOGIN_LONG,
|
||||
'personalDataCheck' => false,
|
||||
'mockNumbers' => [],
|
||||
'sessionAlerts' => false,
|
||||
'membershipsUserName' => false,
|
||||
'membershipsUserEmail' => false,
|
||||
'membershipsMfa' => false,
|
||||
'invalidateSessions' => true
|
||||
];
|
||||
|
||||
foreach ($auth as $method) {
|
||||
$auths[$method['key'] ?? ''] = true;
|
||||
}
|
||||
|
||||
$projectId = ($projectId == 'unique()') ? ID::unique() : $projectId;
|
||||
|
||||
if ($projectId === 'console') {
|
||||
throw new Exception(Exception::PROJECT_RESERVED_PROJECT, "'console' is a reserved project.");
|
||||
}
|
||||
|
||||
$databases = Config::getParam('pools-database', []);
|
||||
|
||||
if ($region !== 'default') {
|
||||
$databaseKeys = System::getEnv('_APP_DATABASE_KEYS', '');
|
||||
$keys = explode(',', $databaseKeys);
|
||||
$databases = array_filter($keys, function ($value) use ($region) {
|
||||
return str_contains($value, $region);
|
||||
});
|
||||
}
|
||||
|
||||
$databaseOverride = System::getEnv('_APP_DATABASE_OVERRIDE');
|
||||
$index = \array_search($databaseOverride, $databases);
|
||||
if ($index !== false) {
|
||||
$dsn = $databases[$index];
|
||||
} else {
|
||||
$dsn = $databases[array_rand($databases)];
|
||||
}
|
||||
|
||||
// TODO: Temporary until all projects are using shared tables.
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
|
||||
if (\in_array($dsn, $sharedTables)) {
|
||||
$schema = 'appwrite';
|
||||
$database = 'appwrite';
|
||||
$namespace = System::getEnv('_APP_DATABASE_SHARED_NAMESPACE', '');
|
||||
$dsn = $schema . '://' . $dsn . '?database=' . $database;
|
||||
|
||||
if (!empty($namespace)) {
|
||||
$dsn .= '&namespace=' . $namespace;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$project = $dbForPlatform->createDocument('projects', new Document([
|
||||
'$id' => $projectId,
|
||||
'$permissions' => $this->getPermissions($teamId, $projectId),
|
||||
'name' => $name,
|
||||
'teamInternalId' => $team->getSequence(),
|
||||
'teamId' => $team->getId(),
|
||||
'region' => $region,
|
||||
'description' => $description,
|
||||
'logo' => $logo,
|
||||
'url' => $url,
|
||||
'version' => APP_VERSION_STABLE,
|
||||
'legalName' => $legalName,
|
||||
'legalCountry' => $legalCountry,
|
||||
'legalState' => $legalState,
|
||||
'legalCity' => $legalCity,
|
||||
'legalAddress' => $legalAddress,
|
||||
'legalTaxId' => ID::custom($legalTaxId),
|
||||
'services' => new \stdClass(),
|
||||
'platforms' => null,
|
||||
'oAuthProviders' => [],
|
||||
'webhooks' => null,
|
||||
'keys' => null,
|
||||
'auths' => $auths,
|
||||
'accessedAt' => DateTime::now(),
|
||||
'search' => implode(' ', [$projectId, $name]),
|
||||
'database' => $dsn,
|
||||
'labels' => [],
|
||||
]));
|
||||
} catch (Duplicate) {
|
||||
throw new Exception(Exception::PROJECT_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
try {
|
||||
$dsn = new DSN($dsn);
|
||||
} catch (\InvalidArgumentException) {
|
||||
// TODO: Temporary until all projects are using shared tables
|
||||
$dsn = new DSN('mysql://' . $dsn);
|
||||
}
|
||||
|
||||
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
|
||||
$sharedTablesV1 = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES_V1', ''));
|
||||
$projectTables = !\in_array($dsn->getHost(), $sharedTables);
|
||||
$sharedTablesV1 = \in_array($dsn->getHost(), $sharedTablesV1);
|
||||
$sharedTablesV2 = !$projectTables && !$sharedTablesV1;
|
||||
$sharedTables = $sharedTablesV1 || $sharedTablesV2;
|
||||
|
||||
if (!$sharedTablesV2) {
|
||||
$adapter = new DatabasePool($pools->get($dsn->getHost()));
|
||||
$dbForProject = new Database($adapter, $cache);
|
||||
$dbForProject->setDatabase(APP_DATABASE);
|
||||
|
||||
if ($sharedTables) {
|
||||
$dbForProject
|
||||
->setSharedTables(true)
|
||||
->setTenant($sharedTablesV1 ? (int)$project->getSequence() : null)
|
||||
->setNamespace($dsn->getParam('namespace'));
|
||||
} else {
|
||||
$dbForProject
|
||||
->setSharedTables(false)
|
||||
->setTenant(null)
|
||||
->setNamespace('_' . $project->getSequence());
|
||||
}
|
||||
|
||||
$create = true;
|
||||
|
||||
try {
|
||||
$dbForProject->create();
|
||||
} catch (Duplicate) {
|
||||
$create = false;
|
||||
}
|
||||
|
||||
if ($create || $projectTables) {
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
$audit = new Audit($adapter);
|
||||
$audit->setup();
|
||||
}
|
||||
|
||||
if (!$create && $sharedTablesV1) {
|
||||
$adapter = new AdapterDatabase($dbForProject);
|
||||
$attributes = $adapter->getAttributeDocuments();
|
||||
$indexes = $adapter->getIndexDocuments();
|
||||
$dbForProject->createDocument(Database::METADATA, new Document([
|
||||
'$id' => ID::custom('audit'),
|
||||
'$permissions' => [Permission::create(Role::any())],
|
||||
'name' => 'audit',
|
||||
'attributes' => $attributes,
|
||||
'indexes' => $indexes,
|
||||
'documentSecurity' => true
|
||||
]));
|
||||
}
|
||||
|
||||
if ($create || $sharedTablesV1) {
|
||||
/** @var array $collections */
|
||||
$collections = Config::getParam('collections', [])['projects'] ?? [];
|
||||
|
||||
foreach ($collections as $key => $collection) {
|
||||
if (($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$attributes = \array_map(fn ($attribute) => new Document($attribute), $collection['attributes']);
|
||||
$indexes = \array_map(fn (array $index) => new Document($index), $collection['indexes']);
|
||||
|
||||
try {
|
||||
$dbForProject->createCollection($key, $attributes, $indexes);
|
||||
} catch (Duplicate) {
|
||||
$dbForProject->createDocument(Database::METADATA, new Document([
|
||||
'$id' => ID::custom($key),
|
||||
'$permissions' => [Permission::create(Role::any())],
|
||||
'name' => $key,
|
||||
'attributes' => $attributes,
|
||||
'indexes' => $indexes,
|
||||
'documentSecurity' => true
|
||||
]));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Hook allowing instant project mirroring during migration
|
||||
// Outside of migration, hook is not registered and has no effect
|
||||
$hooks->trigger('afterProjectCreation', [$project, $pools, $cache]);
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
->dynamic($project, Response::MODEL_PROJECT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Projects\Http\Projects\Team;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Projects\Http\Projects\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator;
|
||||
|
||||
class Update extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'updateProjectTeam';
|
||||
}
|
||||
|
||||
protected function getQueriesValidator(): Validator
|
||||
{
|
||||
return new Projects();
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/projects/:projectId/team')
|
||||
->desc('Update project team')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'updateTeam',
|
||||
description: '/docs/references/projects/update-team.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROJECT,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('teamId', '', new UID(), 'Team ID of the team to transfer project to.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $projectId, string $teamId, Response $response, Database $dbForPlatform)
|
||||
{
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
$team = $dbForPlatform->getDocument('teams', $teamId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($team->isEmpty()) {
|
||||
throw new Exception(Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$permissions = $this->getPermissions($teamId, $projectId);
|
||||
|
||||
$project
|
||||
->setAttribute('teamId', $teamId)
|
||||
->setAttribute('teamInternalId', $team->getSequence())
|
||||
->setAttribute('$permissions', $permissions);
|
||||
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project);
|
||||
|
||||
$installations = $dbForPlatform->find('installations', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($installations as $installation) {
|
||||
$installation->setAttribute('$permissions', $permissions);
|
||||
$dbForPlatform->updateDocument('installations', $installation->getId(), $installation);
|
||||
}
|
||||
|
||||
$repositories = $dbForPlatform->find('repositories', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($repositories as $repository) {
|
||||
$repository->setAttribute('$permissions', $permissions);
|
||||
$dbForPlatform->updateDocument('repositories', $repository->getId(), $repository);
|
||||
}
|
||||
|
||||
$vcsComments = $dbForPlatform->find('vcsComments', [
|
||||
Query::equal('projectInternalId', [$project->getSequence()]),
|
||||
]);
|
||||
foreach ($vcsComments as $vcsComment) {
|
||||
$vcsComment->setAttribute('$permissions', $permissions);
|
||||
$dbForPlatform->updateDocument('vcsComments', $vcsComment->getId(), $vcsComment);
|
||||
}
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Projects\Http\Projects;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Projects;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\URL;
|
||||
|
||||
class Update extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'updateProject';
|
||||
}
|
||||
|
||||
protected function getQueriesValidator(): Validator
|
||||
{
|
||||
return new Projects();
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_PATCH)
|
||||
->setHttpPath('/v1/projects/:projectId')
|
||||
->desc('Update project')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'projects.write')
|
||||
->label('audits.event', 'projects.update')
|
||||
->label('audits.resource', 'project/{request.projectId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'projects',
|
||||
group: 'projects',
|
||||
name: 'update',
|
||||
description: '/docs/references/projects/update.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROJECT,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('name', null, new Text(128), 'Project name. Max length: 128 chars.')
|
||||
->param('description', '', new Text(256), 'Project description. Max length: 256 chars.', true)
|
||||
->param('logo', '', new Text(1024), 'Project logo.', true)
|
||||
->param('url', '', new URL(), 'Project URL.', true)
|
||||
->param('legalName', '', new Text(256), 'Project legal name. Max length: 256 chars.', true)
|
||||
->param('legalCountry', '', new Text(256), 'Project legal country. Max length: 256 chars.', true)
|
||||
->param('legalState', '', new Text(256), 'Project legal state. Max length: 256 chars.', true)
|
||||
->param('legalCity', '', new Text(256), 'Project legal city. Max length: 256 chars.', true)
|
||||
->param('legalAddress', '', new Text(256), 'Project legal address. Max length: 256 chars.', true)
|
||||
->param('legalTaxId', '', new Text(256), 'Project legal tax ID. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(string $projectId, string $name, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForPlatform)
|
||||
{
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('description', $description)
|
||||
->setAttribute('logo', $logo)
|
||||
->setAttribute('url', $url)
|
||||
->setAttribute('legalName', $legalName)
|
||||
->setAttribute('legalCountry', $legalCountry)
|
||||
->setAttribute('legalState', $legalState)
|
||||
->setAttribute('legalCity', $legalCity)
|
||||
->setAttribute('legalAddress', $legalAddress)
|
||||
->setAttribute('legalTaxId', $legalTaxId)
|
||||
->setAttribute('search', implode(' ', [$projectId, $name])));
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
}
|
||||
}
|
||||
@@ -86,16 +86,10 @@ class XList extends Action
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -7,7 +7,10 @@ use Appwrite\Platform\Modules\Projects\Http\DevKeys\Delete as DeleteDevKey;
|
||||
use Appwrite\Platform\Modules\Projects\Http\DevKeys\Get as GetDevKey;
|
||||
use Appwrite\Platform\Modules\Projects\Http\DevKeys\Update as UpdateDevKey;
|
||||
use Appwrite\Platform\Modules\Projects\Http\DevKeys\XList as ListDevKeys;
|
||||
use Appwrite\Platform\Modules\Projects\Http\Projects\Create as CreateProject;
|
||||
use Appwrite\Platform\Modules\Projects\Http\Projects\Labels\Update as UpdateProjectLabels;
|
||||
use Appwrite\Platform\Modules\Projects\Http\Projects\Team\Update as UpdateProjectTeam;
|
||||
use Appwrite\Platform\Modules\Projects\Http\Projects\Update as UpdateProject;
|
||||
use Appwrite\Platform\Modules\Projects\Http\Projects\XList as ListProjects;
|
||||
use Utopia\Platform\Service;
|
||||
|
||||
@@ -22,7 +25,10 @@ class Http extends Service
|
||||
$this->addAction(ListDevKeys::getName(), new ListDevKeys());
|
||||
$this->addAction(DeleteDevKey::getName(), new DeleteDevKey());
|
||||
|
||||
$this->addAction(CreateProject::getName(), new CreateProject());
|
||||
$this->addAction(UpdateProject::getName(), new UpdateProject());
|
||||
$this->addAction(ListProjects::getName(), new ListProjects());
|
||||
$this->addAction(UpdateProjectLabels::getName(), new UpdateProjectLabels());
|
||||
$this->addAction(UpdateProjectTeam::getName(), new UpdateProjectTeam());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,16 +81,10 @@ class XList extends Action
|
||||
|
||||
$queries[] = Query::equal('projectInternalId', [$project->getSequence()]);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -95,16 +95,10 @@ class XList extends Base
|
||||
$queries[] = Query::equal('resourceInternalId', [$site->getSequence()]);
|
||||
$queries[] = Query::equal('resourceType', ['sites']);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -80,16 +80,10 @@ class XList extends Base
|
||||
$queries[] = Query::equal('resourceInternalId', [$site->getSequence()]);
|
||||
$queries[] = Query::equal('resourceType', ['sites']);
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -15,8 +15,6 @@ use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
@@ -176,13 +174,7 @@ class Create extends Base
|
||||
|
||||
$repository = $dbForPlatform->createDocument('repositories', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'$permissions' => $this->getPermissions($teamId, $project->getId()),
|
||||
'installationId' => $installation->getId(),
|
||||
'installationInternalId' => $installation->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
|
||||
@@ -16,8 +16,6 @@ use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
@@ -198,13 +196,7 @@ class Update extends Base
|
||||
|
||||
$repository = $dbForPlatform->createDocument('repositories', new Document([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'$permissions' => $this->getPermissions($teamId, $project->getId()),
|
||||
'installationId' => $installation->getId(),
|
||||
'installationInternalId' => $installation->getSequence(),
|
||||
'projectId' => $project->getId(),
|
||||
|
||||
@@ -73,16 +73,10 @@ class XList extends Base
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -12,8 +12,6 @@ use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
@@ -78,13 +76,7 @@ class Create extends Base
|
||||
$teamId = $project->getAttribute('teamId', '');
|
||||
$variable = new Document([
|
||||
'$id' => $variableId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'$permissions' => $this->getPermissions($teamId, $project->getId()),
|
||||
'resourceInternalId' => $site->getSequence(),
|
||||
'resourceId' => $site->getId(),
|
||||
'resourceType' => 'site',
|
||||
|
||||
@@ -101,16 +101,10 @@ class XList extends Action
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -82,16 +82,10 @@ class XList extends Action
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
|
||||
@@ -2,18 +2,18 @@
|
||||
|
||||
namespace Appwrite\Platform\Modules\Tokens\Http\Tokens\Buckets\Files;
|
||||
|
||||
use Appwrite\Extend\Exception as ExtendException;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\FileTokens;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Exception;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Boolean;
|
||||
@@ -69,18 +69,21 @@ class XList extends Action
|
||||
$queries = Query::parseQueries($queries);
|
||||
$queries[] = Query::equal('resourceType', [TOKENS_RESOURCE_TYPE_FILES]);
|
||||
$queries[] = Query::equal('resourceInternalId', [$bucket->getSequence() . ':' . $file->getSequence()]);
|
||||
// Get cursor document if there was a cursor query
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$cursor = Query::getCursorQueries($queries, false);
|
||||
$cursor = \reset($cursor);
|
||||
|
||||
if ($cursor !== false) {
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$tokenId = $cursor->getValue();
|
||||
$cursorDocument = $dbForProject->getDocument('resourceTokens', $tokenId);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(ExtendException::GENERAL_CURSOR_NOT_FOUND, "File token '{$tokenId}' for the 'cursor' value not found.");
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "File token '{$tokenId}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\GitHub\Authorize;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\MethodType;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'getVCSGitHubAuthorize';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/vcs/github/authorize')
|
||||
->desc('Create GitHub app installation')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('error', APP_VIEWS_DIR . '/general/error.phtml')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'createGitHubInstallation',
|
||||
description: '/docs/references/vcs/create-github-installation.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_MOVED_PERMANENTLY,
|
||||
model: Response::MODEL_NONE,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::HTML,
|
||||
type: MethodType::WEBAUTH,
|
||||
hide: true,
|
||||
))
|
||||
->param('success', '', fn ($redirectValidator) => $redirectValidator, 'URL to redirect back to console after a successful installation attempt.', true, ['redirectValidator'])
|
||||
->param('failure', '', fn ($redirectValidator) => $redirectValidator, 'URL to redirect back to console after a failed installation attempt.', true, ['redirectValidator'])
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('platform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $success,
|
||||
string $failure,
|
||||
Response $response,
|
||||
Document $project,
|
||||
array $platform
|
||||
) {
|
||||
$state = \json_encode([
|
||||
'projectId' => $project->getId(),
|
||||
'success' => $success,
|
||||
'failure' => $failure,
|
||||
]);
|
||||
|
||||
$appName = System::getEnv('_APP_VCS_GITHUB_APP_NAME');
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = $platform['consoleHostname'] ?? '';
|
||||
|
||||
if (empty($appName)) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'GitHub App name is not configured. Please configure VCS (Version Control System) variables in .env file.');
|
||||
}
|
||||
|
||||
$url = "https://github.com/apps/$appName/installations/new?" . \http_build_query([
|
||||
'state' => $state,
|
||||
'redirect_uri' => $protocol . '://' . $hostname . "/v1/vcs/github/callback"
|
||||
]);
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($url);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\GitHub\Callback;
|
||||
|
||||
use Appwrite\Auth\OAuth2\Github as OAuth2Github;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'getVCSGitHubCallback';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/vcs/github/callback')
|
||||
->desc('Get installation and authorization from GitHub app')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'public')
|
||||
->label('error', APP_VIEWS_DIR . '/general/error.phtml')
|
||||
->param('installation_id', '', new Text(256, 0), 'GitHub installation ID', true)
|
||||
->param('setup_action', '', new Text(256, 0), 'GitHub setup action type', true)
|
||||
->param('state', '', new Text(2048), 'GitHub state. Contains info sent when starting authorization flow.', true)
|
||||
->param('code', '', new Text(2048, 0), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true)
|
||||
->inject('gitHub')
|
||||
->inject('project')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('platform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $providerInstallationId,
|
||||
string $setupAction,
|
||||
string $state,
|
||||
string $code,
|
||||
GitHub $github,
|
||||
Document $project,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
array $platform
|
||||
) {
|
||||
if (empty($state)) {
|
||||
$error = 'Installation requests from organisation members for the Appwrite GitHub App are currently unsupported. To proceed with the installation, login to the Appwrite Console and install the GitHub App.';
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $error);
|
||||
}
|
||||
|
||||
$state = \json_decode($state, true);
|
||||
$projectId = $state['projectId'] ?? '';
|
||||
|
||||
$project = $dbForPlatform->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
$error = 'Project with the ID from state could not be found.';
|
||||
|
||||
if (!empty($redirectFailure)) {
|
||||
$separator = \str_contains($redirectFailure, '?') ? '&' : ':';
|
||||
return $response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($redirectFailure . $separator . \http_build_query(['error' => $error]));
|
||||
}
|
||||
|
||||
throw new Exception(Exception::PROJECT_NOT_FOUND, $error);
|
||||
}
|
||||
|
||||
$region = $project->getAttribute('region', 'default');
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
|
||||
$hostname = $platform['consoleHostname'] ?? '';
|
||||
|
||||
$defaultState = [
|
||||
'success' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",
|
||||
'failure' => $protocol . '://' . $hostname . "/console/project-$region-$projectId/settings/git-installations",
|
||||
];
|
||||
|
||||
$state = \array_merge($defaultState, $state ?? []);
|
||||
|
||||
$redirectSuccess = $state['success'] ?? '';
|
||||
$redirectFailure = $state['failure'] ?? '';
|
||||
|
||||
// Create / Update installation
|
||||
if (!empty($providerInstallationId)) {
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
$owner = $github->getOwnerName($providerInstallationId) ?? '';
|
||||
|
||||
$projectInternalId = $project->getSequence();
|
||||
|
||||
$installation = $dbForPlatform->findOne('installations', [
|
||||
Query::equal('providerInstallationId', [$providerInstallationId]),
|
||||
Query::equal('projectInternalId', [$projectInternalId])
|
||||
]);
|
||||
|
||||
$personal = false;
|
||||
$refreshToken = null;
|
||||
$accessToken = null;
|
||||
$accessTokenExpiry = null;
|
||||
|
||||
if (!empty($code)) {
|
||||
$oauth2 = new OAuth2Github(System::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), System::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), "");
|
||||
|
||||
$accessToken = $oauth2->getAccessToken($code) ?? '';
|
||||
$refreshToken = $oauth2->getRefreshToken($code) ?? '';
|
||||
$accessTokenExpiry = DateTime::addSeconds(new \DateTime(), \intval($oauth2->getAccessTokenExpiry($code)));
|
||||
|
||||
$personalSlug = $oauth2->getUserSlug($accessToken) ?? '';
|
||||
$personal = $personalSlug === $owner;
|
||||
}
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
$teamId = $project->getAttribute('teamId', '');
|
||||
|
||||
$installation = new Document([
|
||||
'$id' => ID::unique(),
|
||||
'$permissions' => [
|
||||
Permission::read(Role::team(ID::custom($teamId))),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::update(Role::team(ID::custom($teamId), 'developer')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
|
||||
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
|
||||
],
|
||||
'providerInstallationId' => $providerInstallationId,
|
||||
'projectId' => $projectId,
|
||||
'projectInternalId' => $projectInternalId,
|
||||
'provider' => 'github',
|
||||
'organization' => $owner,
|
||||
'personal' => $personal,
|
||||
'personalRefreshToken' => $refreshToken,
|
||||
'personalAccessToken' => $accessToken,
|
||||
'personalAccessTokenExpiry' => $accessTokenExpiry,
|
||||
]);
|
||||
|
||||
$installation = $dbForPlatform->createDocument('installations', $installation);
|
||||
} else {
|
||||
$installation = $installation
|
||||
->setAttribute('organization', $owner)
|
||||
->setAttribute('personal', $personal)
|
||||
->setAttribute('personalRefreshToken', $refreshToken)
|
||||
->setAttribute('personalAccessToken', $accessToken)
|
||||
->setAttribute('personalAccessTokenExpiry', $accessTokenExpiry);
|
||||
$installation = $dbForPlatform->updateDocument('installations', $installation->getId(), $installation);
|
||||
}
|
||||
} else {
|
||||
$error = 'Installation of the Appwrite GitHub App on organization accounts is restricted to organization owners. As a member of the organization, you do not have the necessary permissions to install this GitHub App. Please contact the organization owner to create the installation from the Appwrite console.';
|
||||
|
||||
if (!empty($redirectFailure)) {
|
||||
$separator = \str_contains($redirectFailure, '?') ? '&' : ':';
|
||||
return $response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($redirectFailure . $separator . \http_build_query(['error' => $error]));
|
||||
}
|
||||
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $error);
|
||||
}
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
->addHeader('Pragma', 'no-cache')
|
||||
->redirect($redirectSuccess);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\Installations;
|
||||
|
||||
use Appwrite\Event\Delete as DeleteEvent;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Delete extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'deleteInstallation';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_DELETE)
|
||||
->setHttpPath('/v1/vcs/installations/:installationId')
|
||||
->desc('Delete installation')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.write')
|
||||
->label('resourceType', RESOURCE_TYPE_VCS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'deleteInstallation',
|
||||
description: '/docs/references/vcs/delete-installation.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_NOCONTENT,
|
||||
model: Response::MODEL_NONE,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::NONE
|
||||
))
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->inject('queueForDeletes')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $installationId,
|
||||
Response $response,
|
||||
Database $dbForPlatform,
|
||||
DeleteEvent $queueForDeletes
|
||||
) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!$dbForPlatform->deleteDocument('installations', $installation->getId())) {
|
||||
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove installation from DB');
|
||||
}
|
||||
|
||||
$queueForDeletes
|
||||
->setType(DELETE_TYPE_DOCUMENT)
|
||||
->setDocument($installation);
|
||||
|
||||
$response->noContent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\Installations;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'getInstallation';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/vcs/installations/:installationId')
|
||||
->desc('Get installation')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('resourceType', RESOURCE_TYPE_VCS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'getInstallation',
|
||||
description: '/docs/references/vcs/get-installation.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_INSTALLATION,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $installationId,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation === false || $installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($installation->getAttribute('projectInternalId') !== $project->getSequence()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$response->dynamic($installation, Response::MODEL_INSTALLATION);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\Installations\Repositories\Branches;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
use Utopia\VCS\Exception\RepositoryNotFound;
|
||||
|
||||
class XList extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'listRepositoryBranches';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/branches')
|
||||
->desc('List repository branches')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('resourceType', RESOURCE_TYPE_VCS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'listRepositoryBranches',
|
||||
description: '/docs/references/vcs/list-repository-branches.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_BRANCH_LIST,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('providerRepositoryId', '', new Text(256), 'Repository Id')
|
||||
->inject('gitHub')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $installationId,
|
||||
string $providerRepositoryId,
|
||||
GitHub $github,
|
||||
Response $response,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$providerInstallationId = $installation->getAttribute('providerInstallationId');
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
|
||||
$owner = $github->getOwnerName($providerInstallationId) ?? '';
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
if (empty($repositoryName)) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
} catch (RepositoryNotFound $e) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
|
||||
$branches = $github->listBranches($owner, $repositoryName) ?? [];
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'branches' => \array_map(function ($branch) {
|
||||
return new Document(['name' => $branch]);
|
||||
}, $branches),
|
||||
'total' => \count($branches),
|
||||
]), Response::MODEL_BRANCH_LIST);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\Installations\Repositories\Contents;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
use Utopia\VCS\Exception\RepositoryNotFound;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'getRepositoryContents';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/contents')
|
||||
->desc('Get files and directories of a VCS repository')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('resourceType', RESOURCE_TYPE_VCS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'getRepositoryContents',
|
||||
description: '/docs/references/vcs/get-repository-contents.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_VCS_CONTENT_LIST,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('providerRepositoryId', '', new Text(256), 'Repository Id')
|
||||
->param('providerRootDirectory', '', new Text(256, 0), 'Path to get contents of nested directory', true)
|
||||
->param('providerReference', '', new Text(256, 0), 'Git reference (branch, tag, commit) to get contents from', true)
|
||||
->inject('gitHub')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $installationId,
|
||||
string $providerRepositoryId,
|
||||
string $providerRootDirectory,
|
||||
string $providerReference,
|
||||
GitHub $github,
|
||||
Response $response,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$providerInstallationId = $installation->getAttribute('providerInstallationId');
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
if (empty($repositoryName)) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
} catch (RepositoryNotFound $e) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
|
||||
$contents = $github->listRepositoryContents($owner, $repositoryName, $providerRootDirectory, $providerReference);
|
||||
|
||||
$vcsContents = [];
|
||||
foreach ($contents as $content) {
|
||||
$isDirectory = false;
|
||||
if ($content['type'] === GitHub::CONTENTS_DIRECTORY) {
|
||||
$isDirectory = true;
|
||||
}
|
||||
|
||||
$vcsContents[] = new Document([
|
||||
'isDirectory' => $isDirectory,
|
||||
'name' => $content['name'] ?? '',
|
||||
'size' => $content['size'] ?? 0
|
||||
]);
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'contents' => $vcsContents
|
||||
]), Response::MODEL_VCS_CONTENT_LIST);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\Installations\Repositories;
|
||||
|
||||
use Appwrite\Auth\OAuth2\Github as OAuth2Github;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
class Create extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'createRepository';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
->desc('Create repository')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.write')
|
||||
->label('resourceType', RESOURCE_TYPE_VCS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'createRepository',
|
||||
description: '/docs/references/vcs/create-repository.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROVIDER_REPOSITORY,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('name', '', new Text(256), 'Repository name (slug)')
|
||||
->param('private', '', new Boolean(false), 'Mark repository public or private')
|
||||
->inject('gitHub')
|
||||
->inject('user')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $installationId,
|
||||
string $name,
|
||||
bool $private,
|
||||
GitHub $github,
|
||||
Document $user,
|
||||
Response $response,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($installation->getAttribute('personal', false) === true) {
|
||||
$oauth2 = new OAuth2Github(System::getEnv('_APP_VCS_GITHUB_CLIENT_ID', ''), System::getEnv('_APP_VCS_GITHUB_CLIENT_SECRET', ''), "");
|
||||
|
||||
$accessToken = $installation->getAttribute('personalAccessToken');
|
||||
$refreshToken = $installation->getAttribute('personalRefreshToken');
|
||||
$accessTokenExpiry = $installation->getAttribute('personalAccessTokenExpiry');
|
||||
|
||||
if (empty($accessToken) || empty($refreshToken) || empty($accessTokenExpiry)) {
|
||||
$identity = $dbForPlatform->findOne('identities', [
|
||||
Query::equal('provider', ['github']),
|
||||
Query::equal('userInternalId', [$user->getSequence()]),
|
||||
]);
|
||||
if ($identity->isEmpty()) {
|
||||
throw new Exception(Exception::USER_IDENTITY_NOT_FOUND);
|
||||
}
|
||||
|
||||
$accessToken = $accessToken ?? $identity->getAttribute('providerAccessToken');
|
||||
$refreshToken = $refreshToken ?? $identity->getAttribute('providerRefreshToken');
|
||||
$accessTokenExpiry = $accessTokenExpiry ?? $identity->getAttribute('providerAccessTokenExpiry');
|
||||
}
|
||||
|
||||
$isExpired = new \DateTime($accessTokenExpiry) < new \DateTime('now');
|
||||
if ($isExpired) {
|
||||
$oauth2->refreshTokens($refreshToken);
|
||||
|
||||
$accessToken = $oauth2->getAccessToken('');
|
||||
$refreshToken = $oauth2->getRefreshToken('');
|
||||
|
||||
$verificationId = $oauth2->getUserID($accessToken);
|
||||
|
||||
if (empty($verificationId)) {
|
||||
throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED, "Another request is currently refreshing OAuth token. Please try again.");
|
||||
}
|
||||
|
||||
$installation = $installation
|
||||
->setAttribute('personalAccessToken', $accessToken)
|
||||
->setAttribute('personalRefreshToken', $refreshToken)
|
||||
->setAttribute('personalAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry('')));
|
||||
|
||||
$dbForPlatform->updateDocument('installations', $installation->getId(), $installation);
|
||||
}
|
||||
|
||||
try {
|
||||
$repository = $oauth2->createRepository($accessToken, $name, $private);
|
||||
} catch (Exception $exception) {
|
||||
throw new Exception(Exception::GENERAL_PROVIDER_FAILURE, "GitHub failed to process the request: " . $exception->getMessage());
|
||||
}
|
||||
} else {
|
||||
$providerInstallationId = $installation->getAttribute('providerInstallationId');
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
|
||||
try {
|
||||
$repository = $github->createRepository($owner, $name, $private);
|
||||
} catch (Exception $exception) {
|
||||
throw new Exception(Exception::GENERAL_PROVIDER_FAILURE, "GitHub failed to process the request: " . $exception->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($repository['errors'])) {
|
||||
$message = $repository['message'] ?? 'Unknown error.';
|
||||
if (isset($repository['errors'][0])) {
|
||||
$message .= ' ' . $repository['errors'][0]['message'];
|
||||
}
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Provider Error: ' . $message);
|
||||
}
|
||||
|
||||
if (isset($repository['message'])) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Provider Error: ' . $repository['message']);
|
||||
}
|
||||
|
||||
$repository['id'] = \strval($repository['id']) ?? '';
|
||||
$repository['pushedAt'] = $repository['pushed_at'] ?? '';
|
||||
$repository['organization'] = $installation->getAttribute('organization', '');
|
||||
$repository['provider'] = $installation->getAttribute('provider', '');
|
||||
|
||||
$response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY);
|
||||
}
|
||||
}
|
||||
+315
@@ -0,0 +1,315 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\Installations\Repositories\Detections;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Coroutine\WaitGroup;
|
||||
use Utopia\Config\Adapters\Dotenv as ConfigDotenv;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Config\Exceptions\Parse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Detector\Detection\Framework\Analog;
|
||||
use Utopia\Detector\Detection\Framework\Angular;
|
||||
use Utopia\Detector\Detection\Framework\Astro;
|
||||
use Utopia\Detector\Detection\Framework\Flutter;
|
||||
use Utopia\Detector\Detection\Framework\Lynx;
|
||||
use Utopia\Detector\Detection\Framework\NextJs;
|
||||
use Utopia\Detector\Detection\Framework\Nuxt;
|
||||
use Utopia\Detector\Detection\Framework\React;
|
||||
use Utopia\Detector\Detection\Framework\ReactNative;
|
||||
use Utopia\Detector\Detection\Framework\Remix;
|
||||
use Utopia\Detector\Detection\Framework\Svelte;
|
||||
use Utopia\Detector\Detection\Framework\SvelteKit;
|
||||
use Utopia\Detector\Detection\Framework\TanStackStart;
|
||||
use Utopia\Detector\Detection\Framework\Vue;
|
||||
use Utopia\Detector\Detection\Packager\NPM;
|
||||
use Utopia\Detector\Detection\Packager\PNPM;
|
||||
use Utopia\Detector\Detection\Packager\Yarn;
|
||||
use Utopia\Detector\Detection\Runtime\Bun;
|
||||
use Utopia\Detector\Detection\Runtime\CPP;
|
||||
use Utopia\Detector\Detection\Runtime\Dart;
|
||||
use Utopia\Detector\Detection\Runtime\Deno;
|
||||
use Utopia\Detector\Detection\Runtime\Dotnet;
|
||||
use Utopia\Detector\Detection\Runtime\Java;
|
||||
use Utopia\Detector\Detection\Runtime\Node;
|
||||
use Utopia\Detector\Detection\Runtime\PHP;
|
||||
use Utopia\Detector\Detection\Runtime\Python;
|
||||
use Utopia\Detector\Detection\Runtime\Ruby;
|
||||
use Utopia\Detector\Detection\Runtime\Swift;
|
||||
use Utopia\Detector\Detector\Framework;
|
||||
use Utopia\Detector\Detector\Packager;
|
||||
use Utopia\Detector\Detector\Runtime;
|
||||
use Utopia\Detector\Detector\Strategy;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
use Utopia\VCS\Exception\FileNotFound;
|
||||
use Utopia\VCS\Exception\RepositoryNotFound;
|
||||
|
||||
class Create extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'createRepositoryDetection';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/vcs/github/installations/:installationId/detections')
|
||||
->httpAlias('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
|
||||
->desc('Create repository detection')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.write')
|
||||
->label('resourceType', RESOURCE_TYPE_VCS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'createRepositoryDetection',
|
||||
description: '/docs/references/vcs/create-repository-detection.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_DETECTION_RUNTIME,
|
||||
),
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_DETECTION_FRAMEWORK,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('providerRepositoryId', '', new Text(256), 'Repository Id')
|
||||
->param('type', '', new WhiteList(['runtime', 'framework']), 'Detector type. Must be one of the following: runtime, framework')
|
||||
->param('providerRootDirectory', '', new Text(256, 0), 'Path to Root Directory', true)
|
||||
->inject('gitHub')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $installationId,
|
||||
string $providerRepositoryId,
|
||||
string $type,
|
||||
string $providerRootDirectory,
|
||||
GitHub $github,
|
||||
Response $response,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$providerInstallationId = $installation->getAttribute('providerInstallationId');
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
if (empty($repositoryName)) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
} catch (RepositoryNotFound $e) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
|
||||
$files = $github->listRepositoryContents($owner, $repositoryName, $providerRootDirectory);
|
||||
$files = \array_column($files, 'name');
|
||||
$languages = $github->listRepositoryLanguages($owner, $repositoryName);
|
||||
|
||||
$detector = new Packager();
|
||||
foreach ($files as $file) {
|
||||
$detector->addInput($file);
|
||||
}
|
||||
$detector
|
||||
->addOption(new Yarn())
|
||||
->addOption(new PNPM())
|
||||
->addOption(new NPM());
|
||||
$detection = $detector->detect();
|
||||
|
||||
$packager = !\is_null($detection) ? $detection->getName() : 'npm';
|
||||
|
||||
if ($type === 'framework') {
|
||||
$packages = '';
|
||||
try {
|
||||
$contentResponse = $github->getRepositoryContent($owner, $repositoryName, \rtrim($providerRootDirectory, '/') . '/package.json');
|
||||
$packages = $contentResponse['content'] ?? '';
|
||||
} catch (FileNotFound $e) {
|
||||
// Continue detection without package.json
|
||||
}
|
||||
|
||||
$output = new Document([
|
||||
'framework' => '',
|
||||
'installCommand' => '',
|
||||
'buildCommand' => '',
|
||||
'outputDirectory' => '',
|
||||
]);
|
||||
|
||||
$detector = new Framework($packager);
|
||||
$detector->addInput($packages, Framework::INPUT_PACKAGES);
|
||||
foreach ($files as $file) {
|
||||
$detector->addInput($file, Framework::INPUT_FILE);
|
||||
}
|
||||
|
||||
$detector
|
||||
->addOption(new Analog())
|
||||
->addOption(new Angular())
|
||||
->addOption(new Astro())
|
||||
->addOption(new Flutter())
|
||||
->addOption(new Lynx())
|
||||
->addOption(new NextJs())
|
||||
->addOption(new Nuxt())
|
||||
->addOption(new React())
|
||||
->addOption(new ReactNative())
|
||||
->addOption(new Remix())
|
||||
->addOption(new Svelte())
|
||||
->addOption(new SvelteKit())
|
||||
->addOption(new TanStackStart())
|
||||
->addOption(new Vue());
|
||||
|
||||
$framework = $detector->detect();
|
||||
|
||||
if (!\is_null($framework)) {
|
||||
$output->setAttribute('installCommand', $framework->getInstallCommand());
|
||||
$output->setAttribute('buildCommand', $framework->getBuildCommand());
|
||||
$output->setAttribute('outputDirectory', $framework->getOutputDirectory());
|
||||
$framework = $framework->getName();
|
||||
} else {
|
||||
$framework = 'other';
|
||||
$output->setAttribute('installCommand', '');
|
||||
$output->setAttribute('buildCommand', '');
|
||||
$output->setAttribute('outputDirectory', '');
|
||||
}
|
||||
|
||||
$frameworks = Config::getParam('frameworks');
|
||||
if (!\in_array($framework, \array_keys($frameworks), true)) {
|
||||
$framework = 'other';
|
||||
}
|
||||
$output->setAttribute('framework', $framework);
|
||||
} else {
|
||||
$output = new Document([
|
||||
'runtime' => '',
|
||||
'commands' => '',
|
||||
'entrypoint' => '',
|
||||
]);
|
||||
|
||||
$strategies = [
|
||||
new Strategy(Strategy::FILEMATCH),
|
||||
new Strategy(Strategy::LANGUAGES),
|
||||
new Strategy(Strategy::EXTENSION),
|
||||
];
|
||||
|
||||
foreach ($strategies as $strategy) {
|
||||
$detector = new Runtime($strategy, $packager);
|
||||
|
||||
if ($strategy->getValue() === Strategy::LANGUAGES) {
|
||||
foreach ($languages as $language) {
|
||||
$detector->addInput($language);
|
||||
}
|
||||
} else {
|
||||
foreach ($files as $file) {
|
||||
$detector->addInput($file);
|
||||
}
|
||||
}
|
||||
|
||||
$detector
|
||||
->addOption(new Node())
|
||||
->addOption(new Bun())
|
||||
->addOption(new Deno())
|
||||
->addOption(new PHP())
|
||||
->addOption(new Python())
|
||||
->addOption(new Dart())
|
||||
->addOption(new Swift())
|
||||
->addOption(new Ruby())
|
||||
->addOption(new Java())
|
||||
->addOption(new CPP())
|
||||
->addOption(new Dotnet());
|
||||
|
||||
$runtime = $detector->detect();
|
||||
|
||||
if (!\is_null($runtime)) {
|
||||
$output->setAttribute('commands', $runtime->getCommands());
|
||||
$output->setAttribute('entrypoint', $runtime->getEntrypoint());
|
||||
$runtime = $runtime->getName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($runtime)) {
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
$runtimeWithVersion = '';
|
||||
foreach ($runtimes as $runtimeKey => $runtimeConfig) {
|
||||
if ($runtimeConfig['key'] === $runtime) {
|
||||
$runtimeWithVersion = $runtimeKey;
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($runtimeWithVersion)) {
|
||||
throw new Exception(Exception::FUNCTION_RUNTIME_NOT_DETECTED);
|
||||
}
|
||||
|
||||
$output->setAttribute('runtime', $runtimeWithVersion);
|
||||
} else {
|
||||
throw new Exception(Exception::FUNCTION_RUNTIME_NOT_DETECTED);
|
||||
}
|
||||
}
|
||||
|
||||
$wg = new WaitGroup();
|
||||
$envs = [];
|
||||
foreach ($files as $file) {
|
||||
if (!(\str_starts_with($file, '.env'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$wg->add();
|
||||
go(function () use ($github, $owner, $repositoryName, $providerRootDirectory, $file, $wg, &$envs) {
|
||||
try {
|
||||
$contentResponse = $github->getRepositoryContent($owner, $repositoryName, \rtrim($providerRootDirectory, '/') . '/' . $file);
|
||||
$envFile = $contentResponse['content'] ?? '';
|
||||
|
||||
$configAdapter = new ConfigDotenv();
|
||||
try {
|
||||
$envObject = $configAdapter->parse($envFile);
|
||||
foreach ($envObject as $envName => $envValue) {
|
||||
$envs[$envName] = $envValue;
|
||||
}
|
||||
} catch (Parse $err) {
|
||||
// Silence error, so rest of endpoint can return
|
||||
}
|
||||
} finally {
|
||||
$wg->done();
|
||||
}
|
||||
});
|
||||
}
|
||||
$wg->wait();
|
||||
|
||||
$variables = [];
|
||||
foreach ($envs as $key => $value) {
|
||||
$variables[] = [
|
||||
'name' => $key,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
|
||||
$output->setAttribute('variables', $variables);
|
||||
|
||||
$response->dynamic($output, $type === 'framework' ? Response::MODEL_DETECTION_FRAMEWORK : Response::MODEL_DETECTION_RUNTIME);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\Installations\Repositories;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
use Utopia\VCS\Exception\RepositoryNotFound;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'getRepository';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId')
|
||||
->desc('Get repository')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('resourceType', RESOURCE_TYPE_VCS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'getRepository',
|
||||
description: '/docs/references/vcs/get-repository.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROVIDER_REPOSITORY,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('providerRepositoryId', '', new Text(256), 'Repository Id')
|
||||
->inject('gitHub')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $installationId,
|
||||
string $providerRepositoryId,
|
||||
GitHub $github,
|
||||
Response $response,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$providerInstallationId = $installation->getAttribute('providerInstallationId');
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
|
||||
$owner = $github->getOwnerName($providerInstallationId) ?? '';
|
||||
try {
|
||||
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
|
||||
if (empty($repositoryName)) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
} catch (RepositoryNotFound $e) {
|
||||
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
|
||||
}
|
||||
|
||||
$repository = $github->getRepository($owner, $repositoryName);
|
||||
|
||||
$repository['id'] = \strval($repository['id']) ?? '';
|
||||
$repository['pushedAt'] = $repository['pushed_at'] ?? '';
|
||||
$repository['organization'] = $installation->getAttribute('organization', '');
|
||||
$repository['provider'] = $installation->getAttribute('provider', '');
|
||||
$repository['defaultBranch'] = $repository['default_branch'] ?? '';
|
||||
|
||||
$response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,319 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\Installations\Repositories;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Coroutine\WaitGroup;
|
||||
use Utopia\Config\Adapters\Dotenv as ConfigDotenv;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Config\Exceptions\Parse;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Queries;
|
||||
use Utopia\Database\Validator\Query\Limit;
|
||||
use Utopia\Database\Validator\Query\Offset;
|
||||
use Utopia\Detector\Detection\Framework\Analog;
|
||||
use Utopia\Detector\Detection\Framework\Angular;
|
||||
use Utopia\Detector\Detection\Framework\Astro;
|
||||
use Utopia\Detector\Detection\Framework\Flutter;
|
||||
use Utopia\Detector\Detection\Framework\Lynx;
|
||||
use Utopia\Detector\Detection\Framework\NextJs;
|
||||
use Utopia\Detector\Detection\Framework\Nuxt;
|
||||
use Utopia\Detector\Detection\Framework\React;
|
||||
use Utopia\Detector\Detection\Framework\ReactNative;
|
||||
use Utopia\Detector\Detection\Framework\Remix;
|
||||
use Utopia\Detector\Detection\Framework\Svelte;
|
||||
use Utopia\Detector\Detection\Framework\SvelteKit;
|
||||
use Utopia\Detector\Detection\Framework\TanStackStart;
|
||||
use Utopia\Detector\Detection\Framework\Vue;
|
||||
use Utopia\Detector\Detection\Packager\NPM;
|
||||
use Utopia\Detector\Detection\Packager\PNPM;
|
||||
use Utopia\Detector\Detection\Packager\Yarn;
|
||||
use Utopia\Detector\Detection\Runtime\Bun;
|
||||
use Utopia\Detector\Detection\Runtime\CPP;
|
||||
use Utopia\Detector\Detection\Runtime\Dart;
|
||||
use Utopia\Detector\Detection\Runtime\Deno;
|
||||
use Utopia\Detector\Detection\Runtime\Dotnet;
|
||||
use Utopia\Detector\Detection\Runtime\Java;
|
||||
use Utopia\Detector\Detection\Runtime\Node;
|
||||
use Utopia\Detector\Detection\Runtime\PHP;
|
||||
use Utopia\Detector\Detection\Runtime\Python;
|
||||
use Utopia\Detector\Detection\Runtime\Ruby;
|
||||
use Utopia\Detector\Detection\Runtime\Swift;
|
||||
use Utopia\Detector\Detector\Framework;
|
||||
use Utopia\Detector\Detector\Packager;
|
||||
use Utopia\Detector\Detector\Runtime;
|
||||
use Utopia\Detector\Detector\Strategy;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
use Utopia\VCS\Exception\FileNotFound;
|
||||
|
||||
use function Swoole\Coroutine\batch;
|
||||
|
||||
class XList extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'listRepositories';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/vcs/github/installations/:installationId/providerRepositories')
|
||||
->desc('List repositories')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('resourceType', RESOURCE_TYPE_VCS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'repositories',
|
||||
name: 'listRepositories',
|
||||
description: '/docs/references/vcs/list-repositories.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST,
|
||||
),
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('installationId', '', new Text(256), 'Installation Id')
|
||||
->param('type', '', new WhiteList(['runtime', 'framework']), 'Detector type. Must be one of the following: runtime, framework')
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('queries', [], new Queries([new Limit(), new Offset()]), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Only supported methods are limit and offset', true)
|
||||
->inject('gitHub')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
string $installationId,
|
||||
string $type,
|
||||
string $search,
|
||||
array $queries,
|
||||
GitHub $github,
|
||||
Response $response,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
if (empty($search)) {
|
||||
$search = "";
|
||||
}
|
||||
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($installation->isEmpty()) {
|
||||
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$providerInstallationId = $installation->getAttribute('providerInstallationId');
|
||||
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
|
||||
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
|
||||
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
|
||||
|
||||
$queries = Query::parseQueries($queries);
|
||||
$limitQuery = current(array_filter($queries, fn ($query) => $query->getMethod() === Query::TYPE_LIMIT));
|
||||
$offsetQuery = current(array_filter($queries, fn ($query) => $query->getMethod() === Query::TYPE_OFFSET));
|
||||
|
||||
$limit = !empty($limitQuery) ? $limitQuery->getValue() : 4;
|
||||
$offset = !empty($offsetQuery) ? $offsetQuery->getValue() : 0;
|
||||
|
||||
if ($offset % $limit !== 0) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'offset must be a multiple of the limit');
|
||||
}
|
||||
|
||||
$page = ($offset / $limit) + 1;
|
||||
$owner = $github->getOwnerName($providerInstallationId);
|
||||
['items' => $repos, 'total' => $total] = $github->searchRepositories($owner, $page, $limit, $search);
|
||||
|
||||
$repos = \array_map(function ($repo) use ($installation) {
|
||||
$repo['id'] = \strval($repo['id'] ?? '');
|
||||
$repo['pushedAt'] = $repo['pushed_at'] ?? null;
|
||||
$repo['provider'] = $installation->getAttribute('provider', '') ?? '';
|
||||
$repo['organization'] = $installation->getAttribute('organization', '') ?? '';
|
||||
return $repo;
|
||||
}, $repos);
|
||||
|
||||
$repos = batch(\array_map(function ($repo) use ($type, $github) {
|
||||
return function () use ($repo, $type, $github) {
|
||||
$files = $github->listRepositoryContents($repo['organization'], $repo['name'], '');
|
||||
$files = \array_column($files, 'name');
|
||||
|
||||
$detector = new Packager();
|
||||
foreach ($files as $file) {
|
||||
$detector->addInput($file);
|
||||
}
|
||||
$detector
|
||||
->addOption(new Yarn())
|
||||
->addOption(new PNPM())
|
||||
->addOption(new NPM());
|
||||
$detection = $detector->detect();
|
||||
|
||||
$packager = !\is_null($detection) ? $detection->getName() : 'npm';
|
||||
|
||||
if ($type === 'framework') {
|
||||
$packages = '';
|
||||
try {
|
||||
$contentResponse = $github->getRepositoryContent($repo['organization'], $repo['name'], 'package.json');
|
||||
$packages = $contentResponse['content'] ?? '';
|
||||
} catch (FileNotFound $e) {
|
||||
// Continue detection without package.json
|
||||
}
|
||||
|
||||
$frameworkDetector = new Framework($packager);
|
||||
$frameworkDetector->addInput($packages, Framework::INPUT_PACKAGES);
|
||||
foreach ($files as $file) {
|
||||
$frameworkDetector->addInput($file, Framework::INPUT_FILE);
|
||||
}
|
||||
|
||||
$frameworkDetector
|
||||
->addOption(new Analog())
|
||||
->addOption(new Angular())
|
||||
->addOption(new Astro())
|
||||
->addOption(new Flutter())
|
||||
->addOption(new Lynx())
|
||||
->addOption(new NextJs())
|
||||
->addOption(new Nuxt())
|
||||
->addOption(new React())
|
||||
->addOption(new ReactNative())
|
||||
->addOption(new Remix())
|
||||
->addOption(new Svelte())
|
||||
->addOption(new SvelteKit())
|
||||
->addOption(new TanStackStart())
|
||||
->addOption(new Vue());
|
||||
|
||||
$detectedFramework = $frameworkDetector->detect();
|
||||
|
||||
if (!\is_null($detectedFramework)) {
|
||||
$framework = $detectedFramework->getName();
|
||||
} else {
|
||||
$framework = 'other';
|
||||
}
|
||||
|
||||
$frameworks = Config::getParam('frameworks');
|
||||
if (!\in_array($framework, \array_keys($frameworks), true)) {
|
||||
$framework = 'other';
|
||||
}
|
||||
$repo['framework'] = $framework;
|
||||
} else {
|
||||
$languages = $github->listRepositoryLanguages($repo['organization'], $repo['name']);
|
||||
|
||||
$strategies = [
|
||||
new Strategy(Strategy::FILEMATCH),
|
||||
new Strategy(Strategy::LANGUAGES),
|
||||
new Strategy(Strategy::EXTENSION),
|
||||
];
|
||||
|
||||
foreach ($strategies as $strategy) {
|
||||
$detector = new Runtime($strategy, $packager);
|
||||
if ($strategy->getValue() === Strategy::LANGUAGES) {
|
||||
foreach ($languages as $language) {
|
||||
$detector->addInput($language);
|
||||
}
|
||||
} else {
|
||||
foreach ($files as $file) {
|
||||
$detector->addInput($file);
|
||||
}
|
||||
}
|
||||
$detector
|
||||
->addOption(new Node())
|
||||
->addOption(new Bun())
|
||||
->addOption(new Deno())
|
||||
->addOption(new PHP())
|
||||
->addOption(new Python())
|
||||
->addOption(new Dart())
|
||||
->addOption(new Swift())
|
||||
->addOption(new Ruby())
|
||||
->addOption(new Java())
|
||||
->addOption(new CPP())
|
||||
->addOption(new Dotnet());
|
||||
|
||||
$runtime = $detector->detect();
|
||||
|
||||
if (!\is_null($runtime)) {
|
||||
$runtime = $runtime->getName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!empty($runtime)) {
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
$runtimeWithVersion = '';
|
||||
foreach ($runtimes as $runtimeKey => $runtimeConfig) {
|
||||
if ($runtimeConfig['key'] === $runtime) {
|
||||
$runtimeWithVersion = $runtimeKey;
|
||||
}
|
||||
}
|
||||
|
||||
$repo['runtime'] = $runtimeWithVersion ?? '';
|
||||
}
|
||||
}
|
||||
|
||||
$wg = new WaitGroup();
|
||||
$envs = [];
|
||||
foreach ($files as $file) {
|
||||
if (!(\str_starts_with($file, '.env'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$wg->add();
|
||||
go(function () use ($github, $repo, $file, $wg, &$envs) {
|
||||
try {
|
||||
$contentResponse = $github->getRepositoryContent($repo['organization'], $repo['name'], $file);
|
||||
$envFile = $contentResponse['content'] ?? '';
|
||||
|
||||
$configAdapter = new ConfigDotenv();
|
||||
try {
|
||||
$envObject = $configAdapter->parse($envFile);
|
||||
foreach ($envObject as $envName => $envValue) {
|
||||
$envs[$envName] = $envValue;
|
||||
}
|
||||
} catch (Parse) {
|
||||
// Silence error, so rest of endpoint can return
|
||||
}
|
||||
} finally {
|
||||
$wg->done();
|
||||
}
|
||||
});
|
||||
}
|
||||
$wg->wait();
|
||||
|
||||
$repo['variables'] = [];
|
||||
foreach ($envs as $key => $value) {
|
||||
$repo['variables'][] = [
|
||||
'name' => $key,
|
||||
'value' => $value,
|
||||
];
|
||||
}
|
||||
|
||||
return $repo;
|
||||
};
|
||||
}, $repos));
|
||||
|
||||
$repos = \array_map(function ($repo) {
|
||||
return new Document($repo);
|
||||
}, $repos);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
$type === 'framework' ? 'frameworkProviderRepositories' : 'runtimeProviderRepositories' => $repos,
|
||||
'total' => $total,
|
||||
]), ($type === 'framework') ? Response::MODEL_PROVIDER_REPOSITORY_FRAMEWORK_LIST : Response::MODEL_PROVIDER_REPOSITORY_RUNTIME_LIST);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\VCS\Http\Installations;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Action;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Installations;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Order as OrderException;
|
||||
use Utopia\Database\Exception\Query as QueryException;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Query\Cursor;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class XList extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'listInstallations';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/vcs/installations')
|
||||
->desc('List installations')
|
||||
->groups(['api', 'vcs'])
|
||||
->label('scope', 'vcs.read')
|
||||
->label('resourceType', RESOURCE_TYPE_VCS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'vcs',
|
||||
group: 'installations',
|
||||
name: 'listInstallations',
|
||||
description: '/docs/references/vcs/list-installations.md',
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_OK,
|
||||
model: Response::MODEL_INSTALLATION_LIST,
|
||||
)
|
||||
]
|
||||
))
|
||||
->param('queries', [], new Installations(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Installations::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->inject('dbForPlatform')
|
||||
->callback($this->action(...));
|
||||
}
|
||||
|
||||
public function action(
|
||||
array $queries,
|
||||
string $search,
|
||||
bool $includeTotal,
|
||||
Response $response,
|
||||
Document $project,
|
||||
Database $dbForPlatform
|
||||
) {
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
$queries[] = Query::equal('projectInternalId', [$project->getSequence()]);
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get cursor document if there was a cursor query, we use array_filter and reset for reference $cursor to $queries
|
||||
*/
|
||||
$cursor = \array_filter($queries, function ($query) {
|
||||
return \in_array($query->getMethod(), [Query::TYPE_CURSOR_AFTER, Query::TYPE_CURSOR_BEFORE]);
|
||||
});
|
||||
$cursor = reset($cursor);
|
||||
if ($cursor) {
|
||||
/** @var Query $cursor */
|
||||
|
||||
$validator = new Cursor();
|
||||
if (!$validator->isValid($cursor)) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$installationId = $cursor->getValue();
|
||||
$cursorDocument = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if ($cursorDocument->isEmpty()) {
|
||||
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Installation '{$installationId}' for the 'cursor' value not found.");
|
||||
}
|
||||
|
||||
$cursor->setValue($cursorDocument);
|
||||
}
|
||||
|
||||
$filterQueries = Query::groupByType($queries)['filters'];
|
||||
try {
|
||||
$results = $dbForPlatform->find('installations', $queries);
|
||||
$total = $includeTotal ? $dbForPlatform->count('installations', $filterQueries, APP_LIMIT_COUNT) : 0;
|
||||
} catch (OrderException $e) {
|
||||
throw new Exception(Exception::DATABASE_QUERY_ORDER_NULL, "The order attribute '{$e->getAttribute()}' had a null value. Cursor pagination requires all documents order attribute values are non-null.");
|
||||
}
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'installations' => $results,
|
||||
'total' => $total,
|
||||
]), Response::MODEL_INSTALLATION_LIST);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user