Merge remote-tracking branch 'origin/1.8.x' into feat-health-module

This commit is contained in:
Damodar Lohani
2026-01-15 13:55:09 +00:00
230 changed files with 6845 additions and 4575 deletions
+21 -10
View File
@@ -41,8 +41,6 @@ Config::setParam('runtimes', (new Runtimes('v5'))->getAll(supported: false));
// require controllers after overwriting runtimes
require_once __DIR__ . '/controllers/general.php';
Authorization::disable();
CLI::setResource('register', fn () => $register);
CLI::setResource('cache', function ($pools) {
@@ -60,7 +58,13 @@ CLI::setResource('pools', function (Registry $register) {
return $register->get('pools');
}, ['register']);
CLI::setResource('dbForPlatform', function ($pools, $cache) {
CLI::setResource('authorization', function () {
$authorization = new Authorization();
$authorization->disable();
return $authorization;
}, []);
CLI::setResource('dbForPlatform', function ($pools, $cache, $authorization) {
$sleep = 3;
$maxAttempts = 5;
$attempts = 0;
@@ -74,6 +78,8 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) {
$dbForPlatform = new Database($adapter, $cache);
$dbForPlatform
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', 'console');
@@ -99,7 +105,7 @@ CLI::setResource('dbForPlatform', function ($pools, $cache) {
}
return $dbForPlatform;
}, ['pools', 'cache']);
}, ['pools', 'cache', 'authorization']);
CLI::setResource('console', function () {
return new Document(Config::getParam('console'));
@@ -110,10 +116,10 @@ CLI::setResource(
fn () => fn (Document $project, string $resourceType, ?string $resourceId) => false
);
CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, $authorization) {
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -146,6 +152,7 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
$adapter = new DatabasePool($pools->get($dsn->getHost()));
$database = new Database($adapter, $cache);
$databases[$dsn->getHost()] = $database;
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
@@ -162,17 +169,19 @@ CLI::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
}
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId());
return $database;
};
}, ['pools', 'dbForPlatform', 'cache']);
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
CLI::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $database) {
return function (?Document $project = null) use ($pools, $cache, $database, $authorization) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int)$project->getSequence());
return $database;
@@ -182,6 +191,8 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = new Database($adapter, $cache);
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_TASK)
@@ -194,7 +205,7 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
return $database;
};
}, ['pools', 'cache']);
}, ['pools', 'cache', 'authorization']);
CLI::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
+3 -3
View File
@@ -60,7 +60,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '20.3.2',
'version' => '20.3.3',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -227,7 +227,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '12.0.1',
'version' => '13.0.0',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -377,7 +377,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '20.1.0',
'version' => '20.1.1',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
+113 -87
View File
@@ -1,7 +1,7 @@
{
"openapi": "3.0.0",
"info": {
"version": "1.8.0",
"version": "1.8.1",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -140,7 +140,8 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
@@ -224,12 +225,14 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -439,7 +442,8 @@
"duration": {
"type": "integer",
"description": "Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.",
"x-example": 0
"x-example": 0,
"format": "int32"
}
}
}
@@ -551,7 +555,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 288,
"weight": 277,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -623,7 +627,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 290,
"weight": 279,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -747,7 +751,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 291,
"weight": 280,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -887,7 +891,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 292,
"weight": 281,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -1011,7 +1015,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 296,
"weight": 285,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1145,7 +1149,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 297,
"weight": 286,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1283,7 +1287,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 289,
"weight": 278,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1384,7 +1388,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 295,
"weight": 284,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1483,7 +1487,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 293,
"weight": 282,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1582,7 +1586,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 294,
"weight": 283,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -1796,7 +1800,8 @@
"oldPassword": {
"type": "string",
"description": "Current user password. Must be at least 8 chars.",
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -1868,12 +1873,14 @@
"phone": {
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -2072,12 +2079,14 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"url": {
"type": "string",
"description": "URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
}
},
"required": [
@@ -2382,12 +2391,14 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -2573,9 +2584,7 @@
"yammer",
"yandex",
"zoho",
"zoom",
"mock",
"mock-unverified"
"zoom"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": []
@@ -3304,7 +3313,8 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"phrase": {
"type": "boolean",
@@ -3390,12 +3400,14 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"url": {
"type": "string",
"description": "URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
},
"phrase": {
"type": "boolean",
@@ -3504,9 +3516,7 @@
"yammer",
"yandex",
"zoho",
"zoom",
"mock",
"mock-unverified"
"zoom"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": []
@@ -3621,7 +3631,8 @@
"phone": {
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
}
},
"required": [
@@ -3746,7 +3757,8 @@
"url": {
"type": "string",
"description": "URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
}
},
"required": [
@@ -4039,7 +4051,7 @@
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 50,
"weight": 288,
"cookies": false,
"type": "location",
"demo": "avatars\/get-browser.md",
@@ -4167,7 +4179,7 @@
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 49,
"weight": 287,
"cookies": false,
"type": "location",
"demo": "avatars\/get-credit-card.md",
@@ -4301,7 +4313,7 @@
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 53,
"weight": 291,
"cookies": false,
"type": "location",
"demo": "avatars\/get-favicon.md",
@@ -4361,7 +4373,7 @@
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 51,
"weight": 289,
"cookies": false,
"type": "location",
"demo": "avatars\/get-flag.md",
@@ -4851,7 +4863,7 @@
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 52,
"weight": 290,
"cookies": false,
"type": "location",
"demo": "avatars\/get-image.md",
@@ -4935,7 +4947,7 @@
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 55,
"weight": 293,
"cookies": false,
"type": "location",
"demo": "avatars\/get-initials.md",
@@ -5029,7 +5041,7 @@
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 54,
"weight": 292,
"cookies": false,
"type": "location",
"demo": "avatars\/get-qr.md",
@@ -5123,7 +5135,7 @@
"x-appwrite": {
"method": "getScreenshot",
"group": null,
"weight": 56,
"weight": 294,
"cookies": false,
"type": "location",
"demo": "avatars\/get-screenshot.md",
@@ -5980,7 +5992,8 @@
"ttl": {
"type": "integer",
"description": "Seconds before the transaction expires.",
"x-example": 60
"x-example": 60,
"format": "int32"
}
}
}
@@ -7146,12 +7159,14 @@
"value": {
"type": "number",
"description": "Value to increment the attribute by. The value must be a number.",
"x-example": null
"x-example": null,
"format": "float"
},
"min": {
"type": "number",
"description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.",
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -7273,12 +7288,14 @@
"value": {
"type": "number",
"description": "Value to increment the attribute by. The value must be a number.",
"x-example": null
"x-example": null,
"format": "float"
},
"max": {
"type": "number",
"description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.",
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -7318,7 +7335,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 454,
"weight": 455,
"cookies": false,
"type": "",
"demo": "functions\/list-executions.md",
@@ -7405,7 +7422,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 452,
"weight": 453,
"cookies": false,
"type": "",
"demo": "functions\/create-execution.md",
@@ -7523,7 +7540,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 453,
"weight": 454,
"cookies": false,
"type": "",
"demo": "functions\/get-execution.md",
@@ -7598,7 +7615,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 225,
"weight": 214,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7652,7 +7669,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 224,
"weight": 213,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -7706,7 +7723,7 @@
"x-appwrite": {
"method": "get",
"group": null,
"weight": 60,
"weight": 49,
"cookies": false,
"type": "",
"demo": "locale\/get.md",
@@ -7760,7 +7777,7 @@
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 61,
"weight": 50,
"cookies": false,
"type": "",
"demo": "locale\/list-codes.md",
@@ -7814,7 +7831,7 @@
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 65,
"weight": 54,
"cookies": false,
"type": "",
"demo": "locale\/list-continents.md",
@@ -7868,7 +7885,7 @@
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 62,
"weight": 51,
"cookies": false,
"type": "",
"demo": "locale\/list-countries.md",
@@ -7922,7 +7939,7 @@
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 63,
"weight": 52,
"cookies": false,
"type": "",
"demo": "locale\/list-countries-eu.md",
@@ -7976,7 +7993,7 @@
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 64,
"weight": 53,
"cookies": false,
"type": "",
"demo": "locale\/list-countries-phones.md",
@@ -8030,7 +8047,7 @@
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 66,
"weight": 55,
"cookies": false,
"type": "",
"demo": "locale\/list-currencies.md",
@@ -8084,7 +8101,7 @@
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 67,
"weight": 56,
"cookies": false,
"type": "",
"demo": "locale\/list-languages.md",
@@ -8138,7 +8155,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 272,
"weight": 261,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8222,7 +8239,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 276,
"weight": 265,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -8298,7 +8315,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 525,
"weight": 526,
"cookies": false,
"type": "",
"demo": "storage\/list-files.md",
@@ -8397,7 +8414,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 523,
"weight": 524,
"cookies": false,
"type": "upload",
"demo": "storage\/create-file.md",
@@ -8452,7 +8469,8 @@
"file": {
"type": "string",
"description": "Binary file. Appwrite SDKs provide helpers to handle file input. [Learn about file input](https:\/\/appwrite.io\/docs\/products\/storage\/upload-download#input-file).",
"x-example": null
"x-example": null,
"format": "binary"
},
"permissions": {
"type": "array",
@@ -8498,7 +8516,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 524,
"weight": 525,
"cookies": false,
"type": "",
"demo": "storage\/get-file.md",
@@ -8572,7 +8590,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 526,
"weight": 527,
"cookies": false,
"type": "",
"demo": "storage\/update-file.md",
@@ -8664,7 +8682,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 527,
"weight": 528,
"cookies": false,
"type": "",
"demo": "storage\/delete-file.md",
@@ -8733,7 +8751,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 529,
"weight": 530,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-download.md",
@@ -8813,7 +8831,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 528,
"weight": 529,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-preview.md",
@@ -9043,7 +9061,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 530,
"weight": 531,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-view.md",
@@ -9240,7 +9258,8 @@
"ttl": {
"type": "integer",
"description": "Seconds before the transaction expires.",
"x-example": 60
"x-example": 60,
"format": "int32"
}
}
}
@@ -10403,12 +10422,14 @@
"value": {
"type": "number",
"description": "Value to increment the column by. The value must be a number.",
"x-example": null
"x-example": null,
"format": "float"
},
"min": {
"type": "number",
"description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.",
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -10529,12 +10550,14 @@
"value": {
"type": "number",
"description": "Value to increment the column by. The value must be a number.",
"x-example": null
"x-example": null,
"format": "float"
},
"max": {
"type": "number",
"description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.",
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -10574,7 +10597,7 @@
"x-appwrite": {
"method": "list",
"group": "teams",
"weight": 145,
"weight": 134,
"cookies": false,
"type": "",
"demo": "teams\/list.md",
@@ -10663,7 +10686,7 @@
"x-appwrite": {
"method": "create",
"group": "teams",
"weight": 144,
"weight": 133,
"cookies": false,
"type": "",
"demo": "teams\/create.md",
@@ -10750,7 +10773,7 @@
"x-appwrite": {
"method": "get",
"group": "teams",
"weight": 146,
"weight": 135,
"cookies": false,
"type": "",
"demo": "teams\/get.md",
@@ -10814,7 +10837,7 @@
"x-appwrite": {
"method": "updateName",
"group": "teams",
"weight": 148,
"weight": 137,
"cookies": false,
"type": "",
"demo": "teams\/update-name.md",
@@ -10890,7 +10913,7 @@
"x-appwrite": {
"method": "delete",
"group": "teams",
"weight": 150,
"weight": 139,
"cookies": false,
"type": "",
"demo": "teams\/delete.md",
@@ -10956,7 +10979,7 @@
"x-appwrite": {
"method": "listMemberships",
"group": "memberships",
"weight": 152,
"weight": 141,
"cookies": false,
"type": "",
"demo": "teams\/list-memberships.md",
@@ -11055,7 +11078,7 @@
"x-appwrite": {
"method": "createMembership",
"group": "memberships",
"weight": 151,
"weight": 140,
"cookies": false,
"type": "",
"demo": "teams\/create-membership.md",
@@ -11104,7 +11127,8 @@
"email": {
"type": "string",
"description": "Email of the new team member.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"userId": {
"type": "string",
@@ -11114,7 +11138,8 @@
"phone": {
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
},
"roles": {
"type": "array",
@@ -11134,7 +11159,8 @@
"url": {
"type": "string",
"description": "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.",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
},
"name": {
"type": "string",
@@ -11175,7 +11201,7 @@
"x-appwrite": {
"method": "getMembership",
"group": "memberships",
"weight": 153,
"weight": 142,
"cookies": false,
"type": "",
"demo": "teams\/get-membership.md",
@@ -11249,7 +11275,7 @@
"x-appwrite": {
"method": "updateMembership",
"group": "memberships",
"weight": 154,
"weight": 143,
"cookies": false,
"type": "",
"demo": "teams\/update-membership.md",
@@ -11345,7 +11371,7 @@
"x-appwrite": {
"method": "deleteMembership",
"group": "memberships",
"weight": 156,
"weight": 145,
"cookies": false,
"type": "",
"demo": "teams\/delete-membership.md",
@@ -11421,7 +11447,7 @@
"x-appwrite": {
"method": "updateMembershipStatus",
"group": "memberships",
"weight": 155,
"weight": 144,
"cookies": false,
"type": "",
"demo": "teams\/update-membership-status.md",
@@ -11521,7 +11547,7 @@
"x-appwrite": {
"method": "getPrefs",
"group": "teams",
"weight": 147,
"weight": 136,
"cookies": false,
"type": "",
"demo": "teams\/get-prefs.md",
@@ -11584,7 +11610,7 @@
"x-appwrite": {
"method": "updatePrefs",
"group": "teams",
"weight": 149,
"weight": 138,
"cookies": false,
"type": "",
"demo": "teams\/update-prefs.md",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+117 -77
View File
@@ -140,7 +140,8 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
@@ -224,12 +225,14 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -439,7 +442,8 @@
"duration": {
"type": "integer",
"description": "Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.",
"x-example": 0
"x-example": 0,
"format": "int32"
}
}
}
@@ -551,7 +555,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 288,
"weight": 277,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -623,7 +627,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 290,
"weight": 279,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -747,7 +751,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 291,
"weight": 280,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -887,7 +891,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 292,
"weight": 281,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -1011,7 +1015,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 296,
"weight": 285,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1145,7 +1149,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 297,
"weight": 286,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1283,7 +1287,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 289,
"weight": 278,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1384,7 +1388,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 295,
"weight": 284,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1483,7 +1487,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 293,
"weight": 282,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1582,7 +1586,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 294,
"weight": 283,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -1796,7 +1800,8 @@
"oldPassword": {
"type": "string",
"description": "Current user password. Must be at least 8 chars.",
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -1868,12 +1873,14 @@
"phone": {
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -2072,12 +2079,14 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"url": {
"type": "string",
"description": "URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
}
},
"required": [
@@ -2382,12 +2391,14 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -2573,9 +2584,7 @@
"yammer",
"yandex",
"zoho",
"zoom",
"mock",
"mock-unverified"
"zoom"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": []
@@ -3304,7 +3313,8 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"phrase": {
"type": "boolean",
@@ -3390,12 +3400,14 @@
"email": {
"type": "string",
"description": "User email.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"url": {
"type": "string",
"description": "URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
},
"phrase": {
"type": "boolean",
@@ -3504,9 +3516,7 @@
"yammer",
"yandex",
"zoho",
"zoom",
"mock",
"mock-unverified"
"zoom"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": []
@@ -3621,7 +3631,8 @@
"phone": {
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
}
},
"required": [
@@ -3746,7 +3757,8 @@
"url": {
"type": "string",
"description": "URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
}
},
"required": [
@@ -4039,7 +4051,7 @@
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 50,
"weight": 288,
"cookies": false,
"type": "location",
"demo": "avatars\/get-browser.md",
@@ -4167,7 +4179,7 @@
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 49,
"weight": 287,
"cookies": false,
"type": "location",
"demo": "avatars\/get-credit-card.md",
@@ -4301,7 +4313,7 @@
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 53,
"weight": 291,
"cookies": false,
"type": "location",
"demo": "avatars\/get-favicon.md",
@@ -4361,7 +4373,7 @@
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 51,
"weight": 289,
"cookies": false,
"type": "location",
"demo": "avatars\/get-flag.md",
@@ -4851,7 +4863,7 @@
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 52,
"weight": 290,
"cookies": false,
"type": "location",
"demo": "avatars\/get-image.md",
@@ -4935,7 +4947,7 @@
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 55,
"weight": 293,
"cookies": false,
"type": "location",
"demo": "avatars\/get-initials.md",
@@ -5029,7 +5041,7 @@
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 54,
"weight": 292,
"cookies": false,
"type": "location",
"demo": "avatars\/get-qr.md",
@@ -5123,7 +5135,7 @@
"x-appwrite": {
"method": "getScreenshot",
"group": null,
"weight": 56,
"weight": 294,
"cookies": false,
"type": "location",
"demo": "avatars\/get-screenshot.md",
@@ -5980,7 +5992,8 @@
"ttl": {
"type": "integer",
"description": "Seconds before the transaction expires.",
"x-example": 60
"x-example": 60,
"format": "int32"
}
}
}
@@ -7146,12 +7159,14 @@
"value": {
"type": "number",
"description": "Value to increment the attribute by. The value must be a number.",
"x-example": null
"x-example": null,
"format": "float"
},
"min": {
"type": "number",
"description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.",
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -7273,12 +7288,14 @@
"value": {
"type": "number",
"description": "Value to increment the attribute by. The value must be a number.",
"x-example": null
"x-example": null,
"format": "float"
},
"max": {
"type": "number",
"description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.",
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -7598,7 +7615,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 225,
"weight": 214,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7652,7 +7669,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 224,
"weight": 213,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -7706,7 +7723,7 @@
"x-appwrite": {
"method": "get",
"group": null,
"weight": 60,
"weight": 49,
"cookies": false,
"type": "",
"demo": "locale\/get.md",
@@ -7760,7 +7777,7 @@
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 61,
"weight": 50,
"cookies": false,
"type": "",
"demo": "locale\/list-codes.md",
@@ -7814,7 +7831,7 @@
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 65,
"weight": 54,
"cookies": false,
"type": "",
"demo": "locale\/list-continents.md",
@@ -7868,7 +7885,7 @@
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 62,
"weight": 51,
"cookies": false,
"type": "",
"demo": "locale\/list-countries.md",
@@ -7922,7 +7939,7 @@
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 63,
"weight": 52,
"cookies": false,
"type": "",
"demo": "locale\/list-countries-eu.md",
@@ -7976,7 +7993,7 @@
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 64,
"weight": 53,
"cookies": false,
"type": "",
"demo": "locale\/list-countries-phones.md",
@@ -8030,7 +8047,7 @@
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 66,
"weight": 55,
"cookies": false,
"type": "",
"demo": "locale\/list-currencies.md",
@@ -8084,7 +8101,7 @@
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 67,
"weight": 56,
"cookies": false,
"type": "",
"demo": "locale\/list-languages.md",
@@ -8138,7 +8155,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 272,
"weight": 261,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8222,7 +8239,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 276,
"weight": 265,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -8452,7 +8469,8 @@
"file": {
"type": "string",
"description": "Binary file. Appwrite SDKs provide helpers to handle file input. [Learn about file input](https:\/\/appwrite.io\/docs\/products\/storage\/upload-download#input-file).",
"x-example": null
"x-example": null,
"format": "binary"
},
"permissions": {
"type": "array",
@@ -9240,7 +9258,8 @@
"ttl": {
"type": "integer",
"description": "Seconds before the transaction expires.",
"x-example": 60
"x-example": 60,
"format": "int32"
}
}
}
@@ -10403,12 +10422,14 @@
"value": {
"type": "number",
"description": "Value to increment the column by. The value must be a number.",
"x-example": null
"x-example": null,
"format": "float"
},
"min": {
"type": "number",
"description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.",
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -10529,12 +10550,14 @@
"value": {
"type": "number",
"description": "Value to increment the column by. The value must be a number.",
"x-example": null
"x-example": null,
"format": "float"
},
"max": {
"type": "number",
"description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.",
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -10574,7 +10597,7 @@
"x-appwrite": {
"method": "list",
"group": "teams",
"weight": 145,
"weight": 134,
"cookies": false,
"type": "",
"demo": "teams\/list.md",
@@ -10663,7 +10686,7 @@
"x-appwrite": {
"method": "create",
"group": "teams",
"weight": 144,
"weight": 133,
"cookies": false,
"type": "",
"demo": "teams\/create.md",
@@ -10750,7 +10773,7 @@
"x-appwrite": {
"method": "get",
"group": "teams",
"weight": 146,
"weight": 135,
"cookies": false,
"type": "",
"demo": "teams\/get.md",
@@ -10814,7 +10837,7 @@
"x-appwrite": {
"method": "updateName",
"group": "teams",
"weight": 148,
"weight": 137,
"cookies": false,
"type": "",
"demo": "teams\/update-name.md",
@@ -10890,7 +10913,7 @@
"x-appwrite": {
"method": "delete",
"group": "teams",
"weight": 150,
"weight": 139,
"cookies": false,
"type": "",
"demo": "teams\/delete.md",
@@ -10956,7 +10979,7 @@
"x-appwrite": {
"method": "listMemberships",
"group": "memberships",
"weight": 152,
"weight": 141,
"cookies": false,
"type": "",
"demo": "teams\/list-memberships.md",
@@ -11055,7 +11078,7 @@
"x-appwrite": {
"method": "createMembership",
"group": "memberships",
"weight": 151,
"weight": 140,
"cookies": false,
"type": "",
"demo": "teams\/create-membership.md",
@@ -11104,7 +11127,8 @@
"email": {
"type": "string",
"description": "Email of the new team member.",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"userId": {
"type": "string",
@@ -11114,7 +11138,8 @@
"phone": {
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
},
"roles": {
"type": "array",
@@ -11134,7 +11159,8 @@
"url": {
"type": "string",
"description": "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.",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
},
"name": {
"type": "string",
@@ -11175,7 +11201,7 @@
"x-appwrite": {
"method": "getMembership",
"group": "memberships",
"weight": 153,
"weight": 142,
"cookies": false,
"type": "",
"demo": "teams\/get-membership.md",
@@ -11249,7 +11275,7 @@
"x-appwrite": {
"method": "updateMembership",
"group": "memberships",
"weight": 154,
"weight": 143,
"cookies": false,
"type": "",
"demo": "teams\/update-membership.md",
@@ -11345,7 +11371,7 @@
"x-appwrite": {
"method": "deleteMembership",
"group": "memberships",
"weight": 156,
"weight": 145,
"cookies": false,
"type": "",
"demo": "teams\/delete-membership.md",
@@ -11421,7 +11447,7 @@
"x-appwrite": {
"method": "updateMembershipStatus",
"group": "memberships",
"weight": 155,
"weight": 144,
"cookies": false,
"type": "",
"demo": "teams\/update-membership-status.md",
@@ -11521,7 +11547,7 @@
"x-appwrite": {
"method": "getPrefs",
"group": "teams",
"weight": 147,
"weight": 136,
"cookies": false,
"type": "",
"demo": "teams\/get-prefs.md",
@@ -11584,7 +11610,7 @@
"x-appwrite": {
"method": "updatePrefs",
"group": "teams",
"weight": 149,
"weight": 138,
"cookies": false,
"type": "",
"demo": "teams\/update-prefs.md",
@@ -13428,6 +13454,16 @@
"description": "Total number of chunks uploaded",
"x-example": 17890,
"format": "int32"
},
"encryption": {
"type": "boolean",
"description": "Whether file contents are encrypted at rest.",
"x-example": true
},
"compression": {
"type": "string",
"description": "Compression algorithm used for the file. Will be one of none, [gzip](https:\/\/en.wikipedia.org\/wiki\/Gzip), or [zstd](https:\/\/en.wikipedia.org\/wiki\/Zstd).",
"x-example": "gzip"
}
},
"required": [
@@ -13441,7 +13477,9 @@
"mimeType",
"sizeOriginal",
"chunksTotal",
"chunksUploaded"
"chunksUploaded",
"encryption",
"compression"
],
"example": {
"$id": "5e5ea5c16897e",
@@ -13456,7 +13494,9 @@
"mimeType": "image\/png",
"sizeOriginal": 17890,
"chunksTotal": 17890,
"chunksUploaded": 17890
"chunksUploaded": 17890,
"encryption": true,
"compression": "gzip"
}
},
"team": {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+111 -86
View File
@@ -1,7 +1,7 @@
{
"swagger": "2.0",
"info": {
"version": "1.8.0",
"version": "1.8.1",
"title": "Appwrite",
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
@@ -191,7 +191,8 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
@@ -280,13 +281,15 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"default": null,
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -498,7 +501,8 @@
"type": "integer",
"description": "Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.",
"default": 900,
"x-example": 0
"x-example": 0,
"format": "int32"
}
}
}
@@ -608,7 +612,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 288,
"weight": 277,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -683,7 +687,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 290,
"weight": 279,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -807,7 +811,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 291,
"weight": 280,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -948,7 +952,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 292,
"weight": 281,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -1072,7 +1076,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 296,
"weight": 285,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1209,7 +1213,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 297,
"weight": 286,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1349,7 +1353,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 289,
"weight": 278,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1450,7 +1454,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 295,
"weight": 284,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1551,7 +1555,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 293,
"weight": 282,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1652,7 +1656,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 294,
"weight": 283,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -1874,7 +1878,8 @@
"type": "string",
"description": "Current user password. Must be at least 8 chars.",
"default": "",
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -1949,13 +1954,15 @@
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"default": null,
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"default": null,
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -2160,13 +2167,15 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"url": {
"type": "string",
"description": "URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"default": null,
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
}
},
"required": [
@@ -2485,13 +2494,15 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"default": null,
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -2683,9 +2694,7 @@
"yammer",
"yandex",
"zoho",
"zoom",
"mock",
"mock-unverified"
"zoom"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": [],
@@ -3429,7 +3438,8 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"phrase": {
"type": "boolean",
@@ -3520,13 +3530,15 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"url": {
"type": "string",
"description": "URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"default": "",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
},
"phrase": {
"type": "boolean",
@@ -3638,9 +3650,7 @@
"yammer",
"yandex",
"zoho",
"zoom",
"mock",
"mock-unverified"
"zoom"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": [],
@@ -3754,7 +3764,8 @@
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"default": null,
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
}
},
"required": [
@@ -3882,7 +3893,8 @@
"type": "string",
"description": "URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"default": null,
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
}
},
"required": [
@@ -4191,7 +4203,7 @@
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 50,
"weight": 288,
"cookies": false,
"type": "location",
"demo": "avatars\/get-browser.md",
@@ -4317,7 +4329,7 @@
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 49,
"weight": 287,
"cookies": false,
"type": "location",
"demo": "avatars\/get-credit-card.md",
@@ -4449,7 +4461,7 @@
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 53,
"weight": 291,
"cookies": false,
"type": "location",
"demo": "avatars\/get-favicon.md",
@@ -4513,7 +4525,7 @@
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 51,
"weight": 289,
"cookies": false,
"type": "location",
"demo": "avatars\/get-flag.md",
@@ -5001,7 +5013,7 @@
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 52,
"weight": 290,
"cookies": false,
"type": "location",
"demo": "avatars\/get-image.md",
@@ -5085,7 +5097,7 @@
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 55,
"weight": 293,
"cookies": false,
"type": "location",
"demo": "avatars\/get-initials.md",
@@ -5177,7 +5189,7 @@
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 54,
"weight": 292,
"cookies": false,
"type": "location",
"demo": "avatars\/get-qr.md",
@@ -5269,7 +5281,7 @@
"x-appwrite": {
"method": "getScreenshot",
"group": null,
"weight": 56,
"weight": 294,
"cookies": false,
"type": "location",
"demo": "avatars\/get-screenshot.md",
@@ -6087,7 +6099,8 @@
"type": "integer",
"description": "Seconds before the transaction expires.",
"default": 300,
"x-example": 60
"x-example": 60,
"format": "int32"
}
}
}
@@ -7208,13 +7221,15 @@
"type": "number",
"description": "Value to increment the attribute by. The value must be a number.",
"default": 1,
"x-example": null
"x-example": null,
"format": "float"
},
"min": {
"type": "number",
"description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.",
"default": null,
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -7329,13 +7344,15 @@
"type": "number",
"description": "Value to increment the attribute by. The value must be a number.",
"default": 1,
"x-example": null
"x-example": null,
"format": "float"
},
"max": {
"type": "number",
"description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.",
"default": null,
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -7375,7 +7392,7 @@
"x-appwrite": {
"method": "listExecutions",
"group": "executions",
"weight": 454,
"weight": 455,
"cookies": false,
"type": "",
"demo": "functions\/list-executions.md",
@@ -7458,7 +7475,7 @@
"x-appwrite": {
"method": "createExecution",
"group": "executions",
"weight": 452,
"weight": 453,
"cookies": false,
"type": "",
"demo": "functions\/create-execution.md",
@@ -7577,7 +7594,7 @@
"x-appwrite": {
"method": "getExecution",
"group": "executions",
"weight": 453,
"weight": 454,
"cookies": false,
"type": "",
"demo": "functions\/get-execution.md",
@@ -7649,7 +7666,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 225,
"weight": 214,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7724,7 +7741,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 224,
"weight": 213,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -7797,7 +7814,7 @@
"x-appwrite": {
"method": "get",
"group": null,
"weight": 60,
"weight": 49,
"cookies": false,
"type": "",
"demo": "locale\/get.md",
@@ -7850,7 +7867,7 @@
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 61,
"weight": 50,
"cookies": false,
"type": "",
"demo": "locale\/list-codes.md",
@@ -7903,7 +7920,7 @@
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 65,
"weight": 54,
"cookies": false,
"type": "",
"demo": "locale\/list-continents.md",
@@ -7956,7 +7973,7 @@
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 62,
"weight": 51,
"cookies": false,
"type": "",
"demo": "locale\/list-countries.md",
@@ -8009,7 +8026,7 @@
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 63,
"weight": 52,
"cookies": false,
"type": "",
"demo": "locale\/list-countries-eu.md",
@@ -8062,7 +8079,7 @@
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 64,
"weight": 53,
"cookies": false,
"type": "",
"demo": "locale\/list-countries-phones.md",
@@ -8115,7 +8132,7 @@
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 66,
"weight": 55,
"cookies": false,
"type": "",
"demo": "locale\/list-currencies.md",
@@ -8168,7 +8185,7 @@
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 67,
"weight": 56,
"cookies": false,
"type": "",
"demo": "locale\/list-languages.md",
@@ -8223,7 +8240,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 272,
"weight": 261,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8308,7 +8325,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 276,
"weight": 265,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -8379,7 +8396,7 @@
"x-appwrite": {
"method": "listFiles",
"group": "files",
"weight": 525,
"weight": 526,
"cookies": false,
"type": "",
"demo": "storage\/list-files.md",
@@ -8472,7 +8489,7 @@
"x-appwrite": {
"method": "createFile",
"group": "files",
"weight": 523,
"weight": 524,
"cookies": false,
"type": "upload",
"demo": "storage\/create-file.md",
@@ -8563,7 +8580,7 @@
"x-appwrite": {
"method": "getFile",
"group": "files",
"weight": 524,
"weight": 525,
"cookies": false,
"type": "",
"demo": "storage\/get-file.md",
@@ -8634,7 +8651,7 @@
"x-appwrite": {
"method": "updateFile",
"group": "files",
"weight": 526,
"weight": 527,
"cookies": false,
"type": "",
"demo": "storage\/update-file.md",
@@ -8725,7 +8742,7 @@
"x-appwrite": {
"method": "deleteFile",
"group": "files",
"weight": 527,
"weight": 528,
"cookies": false,
"type": "",
"demo": "storage\/delete-file.md",
@@ -8796,7 +8813,7 @@
"x-appwrite": {
"method": "getFileDownload",
"group": "files",
"weight": 529,
"weight": 530,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-download.md",
@@ -8876,7 +8893,7 @@
"x-appwrite": {
"method": "getFilePreview",
"group": "files",
"weight": 528,
"weight": 529,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-preview.md",
@@ -9084,7 +9101,7 @@
"x-appwrite": {
"method": "getFileView",
"group": "files",
"weight": 530,
"weight": 531,
"cookies": false,
"type": "location",
"demo": "storage\/get-file-view.md",
@@ -9275,7 +9292,8 @@
"type": "integer",
"description": "Seconds before the transaction expires.",
"default": 300,
"x-example": 60
"x-example": 60,
"format": "int32"
}
}
}
@@ -10393,13 +10411,15 @@
"type": "number",
"description": "Value to increment the column by. The value must be a number.",
"default": 1,
"x-example": null
"x-example": null,
"format": "float"
},
"min": {
"type": "number",
"description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.",
"default": null,
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -10513,13 +10533,15 @@
"type": "number",
"description": "Value to increment the column by. The value must be a number.",
"default": 1,
"x-example": null
"x-example": null,
"format": "float"
},
"max": {
"type": "number",
"description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.",
"default": null,
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -10559,7 +10581,7 @@
"x-appwrite": {
"method": "list",
"group": "teams",
"weight": 145,
"weight": 134,
"cookies": false,
"type": "",
"demo": "teams\/list.md",
@@ -10644,7 +10666,7 @@
"x-appwrite": {
"method": "create",
"group": "teams",
"weight": 144,
"weight": 133,
"cookies": false,
"type": "",
"demo": "teams\/create.md",
@@ -10735,7 +10757,7 @@
"x-appwrite": {
"method": "get",
"group": "teams",
"weight": 146,
"weight": 135,
"cookies": false,
"type": "",
"demo": "teams\/get.md",
@@ -10798,7 +10820,7 @@
"x-appwrite": {
"method": "updateName",
"group": "teams",
"weight": 148,
"weight": 137,
"cookies": false,
"type": "",
"demo": "teams\/update-name.md",
@@ -10874,7 +10896,7 @@
"x-appwrite": {
"method": "delete",
"group": "teams",
"weight": 150,
"weight": 139,
"cookies": false,
"type": "",
"demo": "teams\/delete.md",
@@ -10937,7 +10959,7 @@
"x-appwrite": {
"method": "listMemberships",
"group": "memberships",
"weight": 152,
"weight": 141,
"cookies": false,
"type": "",
"demo": "teams\/list-memberships.md",
@@ -11030,7 +11052,7 @@
"x-appwrite": {
"method": "createMembership",
"group": "memberships",
"weight": 151,
"weight": 140,
"cookies": false,
"type": "",
"demo": "teams\/create-membership.md",
@@ -11076,7 +11098,8 @@
"type": "string",
"description": "Email of the new team member.",
"default": "",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"userId": {
"type": "string",
@@ -11088,7 +11111,8 @@
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"default": "",
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
},
"roles": {
"type": "array",
@@ -11110,7 +11134,8 @@
"type": "string",
"description": "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.",
"default": "",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
},
"name": {
"type": "string",
@@ -11151,7 +11176,7 @@
"x-appwrite": {
"method": "getMembership",
"group": "memberships",
"weight": 153,
"weight": 142,
"cookies": false,
"type": "",
"demo": "teams\/get-membership.md",
@@ -11222,7 +11247,7 @@
"x-appwrite": {
"method": "updateMembership",
"group": "memberships",
"weight": 154,
"weight": 143,
"cookies": false,
"type": "",
"demo": "teams\/update-membership.md",
@@ -11316,7 +11341,7 @@
"x-appwrite": {
"method": "deleteMembership",
"group": "memberships",
"weight": 156,
"weight": 145,
"cookies": false,
"type": "",
"demo": "teams\/delete-membership.md",
@@ -11389,7 +11414,7 @@
"x-appwrite": {
"method": "updateMembershipStatus",
"group": "memberships",
"weight": 155,
"weight": 144,
"cookies": false,
"type": "",
"demo": "teams\/update-membership-status.md",
@@ -11485,7 +11510,7 @@
"x-appwrite": {
"method": "getPrefs",
"group": "teams",
"weight": 147,
"weight": 136,
"cookies": false,
"type": "",
"demo": "teams\/get-prefs.md",
@@ -11548,7 +11573,7 @@
"x-appwrite": {
"method": "updatePrefs",
"group": "teams",
"weight": 149,
"weight": 138,
"cookies": false,
"type": "",
"demo": "teams\/update-prefs.md",
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+115 -76
View File
@@ -191,7 +191,8 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
@@ -280,13 +281,15 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"default": null,
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -498,7 +501,8 @@
"type": "integer",
"description": "Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.",
"default": 900,
"x-example": 0
"x-example": 0,
"format": "int32"
}
}
}
@@ -608,7 +612,7 @@
"x-appwrite": {
"method": "updateMFA",
"group": "mfa",
"weight": 288,
"weight": 277,
"cookies": false,
"type": "",
"demo": "account\/update-mfa.md",
@@ -683,7 +687,7 @@
"x-appwrite": {
"method": "createMfaAuthenticator",
"group": "mfa",
"weight": 290,
"weight": 279,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-authenticator.md",
@@ -807,7 +811,7 @@
"x-appwrite": {
"method": "updateMfaAuthenticator",
"group": "mfa",
"weight": 291,
"weight": 280,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-authenticator.md",
@@ -948,7 +952,7 @@
"x-appwrite": {
"method": "deleteMfaAuthenticator",
"group": "mfa",
"weight": 292,
"weight": 281,
"cookies": false,
"type": "",
"demo": "account\/delete-mfa-authenticator.md",
@@ -1072,7 +1076,7 @@
"x-appwrite": {
"method": "createMfaChallenge",
"group": "mfa",
"weight": 296,
"weight": 285,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-challenge.md",
@@ -1209,7 +1213,7 @@
"x-appwrite": {
"method": "updateMfaChallenge",
"group": "mfa",
"weight": 297,
"weight": 286,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-challenge.md",
@@ -1349,7 +1353,7 @@
"x-appwrite": {
"method": "listMfaFactors",
"group": "mfa",
"weight": 289,
"weight": 278,
"cookies": false,
"type": "",
"demo": "account\/list-mfa-factors.md",
@@ -1450,7 +1454,7 @@
"x-appwrite": {
"method": "getMfaRecoveryCodes",
"group": "mfa",
"weight": 295,
"weight": 284,
"cookies": false,
"type": "",
"demo": "account\/get-mfa-recovery-codes.md",
@@ -1551,7 +1555,7 @@
"x-appwrite": {
"method": "createMfaRecoveryCodes",
"group": "mfa",
"weight": 293,
"weight": 282,
"cookies": false,
"type": "",
"demo": "account\/create-mfa-recovery-codes.md",
@@ -1652,7 +1656,7 @@
"x-appwrite": {
"method": "updateMfaRecoveryCodes",
"group": "mfa",
"weight": 294,
"weight": 283,
"cookies": false,
"type": "",
"demo": "account\/update-mfa-recovery-codes.md",
@@ -1874,7 +1878,8 @@
"type": "string",
"description": "Current user password. Must be at least 8 chars.",
"default": "",
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -1949,13 +1954,15 @@
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"default": null,
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"default": null,
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -2160,13 +2167,15 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"url": {
"type": "string",
"description": "URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"default": null,
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
}
},
"required": [
@@ -2485,13 +2494,15 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"password": {
"type": "string",
"description": "User password. Must be at least 8 chars.",
"default": null,
"x-example": "password"
"x-example": "password",
"format": "password"
}
},
"required": [
@@ -2683,9 +2694,7 @@
"yammer",
"yandex",
"zoho",
"zoom",
"mock",
"mock-unverified"
"zoom"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": [],
@@ -3429,7 +3438,8 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"phrase": {
"type": "boolean",
@@ -3520,13 +3530,15 @@
"type": "string",
"description": "User email.",
"default": null,
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"url": {
"type": "string",
"description": "URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"default": "",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
},
"phrase": {
"type": "boolean",
@@ -3638,9 +3650,7 @@
"yammer",
"yandex",
"zoho",
"zoom",
"mock",
"mock-unverified"
"zoom"
],
"x-enum-name": "OAuthProvider",
"x-enum-keys": [],
@@ -3754,7 +3764,8 @@
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"default": null,
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
}
},
"required": [
@@ -3882,7 +3893,8 @@
"type": "string",
"description": "URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https:\/\/cheatsheetseries.owasp.org\/cheatsheets\/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.",
"default": null,
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
}
},
"required": [
@@ -4191,7 +4203,7 @@
"x-appwrite": {
"method": "getBrowser",
"group": null,
"weight": 50,
"weight": 288,
"cookies": false,
"type": "location",
"demo": "avatars\/get-browser.md",
@@ -4317,7 +4329,7 @@
"x-appwrite": {
"method": "getCreditCard",
"group": null,
"weight": 49,
"weight": 287,
"cookies": false,
"type": "location",
"demo": "avatars\/get-credit-card.md",
@@ -4449,7 +4461,7 @@
"x-appwrite": {
"method": "getFavicon",
"group": null,
"weight": 53,
"weight": 291,
"cookies": false,
"type": "location",
"demo": "avatars\/get-favicon.md",
@@ -4513,7 +4525,7 @@
"x-appwrite": {
"method": "getFlag",
"group": null,
"weight": 51,
"weight": 289,
"cookies": false,
"type": "location",
"demo": "avatars\/get-flag.md",
@@ -5001,7 +5013,7 @@
"x-appwrite": {
"method": "getImage",
"group": null,
"weight": 52,
"weight": 290,
"cookies": false,
"type": "location",
"demo": "avatars\/get-image.md",
@@ -5085,7 +5097,7 @@
"x-appwrite": {
"method": "getInitials",
"group": null,
"weight": 55,
"weight": 293,
"cookies": false,
"type": "location",
"demo": "avatars\/get-initials.md",
@@ -5177,7 +5189,7 @@
"x-appwrite": {
"method": "getQR",
"group": null,
"weight": 54,
"weight": 292,
"cookies": false,
"type": "location",
"demo": "avatars\/get-qr.md",
@@ -5269,7 +5281,7 @@
"x-appwrite": {
"method": "getScreenshot",
"group": null,
"weight": 56,
"weight": 294,
"cookies": false,
"type": "location",
"demo": "avatars\/get-screenshot.md",
@@ -6087,7 +6099,8 @@
"type": "integer",
"description": "Seconds before the transaction expires.",
"default": 300,
"x-example": 60
"x-example": 60,
"format": "int32"
}
}
}
@@ -7208,13 +7221,15 @@
"type": "number",
"description": "Value to increment the attribute by. The value must be a number.",
"default": 1,
"x-example": null
"x-example": null,
"format": "float"
},
"min": {
"type": "number",
"description": "Minimum value for the attribute. If the current value is lesser than this value, an exception will be thrown.",
"default": null,
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -7329,13 +7344,15 @@
"type": "number",
"description": "Value to increment the attribute by. The value must be a number.",
"default": 1,
"x-example": null
"x-example": null,
"format": "float"
},
"max": {
"type": "number",
"description": "Maximum value for the attribute. If the current value is greater than this value, an error will be thrown.",
"default": null,
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -7649,7 +7666,7 @@
"x-appwrite": {
"method": "query",
"group": "graphql",
"weight": 225,
"weight": 214,
"cookies": false,
"type": "graphql",
"demo": "graphql\/query.md",
@@ -7724,7 +7741,7 @@
"x-appwrite": {
"method": "mutation",
"group": "graphql",
"weight": 224,
"weight": 213,
"cookies": false,
"type": "graphql",
"demo": "graphql\/mutation.md",
@@ -7797,7 +7814,7 @@
"x-appwrite": {
"method": "get",
"group": null,
"weight": 60,
"weight": 49,
"cookies": false,
"type": "",
"demo": "locale\/get.md",
@@ -7850,7 +7867,7 @@
"x-appwrite": {
"method": "listCodes",
"group": null,
"weight": 61,
"weight": 50,
"cookies": false,
"type": "",
"demo": "locale\/list-codes.md",
@@ -7903,7 +7920,7 @@
"x-appwrite": {
"method": "listContinents",
"group": null,
"weight": 65,
"weight": 54,
"cookies": false,
"type": "",
"demo": "locale\/list-continents.md",
@@ -7956,7 +7973,7 @@
"x-appwrite": {
"method": "listCountries",
"group": null,
"weight": 62,
"weight": 51,
"cookies": false,
"type": "",
"demo": "locale\/list-countries.md",
@@ -8009,7 +8026,7 @@
"x-appwrite": {
"method": "listCountriesEU",
"group": null,
"weight": 63,
"weight": 52,
"cookies": false,
"type": "",
"demo": "locale\/list-countries-eu.md",
@@ -8062,7 +8079,7 @@
"x-appwrite": {
"method": "listCountriesPhones",
"group": null,
"weight": 64,
"weight": 53,
"cookies": false,
"type": "",
"demo": "locale\/list-countries-phones.md",
@@ -8115,7 +8132,7 @@
"x-appwrite": {
"method": "listCurrencies",
"group": null,
"weight": 66,
"weight": 55,
"cookies": false,
"type": "",
"demo": "locale\/list-currencies.md",
@@ -8168,7 +8185,7 @@
"x-appwrite": {
"method": "listLanguages",
"group": null,
"weight": 67,
"weight": 56,
"cookies": false,
"type": "",
"demo": "locale\/list-languages.md",
@@ -8223,7 +8240,7 @@
"x-appwrite": {
"method": "createSubscriber",
"group": "subscribers",
"weight": 272,
"weight": 261,
"cookies": false,
"type": "",
"demo": "messaging\/create-subscriber.md",
@@ -8308,7 +8325,7 @@
"x-appwrite": {
"method": "deleteSubscriber",
"group": "subscribers",
"weight": 276,
"weight": 265,
"cookies": false,
"type": "",
"demo": "messaging\/delete-subscriber.md",
@@ -9275,7 +9292,8 @@
"type": "integer",
"description": "Seconds before the transaction expires.",
"default": 300,
"x-example": 60
"x-example": 60,
"format": "int32"
}
}
}
@@ -10393,13 +10411,15 @@
"type": "number",
"description": "Value to increment the column by. The value must be a number.",
"default": 1,
"x-example": null
"x-example": null,
"format": "float"
},
"min": {
"type": "number",
"description": "Minimum value for the column. If the current value is lesser than this value, an exception will be thrown.",
"default": null,
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -10513,13 +10533,15 @@
"type": "number",
"description": "Value to increment the column by. The value must be a number.",
"default": 1,
"x-example": null
"x-example": null,
"format": "float"
},
"max": {
"type": "number",
"description": "Maximum value for the column. If the current value is greater than this value, an error will be thrown.",
"default": null,
"x-example": null,
"format": "float",
"x-nullable": true
},
"transactionId": {
@@ -10559,7 +10581,7 @@
"x-appwrite": {
"method": "list",
"group": "teams",
"weight": 145,
"weight": 134,
"cookies": false,
"type": "",
"demo": "teams\/list.md",
@@ -10644,7 +10666,7 @@
"x-appwrite": {
"method": "create",
"group": "teams",
"weight": 144,
"weight": 133,
"cookies": false,
"type": "",
"demo": "teams\/create.md",
@@ -10735,7 +10757,7 @@
"x-appwrite": {
"method": "get",
"group": "teams",
"weight": 146,
"weight": 135,
"cookies": false,
"type": "",
"demo": "teams\/get.md",
@@ -10798,7 +10820,7 @@
"x-appwrite": {
"method": "updateName",
"group": "teams",
"weight": 148,
"weight": 137,
"cookies": false,
"type": "",
"demo": "teams\/update-name.md",
@@ -10874,7 +10896,7 @@
"x-appwrite": {
"method": "delete",
"group": "teams",
"weight": 150,
"weight": 139,
"cookies": false,
"type": "",
"demo": "teams\/delete.md",
@@ -10937,7 +10959,7 @@
"x-appwrite": {
"method": "listMemberships",
"group": "memberships",
"weight": 152,
"weight": 141,
"cookies": false,
"type": "",
"demo": "teams\/list-memberships.md",
@@ -11030,7 +11052,7 @@
"x-appwrite": {
"method": "createMembership",
"group": "memberships",
"weight": 151,
"weight": 140,
"cookies": false,
"type": "",
"demo": "teams\/create-membership.md",
@@ -11076,7 +11098,8 @@
"type": "string",
"description": "Email of the new team member.",
"default": "",
"x-example": "email@example.com"
"x-example": "email@example.com",
"format": "email"
},
"userId": {
"type": "string",
@@ -11088,7 +11111,8 @@
"type": "string",
"description": "Phone number. Format this number with a leading '+' and a country code, e.g., +16175551212.",
"default": "",
"x-example": "+12065550100"
"x-example": "+12065550100",
"format": "phone"
},
"roles": {
"type": "array",
@@ -11110,7 +11134,8 @@
"type": "string",
"description": "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.",
"default": "",
"x-example": "https:\/\/example.com"
"x-example": "https:\/\/example.com",
"format": "url"
},
"name": {
"type": "string",
@@ -11151,7 +11176,7 @@
"x-appwrite": {
"method": "getMembership",
"group": "memberships",
"weight": 153,
"weight": 142,
"cookies": false,
"type": "",
"demo": "teams\/get-membership.md",
@@ -11222,7 +11247,7 @@
"x-appwrite": {
"method": "updateMembership",
"group": "memberships",
"weight": 154,
"weight": 143,
"cookies": false,
"type": "",
"demo": "teams\/update-membership.md",
@@ -11316,7 +11341,7 @@
"x-appwrite": {
"method": "deleteMembership",
"group": "memberships",
"weight": 156,
"weight": 145,
"cookies": false,
"type": "",
"demo": "teams\/delete-membership.md",
@@ -11389,7 +11414,7 @@
"x-appwrite": {
"method": "updateMembershipStatus",
"group": "memberships",
"weight": 155,
"weight": 144,
"cookies": false,
"type": "",
"demo": "teams\/update-membership-status.md",
@@ -11485,7 +11510,7 @@
"x-appwrite": {
"method": "getPrefs",
"group": "teams",
"weight": 147,
"weight": 136,
"cookies": false,
"type": "",
"demo": "teams\/get-prefs.md",
@@ -11548,7 +11573,7 @@
"x-appwrite": {
"method": "updatePrefs",
"group": "teams",
"weight": 149,
"weight": 138,
"cookies": false,
"type": "",
"demo": "teams\/update-prefs.md",
@@ -13368,6 +13393,16 @@
"description": "Total number of chunks uploaded",
"x-example": 17890,
"format": "int32"
},
"encryption": {
"type": "boolean",
"description": "Whether file contents are encrypted at rest.",
"x-example": true
},
"compression": {
"type": "string",
"description": "Compression algorithm used for the file. Will be one of none, [gzip](https:\/\/en.wikipedia.org\/wiki\/Gzip), or [zstd](https:\/\/en.wikipedia.org\/wiki\/Zstd).",
"x-example": "gzip"
}
},
"required": [
@@ -13381,7 +13416,9 @@
"mimeType",
"sizeOriginal",
"chunksTotal",
"chunksUploaded"
"chunksUploaded",
"encryption",
"compression"
],
"example": {
"$id": "5e5ea5c16897e",
@@ -13396,7 +13433,9 @@
"mimeType": "image\/png",
"sizeOriginal": 17890,
"chunksTotal": 17890,
"chunksUploaded": 17890
"chunksUploaded": 17890,
"encryption": true,
"compression": "gzip"
}
},
"team": {
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -3,4 +3,6 @@
use Utopia\Image\Image;
use Utopia\System\System;
Image::setResourceLimit('memory', intval(System::getEnv('_APP_IMAGES_RESOURCE_LIMIT_MEMORY', 1024*1024*64)));
if (\class_exists('Imagick')) {
Image::setResourceLimit('memory', intval(System::getEnv('_APP_IMAGES_RESOURCE_LIMIT_MEMORY', 1024*1024*64)));
}
+84 -62
View File
@@ -207,10 +207,10 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, arr
}
$createSession = function (string $userId, string $secret, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Store $store, ProofsToken $proofForToken, ProofsCode $proofForCode) {
$createSession = function (string $userId, string $secret, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Store $store, ProofsToken $proofForToken, ProofsCode $proofForCode, Authorization $authorization) {
/** @var Appwrite\Utopia\Database\Documents\User $userFromRequest */
$userFromRequest = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
$userFromRequest = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId));
if ($userFromRequest->isEmpty()) {
throw new Exception(Exception::USER_INVALID_TOKEN);
@@ -266,7 +266,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
$detector->getDevice()
));
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$session = $dbForProject->createDocument('sessions', $session
->setAttribute('$permissions', [
@@ -275,7 +275,7 @@ $createSession = function (string $userId, string $secret, Request $request, Res
Permission::delete(Role::user($user->getId())),
]));
Authorization::skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
$authorization->skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
$dbForProject->purgeCachedDocument('users', $user->getId());
// Magic URL + Email OTP
@@ -376,8 +376,9 @@ App::post('/v1/account')
->inject('user')
->inject('project')
->inject('dbForProject')
->inject('authorization')
->inject('hooks')
->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Hooks $hooks) {
->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Authorization $authorization, Hooks $hooks) {
$email = \strtolower($email);
if ('console' === $project->getId()) {
@@ -469,9 +470,9 @@ App::post('/v1/account')
]);
$user->removeAttribute('$sequence');
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@@ -497,9 +498,9 @@ App::post('/v1/account')
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
Authorization::unsetRole(Role::guests()->toString());
Authorization::setRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::users()->toString());
$authorization->removeRole(Role::guests()->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::users()->toString());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
@@ -976,7 +977,8 @@ App::post('/v1/account/sessions/email')
->inject('store')
->inject('proofForPassword')
->inject('proofForToken')
->action(function (string $email, string $password, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken) {
->inject('authorization')
->action(function (string $email, string $password, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) {
$email = \strtolower($email);
$protocol = $request->getProtocol();
@@ -1021,7 +1023,7 @@ App::post('/v1/account/sessions/email')
$detector->getDevice()
));
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
// Re-hash if not using recommended algo
if ($user->getAttribute('hash') !== $proofForPassword->getHash()->getName()) {
@@ -1120,7 +1122,8 @@ App::post('/v1/account/sessions/anonymous')
->inject('store')
->inject('proofForPassword')
->inject('proofForToken')
->action(function (Request $request, Response $response, Locale $locale, User $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken) {
->inject('authorization')
->action(function (Request $request, Response $response, Locale $locale, User $user, Document $project, Database $dbForProject, Reader $geodb, Event $queueForEvents, Store $store, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) {
$protocol = $request->getProtocol();
if ('console' === $project->getId()) {
@@ -1165,7 +1168,7 @@ App::post('/v1/account/sessions/anonymous')
'accessedAt' => DateTime::now(),
]);
$user->removeAttribute('$sequence');
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
// Create session token
$duration = $project->getAttribute('auths', [])['duration'] ?? TOKEN_EXPIRATION_LOGIN_LONG;
@@ -1191,7 +1194,7 @@ App::post('/v1/account/sessions/anonymous')
$detector->getDevice()
));
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$session = $dbForProject->createDocument('sessions', $session->setAttribute('$permissions', [
Permission::read(Role::user($user->getId())),
@@ -1274,6 +1277,7 @@ App::post('/v1/account/sessions/token')
->inject('store')
->inject('proofForToken')
->inject('proofForCode')
->inject('authorization')
->action($createSession);
App::get('/v1/account/sessions/oauth2/:provider')
@@ -1470,7 +1474,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->inject('store')
->inject('proofForPassword')
->inject('proofForToken')
->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) use ($oauthDefaultSuccess) {
->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) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
@@ -1726,7 +1731,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
]);
$user->removeAttribute('$sequence');
$userDoc = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$userDoc = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
$dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
@@ -1744,8 +1749,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
}
}
Authorization::setRole(Role::user($user->getId())->toString());
Authorization::setRole(Role::users()->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::users()->toString());
if (false === $user->getAttribute('status')) { // Account is blocked
$failureRedirect(Exception::USER_BLOCKED); // User is in status blocked
@@ -1816,7 +1821,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$dbForProject->updateDocument('users', $user->getId(), $user);
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$state['success'] = URLParser::parse($state['success']);
$query = URLParser::parseQuery($state['success']['query']);
@@ -1840,7 +1845,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -2077,7 +2082,8 @@ App::post('/v1/account/tokens/magic-url')
->inject('queueForMails')
->inject('proofForPassword')
->inject('platform')
->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, User $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, array $platform) {
->inject('authorization')
->action(function (string $userId, string $email, string $url, bool $phrase, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, array $platform, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
}
@@ -2150,7 +2156,7 @@ App::post('/v1/account/tokens/magic-url')
]);
$user->removeAttribute('$sequence');
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
}
$proofForToken = new ProofsToken(TOKEN_LENGTH_MAGIC_URL);
@@ -2170,7 +2176,7 @@ App::post('/v1/account/tokens/magic-url')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -2356,7 +2362,8 @@ App::post('/v1/account/tokens/email')
->inject('queueForMails')
->inject('proofForPassword')
->inject('proofForCode')
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, User $user, Document $project, array $platform, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, ProofsCode $proofForCode) {
->inject('authorization')
->action(function (string $userId, string $email, bool $phrase, Request $request, Response $response, User $user, Document $project, array $platform, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsPassword $proofForPassword, ProofsCode $proofForCode, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP disabled');
}
@@ -2425,9 +2432,9 @@ App::post('/v1/account/tokens/email')
]);
$user->removeAttribute('$sequence');
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@@ -2465,7 +2472,7 @@ App::post('/v1/account/tokens/email')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -2662,10 +2669,11 @@ App::put('/v1/account/sessions/magic-url')
->inject('queueForMails')
->inject('store')
->inject('proofForCode')
->action(function ($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForCode) use ($createSession) {
->inject('authorization')
->action(function ($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForCode, $authorization) use ($createSession) {
$proofForToken = new ProofsToken(TOKEN_LENGTH_MAGIC_URL);
$proofForToken->setHash(new Sha());
$createSession($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForToken, $proofForCode);
$createSession($userId, $secret, $request, $response, $user, $dbForProject, $project, $platform, $locale, $geodb, $queueForEvents, $queueForMails, $store, $proofForToken, $proofForCode, $authorization);
});
App::put('/v1/account/sessions/phone')
@@ -2711,6 +2719,7 @@ App::put('/v1/account/sessions/phone')
->inject('store')
->inject('proofForToken')
->inject('proofForCode')
->inject('authorization')
->action($createSession);
App::post('/v1/account/tokens/phone')
@@ -2754,7 +2763,8 @@ App::post('/v1/account/tokens/phone')
->inject('plan')
->inject('store')
->inject('proofForCode')
->action(function (string $userId, string $phone, Request $request, Response $response, User $user, Document $project, array $platform, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Store $store, ProofsCode $proofForCode) {
->inject('authorization')
->action(function (string $userId, string $phone, Request $request, Response $response, User $user, Document $project, array $platform, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Store $store, ProofsCode $proofForCode, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@@ -2804,9 +2814,9 @@ App::post('/v1/account/tokens/phone')
]);
$user->removeAttribute('$sequence');
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
$user = $authorization->skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
$target = $authorization->skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
@@ -2852,7 +2862,7 @@ App::post('/v1/account/tokens/phone')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$token = $dbForProject->createDocument('tokens', $token
->setAttribute('$permissions', [
@@ -3243,7 +3253,8 @@ App::patch('/v1/account/email')
->inject('project')
->inject('hooks')
->inject('proofForPassword')
->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword) {
->inject('authorization')
->action(function (string $email, string $password, ?\DateTime $requestTimestamp, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword, Authorization $authorization) {
// passwordUpdate will be empty if the user has never set a password
$passwordUpdate = $user->getAttribute('passwordUpdate');
@@ -3295,7 +3306,7 @@ App::patch('/v1/account/email')
->setAttribute('passwordUpdate', DateTime::now());
}
$target = Authorization::skip(fn () => $dbForProject->findOne('targets', [
$target = $authorization->skip(fn () => $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]));
@@ -3311,7 +3322,7 @@ App::patch('/v1/account/email')
$oldTarget = $user->find('identifier', $oldEmail, 'targets');
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
$authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $email)));
}
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate) {
@@ -3352,8 +3363,9 @@ App::patch('/v1/account/phone')
->inject('queueForEvents')
->inject('project')
->inject('hooks')
->inject('proofForPassword')
->action(function (string $phone, string $password, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword) {
->inject('proofForPassword')
->inject('authorization')
->action(function (string $phone, string $password, Response $response, Document $user, Database $dbForProject, Event $queueForEvents, Document $project, Hooks $hooks, ProofsPassword $proofForPassword, Authorization $authorization) {
// passwordUpdate will be empty if the user has never set a password
$passwordUpdate = $user->getAttribute('passwordUpdate');
@@ -3368,7 +3380,7 @@ App::patch('/v1/account/phone')
$hooks->trigger('passwordValidator', [$dbForProject, $project, $password, &$user, false]);
$target = Authorization::skip(fn () => $dbForProject->findOne('targets', [
$target = $authorization->skip(fn () => $dbForProject->findOne('targets', [
Query::equal('identifier', [$phone]),
]));
@@ -3399,7 +3411,7 @@ App::patch('/v1/account/phone')
$oldTarget = $user->find('identifier', $oldPhone, 'targets');
if ($oldTarget instanceof Document && !$oldTarget->isEmpty()) {
Authorization::skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
$authorization->skip(fn () => $dbForProject->updateDocument('targets', $oldTarget->getId(), $oldTarget->setAttribute('identifier', $phone)));
}
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Duplicate $th) {
@@ -3535,7 +3547,9 @@ App::post('/v1/account/recovery')
->inject('queueForMails')
->inject('queueForEvents')
->inject('proofForToken')
->action(function (string $email, string $url, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Mail $queueForMails, Event $queueForEvents, ProofsToken $proofForToken) {
->inject('authorization')
->action(function (string $email, string $url, Request $request, Response $response, User $user, Database $dbForProject, Document $project, array $platform, Locale $locale, Mail $queueForMails, Event $queueForEvents, ProofsToken $proofForToken, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
}
@@ -3571,7 +3585,7 @@ App::post('/v1/account/recovery')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($profile->getId())->toString());
$authorization->addRole(Role::user($profile->getId())->toString());
$recovery = $dbForProject->createDocument('tokens', $recovery
->setAttribute('$permissions', [
@@ -3727,7 +3741,8 @@ App::put('/v1/account/recovery')
->inject('hooks')
->inject('proofForPassword')
->inject('proofForToken')
->action(function (string $userId, string $secret, string $password, Response $response, User $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, ProofsPassword $proofForPassword, ProofsToken $proofForToken) {
->inject('authorization')
->action(function (string $userId, string $secret, string $password, Response $response, User $user, Database $dbForProject, Document $project, Event $queueForEvents, Hooks $hooks, ProofsPassword $proofForPassword, ProofsToken $proofForToken, Authorization $authorization) {
/** @var Appwrite\Utopia\Database\Documents\User $profile */
$profile = $dbForProject->getDocument('users', $userId);
@@ -3741,7 +3756,7 @@ App::put('/v1/account/recovery')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
Authorization::setRole(Role::user($profile->getId())->toString());
$authorization->addRole(Role::user($profile->getId())->toString());
$newPassword = $proofForPassword->hash($password);
@@ -3844,7 +3859,8 @@ App::post('/v1/account/verifications/email')
->inject('queueForEvents')
->inject('queueForMails')
->inject('proofForToken')
->action(function (string $url, Request $request, Response $response, Document $project, array $platform, User $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsToken $proofForToken) {
->inject('authorization')
->action(function (string $url, Request $request, Response $response, Document $project, array $platform, User $user, Database $dbForProject, Locale $locale, Event $queueForEvents, Mail $queueForMails, ProofsToken $proofForToken, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMTP_HOST'))) {
throw new Exception(Exception::GENERAL_SMTP_DISABLED, 'SMTP Disabled');
@@ -3873,7 +3889,7 @@ App::post('/v1/account/verifications/email')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$verification = $dbForProject->createDocument('tokens', $verification
->setAttribute('$permissions', [
@@ -4072,9 +4088,10 @@ App::put('/v1/account/verifications/email')
->inject('dbForProject')
->inject('queueForEvents')
->inject('proofForToken')
->action(function (string $userId, string $secret, Response $response, User $user, Database $dbForProject, Event $queueForEvents, ProofsToken $proofForToken) {
->inject('authorization')
->action(function (string $userId, string $secret, Response $response, User $user, Database $dbForProject, Event $queueForEvents, ProofsToken $proofForToken, Authorization $authorization) {
/** @var Appwrite\Utopia\Database\Documents\User $profile */
$profile = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
$profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId));
if ($profile->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -4086,7 +4103,7 @@ App::put('/v1/account/verifications/email')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
Authorization::setRole(Role::user($profile->getId())->toString());
$authorization->addRole(Role::user($profile->getId())->toString());
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('emailVerification', true));
@@ -4146,7 +4163,8 @@ App::post('/v1/account/verifications/phone')
->inject('queueForStatsUsage')
->inject('plan')
->inject('proofForCode')
->action(function (Request $request, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, ProofsCode $proofForCode) {
->inject('authorization')
->action(function (Request $request, Response $response, User $user, Database $dbForProject, Event $queueForEvents, Messaging $queueForMessaging, Document $project, Locale $locale, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, ProofsCode $proofForCode, Authorization $authorization) {
if (empty(System::getEnv('_APP_SMS_PROVIDER'))) {
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
@@ -4185,7 +4203,7 @@ App::post('/v1/account/verifications/phone')
'ip' => $request->getIP(),
]);
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$verification = $dbForProject->createDocument('tokens', $verification
->setAttribute('$permissions', [
@@ -4291,9 +4309,10 @@ App::put('/v1/account/verifications/phone')
->inject('dbForProject')
->inject('queueForEvents')
->inject('proofForCode')
->action(function (string $userId, string $secret, Response $response, User $user, Database $dbForProject, Event $queueForEvents, ProofsCode $proofForCode) {
->inject('authorization')
->action(function (string $userId, string $secret, Response $response, User $user, Database $dbForProject, Event $queueForEvents, ProofsCode $proofForCode, Authorization $authorization) {
/** @var Appwrite\Utopia\Database\Documents\User $profile */
$profile = Authorization::skip(fn () => $dbForProject->getDocument('users', $userId));
$profile = $authorization->skip(fn () => $dbForProject->getDocument('users', $userId));
if ($profile->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -4305,7 +4324,7 @@ App::put('/v1/account/verifications/phone')
throw new Exception(Exception::USER_INVALID_TOKEN);
}
Authorization::setRole(Role::user($profile->getId())->toString());
$authorization->addRole(Role::user($profile->getId())->toString());
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile->setAttribute('phoneVerification', true));
@@ -4358,12 +4377,13 @@ App::post('/v1/account/targets/push')
->inject('dbForProject')
->inject('store')
->inject('proofForToken')
->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, User $user, Request $request, Response $response, Database $dbForProject, Store $store, ProofsToken $proofForToken) {
->inject('authorization')
->action(function (string $targetId, string $identifier, string $providerId, Event $queueForEvents, User $user, Request $request, Response $response, Database $dbForProject, Store $store, ProofsToken $proofForToken, Authorization $authorization) {
$targetId = $targetId == 'unique()' ? ID::unique() : $targetId;
$provider = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
$provider = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId));
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if (!$target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_ALREADY_EXISTS);
@@ -4438,9 +4458,10 @@ App::put('/v1/account/targets/:targetId/push')
->inject('request')
->inject('response')
->inject('dbForProject')
->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject) {
->inject('authorization')
->action(function (string $targetId, string $identifier, Event $queueForEvents, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) {
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
@@ -4503,8 +4524,9 @@ App::delete('/v1/account/targets/:targetId/push')
->inject('request')
->inject('response')
->inject('dbForProject')
->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject) {
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
->inject('authorization')
->action(function (string $targetId, Event $queueForEvents, Delete $queueForDeletes, Document $user, Request $request, Response $response, Database $dbForProject, Authorization $authorization) {
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
+3 -2
View File
@@ -28,11 +28,12 @@ use Utopia\Validator\Text;
App::init()
->groups(['graphql'])
->inject('project')
->action(function (Document $project) {
->inject('authorization')
->action(function (Document $project, Authorization $authorization) {
if (
array_key_exists('graphql', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['graphql']
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
&& !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
+35 -30
View File
@@ -36,6 +36,7 @@ use Utopia\Database\Exception\Query as QueryException;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Queries;
use Utopia\Database\Validator\Query\Cursor;
@@ -1073,8 +1074,9 @@ App::get('/v1/messaging/providers')
->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('dbForProject')
->inject('authorization')
->inject('response')
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Response $response) {
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Authorization $authorization, Response $response) {
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
@@ -1100,7 +1102,7 @@ App::get('/v1/messaging/providers')
}
$providerId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('providers', $providerId));
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('providers', $providerId));
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Provider '{$providerId}' for the 'cursor' value not found.");
@@ -2481,8 +2483,9 @@ App::get('/v1/messaging/topics')
->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('dbForProject')
->inject('authorization')
->inject('response')
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Response $response) {
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Authorization $authorization, Response $response) {
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
@@ -2508,7 +2511,7 @@ App::get('/v1/messaging/topics')
}
$topicId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Topic '{$topicId}' for the 'cursor' value not found.");
@@ -2782,29 +2785,27 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
->param('targetId', '', new UID(), 'Target ID. The target ID to link to the specified Topic ID.')
->inject('queueForEvents')
->inject('dbForProject')
->inject('authorization')
->inject('response')
->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Response $response) {
->action(function (string $subscriberId, string $topicId, string $targetId, Event $queueForEvents, Database $dbForProject, Authorization $authorization, Response $response) {
$subscriberId = $subscriberId == 'unique()' ? ID::unique() : $subscriberId;
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
}
$validator = new Authorization('subscribe');
if (!$validator->isValid($topic->getAttribute('subscribe'))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $validator->getDescription());
if (!$authorization->isValid(new Input('subscribe', $topic->getAttribute('subscribe')))) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
}
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $targetId));
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $targetId));
if ($target->isEmpty()) {
throw new Exception(Exception::USER_TARGET_NOT_FOUND);
}
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$subscriber = new Document([
'$id' => $subscriberId,
@@ -2837,7 +2838,7 @@ App::post('/v1/messaging/topics/:topicId/subscribers')
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
};
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute(
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
@@ -2882,8 +2883,9 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
->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('dbForProject')
->inject('authorization')
->inject('response')
->action(function (string $topicId, array $queries, string $search, bool $includeTotal, Database $dbForProject, Response $response) {
->action(function (string $topicId, array $queries, string $search, bool $includeTotal, Database $dbForProject, Authorization $authorization, Response $response) {
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
@@ -2894,7 +2896,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
$queries[] = Query::search('search', $search);
}
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
@@ -2917,7 +2919,7 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
}
$subscriberId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId));
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('subscribers', $subscriberId));
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Subscriber '{$subscriberId}' for the 'cursor' value not found.");
@@ -2931,10 +2933,10 @@ App::get('/v1/messaging/topics/:topicId/subscribers')
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.");
}
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject) {
return function () use ($subscriber, $dbForProject) {
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$subscribers = batch(\array_map(function (Document $subscriber) use ($dbForProject, $authorization) {
return function () use ($subscriber, $dbForProject, $authorization) {
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
return $subscriber
->setAttribute('target', $target)
@@ -3067,9 +3069,10 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->param('topicId', '', new UID(), 'Topic ID. The topic ID subscribed to.')
->param('subscriberId', '', new UID(), 'Subscriber ID.')
->inject('dbForProject')
->inject('authorization')
->inject('response')
->action(function (string $topicId, string $subscriberId, Database $dbForProject, Response $response) {
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
->action(function (string $topicId, string $subscriberId, Database $dbForProject, Authorization $authorization, Response $response) {
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
@@ -3081,8 +3084,8 @@ App::get('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
throw new Exception(Exception::SUBSCRIBER_NOT_FOUND);
}
$target = Authorization::skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = Authorization::skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$target = $authorization->skip(fn () => $dbForProject->getDocument('targets', $subscriber->getAttribute('targetId')));
$user = $authorization->skip(fn () => $dbForProject->getDocument('users', $target->getAttribute('userId')));
$subscriber
->setAttribute('target', $target)
@@ -3118,9 +3121,10 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
->param('subscriberId', '', new UID(), 'Subscriber ID.')
->inject('queueForEvents')
->inject('dbForProject')
->inject('authorization')
->inject('response')
->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Response $response) {
$topic = Authorization::skip(fn () => $dbForProject->getDocument('topics', $topicId));
->action(function (string $topicId, string $subscriberId, Event $queueForEvents, Database $dbForProject, Authorization $authorization, Response $response) {
$topic = $authorization->skip(fn () => $dbForProject->getDocument('topics', $topicId));
if ($topic->isEmpty()) {
throw new Exception(Exception::TOPIC_NOT_FOUND);
@@ -3143,7 +3147,7 @@ App::delete('/v1/messaging/topics/:topicId/subscribers/:subscriberId')
default => throw new Exception(Exception::TARGET_PROVIDER_INVALID_TYPE),
};
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute(
$authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute(
'topics',
$topicId,
$totalAttribute,
@@ -3702,8 +3706,9 @@ App::get('/v1/messaging/messages')
->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('dbForProject')
->inject('authorization')
->inject('response')
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Response $response) {
->action(function (array $queries, string $search, bool $includeTotal, Database $dbForProject, Authorization $authorization, Response $response) {
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
@@ -3729,7 +3734,7 @@ App::get('/v1/messaging/messages')
}
$messageId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('messages', $messageId));
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('messages', $messageId));
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Message '{$messageId}' for the 'cursor' value not found.");
+9 -5
View File
@@ -342,6 +342,7 @@ App::post('/v1/migrations/csv/imports')
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('authorization')
->inject('project')
->inject('platform')
->inject('deviceForFiles')
@@ -356,6 +357,7 @@ App::post('/v1/migrations/csv/imports')
Response $response,
Database $dbForProject,
Database $dbForPlatform,
Authorization $authorization,
Document $project,
array $platform,
Device $deviceForFiles,
@@ -363,7 +365,7 @@ App::post('/v1/migrations/csv/imports')
Event $queueForEvents,
Migration $queueForMigrations
) {
$bucket = Authorization::skip(function () use ($internalFile, $dbForPlatform, $dbForProject, $bucketId) {
$bucket = $authorization->skip(function () use ($internalFile, $dbForPlatform, $dbForProject, $bucketId) {
if ($internalFile) {
return $dbForPlatform->getDocument('buckets', 'default');
}
@@ -374,7 +376,7 @@ App::post('/v1/migrations/csv/imports')
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
$file = Authorization::skip(fn () => $internalFile ? $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $fileId) : $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $authorization->skip(fn () => $internalFile ? $dbForPlatform->getDocument('bucket_' . $bucket->getSequence(), $fileId) : $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
if ($file->isEmpty()) {
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
@@ -491,6 +493,7 @@ App::post('/v1/migrations/csv/exports')
->inject('response')
->inject('dbForProject')
->inject('dbForPlatform')
->inject('authorization')
->inject('project')
->inject('platform')
->inject('queueForEvents')
@@ -509,6 +512,7 @@ App::post('/v1/migrations/csv/exports')
Response $response,
Database $dbForProject,
Database $dbForPlatform,
Authorization $authorization,
Document $project,
array $platform,
Event $queueForEvents,
@@ -520,7 +524,7 @@ App::post('/v1/migrations/csv/exports')
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
$bucket = Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'default'));
$bucket = $authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'default'));
if ($bucket->isEmpty()) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
}
@@ -533,12 +537,12 @@ App::post('/v1/migrations/csv/exports')
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception(Exception::COLLECTION_NOT_FOUND);
}
+5 -4
View File
@@ -45,9 +45,10 @@ App::get('/v1/project/usage')
->inject('response')
->inject('project')
->inject('dbForProject')
->inject('authorization')
->inject('getLogsDB')
->inject('smsRates')
->action(function (string $startDate, string $endDate, string $period, Response $response, Document $project, Database $dbForProject, callable $getLogsDB, array $smsRates) {
->action(function (string $startDate, string $endDate, string $period, Response $response, Document $project, Database $dbForProject, Authorization $authorization, callable $getLogsDB, array $smsRates) {
$stats = $total = $usage = [];
$format = 'Y-m-d 00:00:00';
$firstDay = (new DateTime($startDate))->format($format);
@@ -102,7 +103,7 @@ App::get('/v1/project/usage')
'1d' => 'Y-m-d\T00:00:00.000P',
};
Authorization::skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
$authorization->skip(function () use ($dbForProject, $dbForLogs, $firstDay, $lastDay, $period, $metrics, $limit, &$total, &$stats) {
foreach ($metrics['total'] as $metric) {
$db = ($metric === METRIC_FILES_IMAGES_TRANSFORMED) ? $dbForLogs : $dbForProject;
@@ -286,7 +287,7 @@ App::get('/v1/project/usage')
}, $dbForProject->find('functions'));
// This total is includes free and paid SMS usage
$authPhoneTotal = Authorization::skip(fn () => $dbForProject->sum('stats', 'value', [
$authPhoneTotal = $authorization->skip(fn () => $dbForProject->sum('stats', 'value', [
Query::equal('metric', [METRIC_AUTH_METHOD_PHONE]),
Query::equal('period', ['1d']),
Query::greaterThanEqual('time', $firstDay),
@@ -294,7 +295,7 @@ App::get('/v1/project/usage')
]));
// This estimate is only for paid SMS usage
$authPhoneMetrics = Authorization::skip(fn () => $dbForProject->find('stats', [
$authPhoneMetrics = $authorization->skip(fn () => $dbForProject->find('stats', [
Query::startsWith('metric', METRIC_AUTH_METHOD_PHONE . '.'),
Query::equal('period', ['1d']),
Query::greaterThanEqual('time', $firstDay),
+1
View File
@@ -227,6 +227,7 @@ App::post('/v1/projects')
if (!$sharedTablesV2) {
$adapter = new DatabasePool($pools->get($dsn->getHost()));
$dbForProject = new Database($adapter, $cache);
$dbForProject->setDatabase(APP_DATABASE);
if ($sharedTables) {
$dbForProject
+35 -28
View File
@@ -86,16 +86,17 @@ App::post('/v1/teams')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('authorization')
->inject('queueForEvents')
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
->action(function (string $teamId, string $name, array $roles, Response $response, Document $user, Database $dbForProject, Authorization $authorization, Event $queueForEvents) {
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAppUser = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAppUser = User::isApp($authorization->getRoles());
$teamId = $teamId == 'unique()' ? ID::unique() : $teamId;
try {
$team = Authorization::skip(fn () => $dbForProject->createDocument('teams', new Document([
$team = $authorization->skip(fn () => $dbForProject->createDocument('teams', new Document([
'$id' => $teamId,
'$permissions' => [
Permission::read(Role::team($teamId)),
@@ -491,6 +492,7 @@ App::post('/v1/teams/:teamId/memberships')
->inject('project')
->inject('user')
->inject('dbForProject')
->inject('authorization')
->inject('locale')
->inject('queueForMails')
->inject('queueForMessaging')
@@ -500,9 +502,9 @@ App::post('/v1/teams/:teamId/memberships')
->inject('plan')
->inject('proofForPassword')
->inject('proofForToken')
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Password $proofForPassword, Token $proofForToken) {
$isAppUser = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
->action(function (string $teamId, string $email, string $userId, string $phone, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Authorization $authorization, Locale $locale, Mail $queueForMails, Messaging $queueForMessaging, Event $queueForEvents, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan, Password $proofForPassword, Token $proofForToken) {
$isAppUser = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$url = htmlentities($url);
if (empty($url)) {
@@ -619,13 +621,13 @@ App::post('/v1/teams/:teamId/memberships')
]);
try {
$invitee = Authorization::skip(fn () => $dbForProject->createDocument('users', $userDocument));
$invitee = $authorization->skip(fn () => $dbForProject->createDocument('users', $userDocument));
} catch (Duplicate $th) {
throw new Exception(Exception::USER_ALREADY_EXISTS);
}
}
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
$isOwner = $authorization->hasRole('team:' . $team->getId() . '/owner');
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
throw new Exception(Exception::USER_UNAUTHORIZED, 'User is not allowed to send invitations for this team');
@@ -661,11 +663,11 @@ App::post('/v1/teams/:teamId/memberships')
]);
$membership = ($isPrivilegedUser || $isAppUser) ?
Authorization::skip(fn () => $dbForProject->createDocument('memberships', $membership)) :
$authorization->skip(fn () => $dbForProject->createDocument('memberships', $membership)) :
$dbForProject->createDocument('memberships', $membership);
if ($isPrivilegedUser || $isAppUser) {
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
}
} elseif ($membership->getAttribute('confirm') === false) {
$membership->setAttribute('secret', $proofForToken->hash($secret));
@@ -677,7 +679,7 @@ App::post('/v1/teams/:teamId/memberships')
}
$membership = ($isPrivilegedUser || $isAppUser) ?
Authorization::skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) :
$authorization->skip(fn () => $dbForProject->updateDocument('memberships', $membership->getId(), $membership)) :
$dbForProject->updateDocument('memberships', $membership->getId(), $membership);
} else {
throw new Exception(Exception::MEMBERSHIP_ALREADY_CONFIRMED);
@@ -863,7 +865,8 @@ App::get('/v1/teams/:teamId/memberships')
->inject('response')
->inject('project')
->inject('dbForProject')
->action(function (string $teamId, array $queries, string $search, bool $includeTotal, Response $response, Document $project, Database $dbForProject) {
->inject('authorization')
->action(function (string $teamId, array $queries, string $search, bool $includeTotal, Response $response, Document $project, Database $dbForProject, Authorization $authorization) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -933,7 +936,7 @@ App::get('/v1/teams/:teamId/memberships')
'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true,
];
$roles = Authorization::getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = User::isPrivileged($roles);
$isAppUser = User::isApp($roles);
@@ -1004,7 +1007,8 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
->inject('response')
->inject('project')
->inject('dbForProject')
->action(function (string $teamId, string $membershipId, Response $response, Document $project, Database $dbForProject) {
->inject('authorization')
->action(function (string $teamId, string $membershipId, Response $response, Document $project, Database $dbForProject, Authorization $authorization) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -1024,7 +1028,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
'mfa' => $project->getAttribute('auths', [])['membershipsMfa'] ?? true,
];
$roles = Authorization::getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = User::isPrivileged($roles);
$isAppUser = User::isApp($roles);
@@ -1103,8 +1107,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
->inject('user')
->inject('project')
->inject('dbForProject')
->inject('authorization')
->inject('queueForEvents')
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Event $queueForEvents) {
->action(function (string $teamId, string $membershipId, array $roles, Request $request, Response $response, Document $user, Document $project, Database $dbForProject, Authorization $authorization, Event $queueForEvents) {
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
@@ -1121,9 +1126,9 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
throw new Exception(Exception::USER_NOT_FOUND);
}
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAppUser = User::isApp(Authorization::getRoles());
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAppUser = User::isApp($authorization->getRoles());
$isOwner = $authorization->hasRole('team:' . $team->getId() . '/owner');
if ($project->getId() === 'console') {
// Quick check: fetch up to 2 owners to determine if only one exists
@@ -1204,12 +1209,13 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('authorization')
->inject('project')
->inject('geodb')
->inject('queueForEvents')
->inject('store')
->inject('proofForToken')
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Reader $geodb, Event $queueForEvents, Store $store, Token $proofForToken) {
->action(function (string $teamId, string $membershipId, string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Authorization $authorization, $project, Reader $geodb, Event $queueForEvents, Store $store, Token $proofForToken) {
$protocol = $request->getProtocol();
$membership = $dbForProject->getDocument('memberships', $membershipId);
@@ -1218,7 +1224,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
throw new Exception(Exception::MEMBERSHIP_NOT_FOUND);
}
$team = Authorization::skip(fn () => $dbForProject->getDocument('teams', $teamId));
$team = $authorization->skip(fn () => $dbForProject->getDocument('teams', $teamId));
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
@@ -1254,11 +1260,11 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
->setAttribute('confirm', true)
;
Authorization::skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
$authorization->skip(fn () => $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('emailVerification', true)));
// Create session for the user if not logged in
if (!$hasSession) {
Authorization::setRole(Role::user($user->getId())->toString());
$authorization->addRole(Role::user($user->getId())->toString());
$detector = new Detector($request->getUserAgent('UNKNOWN'));
$record = $geodb->get($request->getIP());
@@ -1286,7 +1292,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$session = $dbForProject->createDocument('sessions', $session);
Authorization::setRole(Role::user($userId)->toString());
$authorization->addRole(Role::user($userId)->toString());
$encoded = $store
->setProperty('id', $user->getId())
@@ -1324,7 +1330,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
$dbForProject->purgeCachedDocument('users', $user->getId());
Authorization::skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$authorization->skip(fn () => $dbForProject->increaseDocumentAttribute('teams', $team->getId(), 'total', 1));
$queueForEvents
->setParam('userId', $user->getId())
@@ -1368,8 +1374,9 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
->inject('project')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->inject('queueForEvents')
->action(function (string $teamId, string $membershipId, Document $user, Document $project, Response $response, Database $dbForProject, Event $queueForEvents) {
->action(function (string $teamId, string $membershipId, Document $user, Document $project, Response $response, Database $dbForProject, Authorization $authorization, Event $queueForEvents) {
$membership = $dbForProject->getDocument('memberships', $membershipId);
@@ -1427,7 +1434,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
$dbForProject->purgeCachedDocument('users', $profile->getId());
if ($membership->getAttribute('confirm')) { // Count only confirmed members
Authorization::skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
$authorization->skip(fn () => $dbForProject->decreaseDocumentAttribute('teams', $team->getId(), 'total', 1, 0));
}
$queueForEvents
+3 -3
View File
@@ -2678,8 +2678,8 @@ App::get('/v1/users/usage')
->param('range', '30d', new WhiteList(['24h', '30d', '90d'], true), 'Date range.', true)
->inject('response')
->inject('dbForProject')
->inject('register')
->action(function (string $range, Response $response, Database $dbForProject) {
->inject('authorization')
->action(function (string $range, Response $response, Database $dbForProject, Authorization $authorization) {
$periods = Config::getParam('usage', []);
$stats = $usage = [];
@@ -2689,7 +2689,7 @@ App::get('/v1/users/usage')
METRIC_SESSIONS,
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $count => $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
+31 -29
View File
@@ -76,7 +76,7 @@ use Utopia\VCS\Exception\RepositoryNotFound;
use function Swoole\Coroutine\batch;
$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForPlatform, Build $queueForBuilds, callable $getProjectDB, array $platform) {
$createGitDeployments = function (GitHub $github, string $providerInstallationId, array $repositories, string $providerBranch, string $providerBranchUrl, string $providerRepositoryName, string $providerRepositoryUrl, string $providerRepositoryOwner, string $providerCommitHash, string $providerCommitAuthor, string $providerCommitAuthorUrl, string $providerCommitMessage, string $providerCommitUrl, string $providerPullRequestId, bool $external, Database $dbForPlatform, Authorization $authorization, Build $queueForBuilds, callable $getProjectDB, Request $request, array $platform) {
$errors = [];
foreach ($repositories as $repository) {
try {
@@ -87,12 +87,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
}
$projectId = $repository->getAttribute('projectId');
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$project = $authorization->skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$dbForProject = $getProjectDB($project);
$resourceCollection = $resourceType === "function" ? 'functions' : 'sites';
$resourceId = $repository->getAttribute('resourceId');
$resource = Authorization::skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId));
$resource = $authorization->skip(fn () => $dbForProject->getDocument($resourceCollection, $resourceId));
$resourceInternalId = $resource->getSequence();
$deploymentId = ID::unique();
@@ -141,7 +141,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$latestCommentId = '';
if (!empty($providerPullRequestId) && $resource->getAttribute('providerSilentMode', false) === false) {
$latestComment = Authorization::skip(fn () => $dbForPlatform->findOne('vcsComments', [
$latestComment = $authorization->skip(fn () => $dbForPlatform->findOne('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('providerPullRequestId', [$providerPullRequestId]),
Query::orderDesc('$createdAt'),
@@ -180,7 +180,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()));
} finally {
Authorization::skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
$authorization->skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
}
}
} else {
@@ -191,7 +191,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
if (!empty($latestCommentId)) {
$teamId = $project->getAttribute('teamId', '');
$latestComment = Authorization::skip(fn () => $dbForPlatform->createDocument('vcsComments', new Document([
$latestComment = $authorization->skip(fn () => $dbForPlatform->createDocument('vcsComments', new Document([
'$id' => ID::unique(),
'$permissions' => [
Permission::read(Role::team(ID::custom($teamId))),
@@ -212,7 +212,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
}
}
} elseif (!empty($providerBranch)) {
$latestComments = Authorization::skip(fn () => $dbForPlatform->find('vcsComments', [
$latestComments = $authorization->skip(fn () => $dbForPlatform->find('vcsComments', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::equal('providerBranch', [$providerBranch]),
Query::orderDesc('$createdAt'),
@@ -251,7 +251,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()));
} finally {
Authorization::skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
$authorization->skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
}
}
}
@@ -294,7 +294,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$commands[] = $resource->getAttribute('commands', '');
}
$deployment = Authorization::skip(fn () => $dbForProject->createDocument('deployments', new Document([
$deployment = $authorization->skip(fn () => $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$permissions' => [
Permission::read(Role::any()),
@@ -334,7 +334,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
->setAttribute('latestDeploymentInternalId', $deployment->getSequence())
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
Authorization::skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource));
$authorization->skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource));
if ($resource->getCollection() === 'sites') {
$projectId = $project->getId();
@@ -344,7 +344,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$domain = ID::unique() . "." . $sitesDomain;
$ruleId = md5($domain);
$previewRuleId = $ruleId;
Authorization::skip(
$authorization->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -377,7 +377,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
$authorization->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -408,7 +408,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$domain = "commit-" . substr($providerCommitHash, 0, 16) . ".{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
$authorization->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -460,7 +460,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
if ($lockAcquired) {
// Wrap in try/finally to ensure lock file gets deleted
try {
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', $previewRuleId));
$rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', $previewRuleId));
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$previewUrl = !empty($rule) ? ("{$protocol}://" . $rule->getAttribute('domain', '')) : '';
@@ -472,7 +472,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment());
}
} finally {
Authorization::skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
$authorization->skip(fn () => $dbForPlatform->deleteDocument('vcsCommentLocks', $latestCommentId));
}
}
}
@@ -1476,11 +1476,12 @@ App::post('/v1/vcs/github/events')
->inject('request')
->inject('response')
->inject('dbForPlatform')
->inject('authorization')
->inject('getProjectDB')
->inject('queueForBuilds')
->inject('platform')
->action(
function (GitHub $github, Request $request, Response $response, Database $dbForPlatform, callable $getProjectDB, Build $queueForBuilds, array $platform) use ($createGitDeployments) {
function (GitHub $github, Request $request, Response $response, Database $dbForPlatform, Authorization $authorization, callable $getProjectDB, Build $queueForBuilds, array $platform) use ($createGitDeployments) {
$payload = $request->getRawPayload();
$signatureRemote = $request->getHeader('x-hub-signature-256', '');
$signatureLocal = System::getEnv('_APP_VCS_GITHUB_WEBHOOK_SECRET', '');
@@ -1516,14 +1517,14 @@ App::post('/v1/vcs/github/events')
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
//find resourceId from relevant resources table
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
$repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::limit(100),
]));
// create new deployment only on push (not committed by us) and not when branch is created or deleted
if ($providerCommitAuthorEmail !== APP_VCS_GITHUB_EMAIL && !$providerBranchCreated && !$providerBranchDeleted) {
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthorName, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $queueForBuilds, $getProjectDB, $platform);
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthorName, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $request, $platform);
}
} elseif ($event == $github::EVENT_INSTALLATION) {
if ($parsedPayload["action"] == "deleted") {
@@ -1536,16 +1537,16 @@ App::post('/v1/vcs/github/events')
]);
foreach ($installations as $installation) {
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
$repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('installationInternalId', [$installation->getSequence()]),
Query::limit(1000)
]));
foreach ($repositories as $repository) {
Authorization::skip(fn () => $dbForPlatform->deleteDocument('repositories', $repository->getId()));
$authorization->skip(fn () => $dbForPlatform->deleteDocument('repositories', $repository->getId()));
}
Authorization::skip(fn () => $dbForPlatform->deleteDocument('installations', $installation->getId()));
$authorization->skip(fn () => $dbForPlatform->deleteDocument('installations', $installation->getId()));
}
}
} elseif ($event == $github::EVENT_PULL_REQUEST) {
@@ -1574,12 +1575,12 @@ App::post('/v1/vcs/github/events')
$providerCommitAuthor = $commitDetails["commitAuthor"] ?? '';
$providerCommitMessage = $commitDetails["commitMessage"] ?? '';
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
$repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::orderDesc('$createdAt')
]));
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForPlatform, $queueForBuilds, $getProjectDB, $platform);
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, $external, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $request, $platform);
} elseif ($parsedPayload["action"] == "closed") {
// Allowed external contributions cleanup
@@ -1588,7 +1589,7 @@ App::post('/v1/vcs/github/events')
$external = $parsedPayload["external"] ?? true;
if ($external) {
$repositories = Authorization::skip(fn () => $dbForPlatform->find('repositories', [
$repositories = $authorization->skip(fn () => $dbForPlatform->find('repositories', [
Query::equal('providerRepositoryId', [$providerRepositoryId]),
Query::orderDesc('$createdAt')
]));
@@ -1599,7 +1600,7 @@ App::post('/v1/vcs/github/events')
if (\in_array($providerPullRequestId, $providerPullRequestIds)) {
$providerPullRequestIds = \array_diff($providerPullRequestIds, [$providerPullRequestId]);
$repository = $repository->setAttribute('providerPullRequestIds', $providerPullRequestIds);
$repository = Authorization::skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
$repository = $authorization->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
}
}
}
@@ -1786,17 +1787,18 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
->inject('response')
->inject('project')
->inject('dbForPlatform')
->inject('authorization')
->inject('getProjectDB')
->inject('queueForBuilds')
->inject('platform')
->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Response $response, Document $project, Database $dbForPlatform, callable $getProjectDB, Build $queueForBuilds, array $platform) use ($createGitDeployments) {
->action(function (string $installationId, string $repositoryId, string $providerPullRequestId, GitHub $github, Request $request, Response $response, Document $project, Database $dbForPlatform, Authorization $authorization, callable $getProjectDB, Build $queueForBuilds, array $platform) use ($createGitDeployments) {
$installation = $dbForPlatform->getDocument('installations', $installationId);
if ($installation->isEmpty()) {
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
$repository = Authorization::skip(fn () => $dbForPlatform->findOne('repositories', [
$repository = $authorization->skip(fn () => $dbForPlatform->findOne('repositories', [
Query::equal('$id', [$repositoryId]),
Query::equal('projectInternalId', [$project->getSequence()])
]));
@@ -1814,7 +1816,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
// TODO: Delete from array when PR is closed
$repository = Authorization::skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
$repository = $authorization->skip(fn () => $dbForPlatform->updateDocument('repositories', $repository->getId(), $repository));
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
@@ -1846,7 +1848,7 @@ App::patch('/v1/vcs/github/installations/:installationId/repositories/:repositor
$providerCommitMessage = $pullRequestResponse['title'] ?? '';
$providerCommitUrl = $pullRequestResponse['html_url'] ?? '';
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, $providerPullRequestId, true, $dbForPlatform, $queueForBuilds, $getProjectDB, $platform);
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, '', '', '', '', $providerCommitHash, '', '', '', '', $providerPullRequestId, true, $dbForPlatform, $authorization, $queueForBuilds, $getProjectDB, $request, $platform);
$response->noContent();
});
+98 -91
View File
@@ -59,7 +59,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, ?Key $apiKey)
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)
{
$host = $request->getHostname() ?? '';
if (!empty($previewHostname)) {
@@ -67,16 +67,16 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
// TODO: (@Meldiron) Remove after 1.7.x migration
$isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5';
$rule = Authorization::skip(function () use ($dbForPlatform, $host, $isMd5) {
if ($isMd5) {
return $dbForPlatform->getDocument('rules', md5($host));
}
return $dbForPlatform->findOne('rules', [
Query::equal('domain', [$host]),
]) ?? new Document();
});
if (System::getEnv('_APP_RULES_FORMAT') === 'md5') {
$rule = $authorization->skip(fn () => $dbForPlatform->getDocument('rules', md5($host)));
} else {
$rule = $authorization->skip(
fn () => $dbForPlatform->find('rules', [
Query::equal('domain', [$host]),
Query::limit(1)
])
)[0] ?? new Document();
}
$errorView = __DIR__ . '/../views/general/error.phtml';
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
@@ -111,7 +111,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
$projectId = $rule->getAttribute('projectId');
$project = Authorization::skip(
$project = $authorization->skip(
fn () => $dbForPlatform->getDocument('projects', $projectId)
);
@@ -119,7 +119,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$accessedAt = $project->getAttribute('accessedAt', 0);
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
$project->setAttribute('accessedAt', DateTime::now());
Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
$authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
}
/**
@@ -158,7 +158,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
/** @var Document $deployment */
if (!empty($rule->getAttribute('deploymentId', ''))) {
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $rule->getAttribute('deploymentId')));
$deployment = $authorization->skip(fn () => $dbForProject->getDocument('deployments', $rule->getAttribute('deploymentId')));
} else {
// 1.6.x DB schema compatibility
// TODO: Make sure deploymentId is never empty, and remove this code
@@ -172,15 +172,15 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
// Document of site or function
$resource = $resourceType === 'function' ?
Authorization::skip(fn () => $dbForProject->getDocument('functions', $resourceId)) :
Authorization::skip(fn () => $dbForProject->getDocument('sites', $resourceId));
$authorization->skip(fn () => $dbForProject->getDocument('functions', $resourceId)) :
$authorization->skip(fn () => $dbForProject->getDocument('sites', $resourceId));
// ID of active deployments
// Attempts to use attribute from both schemas (1.6 and 1.7)
$activeDeploymentId = $resource->getAttribute('deploymentId', $resource->getAttribute('deployment', ''));
// Get deployment document, as intended originally
$deployment = Authorization::skip(fn () => $dbForProject->getDocument('deployments', $activeDeploymentId));
$deployment = $authorization->skip(fn () => $dbForProject->getDocument('deployments', $activeDeploymentId));
}
if ($deployment->getAttribute('resourceType', '') === 'functions') {
@@ -199,8 +199,8 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
$resource = $type === 'function' ?
Authorization::skip(fn () => $dbForProject->getDocument('functions', $deployment->getAttribute('resourceId', ''))) :
Authorization::skip(fn () => $dbForProject->getDocument('sites', $deployment->getAttribute('resourceId', '')));
$authorization->skip(fn () => $dbForProject->getDocument('functions', $deployment->getAttribute('resourceId', ''))) :
$authorization->skip(fn () => $dbForProject->getDocument('sites', $deployment->getAttribute('resourceId', '')));
$isPreview = $type === 'function' ? false : ($rule->getAttribute('trigger', '') !== 'manual');
@@ -242,7 +242,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
$userExists = false;
$userId = $payload['userId'] ?? '';
if (!empty($userId)) {
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
if (!$user->isEmpty() && $user->getAttribute('status', false)) {
$userExists = true;
}
@@ -255,7 +255,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
}
$membershipExists = false;
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$project = $authorization->skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
if (!$project->isEmpty() && isset($user)) {
$teamId = $project->getAttribute('teamId', '');
$membership = $user->find('teamId', $teamId, 'memberships');
@@ -862,15 +862,16 @@ App::init()
->inject('devKey')
->inject('apiKey')
->inject('cors')
->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) {
->inject('authorization')
->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) {
/*
* Appwrite Router
*/
$hostname = $request->getHostname() ?? '';
$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, $apiKey)) {
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)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -1033,7 +1034,8 @@ App::init()
->inject('dbForPlatform')
->inject('queueForCertificates')
->inject('platform')
->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform) {
->inject('authorization')
->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization) {
$hostname = $request->getHostname();
$cache = Config::getParam('hostnames', []);
$platformHostnames = $platform['hostnames'] ?? [];
@@ -1061,64 +1063,64 @@ App::init()
}
// 4. Check/create rule (requires DB access)
Authorization::disable();
try {
// TODO: (@Meldiron) Remove after 1.7.x migration
$isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5';
$document = $isMd5
? $dbForPlatform->getDocument('rules', md5($domain->get()))
: $dbForPlatform->findOne('rules', [
Query::equal('domain', [$domain->get()]),
$authorization->skip(function () use ($dbForPlatform, $domain, $console, $queueForCertificates, &$cache) {
try {
// TODO: (@Meldiron) Remove after 1.7.x migration
$isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5';
$document = $isMd5
? $dbForPlatform->getDocument('rules', md5($domain->get()))
: $dbForPlatform->findOne('rules', [
Query::equal('domain', [$domain->get()]),
]);
if (!$document->isEmpty()) {
return;
}
// 5. Create new rule
$owner = '';
$fallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
$funcDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
$siteDomain = System::getEnv('_APP_DOMAIN_SITES', '');
if (!empty($fallback) && \str_ends_with($domain->get(), $fallback)) {
$funcDomain = $fallback;
}
if (
(!empty($funcDomain) && \str_ends_with($domain->get(), $funcDomain)) ||
(!empty($siteDomain) && \str_ends_with($domain->get(), $siteDomain))
) {
$owner = 'Appwrite';
}
$ruleId = $isMd5 ? md5($domain->get()) : ID::unique();
$document = new Document([
'$id' => $ruleId,
'domain' => $domain->get(),
'type' => 'api',
'status' => 'verifying',
'projectId' => $console->getId(),
'projectInternalId' => $console->getSequence(),
'search' => implode(' ', [$ruleId, $domain->get()]),
'owner' => $owner,
'region' => $console->getAttribute('region')
]);
if (!$document->isEmpty()) {
return;
$dbForPlatform->createDocument('rules', $document);
Console::info('Issuing a TLS certificate for the main domain (' . $domain->get() . ') in a few seconds...');
$queueForCertificates
->setDomain($document)
->setSkipRenewCheck(true)
->trigger();
} catch (Duplicate $e) {
Console::info('Certificate already exists');
} finally {
$cache[$domain->get()] = true;
Config::setParam('hostnames', $cache);
}
// 5. Create new rule
$owner = '';
$fallback = System::getEnv('_APP_DOMAIN_FUNCTIONS_FALLBACK', '');
$funcDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
$siteDomain = System::getEnv('_APP_DOMAIN_SITES', '');
if (!empty($fallback) && \str_ends_with($domain->get(), $fallback)) {
$funcDomain = $fallback;
}
if (
(!empty($funcDomain) && \str_ends_with($domain->get(), $funcDomain)) ||
(!empty($siteDomain) && \str_ends_with($domain->get(), $siteDomain))
) {
$owner = 'Appwrite';
}
$ruleId = $isMd5 ? md5($domain->get()) : ID::unique();
$document = new Document([
'$id' => $ruleId,
'domain' => $domain->get(),
'type' => 'api',
'status' => 'verifying',
'projectId' => $console->getId(),
'projectInternalId' => $console->getSequence(),
'search' => implode(' ', [$ruleId, $domain->get()]),
'owner' => $owner,
'region' => $console->getAttribute('region')
]);
$dbForPlatform->createDocument('rules', $document);
Console::info('Issuing a TLS certificate for the main domain (' . $domain->get() . ') in a few seconds...');
$queueForCertificates
->setDomain($document)
->setSkipRenewCheck(true)
->trigger();
} catch (Duplicate $e) {
Console::info('Certificate already exists');
} finally {
$cache[$domain->get()] = true;
Config::setParam('hostnames', $cache);
Authorization::reset();
}
});
});
App::options()
@@ -1141,14 +1143,15 @@ App::options()
->inject('devKey')
->inject('apiKey')
->inject('cors')
->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) {
->inject('authorization')
->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) {
/*
* 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, $apiKey)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -1182,7 +1185,8 @@ App::error()
->inject('log')
->inject('queueForStatsUsage')
->inject('devKey')
->action(function (Throwable $error, App $utopia, Request $request, Response $response, Document $project, ?Logger $logger, Log $log, StatsUsage $queueForStatsUsage) {
->inject('authorization')
->action(function (Throwable $error, App $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);
@@ -1264,7 +1268,7 @@ App::error()
* If not a publishable error, track usage stats. Publishable errors are >= 500 or those explicitly marked as publish=true in errors.php
*/
if (!$publish && $project->getId() !== 'console') {
if (!DBUser::isPrivileged(Authorization::getRoles())) {
if (!DBUser::isPrivileged($authorization->getRoles())) {
$fileSize = 0;
$file = $request->getFiles('file');
if (!empty($file)) {
@@ -1326,7 +1330,7 @@ App::error()
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('roles', Authorization::getRoles());
$log->addExtra('roles', $authorization->getRoles());
try {
/* add queries to log */
@@ -1530,13 +1534,14 @@ App::get('/robots.txt')
->inject('platform')
->inject('previewHostname')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey) {
->inject('authorization')
->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) {
$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, $apiKey)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -1562,13 +1567,14 @@ App::get('/humans.txt')
->inject('platform')
->inject('previewHostname')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, array $platform, string $previewHostname, ?Key $apiKey) {
->inject('authorization')
->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) {
$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, $apiKey)) {
if (router($utopia, $dbForPlatform, $getProjectDB, $swooleRequest, $request, $response, $log, $queueForEvents, $queueForStatsUsage, $queueForFunctions, $executor, $geodb, $isResourceBlocked, $platform, $previewHostname, $authorization, $apiKey)) {
$utopia->getRoute()?->label('router', true);
}
}
@@ -1652,7 +1658,8 @@ App::get('/v1/ping')
->inject('project')
->inject('dbForPlatform')
->inject('queueForEvents')
->action(function (Response $response, Document $project, Database $dbForPlatform, Event $queueForEvents) {
->inject('authorization')
->action(function (Response $response, Document $project, Database $dbForPlatform, Event $queueForEvents, Authorization $authorization) {
if ($project->isEmpty() || $project->getId() === 'console') {
throw new AppwriteException(AppwriteException::PROJECT_NOT_FOUND);
}
@@ -1664,7 +1671,7 @@ App::get('/v1/ping')
->setAttribute('pingCount', $pingCount)
->setAttribute('pingedAt', $pingedAt);
Authorization::skip(function () use ($dbForPlatform, $project) {
$authorization->skip(function () use ($dbForPlatform, $project) {
$dbForPlatform->updateDocument('projects', $project->getId(), $project);
});
+28 -25
View File
@@ -30,6 +30,7 @@ use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\Queue\Publisher;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
@@ -233,7 +234,8 @@ App::init()
->inject('mode')
->inject('team')
->inject('apiKey')
->action(function (App $utopia, Request $request, Database $dbForPlatform, Database $dbForProject, Audit $queueForAudits, Document $project, User $user, ?Document $session, array $servers, string $mode, Document $team, ?Key $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) {
$route = $utopia->getRoute();
/**
@@ -318,7 +320,7 @@ App::init()
// Handle special app role case
if ($apiKey->getRole() === User::ROLE_APPS) {
// Disable authorization checks for API keys
Authorization::setDefaultStatus(false);
$authorization->setDefaultStatus(false);
$user = new User([
'$id' => '',
@@ -392,14 +394,14 @@ App::init()
$scopes = \array_merge($scopes, $roles[$role]['scopes']);
}
Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
$authorization->setDefaultStatus(false); // Cancel security segmentation for admin users.
}
$scopes = \array_unique($scopes);
Authorization::setRole($role);
foreach ($user->getRoles() as $authRole) {
Authorization::setRole($authRole);
$authorization->addRole($role);
foreach ($user->getRoles($authorization) as $authRole) {
$authorization->addRole($authRole);
}
// Step 6: Update project and user last activity
@@ -407,7 +409,7 @@ App::init()
$accessedAt = $project->getAttribute('accessedAt', 0);
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
$project->setAttribute('accessedAt', DateTime::now());
Authorization::skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
$authorization->skip(fn () => $dbForPlatform->updateDocument('projects', $project->getId(), $project));
}
}
@@ -442,7 +444,7 @@ App::init()
if (
array_key_exists($namespace, $project->getAttribute('services', []))
&& !$project->getAttribute('services', [])[$namespace]
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
&& !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles()))
) {
throw new Exception(Exception::GENERAL_SERVICE_DISABLED);
}
@@ -509,14 +511,15 @@ App::init()
->inject('devKey')
->inject('telemetry')
->inject('platform')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Mail $queueForMails, Migration $queueForMigrations, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform) use ($usageDatabaseListener, $eventDatabaseListener) {
->inject('authorization')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Publisher $publisherFunctions, Publisher $publisherWebhooks, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Mail $queueForMails, Migration $queueForMigrations, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry, array $platform, Authorization $authorization) use ($usageDatabaseListener, $eventDatabaseListener) {
$route = $utopia->getRoute();
if (
array_key_exists('rest', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['rest']
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
&& !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
@@ -546,7 +549,7 @@ App::init()
$closestLimit = null;
$roles = Authorization::getRoles();
$roles = $authorization->getRoles();
$isPrivilegedUser = User::isPrivileged($roles);
$isAppUser = User::isApp($roles);
@@ -657,10 +660,10 @@ App::init()
if ($useCache) {
$route = $utopia->match($request);
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !User::isPrivileged(Authorization::getRoles());
$isDisabled = isset($plan['imageTransformations']) && $plan['imageTransformations'] === -1 && !User::isPrivileged($authorization->getRoles());
$key = $request->cacheIdentifier();
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key));
$cache = new Cache(
new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId())
);
@@ -677,10 +680,10 @@ App::init()
if ($type === 'bucket' && (!$isImageTransformation || !$isDisabled)) {
$bucketId = $parts[1] ?? null;
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$bucket = $authorization->skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
throw new Exception(Exception::STORAGE_BUCKET_NOT_FOUND);
@@ -691,8 +694,7 @@ App::init()
}
$fileSecurity = $bucket->getAttribute('fileSecurity', false);
$validator = new Authorization(Database::PERMISSION_READ);
$valid = $validator->isValid($bucket->getRead());
$valid = $authorization->isValid(new Input(Database::PERMISSION_READ, $bucket->getRead()));
if (!$fileSecurity && !$valid && !$isToken) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
@@ -703,7 +705,7 @@ App::init()
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
$file = $authorization->skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
if (!$resourceToken->isEmpty() && $resourceToken->getAttribute('fileInternalId') !== $file->getSequence()) {
@@ -714,11 +716,11 @@ App::init()
throw new Exception(Exception::STORAGE_FILE_NOT_FOUND);
}
//Do not update transformedAt if it's a console user
if (!User::isPrivileged(Authorization::getRoles())) {
if (!User::isPrivileged($authorization->getRoles())) {
$transformedAt = $file->getAttribute('transformedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $transformedAt) {
$file->setAttribute('transformedAt', DateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
$authorization->skip(fn () => $dbForProject->updateDocument('bucket_' . $file->getAttribute('bucketInternalId'), $file->getId(), $file));
}
}
}
@@ -814,8 +816,9 @@ App::shutdown()
->inject('queueForWebhooks')
->inject('queueForRealtime')
->inject('dbForProject')
->inject('authorization')
->inject('timelimit')
->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, callable $timelimit) use ($parseLabel) {
->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) use ($parseLabel) {
$responsePayload = $response->getPayload();
@@ -976,11 +979,11 @@ App::shutdown()
$key = $request->cacheIdentifier();
$signature = md5($data['payload']);
$cacheLog = Authorization::skip(fn () => $dbForProject->getDocument('cache', $key));
$cacheLog = $authorization->skip(fn () => $dbForProject->getDocument('cache', $key));
$accessedAt = $cacheLog->getAttribute('accessedAt', 0);
$now = DateTime::now();
if ($cacheLog->isEmpty()) {
Authorization::skip(fn () => $dbForProject->createDocument('cache', new Document([
$authorization->skip(fn () => $dbForProject->createDocument('cache', new Document([
'$id' => $key,
'resource' => $resource,
'resourceType' => $resourceType,
@@ -990,7 +993,7 @@ App::shutdown()
])));
} elseif (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_CACHE_UPDATE)) > $accessedAt) {
$cacheLog->setAttribute('accessedAt', $now);
Authorization::skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
$authorization->skip(fn () => $dbForProject->updateDocument('cache', $cacheLog->getId(), $cacheLog));
// Overwrite the file every APP_CACHE_UPDATE seconds to update the file modified time that is used in the TTL checks in cache->load()
$cache->save($key, $data['payload']);
}
@@ -1002,7 +1005,7 @@ App::shutdown()
}
if ($project->getId() !== 'console') {
if (!User::isPrivileged(Authorization::getRoles())) {
if (!User::isPrivileged($authorization->getRoles())) {
$fileSize = 0;
$file = $request->getFiles('file');
if (!empty($file)) {
+4 -3
View File
@@ -36,7 +36,8 @@ App::init()
->inject('request')
->inject('project')
->inject('geodb')
->action(function (App $utopia, Request $request, Document $project, Reader $geodb) {
->inject('authorization')
->action(function (App $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);
@@ -49,8 +50,8 @@ App::init()
$route = $utopia->match($request);
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAppUser = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$isAppUser = User::isApp($authorization->getRoles());
if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
return;
+17 -11
View File
@@ -27,7 +27,6 @@ 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\Logger\Log;
use Utopia\Logger\Log\User;
use Utopia\Pools\Group;
@@ -261,7 +260,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
createDatabase($app, 'getLogsDB', 'logs', $collections['logs'], $pools);
// create appwrite database, `dbForPlatform` is a direct access call.
createDatabase($app, 'dbForPlatform', 'appwrite', $collections['console'], $pools, function (Database $dbForPlatform) use ($collections) {
createDatabase($app, 'dbForPlatform', 'appwrite', $collections['console'], $pools, function (Database $dbForPlatform) use ($collections, $app) {
$authorization = $app->getResource('authorization');
if ($dbForPlatform->getCollection(AuditAdapterSQL::COLLECTION)->isEmpty()) {
$adapter = new AdapterDatabase($dbForPlatform);
$audit = new Audit($adapter);
@@ -321,9 +322,9 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes);
}
if (Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) {
if ($authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots')->isEmpty())) {
Console::info(" └── Creating screenshots bucket...");
Authorization::skip(fn () => $dbForPlatform->createDocument('buckets', new Document([
$authorization->skip(fn () => $dbForPlatform->createDocument('buckets', new Document([
'$id' => ID::custom('screenshots'),
'$collection' => ID::custom('buckets'),
'name' => 'Screenshots',
@@ -338,7 +339,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
'search' => 'buckets Screenshots',
])));
$bucket = Authorization::skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
$bucket = $authorization->skip(fn () => $dbForPlatform->getDocument('buckets', 'screenshots'));
Console::info(" └── Creating files collection for screenshots bucket...");
$files = $collections['buckets']['files'] ?? [];
@@ -366,7 +367,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
'orders' => $index['orders'],
]), $files['indexes']);
Authorization::skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes));
$authorization->skip(fn () => $dbForPlatform->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes));
}
});
@@ -458,8 +459,12 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
App::setResource('pools', fn () => $pools);
try {
Authorization::cleanRoles();
Authorization::setRole(Role::any()->toString());
$authorization = $app->getResource('authorization');
$request->setAuthorization($authorization);
$response->setAuthorization($authorization);
$authorization->cleanRoles();
$authorization->addRole(Role::any()->toString());
$app->run($request, $response);
} catch (\Throwable $th) {
@@ -501,7 +506,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool
$log->addExtra('file', $th->getFile());
$log->addExtra('line', $th->getLine());
$log->addExtra('trace', $th->getTraceAsString());
$log->addExtra('roles', Authorization::getRoles());
$log->addExtra('roles', isset($authorization) ? $authorization->getRoles() : []);
$sdk = $route->getLabel("sdk", false);
@@ -560,7 +565,7 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
/** @var Utopia\Database\Database $dbForPlatform */
$dbForPlatform = $app->getResource('dbForPlatform');
Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $domains, &$lastSyncUpdate) {
Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $domains, &$lastSyncUpdate, $app) {
try {
$time = DateTime::now();
$limit = 1000;
@@ -577,7 +582,8 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) {
}
$results = [];
try {
$results = Authorization::skip(fn () => $dbForPlatform->find('rules', $queries));
$authorization = $app->getResource('authorization');
$results = $authorization->skip(fn () => $dbForPlatform->find('rules', $queries));
} catch (Throwable $th) {
Console::error($th->getMessage());
}
+3
View File
@@ -161,6 +161,9 @@ const ACTIVITY_TYPE_GUEST = 'guest';
const MFA_RECENT_DURATION = 1800; // 30 mins
// Database name
const APP_DATABASE = 'appwrite';
// Database Reconnect
const DATABASE_RECONNECT_SLEEP = 2;
const DATABASE_RECONNECT_MAX_ATTEMPTS = 10;
+23 -24
View File
@@ -4,7 +4,6 @@ use Appwrite\OpenSSL\OpenSSL;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\System\System;
Database::addFilter(
@@ -70,11 +69,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
$attributes = $database->find('attributes', [
$attributes = $database->getAuthorization()->skip(fn () => $database->find('attributes', [
Query::equal('collectionInternalId', [$document->getSequence()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getLimitForAttributes()),
]);
]));
foreach ($attributes as $attribute) {
$attributeType = $attribute->getAttribute('type');
@@ -105,12 +104,12 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
return $database->getAuthorization()->skip(fn () => $database
->find('indexes', [
Query::equal('collectionInternalId', [$document->getSequence()]),
Query::equal('databaseInternalId', [$document->getAttribute('databaseInternalId')]),
Query::limit($database->getLimitForIndexes()),
]);
]));
}
);
@@ -120,11 +119,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
return $database->getAuthorization()->skip(fn () => $database
->find('platforms', [
Query::equal('projectInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
]));
}
);
@@ -134,12 +133,12 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
return $database->getAuthorization()->skip(fn () => $database
->find('keys', [
Query::equal('resourceType', ['projects']),
Query::equal('resourceInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
]));
}
);
@@ -149,11 +148,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
return $database->getAuthorization()->skip(fn () => $database
->find('devKeys', [
Query::equal('projectInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
]));
}
);
@@ -163,11 +162,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
return $database->getAuthorization()->skip(fn () => $database
->find('webhooks', [
Query::equal('projectInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]);
]));
}
);
@@ -177,7 +176,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database->find('sessions', [
return $database->getAuthorization()->skip(fn () => $database->find('sessions', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
]));
@@ -190,7 +189,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
return $database->getAuthorization()->skip(fn () => $database
->find('tokens', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -204,7 +203,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
return $database->getAuthorization()->skip(fn () => $database
->find('challenges', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -218,7 +217,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
return $database->getAuthorization()->skip(fn () => $database
->find('authenticators', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -232,7 +231,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
return $database->getAuthorization()->skip(fn () => $database
->find('memberships', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY),
@@ -252,14 +251,14 @@ Database::addFilter(
default => ['function', 'site']
};
return $database
return $database->getAuthorization()->skip(fn () => $database
->find('variables', [
Query::equal('resourceInternalId', [$document->getSequence()]),
Query::equal('resourceType', $resourceType),
Query::orderAsc('resourceType'),
Query::orderAsc(),
Query::limit(APP_LIMIT_SUBQUERY),
]);
]));
}
);
@@ -295,11 +294,11 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return $database
return $database->getAuthorization()->skip(fn () => $database
->find('variables', [
Query::equal('resourceType', ['project']),
Query::limit(APP_LIMIT_SUBQUERY)
]);
]));
}
);
@@ -332,7 +331,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
return Authorization::skip(fn () => $database
return $database->getAuthorization()->skip(fn () => $database
->find('targets', [
Query::equal('userInternalId', [$document->getSequence()]),
Query::limit(APP_LIMIT_SUBQUERY)
@@ -346,7 +345,7 @@ Database::addFilter(
return;
},
function (mixed $value, Document $document, Database $database) {
$targetIds = Authorization::skip(fn () => \array_map(
$targetIds = $database->getAuthorization()->skip(fn () => \array_map(
fn ($document) => $document->getAttribute('targetInternalId'),
$database->find('subscribers', [
Query::equal('topicInternalId', [$document->getSequence()]),
+6
View File
@@ -324,6 +324,12 @@ $register->set('pools', function () {
Config::setParam('pools-' . $key, $config);
}
$reconnectAttempts = (int) System::getEnv('_APP_CONNECTIONS_RECONNECT_ATTEMPTS', 5);
$reconnectSleep = (int) System::getEnv('_APP_CONNECTIONS_RECONNECT_SLEEP', 2);
$group->setReconnectAttempts($reconnectAttempts);
$group->setReconnectSleep($reconnectSleep);
return $group;
});
+62 -48
View File
@@ -230,7 +230,7 @@ App::setResource('allowedSchemes', function (Document $project) {
/**
* Rule associated with a request origin.
*/
App::setResource('rule', function (Request $request, Database $dbForPlatform, Document $project) {
App::setResource('rule', function (Request $request, Database $dbForPlatform, Document $project, Authorization $authorization) {
$domain = \parse_url($request->getOrigin(), PHP_URL_HOST);
if (empty($domain)) {
return new Document();
@@ -238,7 +238,7 @@ App::setResource('rule', function (Request $request, Database $dbForPlatform, Do
// TODO: (@Meldiron) Remove after 1.7.x migration
$isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5';
$rule = Authorization::skip(function () use ($dbForPlatform, $domain, $isMd5) {
$rule = $authorization->skip(function () use ($dbForPlatform, $domain, $isMd5) {
if ($isMd5) {
return $dbForPlatform->getDocument('rules', md5($domain));
}
@@ -253,7 +253,7 @@ App::setResource('rule', function (Request $request, Database $dbForPlatform, Do
}
return $rule;
}, ['request', 'dbForPlatform', 'project']);
}, ['request', 'dbForPlatform', 'project', 'authorization']);
/**
* CORS service
@@ -321,7 +321,7 @@ App::setResource('redirectValidator', function (Document $devKey, array $allowed
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) {
App::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.
*
@@ -341,7 +341,7 @@ App::setResource('user', function (string $mode, Document $project, Document $co
* overwriting the previous value.
*/
Authorization::setDefaultStatus(true);
$authorization->setDefaultStatus(true);
$store->setKey('a_session_' . $project->getId());
@@ -408,7 +408,7 @@ App::setResource('user', function (string $mode, Document $project, Document $co
}
// if (APP_MODE_ADMIN === $mode) {
// if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) {
// Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users.
// $authorization->setDefaultStatus(false); // Cancel security segmentation for admin users.
// } else {
// $user = new Document([]);
// }
@@ -440,9 +440,9 @@ App::setResource('user', function (string $mode, Document $project, Document $co
$dbForPlatform->setMetadata('user', $user->getId());
return $user;
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'store', 'proofForToken']);
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForPlatform', 'store', 'proofForToken', 'authorization']);
App::setResource('project', function ($dbForPlatform, $request, $console) {
App::setResource('project', function ($dbForPlatform, $request, $console, $authorization) {
/** @var Appwrite\Utopia\Request $request */
/** @var Utopia\Database\Database $dbForPlatform */
/** @var Utopia\Database\Document $console */
@@ -453,10 +453,10 @@ App::setResource('project', function ($dbForPlatform, $request, $console) {
return $console;
}
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
$project = $authorization->skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
return $project;
}, ['dbForPlatform', 'request', 'console']);
}, ['dbForPlatform', 'request', 'console', 'authorization']);
App::setResource('session', function (User $user, Store $store, Token $proofForToken) {
if ($user->isEmpty()) {
@@ -479,10 +479,6 @@ App::setResource('session', function (User $user, Store $store, Token $proofForT
return;
}, ['user', 'store', 'proofForToken']);
App::setResource('console', function () {
return new Document(Config::getParam('console'));
}, []);
App::setResource('store', function (): Store {
return new Store();
});
@@ -513,7 +509,15 @@ App::setResource('proofForCode', function (): Code {
return $code;
});
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project) {
App::setResource('console', function () {
return new Document(Config::getParam('console'));
}, []);
App::setResource('authorization', function () {
return new Authorization();
}, []);
App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform, Cache $cache, Document $project, Authorization $authorization) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -529,6 +533,8 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
$database = new Database($adapter, $cache);
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
@@ -550,13 +556,16 @@ App::setResource('dbForProject', function (Group $pools, Database $dbForPlatform
}
return $database;
}, ['pools', 'dbForPlatform', 'cache', 'project']);
}, ['pools', 'dbForPlatform', 'cache', 'project', 'authorization']);
App::setResource('dbForPlatform', function (Group $pools, Cache $cache, Authorization $authorization) {
App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
$adapter = new DatabasePool($pools->get('console'));
$database = new Database($adapter, $cache);
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', 'console')
@@ -566,12 +575,12 @@ App::setResource('dbForPlatform', function (Group $pools, Cache $cache) {
$database->setDocumentType('users', User::class);
return $database;
}, ['pools', 'cache']);
}, ['pools', 'cache', 'authorization']);
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
$databases = [];
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases) {
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -583,13 +592,16 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
$dsn = new DSN('mysql://' . $project->getAttribute('database'));
}
$configure = (function (Database $database) use ($project, $dsn) {
$configure = (function (Database $database) use ($project, $dsn, $authorization) {
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId())
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES);
$database->setDocumentType('users', User::class);
->setMaxQueryValues(APP_DATABASE_QUERY_MAX_VALUES)
->setDocumentType('users', User::class)
;
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
@@ -619,12 +631,12 @@ App::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform
return $database;
};
}, ['pools', 'dbForPlatform', 'cache']);
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
App::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, &$database) {
return function (?Document $project = null) use ($pools, $cache, $authorization, &$database) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int) $project->getSequence());
return $database;
@@ -634,6 +646,8 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = new Database($adapter, $cache);
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_API)
@@ -646,7 +660,7 @@ App::setResource('getLogsDB', function (Group $pools, Cache $cache) {
return $database;
};
}, ['pools', 'cache']);
}, ['pools', 'cache', 'authorization']);
App::setResource('audit', function ($dbForProject) {
$adapter = new AdapterDatabase($dbForProject);
@@ -845,7 +859,7 @@ App::setResource('promiseAdapter', function ($register) {
return $register->get('promiseAdapter');
}, ['register']);
App::setResource('schema', function ($utopia, $dbForProject) {
App::setResource('schema', function ($utopia, $dbForProject, $authorization) {
$complexity = function (int $complexity, array $args) {
$queries = Query::parseQueries($args['queries'] ?? []);
@@ -855,8 +869,8 @@ App::setResource('schema', function ($utopia, $dbForProject) {
return $complexity * $limit;
};
$attributes = function (int $limit, int $offset) use ($dbForProject) {
$attrs = Authorization::skip(fn () => $dbForProject->find('attributes', [
$attributes = function (int $limit, int $offset) use ($dbForProject, $authorization) {
$attrs = $authorization->skip(fn () => $dbForProject->find('attributes', [
Query::limit($limit),
Query::offset($offset),
]));
@@ -930,7 +944,7 @@ App::setResource('schema', function ($utopia, $dbForProject) {
$urls,
$params,
);
}, ['utopia', 'dbForProject']);
}, ['utopia', 'dbForProject', 'authorization']);
App::setResource('gitHub', function (Cache $cache) {
return new VcsGitHub($cache);
@@ -958,7 +972,7 @@ App::setResource('smsRates', function () {
return [];
});
App::setResource('devKey', function (Request $request, Document $project, array $servers, Database $dbForPlatform) {
App::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
@@ -977,7 +991,7 @@ App::setResource('devKey', function (Request $request, Document $project, array
$accessedAt = $key->getAttribute('accessedAt', 0);
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DatabaseDateTime::now());
Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$authorization->skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
@@ -994,15 +1008,15 @@ App::setResource('devKey', function (Request $request, Document $project, array
/** Update access time as well */
$key->setAttribute('accessedAt', DatabaseDateTime::now());
$key = Authorization::skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$key = $authorization->skip(fn () => $dbForPlatform->updateDocument('devKeys', $key->getId(), $key));
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
}
}
return $key;
}, ['request', 'project', 'servers', 'dbForPlatform']);
}, ['request', 'project', 'servers', 'dbForPlatform', 'authorization']);
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request) {
App::setResource('team', function (Document $project, Database $dbForPlatform, App $utopia, Request $request, Authorization $authorization) {
$teamInternalId = '';
if ($project->getId() !== 'console') {
$teamInternalId = $project->getAttribute('teamInternalId', '');
@@ -1012,7 +1026,7 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
if (str_starts_with($path, '/v1/projects/:projectId')) {
$uri = $request->getURI();
$pid = explode('/', $uri)[3];
$p = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $pid));
$p = $authorization->skip(fn () => $dbForPlatform->getDocument('projects', $pid));
$teamInternalId = $p->getAttribute('teamInternalId', '');
} elseif ($path === '/v1/projects') {
$teamId = $request->getParam('teamId', '');
@@ -1021,7 +1035,7 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
return new Document([]);
}
$team = Authorization::skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
$team = $authorization->skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
return $team;
}
}
@@ -1030,14 +1044,14 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
return new Document([]);
}
$team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) {
$team = $authorization->skip(function () use ($dbForPlatform, $teamInternalId) {
return $dbForPlatform->findOne('teams', [
Query::equal('$sequence', [$teamInternalId]),
]);
});
return $team;
}, ['project', 'dbForPlatform', 'utopia', 'request']);
}, ['project', 'dbForPlatform', 'utopia', 'request', 'authorization']);
App::setResource(
'isResourceBlocked',
@@ -1075,7 +1089,7 @@ App::setResource('apiKey', function (Request $request, Document $project): ?Key
App::setResource('executor', fn () => new Executor());
App::setResource('resourceToken', function ($project, $dbForProject, $request) {
App::setResource('resourceToken', function ($project, $dbForProject, $request, Authorization $authorization) {
$tokenJWT = $request->getParam('token');
if (!empty($tokenJWT) && !$project->isEmpty()) { // JWT authentication
@@ -1093,7 +1107,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
return new Document([]);
}
$token = Authorization::skip(fn () => $dbForProject->getDocument('resourceTokens', $tokenId));
$token = $authorization->skip(fn () => $dbForProject->getDocument('resourceTokens', $tokenId));
if ($token->isEmpty()) {
return new Document([]);
@@ -1111,7 +1125,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
}
return match ($token->getAttribute('resourceType')) {
TOKENS_RESOURCE_TYPE_FILES => (function () use ($token, $dbForProject) {
TOKENS_RESOURCE_TYPE_FILES => (function () use ($token, $dbForProject, $authorization) {
$sequences = explode(':', $token->getAttribute('resourceInternalId'));
$ids = explode(':', $token->getAttribute('resourceId'));
@@ -1122,7 +1136,7 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
$accessedAt = $token->getAttribute('accessedAt', 0);
if (empty($accessedAt) || DatabaseDateTime::formatTz(DatabaseDateTime::addSeconds(new \DateTime(), -APP_RESOURCE_TOKEN_ACCESS)) > $accessedAt) {
$token->setAttribute('accessedAt', DatabaseDateTime::now());
Authorization::skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
$authorization->skip(fn () => $dbForProject->updateDocument('resourceTokens', $token->getId(), $token));
}
return new Document([
@@ -1137,8 +1151,8 @@ App::setResource('resourceToken', function ($project, $dbForProject, $request) {
};
}
return new Document([]);
}, ['project', 'dbForProject', 'request']);
}, ['project', 'dbForProject', 'request', 'authorization']);
App::setResource('transactionState', function (Database $dbForProject) {
return new TransactionState($dbForProject);
}, ['dbForProject']);
App::setResource('transactionState', function (Database $dbForProject, Authorization $authorization) {
return new TransactionState($dbForProject, $authorization);
}, ['dbForProject', 'authorization']);
+32 -11
View File
@@ -32,7 +32,6 @@ use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\DSN\DSN;
use Utopia\Logger\Log;
use Utopia\Pools\Group;
@@ -67,6 +66,7 @@ if (!function_exists('getConsoleDB')) {
$adapter = new DatabasePool($pools->get('console'));
$database = new Database($adapter, getCache());
$database
->setDatabase(APP_DATABASE)
->setNamespace('_console')
->setMetadata('host', \gethostname())
->setMetadata('project', '_console');
@@ -123,6 +123,7 @@ if (!function_exists('getProjectDB')) {
}
$database
->setDatabase(APP_DATABASE)
->setMetadata('host', \gethostname())
->setMetadata('project', $project->getId());
@@ -309,7 +310,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
'value' => '{}'
]);
$statsDocument = Authorization::skip(fn () => $database->createDocument('realtime', $document));
$statsDocument = $database->getAuthorization()->skip(fn () => $database->createDocument('realtime', $document));
break;
} catch (Throwable) {
Console::warning("Collection not ready. Retrying connection ({$attempts})...");
@@ -339,7 +340,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
->setAttribute('timestamp', DateTime::now())
->setAttribute('value', json_encode($payload));
Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
$database->getAuthorization()->skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
} catch (Throwable $th) {
$logError($th, "updateWorkerDocument");
}
@@ -370,7 +371,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
$payload = [];
$list = Authorization::skip(fn () => $database->find('realtime', [
$list = $database->getAuthorization()->skip(fn () => $database->find('realtime', [
Query::greaterThan('timestamp', DateTime::addSeconds(new \DateTime(), -15)),
]));
@@ -464,13 +465,13 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
if ($realtime->hasSubscriber($projectId, 'user:' . $userId)) {
$connection = array_key_first(reset($realtime->subscriptions[$projectId]['user:' . $userId]));
$consoleDatabase = getConsoleDB();
$project = Authorization::skip(fn () => $consoleDatabase->getDocument('projects', $projectId));
$project = $consoleDatabase->getAuthorization()->skip(fn () => $consoleDatabase->getDocument('projects', $projectId));
$database = getProjectDB($project);
/** @var Appwrite\Utopia\Database\Documents\User $user */
$user = $database->getDocument('users', $userId);
$roles = $user->getRoles();
$roles = $user->getRoles($database->getAuthorization());
$channels = $realtime->connections[$connection]['channels'];
$realtime->unsubscribe($connection);
@@ -526,6 +527,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
try {
/** @var Document $project */
$project = $app->getResource('project');
$authorization = $app->getResource('authorization');
/*
* Project Check
@@ -537,7 +539,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
if (
array_key_exists('realtime', $project->getAttribute('apis', []))
&& !$project->getAttribute('apis', [])['realtime']
&& !(User::isPrivileged(Authorization::getRoles()) || User::isApp(Authorization::getRoles()))
&& !(User::isPrivileged($authorization->getRoles()) || User::isApp($authorization->getRoles()))
) {
throw new AppwriteException(AppwriteException::GENERAL_API_DISABLED);
}
@@ -573,7 +575,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
}
$roles = $user->getRoles();
$roles = $user->getRoles($authorization);
$channels = Realtime::convertChannels($request->getQuery('channels', []), $user->getId());
@@ -586,6 +588,8 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$realtime->subscribe($project->getId(), $connection, $roles, $channels);
$realtime->connections[$connection]['authorization'] = $authorization;
$user = empty($user->getId()) ? null : $response->output($user, Response::MODEL_ACCOUNT);
$server->send([$connection], json_encode([
@@ -614,6 +618,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$code = 500;
}
$message = $th->getMessage();
// sanitize 0 && 5xx errors
@@ -643,12 +648,19 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
$server->onMessage(function (int $connection, string $message) use ($server, $register, $realtime, $containerId) {
try {
$response = new Response(new SwooleResponse());
$projectId = $realtime->connections[$connection]['projectId'];
$projectId = $realtime->connections[$connection]['projectId'] ?? null;
// Get authorization from connection (stored during onOpen)
$authorization = $realtime->connections[$connection]['authorization'] ?? null;
$database = getConsoleDB();
$database->setAuthorization($authorization);
if ($projectId !== 'console') {
$project = Authorization::skip(fn () => $database->getDocument('projects', $projectId));
$project = $authorization->skip(fn () => $database->getDocument('projects', $projectId));
$database = getProjectDB($project);
$database->setAuthorization($authorization);
} else {
$project = null;
}
@@ -712,10 +724,19 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.');
}
$roles = $user->getRoles();
$roles = $user->getRoles($database->getAuthorization());
$channels = Realtime::convertChannels(array_flip($realtime->connections[$connection]['channels']), $user->getId());
// Preserve authorization before subscribe overwrites the connection array
$authorization = $realtime->connections[$connection]['authorization'] ?? null;
$realtime->subscribe($realtime->connections[$connection]['projectId'], $connection, $roles, $channels);
// Restore authorization after subscribe
if ($authorization !== null) {
$realtime->connections[$connection]['authorization'] = $authorization;
}
$user = $response->output($user, Response::MODEL_ACCOUNT);
$server->send([$connection], json_encode([
'type' => 'response',
+39 -18
View File
@@ -49,19 +49,31 @@ use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Telemetry\Adapter\None as NoTelemetry;
Authorization::disable();
Runtime::enableCoroutine();
Server::setResource('register', fn () => $register);
Server::setResource('dbForPlatform', function (Cache $cache, Registry $register) {
Server::setResource('authorization', function () {
$authorization = new Authorization();
$authorization->disable();
return $authorization;
}, []);
Server::setResource('dbForPlatform', function (Cache $cache, Registry $register, Authorization $authorization) {
$pools = $register->get('pools');
$adapter = new DatabasePool($pools->get('console'));
$dbForPlatform = new Database($adapter, $cache);
$dbForPlatform->setNamespace('_console');
$dbForPlatform->setDocumentType('users', User::class);
$dbForPlatform
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setNamespace('_console')
->setDocumentType('users', User::class)
;
return $dbForPlatform;
}, ['cache', 'register']);
}, ['cache', 'register', 'authorization']);
Server::setResource('project', function (Message $message, Database $dbForPlatform) {
$payload = $message->getPayload() ?? [];
@@ -74,7 +86,7 @@ Server::setResource('project', function (Message $message, Database $dbForPlatfo
return $dbForPlatform->getDocument('projects', $project->getId());
}, ['message', 'dbForPlatform']);
Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForPlatform) {
Server::setResource('dbForProject', function (Cache $cache, Registry $register, Message $message, Document $project, Database $dbForPlatform, Authorization $authorization) {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -106,15 +118,18 @@ Server::setResource('dbForProject', function (Cache $cache, Registry $register,
->setNamespace('_' . $project->getSequence());
}
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
return $database;
}, ['cache', 'register', 'message', 'project', 'dbForPlatform']);
}, ['cache', 'register', 'message', 'project', 'dbForPlatform', 'authorization']);
Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache) {
Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatform, $cache, Authorization $authorization) {
$databases = []; // TODO: @Meldiron This should probably be responsibility of utopia-php/pools
return function (Document $project) use ($pools, $dbForPlatform, $cache, &$databases): Database {
return function (Document $project) use ($pools, $dbForPlatform, $cache, $authorization, &$databases): Database {
if ($project->isEmpty() || $project->getId() === 'console') {
return $dbForPlatform;
}
@@ -128,7 +143,7 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
if (isset($databases[$dsn->getHost()])) {
$database = $databases[$dsn->getHost()];
$database->setAuthorization($authorization);
$sharedTables = \explode(',', System::getEnv('_APP_DATABASE_SHARED_TABLES', ''));
if (\in_array($dsn->getHost(), $sharedTables)) {
@@ -165,15 +180,18 @@ Server::setResource('getProjectDB', function (Group $pools, Database $dbForPlatf
->setNamespace('_' . $project->getSequence());
}
$database->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER);
return $database;
};
}, ['pools', 'dbForPlatform', 'cache']);
}, ['pools', 'dbForPlatform', 'cache', 'authorization']);
Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
Server::setResource('getLogsDB', function (Group $pools, Cache $cache, Authorization $authorization) {
$database = null;
return function (?Document $project = null) use ($pools, $cache, $database) {
return function (?Document $project = null) use ($pools, $cache, $database, $authorization) {
if ($database !== null && $project !== null && !$project->isEmpty() && $project->getId() !== 'console') {
$database->setTenant((int)$project->getSequence());
return $database;
@@ -183,6 +201,8 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
$database = new Database($adapter, $cache);
$database
->setDatabase(APP_DATABASE)
->setAuthorization($authorization)
->setSharedTables(true)
->setNamespace('logsV1')
->setTimeout(APP_DATABASE_TIMEOUT_MILLISECONDS_WORKER)
@@ -195,7 +215,7 @@ Server::setResource('getLogsDB', function (Group $pools, Cache $cache) {
return $database;
};
}, ['pools', 'cache']);
}, ['pools', 'cache', 'authorization']);
Server::setResource('abuseRetention', function () {
return time() - (int) System::getEnv('_APP_MAINTENANCE_RETENTION_ABUSE', 86400); // 1 day
@@ -514,7 +534,8 @@ $worker
->inject('log')
->inject('pools')
->inject('project')
->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project) use ($worker, $queueName) {
->inject('authorization')
->action(function (Throwable $error, ?Logger $logger, Log $log, Group $pools, Document $project, Authorization $authorization) use ($worker, $queueName) {
$version = System::getEnv('_APP_VERSION', 'UNKNOWN');
if ($logger) {
@@ -530,7 +551,7 @@ $worker
$log->addExtra('file', $error->getFile());
$log->addExtra('line', $error->getLine());
$log->addExtra('trace', $error->getTraceAsString());
$log->addExtra('roles', Authorization::getRoles());
$log->addExtra('roles', $authorization->getRoles());
$isProduction = System::getEnv('_APP_ENV', 'development') === 'production';
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
+8 -8
View File
@@ -45,18 +45,18 @@
"ext-sockets": "*",
"appwrite/php-runtimes": "0.19.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "1.*.*",
"utopia-php/abuse": "1.*",
"utopia-php/analytics": "0.10.*",
"utopia-php/audit": "2.0.2-rc3",
"utopia-php/audit": "2.*",
"utopia-php/auth": "0.5.*",
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "1.*.*",
"utopia-php/database": "3.*.*",
"utopia-php/config": "1.*",
"utopia-php/database": "4.*",
"utopia-php/detector": "0.2.*",
"utopia-php/domains": "0.9.*",
"utopia-php/domains": "0.11.*",
"utopia-php/emails": "0.6.*",
"utopia-php/dns": "1.4.*",
"utopia-php/dns": "1.5.*",
"utopia-php/dsn": "0.2.1",
"utopia-php/framework": "0.33.*",
"utopia-php/fetch": "0.5.*",
@@ -64,7 +64,7 @@
"utopia-php/locale": "0.8.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.20.*",
"utopia-php/migration": "1.3.*",
"utopia-php/migration": "1.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
@@ -72,7 +72,7 @@
"utopia-php/queue": "0.15.*",
"utopia-php/registry": "0.5.*",
"utopia-php/storage": "0.18.*",
"utopia-php/swoole": "0.8.*",
"utopia-php/swoole": "1.*",
"utopia-php/system": "0.9.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/vcs": "0.13.*",
Generated
+128 -87
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "ff3172688b600aa3c560131c9d9c5588",
"content-hash": "33da844fdf5648d1d1a027dfb6ae42bc",
"packages": [
{
"name": "adhocore/jwt",
@@ -756,16 +756,16 @@
},
{
"name": "google/protobuf",
"version": "v4.33.2",
"version": "v4.33.4",
"source": {
"type": "git",
"url": "https://github.com/protocolbuffers/protobuf-php.git",
"reference": "fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318"
"reference": "22d28025cda0d223a2e48c2e16c5284ecc9f5402"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318",
"reference": "fbd96b7bf1343f4b0d8fb358526c7ba4d72f1318",
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/22d28025cda0d223a2e48c2e16c5284ecc9f5402",
"reference": "22d28025cda0d223a2e48c2e16c5284ecc9f5402",
"shasum": ""
},
"require": {
@@ -794,9 +794,9 @@
"proto"
],
"support": {
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.2"
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.33.4"
},
"time": "2025-12-05T22:12:22+00:00"
"time": "2026-01-12T17:58:43+00:00"
},
{
"name": "league/csv",
@@ -3455,25 +3455,25 @@
},
{
"name": "utopia-php/abuse",
"version": "1.2.0",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/abuse.git",
"reference": "3339d057c6bb1fa3e5ac5b2598923f6938425ec2"
"reference": "15cd5dbefa4453e8a2d90649a7078e242966ac3f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/3339d057c6bb1fa3e5ac5b2598923f6938425ec2",
"reference": "3339d057c6bb1fa3e5ac5b2598923f6938425ec2",
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/15cd5dbefa4453e8a2d90649a7078e242966ac3f",
"reference": "15cd5dbefa4453e8a2d90649a7078e242966ac3f",
"shasum": ""
},
"require": {
"appwrite/appwrite": "19.*.*",
"appwrite/appwrite": "19.*",
"ext-curl": "*",
"ext-pdo": "*",
"ext-redis": "*",
"php": ">=8.0",
"utopia-php/database": "3.*.*"
"utopia-php/database": "4.*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -3501,9 +3501,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/abuse/issues",
"source": "https://github.com/utopia-php/abuse/tree/1.2.0"
"source": "https://github.com/utopia-php/abuse/tree/1.2.1"
},
"time": "2026-01-05T21:29:10+00:00"
"time": "2026-01-15T02:09:49+00:00"
},
{
"name": "utopia-php/analytics",
@@ -3553,23 +3553,23 @@
},
{
"name": "utopia-php/audit",
"version": "2.0.2-rc3",
"version": "2.0.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/audit.git",
"reference": "f60a298b516300f56a328403b334b7d62a96e7e7"
"reference": "1301ab2607667b9f86456f86895f3e26f8c0c9a7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/f60a298b516300f56a328403b334b7d62a96e7e7",
"reference": "f60a298b516300f56a328403b334b7d62a96e7e7",
"url": "https://api.github.com/repos/utopia-php/audit/zipball/1301ab2607667b9f86456f86895f3e26f8c0c9a7",
"reference": "1301ab2607667b9f86456f86895f3e26f8c0c9a7",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/database": "3.*",
"utopia-php/database": "4.*",
"utopia-php/fetch": "0.5.*",
"utopia-php/validators": "0.1.*"
"utopia-php/validators": "0.2.*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -3596,9 +3596,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/audit/issues",
"source": "https://github.com/utopia-php/audit/tree/2.0.2-rc3"
"source": "https://github.com/utopia-php/audit/tree/2.0.4"
},
"time": "2026-01-06T15:32:52+00:00"
"time": "2026-01-14T07:22:46+00:00"
},
{
"name": "utopia-php/auth",
@@ -3899,16 +3899,16 @@
},
{
"name": "utopia-php/database",
"version": "3.6.1",
"version": "4.5.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "c8c1b2f5770245dd4006e2680681e3efbe8b1fa7"
"reference": "8e6a033d4da09a2f2ac1f79fd85fcfa2da018d23"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/c8c1b2f5770245dd4006e2680681e3efbe8b1fa7",
"reference": "c8c1b2f5770245dd4006e2680681e3efbe8b1fa7",
"url": "https://api.github.com/repos/utopia-php/database/zipball/8e6a033d4da09a2f2ac1f79fd85fcfa2da018d23",
"reference": "8e6a033d4da09a2f2ac1f79fd85fcfa2da018d23",
"shasum": ""
},
"require": {
@@ -3951,9 +3951,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/3.6.1"
"source": "https://github.com/utopia-php/database/tree/4.5.2"
},
"time": "2025-12-16T09:55:41+00:00"
"time": "2026-01-15T04:23:30+00:00"
},
{
"name": "utopia-php/detector",
@@ -4002,22 +4002,22 @@
},
{
"name": "utopia-php/dns",
"version": "1.4.1",
"version": "1.5.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/dns.git",
"reference": "5daf8b683dad877491c4df84c6be24850b2f363b"
"reference": "a1f490ba425b1a5128e7aaa24eff560900812d21"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/dns/zipball/5daf8b683dad877491c4df84c6be24850b2f363b",
"reference": "5daf8b683dad877491c4df84c6be24850b2f363b",
"url": "https://api.github.com/repos/utopia-php/dns/zipball/a1f490ba425b1a5128e7aaa24eff560900812d21",
"reference": "a1f490ba425b1a5128e7aaa24eff560900812d21",
"shasum": ""
},
"require": {
"php": ">=8.3",
"utopia-php/console": "0.0.*",
"utopia-php/domains": "0.9.*",
"utopia-php/domains": "0.11.*",
"utopia-php/span": "1.0.*",
"utopia-php/telemetry": "*",
"utopia-php/validators": "0.*"
},
@@ -4053,22 +4053,22 @@
],
"support": {
"issues": "https://github.com/utopia-php/dns/issues",
"source": "https://github.com/utopia-php/dns/tree/1.4.1"
"source": "https://github.com/utopia-php/dns/tree/1.5.3"
},
"time": "2025-12-17T09:09:08+00:00"
"time": "2026-01-13T11:39:38+00:00"
},
{
"name": "utopia-php/domains",
"version": "0.9.2",
"version": "0.11.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/domains.git",
"reference": "52b654f8a0e170bfa2e54cb47755b256822477c7"
"reference": "f333e23e721ca5cd3bd21063fa88304114b0467d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/domains/zipball/52b654f8a0e170bfa2e54cb47755b256822477c7",
"reference": "52b654f8a0e170bfa2e54cb47755b256822477c7",
"url": "https://api.github.com/repos/utopia-php/domains/zipball/f333e23e721ca5cd3bd21063fa88304114b0467d",
"reference": "f333e23e721ca5cd3bd21063fa88304114b0467d",
"shasum": ""
},
"require": {
@@ -4115,9 +4115,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/domains/issues",
"source": "https://github.com/utopia-php/domains/tree/0.9.2"
"source": "https://github.com/utopia-php/domains/tree/0.11.0"
},
"time": "2025-11-26T12:16:36+00:00"
"time": "2026-01-13T09:40:08+00:00"
},
{
"name": "utopia-php/dsn",
@@ -4168,22 +4168,22 @@
},
{
"name": "utopia-php/emails",
"version": "0.6.4",
"version": "0.6.5",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/emails.git",
"reference": "fb2bd5c428e88f645b0f7ede0dd29ac0d120ec52"
"reference": "178e57a0f9a24139500c94ce73d166800197b6cf"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/emails/zipball/fb2bd5c428e88f645b0f7ede0dd29ac0d120ec52",
"reference": "fb2bd5c428e88f645b0f7ede0dd29ac0d120ec52",
"url": "https://api.github.com/repos/utopia-php/emails/zipball/178e57a0f9a24139500c94ce73d166800197b6cf",
"reference": "178e57a0f9a24139500c94ce73d166800197b6cf",
"shasum": ""
},
"require": {
"php": ">=8.0",
"utopia-php/cli": "^0.15",
"utopia-php/domains": "^0.9",
"utopia-php/domains": "^0.11",
"utopia-php/fetch": "^0.5",
"utopia-php/validators": "0.*"
},
@@ -4222,9 +4222,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/emails/issues",
"source": "https://github.com/utopia-php/emails/tree/0.6.4"
"source": "https://github.com/utopia-php/emails/tree/0.6.5"
},
"time": "2025-12-18T16:36:50+00:00"
"time": "2026-01-13T09:55:59+00:00"
},
{
"name": "utopia-php/fetch",
@@ -4267,23 +4267,23 @@
},
{
"name": "utopia-php/framework",
"version": "0.33.36",
"version": "0.33.37",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "fd835ed77e1cdf327067ce4e650cce86304e7098"
"reference": "30a119d76531d89da9240496940c84fcd9e1758b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/fd835ed77e1cdf327067ce4e650cce86304e7098",
"reference": "fd835ed77e1cdf327067ce4e650cce86304e7098",
"url": "https://api.github.com/repos/utopia-php/http/zipball/30a119d76531d89da9240496940c84fcd9e1758b",
"reference": "30a119d76531d89da9240496940c84fcd9e1758b",
"shasum": ""
},
"require": {
"php": ">=8.3",
"utopia-php/compression": "0.1.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/validators": "0.1.*"
"utopia-php/validators": "0.2.*"
},
"require-dev": {
"laravel/pint": "1.*",
@@ -4309,9 +4309,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.33.36"
"source": "https://github.com/utopia-php/http/tree/0.33.37"
},
"time": "2026-01-12T07:32:29+00:00"
"time": "2026-01-13T10:10:21+00:00"
},
{
"name": "utopia-php/image",
@@ -4516,16 +4516,16 @@
},
{
"name": "utopia-php/migration",
"version": "1.3.13",
"version": "1.4.3",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "c5e3f5e970e62e8f7db97b5b90baae2af800a715"
"reference": "52ca4234d8229b68e27e052248734a08784d9d3d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/c5e3f5e970e62e8f7db97b5b90baae2af800a715",
"reference": "c5e3f5e970e62e8f7db97b5b90baae2af800a715",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/52ca4234d8229b68e27e052248734a08784d9d3d",
"reference": "52ca4234d8229b68e27e052248734a08784d9d3d",
"shasum": ""
},
"require": {
@@ -4534,7 +4534,7 @@
"ext-openssl": "*",
"php": ">=8.1",
"utopia-php/console": "0.0.*",
"utopia-php/database": "3.*",
"utopia-php/database": "4.*",
"utopia-php/dsn": "0.2.*",
"utopia-php/storage": "0.18.*"
},
@@ -4565,9 +4565,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/1.3.13"
"source": "https://github.com/utopia-php/migration/tree/1.4.3"
},
"time": "2026-01-07T14:48:05+00:00"
"time": "2026-01-13T09:51:08+00:00"
},
{
"name": "utopia-php/mongo",
@@ -4954,6 +4954,49 @@
},
"time": "2021-03-10T10:45:22+00:00"
},
{
"name": "utopia-php/span",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/span.git",
"reference": "f2f6c499ded3a776e8019902e83d140ff0f89693"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/span/zipball/f2f6c499ded3a776e8019902e83d140ff0f89693",
"reference": "f2f6c499ded3a776e8019902e83d140ff0f89693",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
"require-dev": {
"laravel/pint": "^1.0",
"phpstan/phpstan": "^2.0",
"phpunit/phpunit": "^10.0",
"swoole/ide-helper": "^5.0"
},
"suggest": {
"ext-swoole": "Required for coroutine-based storage"
},
"type": "library",
"autoload": {
"psr-4": {
"Utopia\\Span\\": "src/Span/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"description": "Simple span tracing library for PHP with coroutine support",
"support": {
"issues": "https://github.com/utopia-php/span/issues",
"source": "https://github.com/utopia-php/span/tree/1.0.0"
},
"time": "2026-01-12T20:05:10+00:00"
},
{
"name": "utopia-php/storage",
"version": "0.18.18",
@@ -5014,22 +5057,22 @@
},
{
"name": "utopia-php/swoole",
"version": "0.8.6",
"version": "1.0.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/swoole.git",
"reference": "14b00277c35a258cb263706fd4e05c50368feb4f"
"reference": "95a937acb393dbf95cccba239d55886e2848ab0b"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/14b00277c35a258cb263706fd4e05c50368feb4f",
"reference": "14b00277c35a258cb263706fd4e05c50368feb4f",
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/95a937acb393dbf95cccba239d55886e2848ab0b",
"reference": "95a937acb393dbf95cccba239d55886e2848ab0b",
"shasum": ""
},
"require": {
"ext-swoole": "*",
"php": ">=8.0",
"utopia-php/framework": "0.33.36"
"utopia-php/framework": "0.33.37"
},
"require-dev": {
"laravel/pint": "1.2.*",
@@ -5059,9 +5102,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/swoole/issues",
"source": "https://github.com/utopia-php/swoole/tree/0.8.6"
"source": "https://github.com/utopia-php/swoole/tree/1.0.0"
},
"time": "2026-01-12T07:57:35+00:00"
"time": "2026-01-14T14:00:11+00:00"
},
{
"name": "utopia-php/system",
@@ -5171,16 +5214,16 @@
},
{
"name": "utopia-php/validators",
"version": "0.1.0",
"version": "0.2.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/validators.git",
"reference": "5c57d5b6cf964f8981807c1d3ea8df620c869080"
"reference": "30b6030a5b100fc1dff34506e5053759594b2a20"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/validators/zipball/5c57d5b6cf964f8981807c1d3ea8df620c869080",
"reference": "5c57d5b6cf964f8981807c1d3ea8df620c869080",
"url": "https://api.github.com/repos/utopia-php/validators/zipball/30b6030a5b100fc1dff34506e5053759594b2a20",
"reference": "30b6030a5b100fc1dff34506e5053759594b2a20",
"shasum": ""
},
"require": {
@@ -5188,7 +5231,7 @@
},
"require-dev": {
"laravel/pint": "1.*",
"phpstan/phpstan": "1.*",
"phpstan/phpstan": "2.*",
"phpunit/phpunit": "11.*"
},
"type": "library",
@@ -5210,9 +5253,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/validators/issues",
"source": "https://github.com/utopia-php/validators/tree/0.1.0"
"source": "https://github.com/utopia-php/validators/tree/0.2.0"
},
"time": "2025-11-18T11:05:46+00:00"
"time": "2026-01-13T09:16:51+00:00"
},
{
"name": "utopia-php/vcs",
@@ -5439,16 +5482,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "1.8.9",
"version": "1.8.16",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "5fc210f7403f9ecfa068cd2a74210ec6e2a3cec1"
"reference": "e2963002b39f3aa24bd6bf61a78492e182397174"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/5fc210f7403f9ecfa068cd2a74210ec6e2a3cec1",
"reference": "5fc210f7403f9ecfa068cd2a74210ec6e2a3cec1",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/e2963002b39f3aa24bd6bf61a78492e182397174",
"reference": "e2963002b39f3aa24bd6bf61a78492e182397174",
"shasum": ""
},
"require": {
@@ -5484,9 +5527,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/1.8.9"
"source": "https://github.com/appwrite/sdk-generator/tree/1.8.16"
},
"time": "2026-01-02T12:09:51+00:00"
"time": "2026-01-15T08:16:15+00:00"
},
{
"name": "doctrine/annotations",
@@ -8945,9 +8988,7 @@
],
"aliases": [],
"minimum-stability": "stable",
"stability-flags": {
"utopia-php/audit": 5
},
"stability-flags": {},
"prefer-stable": false,
"prefer-lowest": false,
"platform": {
+29
View File
@@ -1,5 +1,34 @@
# Change Log
## 13.0.0
- Mark release as stable
- Feat: add pull sync on destruction of remote resources (+ confirmation)
- Fix: refine zod schema to check string size
- Validate using zod schema during push cli command
- Maintain order of keys in local config
## 13.0.0-rc.5
- Fix push all command not working correctly
## 13.0.0-rc.4
- Fix CLI ES module import issues
## 13.0.0-rc.3
- Add `Schema` class for programmatically pushing and pulling appwrite config
- Add client side db generation using `schema.db.generate()` command
## 13.0.0-rc.2
- Fixes a lot of typescript errors throughout the codebase
## 13.0.0-rc.1
- Migrates codebase from JavaScript to TypeScript
## 12.0.1
Fix type generation for `point`, `lineString` and `polygon` columns
+4
View File
@@ -1,5 +1,9 @@
# Change Log
## 20.1.1
* Fix boolean parameter not handled correctly in Client requests
## 20.1.0
* Added ability to create columns and indexes synchronously while creating a table
+4
View File
@@ -1,5 +1,9 @@
# Change Log
## 20.3.3
* Fix boolean parameter not handled correctly in Client requests
## 20.3.2
* Fix OAuth2 browser infinite redirect issue
+6 -4
View File
@@ -20,10 +20,12 @@ use Utopia\Database\Validator\Authorization;
class TransactionState
{
private Database $dbForProject;
public function __construct(Database $dbForProject)
private Authorization $authorization;
/** @var Authorization $authorization */
public function __construct(Database $dbForProject, Authorization $authorization)
{
$this->dbForProject = $dbForProject;
$this->authorization = $authorization;
}
@@ -342,12 +344,12 @@ class TransactionState
*/
private function getTransactionState(string $transactionId): array
{
$transaction = Authorization::skip(fn () => $this->dbForProject->getDocument('transactions', $transactionId));
$transaction = $this->authorization->skip(fn () => $this->dbForProject->getDocument('transactions', $transactionId));
if ($transaction->isEmpty() || $transaction->getAttribute('status') !== 'pending') {
return [];
}
$operations = Authorization::skip(fn () => $this->dbForProject->find('transactionLogs', [
$operations = $this->authorization->skip(fn () => $this->dbForProject->find('transactionLogs', [
Query::equal('transactionInternalId', [$transaction->getSequence()]),
Query::orderAsc(),
Query::limit(PHP_INT_MAX)
+4 -2
View File
@@ -100,8 +100,6 @@ abstract class Migration
public function __construct()
{
Authorization::disable();
Authorization::setDefaultStatus(false);
$this->collections = Config::getParam('collections', []);
@@ -129,6 +127,7 @@ abstract class Migration
Document $project,
Database $dbForProject,
Database $dbForPlatform,
Authorization $authorization,
?callable $getProjectDB = null
): self {
$this->project = $project;
@@ -136,6 +135,9 @@ abstract class Migration
$this->dbForPlatform = $dbForPlatform;
$this->getProjectDB = $getProjectDB;
$authorization->disable();
$authorization->setDefaultStatus(false);
return $this;
}
@@ -21,7 +21,7 @@ class Action extends PlatformAction
return \dirname(__DIR__, 6);
}
protected function avatarCallback(string $type, string $code, int $width, int $height, int $quality, Response $response): void
protected function avatar(string $type, string $code, int $width, int $height, int $quality, Response $response): void
{
$code = \strtolower($code);
$type = \strtolower($type);
@@ -58,10 +58,10 @@ class Action extends PlatformAction
unset($image);
}
protected function getUserGitHub(string $userId, Document $project, Database $dbForProject, Database $dbForPlatform, ?Logger $logger): array
protected function getUserGitHub(string $userId, Document $project, Database $dbForProject, Database $dbForPlatform, ?Logger $logger, Authorization $authorization): array
{
try {
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
$sessions = $user->getAttribute('sessions', []);
@@ -112,7 +112,7 @@ class Action extends PlatformAction
->setAttribute('providerRefreshToken', $refreshToken)
->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$oauth2->getAccessTokenExpiry('')));
Authorization::skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
$authorization->skip(fn () => $dbForProject->updateDocument('sessions', $gitHubSession->getId(), $gitHubSession));
$dbForProject->purgeCachedDocument('users', $user->getId());
} catch (Throwable $err) {
@@ -120,7 +120,7 @@ class Action extends PlatformAction
do {
$previousAccessToken = $gitHubSession->getAttribute('providerAccessToken');
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
$sessions = $user->getAttribute('sessions', []);
$gitHubSession = new Document();
@@ -59,6 +59,6 @@ class Get extends Action
public function action(string $code, int $width, int $height, int $quality, Response $response)
{
$this->avatarCallback('browsers', $code, $width, $height, $quality, $response);
$this->avatar('browsers', $code, $width, $height, $quality, $response);
}
}
@@ -53,12 +53,13 @@ class Get extends Action
->inject('contributors')
->inject('employees')
->inject('logger')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger)
public function action(string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization)
{
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
if ($user->isEmpty() && empty($mock)) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -68,7 +69,7 @@ class Get extends Action
$userId = $user->getId();
$email = $user->getAttribute('email', '');
$gitHub = $this->getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger);
$gitHub = $this->getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger, $authorization);
$githubId = $gitHub['id'] ?? '';
$isHero = \array_key_exists($email, $heroes);
@@ -53,12 +53,13 @@ class Get extends Action
->inject('contributors')
->inject('employees')
->inject('logger')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger)
public function action(string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization)
{
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
if ($user->isEmpty() && empty($mock)) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -69,7 +70,7 @@ class Get extends Action
$email = $user->getAttribute('email', '');
$createdAt = new \DateTime($user->getCreatedAt());
$gitHub = $this->getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger);
$gitHub = $this->getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger, $authorization);
$githubName = $gitHub['name'] ?? '';
$githubId = $gitHub['id'] ?? '';
@@ -53,12 +53,13 @@ class Get extends Action
->inject('contributors')
->inject('employees')
->inject('logger')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger)
public function action(string $userId, string $mock, int $width, int $height, Document $user, Document $project, Database $dbForProject, Database $dbForPlatform, Response $response, array $heroes, array $contributors, array $employees, ?Logger $logger, Authorization $authorization)
{
$user = Authorization::skip(fn () => $dbForPlatform->getDocument('users', $userId));
$user = $authorization->skip(fn () => $dbForPlatform->getDocument('users', $userId));
if ($user->isEmpty() && empty($mock)) {
throw new Exception(Exception::USER_NOT_FOUND);
@@ -73,7 +74,7 @@ class Get extends Action
$email = $user->getAttribute('email', '');
$createdAt = new \DateTime($user->getCreatedAt());
$gitHub = $this->getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger);
$gitHub = $this->getUserGitHub($user->getId(), $project, $dbForProject, $dbForPlatform, $logger, $authorization);
$githubName = $gitHub['name'] ?? '';
$githubId = $gitHub['id'] ?? '';
@@ -59,6 +59,6 @@ class Get extends Action
public function action(string $code, int $width, int $height, int $quality, Response $response)
{
$this->avatarCallback('credit-cards', $code, $width, $height, $quality, $response);
$this->avatar('credit-cards', $code, $width, $height, $quality, $response);
}
}
@@ -59,6 +59,6 @@ class Get extends Action
public function action(string $code, int $width, int $height, int $quality, Response $response)
{
$this->avatarCallback('flags', $code, $width, $height, $quality, $response);
$this->avatar('flags', $code, $width, $height, $quality, $response);
}
}
+37 -4
View File
@@ -13,6 +13,7 @@ use Utopia\Database\Exception\Duplicate;
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\Swoole\Request;
use Utopia\System\System;
@@ -142,7 +143,7 @@ class Base extends Action
return $deployment;
}
public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, string $referenceType = 'branch', string $reference = ''): Document
public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github, bool $activate, Authorization $authorization, string $referenceType = 'branch', string $reference = ''): Document
{
$deploymentId = ID::unique();
$providerInstallationId = $installation->getAttribute('providerInstallationId', '');
@@ -239,7 +240,7 @@ class Base extends Action
$isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5';
$ruleId = $isMd5 ? md5($domain) : ID::unique();
Authorization::skip(
$authorization->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -265,7 +266,7 @@ class Base extends Action
$domain = "commit-" . substr($commitDetails['commitHash'], 0, 16) . ".{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
$authorization->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -302,7 +303,7 @@ class Base extends Action
$domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}";
$ruleId = md5($domain);
try {
Authorization::skip(
$authorization->skip(
fn () => $dbForPlatform->createDocument('rules', new Document([
'$id' => $ruleId,
'projectId' => $project->getId(),
@@ -328,6 +329,8 @@ class Base extends Action
}
}
$this->updateEmptyManualRule($project, $site, $deployment, $dbForPlatform, $authorization);
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($site)
@@ -336,4 +339,34 @@ class Base extends Action
return $deployment;
}
/**
* Update empty manual rule for deployment.
* In case of first deployment, deployment ID will be empty in the rules, so we need to update it here.
*
* @param \Utopia\Database\Document $project
* @param \Utopia\Database\Document $resource
* @param \Utopia\Database\Document $deployment
* @param \Utopia\Database\Database $dbForPlatform
* @return void
*/
public static function updateEmptyManualRule(Document $project, Document $resource, Document $deployment, Database $dbForPlatform, Authorization $authorization)
{
$resourceType = $resource->getCollection() === 'sites' ? 'site' : 'function';
$queries = [
Query::equal('projectInternalId', [$project->getSequence()]),
Query::equal('deploymentResourceInternalId', [$resource->getSequence()]),
Query::equal('deploymentResourceType', [$resourceType]),
Query::equal('deploymentId', ['']),
Query::equal('type', ['deployment']),
Query::equal('trigger', ['manual']),
];
$dbForPlatform->forEach('rules', function (Document $rule) use ($deployment, $dbForPlatform, $authorization) {
$authorization->skip(fn () => $dbForPlatform->updateDocument('rules', $rule->getId(), new Document([
'deploymentId' => $deployment->getId(),
'deploymentInternalId' => $deployment->getSequence(),
])));
}, $queries);
}
}
@@ -60,6 +60,7 @@ class Get extends Action
->inject('response')
->inject('dbForPlatform')
->inject('platform')
->inject('authorization')
->callback($this->action(...));
}
@@ -68,7 +69,8 @@ class Get extends Action
string $type,
Response $response,
Database $dbForPlatform,
array $platform
array $platform,
Authorization $authorization,
) {
$domains = $platform['hostnames'] ?? [];
if ($type === 'rules') {
@@ -121,7 +123,7 @@ class Get extends Action
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.');
}
$document = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [
$document = $authorization->skip(fn () => $dbForPlatform->findOne('rules', [
Query::equal('domain', [$value]),
]));
@@ -292,7 +292,7 @@ abstract class Action extends UtopiaAction
};
}
protected function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): Document
protected function createAttribute(string $databaseId, string $collectionId, Document $attribute, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): Document
{
$key = $attribute->getAttribute('key');
$type = $attribute->getAttribute('type', '');
@@ -310,7 +310,7 @@ abstract class Action extends UtopiaAction
throw new Exception($this->getSpatialTypeNotSupportedException(), params: [$type]);
}
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -371,7 +371,7 @@ abstract class Action extends UtopiaAction
\in_array($attribute->getAttribute('type'), Database::SPATIAL_TYPES) &&
$attribute->getAttribute('required')
) {
$hasData = !Authorization::skip(fn () => $dbForProject
$hasData = !$authorization->skip(fn () => $dbForProject
->findOne('database_' . $db->getSequence() . '_collection_' . $collection->getSequence()))
->isEmpty();
@@ -472,9 +472,9 @@ abstract class Action extends UtopiaAction
return $attribute;
}
protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, string $type, int $size = null, string $filter = null, string|bool|int|float|array $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document
protected function updateAttribute(string $databaseId, string $collectionId, string $key, Database $dbForProject, Event $queueForEvents, Authorization $authorization, string $type, int $size = null, string $filter = null, string|bool|int|float|array $default = null, bool $required = null, int|float|null $min = null, int|float|null $max = null, array $elements = null, array $options = [], string $newKey = null): Document
{
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
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\Swoole\Response as SwooleResponse;
@@ -69,10 +70,11 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
@@ -81,7 +83,7 @@ class Create extends Action
'required' => $required,
'default' => $default,
'array' => $array,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -68,10 +69,11 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?bool $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -79,6 +81,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_BOOLEAN,
default: $default,
required: $required,
@@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
@@ -70,10 +71,11 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->createAttribute(
$databaseId,
@@ -90,7 +92,8 @@ class Create extends Action
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents
$queueForEvents,
$authorization
);
$response
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
@@ -69,10 +70,11 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -80,6 +82,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_DATETIME,
default: $default,
required: $required,
@@ -67,12 +67,13 @@ class Delete extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -13,6 +13,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
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\Swoole\Response as SwooleResponse;
@@ -70,10 +71,11 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->createAttribute(
$databaseId,
@@ -90,7 +92,8 @@ class Create extends Action
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents
$queueForEvents,
$authorization
);
$response
@@ -12,6 +12,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -69,10 +70,11 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -80,6 +82,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_EMAIL,
default: $default,
@@ -13,6 +13,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
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\Swoole\Response as SwooleResponse;
@@ -73,10 +74,11 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
if (!is_null($default) && !\in_array($default, $elements, true)) {
throw new Exception($this->getInvalidValueException(), 'Default value not found in elements');
@@ -98,7 +100,8 @@ class Create extends Action
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents
$queueForEvents,
$authorization
);
$response
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -71,10 +72,11 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?array $elements, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -82,6 +84,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_ENUM,
default: $default,
@@ -13,6 +13,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
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\Swoole\Response as SwooleResponse;
@@ -74,10 +75,11 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$min ??= -PHP_FLOAT_MAX;
$max ??= PHP_FLOAT_MAX;
@@ -100,7 +102,7 @@ class Create extends Action
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE,
'formatOptions' => ['min' => $min, 'max' => $max],
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
$formatOptions = $attribute->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -71,10 +72,11 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -82,6 +84,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_FLOAT,
default: $default,
required: $required,
@@ -68,12 +68,13 @@ class Get extends Action
->param('key', '', new Key(), 'Attribute Key.')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
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\Swoole\Response as SwooleResponse;
@@ -70,10 +71,11 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->createAttribute(
$databaseId,
@@ -90,7 +92,8 @@ class Create extends Action
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents
$queueForEvents,
$authorization
);
$response
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -69,10 +70,11 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?string $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -80,6 +82,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_IP,
default: $default,
@@ -13,6 +13,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
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\Swoole\Response as SwooleResponse;
@@ -66,18 +67,19 @@ class Create extends Action
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new Integer()), 'Minimum value', true)
->param('max', null, new Nullable(new Integer()), 'Maximum value', true)
->param('default', null, new Nullable(new Integer()), 'Default value. Cannot be set when attribute is required.', true)
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$min ??= \PHP_INT_MIN;
$max ??= \PHP_INT_MAX;
@@ -102,7 +104,7 @@ class Create extends Action
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_INT_RANGE,
'formatOptions' => ['min' => $min, 'max' => $max],
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
$formatOptions = $attribute->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -64,17 +65,18 @@ class Update extends Action
->param('collectionId', '', new UID(), 'Collection ID.')
->param('key', '', new Key(), 'Attribute Key.')
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new Integer()), 'Minimum value', true)
->param('max', null, new Nullable(new Integer()), 'Maximum value', true)
->param('default', null, new Nullable(new Integer()), 'Default value. Cannot be set when attribute is required.')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when attribute is required.')
->param('newKey', null, new Nullable(new Key()), 'New Attribute Key.', true)
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -82,6 +84,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_INTEGER,
default: $default,
required: $required,
@@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -69,17 +70,18 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_LINESTRING,
'required' => $required,
'default' => $default
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -69,10 +70,11 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -80,6 +82,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_LINESTRING,
default: $default,
required: $required,
@@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -69,17 +70,18 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_POINT,
'required' => $required,
'default' => $default,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -69,10 +70,11 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -80,6 +82,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_POINT,
default: $default,
required: $required,
@@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -69,17 +70,18 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_POLYGON,
'required' => $required,
'default' => $default,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Spatial;
use Utopia\Database\Validator\UID;
@@ -69,10 +70,11 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?array $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -80,6 +82,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_POLYGON,
default: $default,
required: $required,
@@ -83,16 +83,17 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $relatedCollectionId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $relatedCollectionId, string $type, bool $twoWay, ?string $key, ?string $twoWayKey, string $onDelete, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$key ??= $relatedCollectionId;
$twoWayKeyWasProvided = $twoWayKey !== null;
$twoWayKey ??= $collectionId;
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -154,7 +155,7 @@ class Create extends Action
'twoWayKey' => $twoWayKey,
'onDelete' => $onDelete,
]
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
foreach ($attribute->getAttribute('options', []) as $k => $option) {
$attribute->setAttribute($k, $option);
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -71,6 +72,7 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
@@ -82,7 +84,8 @@ class Update extends Action
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
Event $queueForEvents,
Authorization $authorization
): void {
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -90,6 +93,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_RELATIONSHIP,
required: false,
options: [
@@ -14,6 +14,7 @@ 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\Swoole\Response as SwooleResponse;
@@ -77,6 +78,7 @@ class Create extends Action
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
@@ -93,7 +95,8 @@ class Create extends Action
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents,
array $plan
array $plan,
Authorization $authorization
): void {
if (!App::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() . '.');
@@ -132,7 +135,8 @@ class Create extends Action
$response,
$dbForProject,
$queueForDatabase,
$queueForEvents
$queueForEvents,
$authorization
);
$attribute->setAttribute('encrypt', $encrypt);
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -72,6 +73,7 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
@@ -85,7 +87,8 @@ class Update extends Action
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
Event $queueForEvents,
Authorization $authorization
): void {
$attribute = $this->updateAttribute(
databaseId: $databaseId,
@@ -93,6 +96,7 @@ class Update extends Action
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_STRING,
size: $size,
default: $default,
@@ -12,6 +12,7 @@ use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
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\Swoole\Response as SwooleResponse;
@@ -70,6 +71,7 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
@@ -83,7 +85,8 @@ class Create extends Action
UtopiaResponse $response,
Database $dbForProject,
EventDatabase $queueForDatabase,
Event $queueForEvents
Event $queueForEvents,
Authorization $authorization
): void {
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
@@ -93,7 +96,7 @@ class Create extends Action
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_URL,
]), $response, $dbForProject, $queueForDatabase, $queueForEvents);
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
@@ -11,6 +11,7 @@ use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -69,6 +70,7 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
@@ -81,7 +83,8 @@ class Update extends Action
?string $newKey,
UtopiaResponse $response,
Database $dbForProject,
Event $queueForEvents
Event $queueForEvents,
Authorization $authorization
): void {
$attribute = $this->updateAttribute(
$databaseId,
@@ -89,6 +92,7 @@ class Update extends Action
$key,
$dbForProject,
$queueForEvents,
$authorization,
type: Database::VAR_STRING,
filter: APP_DATABASE_ATTRIBUTE_URL,
default: $default,
@@ -64,12 +64,13 @@ class XList extends Action
->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('dbForProject')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, array $queries, bool $includeTotal, UtopiaResponse $response, Database $dbForProject): void
public function action(string $databaseId, string $collectionId, array $queries, bool $includeTotal, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -85,12 +85,13 @@ class Create extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, array $attributes, array $indexes, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, array $attributes, array $indexes, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -185,7 +186,13 @@ class Create extends Action
$dbForProject->getAdapter()->getSupportForVectors(),
$dbForProject->getAdapter()->getSupportForAttributes(),
$dbForProject->getAdapter()->getSupportForMultipleFulltextIndexes(),
$dbForProject->getAdapter()->getSupportForIdenticalIndexes()
$dbForProject->getAdapter()->getSupportForIdenticalIndexes(),
$dbForProject->getAdapter()->getSupportForObjectIndexes(),
$dbForProject->getAdapter()->getSupportForTrigramIndex(),
$dbForProject->getAdapter()->getSupportForSpatialAttributes(),
$dbForProject->getAdapter()->getSupportForIndex(),
$dbForProject->getAdapter()->getSupportForUniqueIndex(),
$dbForProject->getAdapter()->getSupportForFulltextIndex(),
);
foreach ($collectionIndexes as $indexDoc) {
@@ -64,12 +64,13 @@ class Delete extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -258,9 +258,9 @@ abstract class Action extends DatabasesAction
Document $collection,
Document $document,
Database $dbForProject,
/* options */
array &$collectionsCache,
Authorization $authorization,
?int &$operations = null,
): bool {
@@ -297,7 +297,7 @@ abstract class Action extends DatabasesAction
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
if (!isset($collectionsCache[$relatedCollectionId])) {
$relatedCollectionDoc = Authorization::skip(
$relatedCollectionDoc = $authorization->skip(
fn () => $dbForProject->getDocument(
'database_' . $database->getSequence(),
$relatedCollectionId
@@ -323,7 +323,8 @@ abstract class Action extends DatabasesAction
document: $relation,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
operations: $operations
operations: $operations,
authorization: $authorization
);
}
}
@@ -85,20 +85,21 @@ class Decrement extends Action
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void
public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $min, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization): void
{
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
}
@@ -106,7 +107,7 @@ class Decrement extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or nonpending transaction');
@@ -85,20 +85,21 @@ class Increment extends Action
->inject('queueForEvents')
->inject('queueForStatsUsage')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan): void
public function action(string $databaseId, string $collectionId, string $documentId, string $attribute, int|float $value, int|float|null $max, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, array $plan, Authorization $authorization): void
{
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty()) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
}
@@ -106,7 +107,7 @@ class Increment extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty() || $transaction->getAttribute('status', '') !== 'pending') {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Invalid or nonpending transaction');
@@ -24,6 +24,7 @@ use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Authorization\Input;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\UID;
use Utopia\Swoole\Response as SwooleResponse;
@@ -132,9 +133,10 @@ class Create extends Action
->inject('queueForFunctions')
->inject('queueForWebhooks')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan): void
public function action(string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, ?array $documents, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, Document $user, Event $queueForEvents, StatsUsage $queueForStatsUsage, Event $queueForRealtime, Event $queueForFunctions, Event $queueForWebhooks, array $plan, Authorization $authorization): void
{
$data = \is_string($data)
? \json_decode($data, true)
@@ -178,19 +180,19 @@ class Create extends Action
$documents = [$data];
}
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
if ($isBulk && !$isAPIKey && !$isPrivilegedUser) {
throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE);
}
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
}
@@ -204,7 +206,7 @@ class Create extends Action
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Bulk create is not supported for ' . $this->getSDKNamespace() .' with relationship ' . $this->getStructureContext());
}
$setPermissions = function (Document $document, ?array $permissions) use ($user, $isAPIKey, $isPrivilegedUser, $isBulk) {
$setPermissions = function (Document $document, ?array $permissions) use ($user, $isAPIKey, $isPrivilegedUser, $isBulk, $dbForProject, $authorization) {
$allowedPermissions = [
Database::PERMISSION_READ,
Database::PERMISSION_UPDATE,
@@ -247,8 +249,8 @@ class Create extends Action
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', Authorization::getRoles()) . ')');
if (!$authorization->hasRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $authorization->getRoles()) . ')');
}
}
}
@@ -259,21 +261,25 @@ class Create extends Action
$operations = 0;
$checkPermissions = function (Document $collection, Document $document, string $permission) use ($isAPIKey, $isPrivilegedUser, &$checkPermissions, $dbForProject, $database, &$operations) {
$checkPermissions = function (Document $collection, Document $document, string $permission) use ($isAPIKey, $isPrivilegedUser, &$checkPermissions, $dbForProject, $database, &$operations, $authorization) {
$operations++;
$documentSecurity = $collection->getAttribute('documentSecurity', false);
$validator = new Authorization($permission);
$valid = $validator->isValid($collection->getPermissionsByType($permission));
if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
$validCollection = $authorization->isValid(
new Input($permission, $collection->getPermissionsByType($permission))
);
if (($permission === Database::PERMISSION_UPDATE && !$documentSecurity) || !$validCollection) {
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
}
if ($permission === Database::PERMISSION_UPDATE) {
$valid = $valid || $validator->isValid($document->getUpdate());
$validDocument = $authorization->isValid(
new Input($permission, $document->getUpdate())
);
$valid = $validCollection || $validDocument;
if ($documentSecurity && !$valid) {
throw new Exception(Exception::USER_UNAUTHORIZED);
throw new Exception(Exception::USER_UNAUTHORIZED, $authorization->getDescription());
}
}
@@ -298,7 +304,7 @@ class Create extends Action
}
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
$relatedCollection = Authorization::skip(
$relatedCollection = $authorization->skip(
fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId)
);
@@ -314,7 +320,7 @@ class Create extends Action
if ($relation instanceof Document) {
$relation = $this->removeReadonlyAttributes($relation, $isAPIKey || $isPrivilegedUser);
$current = Authorization::skip(
$current = $authorization->skip(
fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(), $relation->getId())
);
@@ -369,7 +375,7 @@ class Create extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND, params: [$transactionId]);
@@ -468,6 +474,7 @@ class Create extends Action
document: $document,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
authorization: $authorization
);
}
@@ -83,6 +83,7 @@ class Delete extends Action
->inject('queueForStatsUsage')
->inject('transactionState')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
@@ -97,18 +98,19 @@ class Delete extends Action
Event $queueForEvents,
StatsUsage $queueForStatsUsage,
TransactionState $transactionState,
array $plan
array $plan,
Authorization $authorization
): void {
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
@@ -121,7 +123,7 @@ class Delete extends Action
// Use transaction-aware document retrieval to see changes from same transaction
$document = $transactionState->getDocument($collectionTableId, $documentId, $transactionId);
} else {
$document = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
$document = $authorization->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
}
if ($document->isEmpty()) {
@@ -131,7 +133,7 @@ class Delete extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND, params: [$transactionId]);
@@ -205,6 +207,7 @@ class Delete extends Action
document: $document,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
authorization: $authorization
);
$queueForStatsUsage
@@ -70,20 +70,21 @@ class Get extends Action
->inject('dbForProject')
->inject('queueForStatsUsage')
->inject('transactionState')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState): void
public function action(string $databaseId, string $collectionId, string $documentId, array $queries, ?string $transactionId, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState, Authorization $authorization): void
{
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
@@ -125,6 +126,7 @@ class Get extends Action
document: $document,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
authorization: $authorization,
operations: $operations
);
@@ -72,13 +72,14 @@ class XList extends Action
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('authorization')
->inject('audit')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit): void
public function action(string $databaseId, string $collectionId, string $documentId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization, Audit $audit): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -87,10 +87,11 @@ class Update extends Action
->inject('queueForStatsUsage')
->inject('transactionState')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, array $plan): void
public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, array $plan, Authorization $authorization): void
{
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@@ -98,16 +99,16 @@ class Update extends Action
throw new Exception($this->getMissingPayloadException());
}
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
@@ -125,7 +126,7 @@ class Update extends Action
// Use transaction-aware document retrieval to see changes from same transaction
$document = $transactionState->getDocument($collectionTableId, $documentId, $transactionId);
} else {
$document = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
$document = $authorization->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
}
if ($document->isEmpty()) {
@@ -140,7 +141,7 @@ class Update extends Action
]);
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = Authorization::getRoles();
$roles = $authorization->getRoles();
if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
@@ -153,7 +154,7 @@ class Update extends Action
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
if (!$authorization->hasRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
}
}
@@ -171,7 +172,7 @@ class Update extends Action
$operations = 0;
$setCollection = (function (Document $collection, Document $document) use ($isAPIKey, $isPrivilegedUser, &$setCollection, $dbForProject, $database, &$operations) {
$setCollection = (function (Document $collection, Document $document) use ($isAPIKey, $isPrivilegedUser, &$setCollection, $dbForProject, $database, &$operations, $authorization) {
$operations++;
$relationships = \array_filter(
@@ -195,7 +196,7 @@ class Update extends Action
}
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
$relatedCollection = Authorization::skip(
$relatedCollection = $authorization->skip(
fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId)
);
@@ -212,7 +213,7 @@ class Update extends Action
if ($relation instanceof Document) {
$relation = $this->removeReadonlyAttributes($relation, $isAPIKey || $isPrivilegedUser);
$oldDocument = Authorization::skip(fn () => $dbForProject->getDocument(
$oldDocument = $authorization->skip(fn () => $dbForProject->getDocument(
'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(),
$relation->getId()
));
@@ -249,7 +250,7 @@ class Update extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND, params: [$transactionId]);
@@ -340,6 +341,7 @@ class Update extends Action
document: $document,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
authorization: $authorization,
);
$response->dynamic($document, $this->getResponseModel());
@@ -91,10 +91,11 @@ class Upsert extends Action
->inject('queueForStatsUsage')
->inject('transactionState')
->inject('plan')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, array $plan): void
public function action(string $databaseId, string $collectionId, string $documentId, string|array $data, ?array $permissions, ?string $transactionId, ?\DateTime $requestTimestamp, UtopiaResponse $response, Document $user, Database $dbForProject, Event $queueForEvents, StatsUsage $queueForStatsUsage, TransactionState $transactionState, array $plan, Authorization $authorization): void
{
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
@@ -106,15 +107,15 @@ class Upsert extends Action
throw new Exception($this->getMissingPayloadException());
}
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
}
@@ -139,7 +140,7 @@ class Upsert extends Action
// Use transaction-aware document retrieval to see changes from same transaction
$oldDocument = $transactionState->getDocument($collectionTableId, $documentId, $transactionId);
} else {
$oldDocument = Authorization::skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
$oldDocument = $authorization->skip(fn () => $dbForProject->getDocument($collectionTableId, $documentId));
}
if ($oldDocument->isEmpty()) {
if (!empty($user->getId())) {
@@ -155,7 +156,7 @@ class Upsert extends Action
}
// Users can only manage their own roles, API keys and Admin users can manage any
$roles = Authorization::getRoles();
$roles = $authorization->getRoles();
if (!$isAPIKey && !$isPrivilegedUser && !\is_null($permissions)) {
foreach (Database::PERMISSIONS as $type) {
foreach ($permissions as $permission) {
@@ -168,7 +169,7 @@ class Upsert extends Action
$permission->getIdentifier(),
$permission->getDimension()
))->toString();
if (!Authorization::isRole($role)) {
if (!$authorization->hasRole($role)) {
throw new Exception(Exception::USER_UNAUTHORIZED, 'Permissions must be one of: (' . \implode(', ', $roles) . ')');
}
}
@@ -181,7 +182,7 @@ class Upsert extends Action
$newDocument = new Document($data);
$operations = 0;
$setCollection = (function (Document $collection, Document $document) use ($isAPIKey, $isPrivilegedUser, &$setCollection, $dbForProject, $database, &$operations) {
$setCollection = (function (Document $collection, Document $document) use ($isAPIKey, $isPrivilegedUser, &$setCollection, $dbForProject, $database, &$operations, $authorization) {
$operations++;
$relationships = \array_filter(
@@ -205,7 +206,7 @@ class Upsert extends Action
}
$relatedCollectionId = $relationship->getAttribute('relatedCollection');
$relatedCollection = Authorization::skip(
$relatedCollection = $authorization->skip(
fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $relatedCollectionId)
);
@@ -222,7 +223,7 @@ class Upsert extends Action
if ($relation instanceof Document) {
$relation = $this->removeReadonlyAttributes($relation, $isAPIKey || $isPrivilegedUser);
$oldDocument = Authorization::skip(fn () => $dbForProject->getDocument(
$oldDocument = $authorization->skip(fn () => $dbForProject->getDocument(
'database_' . $database->getSequence() . '_collection_' . $relatedCollection->getSequence(),
$relation->getId()
));
@@ -259,7 +260,7 @@ class Upsert extends Action
// Handle transaction staging
if ($transactionId !== null) {
$transaction = ($isAPIKey || $isPrivilegedUser)
? Authorization::skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
? $authorization->skip(fn () => $dbForProject->getDocument('transactions', $transactionId))
: $dbForProject->getDocument('transactions', $transactionId);
if ($transaction->isEmpty()) {
throw new Exception(Exception::TRANSACTION_NOT_FOUND, params: [$transactionId]);
@@ -361,6 +362,7 @@ class Upsert extends Action
document: $document,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
authorization: $authorization
);
$relationships = \array_map(
@@ -74,20 +74,21 @@ class XList extends Action
->inject('dbForProject')
->inject('queueForStatsUsage')
->inject('transactionState')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, bool $includeTotal, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState): void
public function action(string $databaseId, string $collectionId, array $queries, ?string $transactionId, bool $includeTotal, UtopiaResponse $response, Database $dbForProject, StatsUsage $queueForStatsUsage, TransactionState $transactionState, Authorization $authorization): void
{
$isAPIKey = User::isApp(Authorization::getRoles());
$isPrivilegedUser = User::isPrivileged(Authorization::getRoles());
$isAPIKey = User::isApp($authorization->getRoles());
$isPrivilegedUser = User::isPrivileged($authorization->getRoles());
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty() || (!$database->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
$collection = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
$collection = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId));
if ($collection->isEmpty() || (!$collection->getAttribute('enabled', false) && !$isAPIKey && !$isPrivilegedUser)) {
throw new Exception($this->getParentNotFoundException(), params: [$collectionId]);
}
@@ -115,7 +116,7 @@ class XList extends Action
$documentId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId));
$cursorDocument = $authorization->skip(fn () => $dbForProject->getDocument('database_' . $database->getSequence() . '_collection_' . $collection->getSequence(), $documentId));
if ($cursorDocument->isEmpty()) {
$type = ucfirst($this->getContext());
@@ -161,7 +162,8 @@ class XList extends Action
document: $document,
dbForProject: $dbForProject,
collectionsCache: $collectionsCache,
operations: $operations,
authorization: $authorization,
operations: $operations
);
}
@@ -57,12 +57,13 @@ class Get extends Action
->param('collectionId', '', new UID(), 'Collection ID.')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject): void
public function action(string $databaseId, string $collectionId, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -79,12 +79,13 @@ class Create extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, array $lengths, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, string $type, array $attributes, array $orders, array $lengths, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -198,7 +199,13 @@ class Create extends Action
$dbForProject->getAdapter()->getSupportForVectors(),
$dbForProject->getAdapter()->getSupportForAttributes(),
$dbForProject->getAdapter()->getSupportForMultipleFulltextIndexes(),
$dbForProject->getAdapter()->getSupportForIdenticalIndexes()
$dbForProject->getAdapter()->getSupportForIdenticalIndexes(),
$dbForProject->getAdapter()->getSupportForObjectIndexes(),
$dbForProject->getAdapter()->getSupportForTrigramIndex(),
$dbForProject->getAdapter()->getSupportForSpatialAttributes(),
$dbForProject->getAdapter()->getSupportForIndex(),
$dbForProject->getAdapter()->getSupportForUniqueIndex(),
$dbForProject->getAdapter()->getSupportForFulltextIndex(),
);
if (!$validator->isValid($index)) {
@@ -70,12 +70,13 @@ class Delete extends Action
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$db = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($db->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -59,12 +59,13 @@ class Get extends Action
->param('key', null, new Key(), 'Index Key.')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject): void
public function action(string $databaseId, string $collectionId, string $key, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -66,13 +66,14 @@ class XList extends Action
->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('dbForProject')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, array $queries, bool $includeTotal, UtopiaResponse $response, Database $dbForProject): void
public function action(string $databaseId, string $collectionId, array $queries, bool $includeTotal, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void
{
/** @var Document $database */
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -112,7 +113,7 @@ class XList extends Action
}
$indexId = $cursor->getValue();
$cursorDocument = Authorization::skip(fn () => $dbForProject->find('indexes', [
$cursorDocument = $authorization->skip(fn () => $dbForProject->find('indexes', [
Query::equal('collectionInternalId', [$collection->getSequence()]),
Query::equal('databaseInternalId', [$database->getSequence()]),
Query::equal('key', [$indexId]),
@@ -71,13 +71,14 @@ class XList extends Action
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->inject('authorization')
->inject('audit')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audit): void
public function action(string $databaseId, string $collectionId, array $queries, UtopiaResponse $response, Database $dbForProject, Locale $locale, Reader $geodb, Authorization $authorization, Audit $audit): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
@@ -112,9 +113,9 @@ class XList extends Action
$detector = new Detector($log['userAgent']);
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$os = $detector->getOS();
$client = $detector->getClient();
$device = $detector->getDevice();
$os = $detector->getOS() ?: [];
$client = $detector->getClient() ?: [];
$device = $detector->getDevice() ?: [];
$output[$i] = new Document([
'event' => $log['event'],
@@ -122,20 +123,20 @@ class XList extends Action
'userEmail' => $log['data']['userEmail'] ?? null,
'userName' => $log['data']['userName'] ?? null,
'mode' => $log['data']['mode'] ?? null,
'ip' => $log['ip'],
'time' => $log['time'],
'osCode' => $os['osCode'],
'osName' => $os['osName'],
'osVersion' => $os['osVersion'],
'clientType' => $client['clientType'],
'clientCode' => $client['clientCode'],
'clientName' => $client['clientName'],
'clientVersion' => $client['clientVersion'],
'clientEngine' => $client['clientEngine'],
'clientEngineVersion' => $client['clientEngineVersion'],
'deviceName' => $device['deviceName'],
'deviceBrand' => $device['deviceBrand'],
'deviceModel' => $device['deviceModel']
'ip' => $log['ip'] ?? null,
'time' => $log['time'] ?? null,
'osCode' => $os['osCode'] ?? null,
'osName' => $os['osName'] ?? null,
'osVersion' => $os['osVersion'] ?? null,
'clientType' => $client['clientType'] ?? null,
'clientCode' => $client['clientCode'] ?? null,
'clientName' => $client['clientName'] ?? null,
'clientVersion' => $client['clientVersion'] ?? null,
'clientEngine' => $client['clientEngine'] ?? null,
'clientEngineVersion' => $client['clientEngineVersion'] ?? null,
'deviceName' => $device['deviceName'] ?? null,
'deviceBrand' => $device['deviceBrand'] ?? null,
'deviceModel' => $device['deviceModel'] ?? null
]);
$record = $geodb->get($log['ip']);
@@ -71,12 +71,13 @@ class Update extends Action
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents): void
public function action(string $databaseId, string $collectionId, string $name, ?array $permissions, bool $documentSecurity, bool $enabled, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId));
$database = $authorization->skip(fn () => $dbForProject->getDocument('databases', $databaseId));
if ($database->isEmpty()) {
throw new Exception(Exception::DATABASE_NOT_FOUND, params: [$databaseId]);
}
@@ -63,10 +63,11 @@ class Get extends Action
->param('collectionId', '', new UID(), 'Collection ID.')
->inject('response')
->inject('dbForProject')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $range, string $collectionId, UtopiaResponse $response, Database $dbForProject): void
public function action(string $databaseId, string $range, string $collectionId, UtopiaResponse $response, Database $dbForProject, Authorization $authorization): void
{
$database = $dbForProject->getDocument('databases', $databaseId);
$collectionDocument = $dbForProject->getDocument('database_' . $database->getSequence(), $collectionId);
@@ -83,7 +84,7 @@ class Get extends Action
str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getSequence(), $collectionDocument->getSequence()], METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS),
];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) {
$authorization->skip(function () use ($dbForProject, $days, $metrics, &$stats) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),

Some files were not shown because too many files have changed in this diff Show More