diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index d3a7d060b0..b92361e51a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -319,10 +319,13 @@ These are the current metrics we collect usage stats for: | users | Total number of users per project| | executions | Total number of executions per project | | databases | Total number of databases per project | +| databases.storage | Total amount of storage used by all databases per project (in bytes) | | collections | Total number of collections per project | | {databaseInternalId}.collections | Total number of collections per database| +| {databaseInternalId}.storage | Sum of database storage (in bytes) | | documents | Total number of documents per project | | {databaseInternalId}.{collectionInternalId}.documents | Total number of documents per collection | +| {databaseInternalId}.{collectionInternalId}.storage | Sum of database storage used by the collection (in bytes) | | buckets | Total number of buckets per project | | files | Total number of files per project | | {bucketInternalId}.files.storage | Sum of files.storage per bucket (in bytes) | diff --git a/Dockerfile b/Dockerfile index 22b37982d0..13b018df0f 100755 --- a/Dockerfile +++ b/Dockerfile @@ -28,6 +28,8 @@ RUN \ apk add boost boost-dev; \ fi +RUN apk add libwebp + WORKDIR /usr/src/code COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor diff --git a/app/cli.php b/app/cli.php index ecff5180a2..23502ec402 100644 --- a/app/cli.php +++ b/app/cli.php @@ -197,8 +197,12 @@ CLI::setResource('logError', function (Registry $register) { $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - $responseCode = $logger->addLog($log); - Console::info('Usage stats log pushed with status code: ' . $responseCode); + try { + $responseCode = $logger->addLog($log); + Console::info('Error log pushed with status code: ' . $responseCode); + } catch (Throwable $th) { + Console::error('Error pushing log: ' . $th->getMessage()); + } } Console::warning("Failed: {$error->getMessage()}"); diff --git a/app/config/roles.php b/app/config/roles.php index 65b9643b89..1b64067ecc 100644 --- a/app/config/roles.php +++ b/app/config/roles.php @@ -17,7 +17,6 @@ $member = [ 'files.read', 'files.write', 'projects.read', - 'projects.write', 'locale.read', 'avatars.read', 'execution.read', @@ -49,6 +48,7 @@ $admins = [ 'collections.write', 'platforms.read', 'platforms.write', + 'projects.write', 'keys.read', 'keys.write', 'webhooks.read', diff --git a/app/config/specs/open-api3-1.6.x-client.json b/app/config/specs/open-api3-1.6.x-client.json index 8a9967090d..fc967c13a6 100644 --- a/app/config/specs/open-api3-1.6.x-client.json +++ b/app/config/specs/open-api3-1.6.x-client.json @@ -239,7 +239,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "tags": [ "account" @@ -556,7 +556,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "tags": [ "account" @@ -624,7 +624,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "tags": [ "account" @@ -711,7 +711,7 @@ } }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "tags": [ "account" @@ -774,7 +774,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "tags": [ "account" @@ -850,7 +850,7 @@ } }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "tags": [ "account" @@ -928,7 +928,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "tags": [ "account" @@ -981,7 +981,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "tags": [ "account" @@ -1032,7 +1032,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "tags": [ "account" @@ -1083,7 +1083,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "tags": [ "account" @@ -5368,7 +5368,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "tags": [ "locale" @@ -6268,7 +6268,7 @@ } }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "tags": [ "storage" diff --git a/app/config/specs/open-api3-1.6.x-console.json b/app/config/specs/open-api3-1.6.x-console.json index 07749889d8..8527e27630 100644 --- a/app/config/specs/open-api3-1.6.x-console.json +++ b/app/config/specs/open-api3-1.6.x-console.json @@ -278,7 +278,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "tags": [ "account" @@ -591,7 +591,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "tags": [ "account" @@ -658,7 +658,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "tags": [ "account" @@ -744,7 +744,7 @@ } }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "tags": [ "account" @@ -806,7 +806,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "tags": [ "account" @@ -882,7 +882,7 @@ } }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "tags": [ "account" @@ -959,7 +959,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "tags": [ "account" @@ -1011,7 +1011,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "tags": [ "account" @@ -1061,7 +1061,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "tags": [ "account" @@ -1111,7 +1111,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "tags": [ "account" @@ -4452,7 +4452,7 @@ }, "\/console\/assistant": { "post": { - "summary": "Ask Query", + "summary": "Ask query", "operationId": "assistantChat", "tags": [ "assistant" @@ -13257,7 +13257,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "tags": [ "locale" @@ -18060,7 +18060,7 @@ }, "\/migrations": { "get": { - "summary": "List Migrations", + "summary": "List migrations", "operationId": "migrationsList", "tags": [ "migrations" @@ -18136,7 +18136,7 @@ }, "\/migrations\/appwrite": { "post": { - "summary": "Migrate Appwrite Data", + "summary": "Migrate Appwrite data", "operationId": "migrationsCreateAppwriteMigration", "tags": [ "migrations" @@ -18226,7 +18226,7 @@ }, "\/migrations\/appwrite\/report": { "get": { - "summary": "Generate a report on Appwrite Data", + "summary": "Generate a report on Appwrite data", "operationId": "migrationsGetAppwriteReport", "tags": [ "migrations" @@ -18321,7 +18321,7 @@ }, "\/migrations\/firebase": { "post": { - "summary": "Migrate Firebase Data (Service Account)", + "summary": "Migrate Firebase data (Service Account)", "operationId": "migrationsCreateFirebaseMigration", "tags": [ "migrations" @@ -18399,7 +18399,7 @@ }, "\/migrations\/firebase\/deauthorize": { "get": { - "summary": "Revoke Appwrite's authorization to access Firebase Projects", + "summary": "Revoke Appwrite's authorization to access Firebase projects", "operationId": "migrationsDeleteFirebaseAuth", "tags": [ "migrations" @@ -18442,7 +18442,7 @@ }, "\/migrations\/firebase\/oauth": { "post": { - "summary": "Migrate Firebase Data (OAuth)", + "summary": "Migrate Firebase data (OAuth)", "operationId": "migrationsCreateFirebaseOAuthMigration", "tags": [ "migrations" @@ -18520,7 +18520,7 @@ }, "\/migrations\/firebase\/projects": { "get": { - "summary": "List Firebase Projects", + "summary": "List Firebase projects", "operationId": "migrationsListFirebaseProjects", "tags": [ "migrations" @@ -18570,7 +18570,7 @@ }, "\/migrations\/firebase\/report": { "get": { - "summary": "Generate a report on Firebase Data", + "summary": "Generate a report on Firebase data", "operationId": "migrationsGetFirebaseReport", "tags": [ "migrations" @@ -18644,7 +18644,7 @@ }, "\/migrations\/firebase\/report\/oauth": { "get": { - "summary": "Generate a report on Firebase Data using OAuth", + "summary": "Generate a report on Firebase data using OAuth", "operationId": "migrationsGetFirebaseReportOAuth", "tags": [ "migrations" @@ -18718,7 +18718,7 @@ }, "\/migrations\/nhost": { "post": { - "summary": "Migrate NHost Data", + "summary": "Migrate NHost data", "operationId": "migrationsCreateNHostMigration", "tags": [ "migrations" @@ -18966,7 +18966,7 @@ }, "\/migrations\/supabase": { "post": { - "summary": "Migrate Supabase Data", + "summary": "Migrate Supabase data", "operationId": "migrationsCreateSupabaseMigration", "tags": [ "migrations" @@ -19199,7 +19199,7 @@ }, "\/migrations\/{migrationId}": { "get": { - "summary": "Get Migration", + "summary": "Get migration", "operationId": "migrationsGet", "tags": [ "migrations" @@ -19259,7 +19259,7 @@ ] }, "patch": { - "summary": "Retry Migration", + "summary": "Retry migration", "operationId": "migrationsRetry", "tags": [ "migrations" @@ -19319,7 +19319,7 @@ ] }, "delete": { - "summary": "Delete Migration", + "summary": "Delete migration", "operationId": "migrationsDelete", "tags": [ "migrations" @@ -19464,7 +19464,7 @@ }, "\/project\/variables": { "get": { - "summary": "List Variables", + "summary": "List variables", "operationId": "projectListVariables", "tags": [ "project" @@ -19512,7 +19512,7 @@ ] }, "post": { - "summary": "Create Variable", + "summary": "Create variable", "operationId": "projectCreateVariable", "tags": [ "project" @@ -19587,7 +19587,7 @@ }, "\/project\/variables\/{variableId}": { "get": { - "summary": "Get Variable", + "summary": "Get variable", "operationId": "projectGetVariable", "tags": [ "project" @@ -19647,7 +19647,7 @@ ] }, "put": { - "summary": "Update Variable", + "summary": "Update variable", "operationId": "projectUpdateVariable", "tags": [ "project" @@ -19731,7 +19731,7 @@ } }, "delete": { - "summary": "Delete Variable", + "summary": "Delete variable", "operationId": "projectDeleteVariable", "tags": [ "project" @@ -21282,7 +21282,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "keys.read", "platforms": [ "console" ], @@ -21342,7 +21342,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -21437,7 +21437,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "keys.read", "platforms": [ "console" ], @@ -21507,7 +21507,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -21603,7 +21603,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -21814,7 +21814,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "platforms.read", "platforms": [ "console" ], @@ -21874,7 +21874,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -21995,7 +21995,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "platforms.read", "platforms": [ "console" ], @@ -22065,7 +22065,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -22162,7 +22162,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -24640,7 +24640,7 @@ }, "\/proxy\/rules": { "get": { - "summary": "List Rules", + "summary": "List rules", "operationId": "proxyListRules", "tags": [ "proxy" @@ -24714,7 +24714,7 @@ ] }, "post": { - "summary": "Create Rule", + "summary": "Create rule", "operationId": "proxyCreateRule", "tags": [ "proxy" @@ -24800,7 +24800,7 @@ }, "\/proxy\/rules\/{ruleId}": { "get": { - "summary": "Get Rule", + "summary": "Get rule", "operationId": "proxyGetRule", "tags": [ "proxy" @@ -24860,7 +24860,7 @@ ] }, "delete": { - "summary": "Delete Rule", + "summary": "Delete rule", "operationId": "proxyDeleteRule", "tags": [ "proxy" @@ -24915,7 +24915,7 @@ }, "\/proxy\/rules\/{ruleId}\/verification": { "patch": { - "summary": "Update Rule Verification Status", + "summary": "Update rule verification status", "operationId": "proxyUpdateRuleVerification", "tags": [ "proxy" @@ -25791,7 +25791,7 @@ } }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "tags": [ "storage" @@ -27842,7 +27842,7 @@ }, "\/users\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "usersListIdentities", "tags": [ "users" @@ -29141,7 +29141,7 @@ }, "\/users\/{userId}\/mfa\/authenticators\/{type}": { "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "usersDeleteMfaAuthenticator", "tags": [ "users" @@ -29219,7 +29219,7 @@ }, "\/users\/{userId}\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "usersListMfaFactors", "tags": [ "users" @@ -29282,7 +29282,7 @@ }, "\/users\/{userId}\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "usersGetMfaRecoveryCodes", "tags": [ "users" @@ -29343,7 +29343,7 @@ ] }, "put": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "usersUpdateMfaRecoveryCodes", "tags": [ "users" @@ -29404,7 +29404,7 @@ ] }, "patch": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "usersCreateMfaRecoveryCodes", "tags": [ "users" @@ -30182,7 +30182,7 @@ }, "\/users\/{userId}\/targets": { "get": { - "summary": "List User Targets", + "summary": "List user targets", "operationId": "usersListTargets", "tags": [ "users" @@ -30257,7 +30257,7 @@ ] }, "post": { - "summary": "Create User Target", + "summary": "Create user target", "operationId": "usersCreateTarget", "tags": [ "users" @@ -30369,7 +30369,7 @@ }, "\/users\/{userId}\/targets\/{targetId}": { "get": { - "summary": "Get User Target", + "summary": "Get user target", "operationId": "usersGetTarget", "tags": [ "users" @@ -30441,7 +30441,7 @@ ] }, "patch": { - "summary": "Update User target", + "summary": "Update user target", "operationId": "usersUpdateTarget", "tags": [ "users" @@ -30854,7 +30854,7 @@ }, "\/vcs\/github\/installations\/{installationId}\/providerRepositories": { "get": { - "summary": "List Repositories", + "summary": "List repositories", "operationId": "vcsListRepositories", "tags": [ "vcs" @@ -31084,7 +31084,7 @@ }, "\/vcs\/github\/installations\/{installationId}\/providerRepositories\/{providerRepositoryId}\/branches": { "get": { - "summary": "List Repository Branches", + "summary": "List repository branches", "operationId": "vcsListRepositoryBranches", "tags": [ "vcs" @@ -31547,7 +31547,7 @@ ] }, "delete": { - "summary": "Delete Installation", + "summary": "Delete installation", "operationId": "vcsDeleteInstallation", "tags": [ "vcs" @@ -36747,6 +36747,12 @@ "x-example": 0, "format": "int32" }, + "storageTotal": { + "type": "integer", + "description": "Total aggregated number of total databases storage in bytes.", + "x-example": 0, + "format": "int32" + }, "databases": { "type": "array", "description": "Aggregated number of databases per period.", @@ -36770,6 +36776,14 @@ "$ref": "#\/components\/schemas\/metric" }, "x-example": [] + }, + "storage": { + "type": "array", + "description": "An array of the aggregated number of databases storage in bytes per period.", + "items": { + "$ref": "#\/components\/schemas\/metric" + }, + "x-example": [] } }, "required": [ @@ -36777,9 +36791,11 @@ "databasesTotal", "collectionsTotal", "documentsTotal", + "storageTotal", "databases", "collections", - "documents" + "documents", + "storage" ] }, "usageDatabase": { @@ -36803,6 +36819,12 @@ "x-example": 0, "format": "int32" }, + "storageTotal": { + "type": "integer", + "description": "Total aggregated number of total storage used in bytes.", + "x-example": 0, + "format": "int32" + }, "collections": { "type": "array", "description": "Aggregated number of collections per period.", @@ -36818,14 +36840,24 @@ "$ref": "#\/components\/schemas\/metric" }, "x-example": [] + }, + "storage": { + "type": "array", + "description": "Aggregated storage used in bytes per period.", + "items": { + "$ref": "#\/components\/schemas\/metric" + }, + "x-example": [] } }, "required": [ "range", "collectionsTotal", "documentsTotal", + "storageTotal", "collections", - "documents" + "documents", + "storage" ] }, "usageCollection": { @@ -37366,6 +37398,12 @@ "x-example": 0, "format": "int32" }, + "databasesStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of databases storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, "usersTotal": { "type": "integer", "description": "Total aggregated number of users.", @@ -37462,6 +37500,14 @@ }, "x-example": [] }, + "databasesStorageBreakdown": { + "type": "array", + "description": "An array of the aggregated breakdown of storage usage by databases.", + "items": { + "$ref": "#\/components\/schemas\/metricBreakdown" + }, + "x-example": [] + }, "executionsMbSecondsBreakdown": { "type": "array", "description": "Aggregated breakdown in totals of execution mbSeconds by functions.", @@ -37491,6 +37537,7 @@ "executionsTotal", "documentsTotal", "databasesTotal", + "databasesStorageTotal", "usersTotal", "filesStorageTotal", "functionsStorageTotal", @@ -37505,6 +37552,7 @@ "executions", "executionsBreakdown", "bucketsBreakdown", + "databasesStorageBreakdown", "executionsMbSecondsBreakdown", "buildsMbSecondsBreakdown", "functionsStorageBreakdown" diff --git a/app/config/specs/open-api3-1.6.x-server.json b/app/config/specs/open-api3-1.6.x-server.json index ef41688ca8..7d199f1919 100644 --- a/app/config/specs/open-api3-1.6.x-server.json +++ b/app/config/specs/open-api3-1.6.x-server.json @@ -241,7 +241,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "tags": [ "account" @@ -562,7 +562,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "tags": [ "account" @@ -631,7 +631,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "tags": [ "account" @@ -719,7 +719,7 @@ } }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "tags": [ "account" @@ -783,7 +783,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "tags": [ "account" @@ -859,7 +859,7 @@ } }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "tags": [ "account" @@ -938,7 +938,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "tags": [ "account" @@ -992,7 +992,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "tags": [ "account" @@ -1044,7 +1044,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "tags": [ "account" @@ -1096,7 +1096,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "tags": [ "account" @@ -12097,7 +12097,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "tags": [ "locale" @@ -17789,7 +17789,7 @@ } }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "tags": [ "storage" @@ -19645,7 +19645,7 @@ }, "\/users\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "usersListIdentities", "tags": [ "users" @@ -20885,7 +20885,7 @@ }, "\/users\/{userId}\/mfa\/authenticators\/{type}": { "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "usersDeleteMfaAuthenticator", "tags": [ "users" @@ -20964,7 +20964,7 @@ }, "\/users\/{userId}\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "usersListMfaFactors", "tags": [ "users" @@ -21028,7 +21028,7 @@ }, "\/users\/{userId}\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "usersGetMfaRecoveryCodes", "tags": [ "users" @@ -21090,7 +21090,7 @@ ] }, "put": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "usersUpdateMfaRecoveryCodes", "tags": [ "users" @@ -21152,7 +21152,7 @@ ] }, "patch": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "usersCreateMfaRecoveryCodes", "tags": [ "users" @@ -21941,7 +21941,7 @@ }, "\/users\/{userId}\/targets": { "get": { - "summary": "List User Targets", + "summary": "List user targets", "operationId": "usersListTargets", "tags": [ "users" @@ -22017,7 +22017,7 @@ ] }, "post": { - "summary": "Create User Target", + "summary": "Create user target", "operationId": "usersCreateTarget", "tags": [ "users" @@ -22130,7 +22130,7 @@ }, "\/users\/{userId}\/targets\/{targetId}": { "get": { - "summary": "Get User Target", + "summary": "Get user target", "operationId": "usersGetTarget", "tags": [ "users" @@ -22203,7 +22203,7 @@ ] }, "patch": { - "summary": "Update User target", + "summary": "Update user target", "operationId": "usersUpdateTarget", "tags": [ "users" diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index 8a9967090d..fc967c13a6 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -239,7 +239,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "tags": [ "account" @@ -556,7 +556,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "tags": [ "account" @@ -624,7 +624,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "tags": [ "account" @@ -711,7 +711,7 @@ } }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "tags": [ "account" @@ -774,7 +774,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "tags": [ "account" @@ -850,7 +850,7 @@ } }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "tags": [ "account" @@ -928,7 +928,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "tags": [ "account" @@ -981,7 +981,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "tags": [ "account" @@ -1032,7 +1032,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "tags": [ "account" @@ -1083,7 +1083,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "tags": [ "account" @@ -5368,7 +5368,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "tags": [ "locale" @@ -6268,7 +6268,7 @@ } }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "tags": [ "storage" diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 07749889d8..8527e27630 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -278,7 +278,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "tags": [ "account" @@ -591,7 +591,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "tags": [ "account" @@ -658,7 +658,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "tags": [ "account" @@ -744,7 +744,7 @@ } }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "tags": [ "account" @@ -806,7 +806,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "tags": [ "account" @@ -882,7 +882,7 @@ } }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "tags": [ "account" @@ -959,7 +959,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "tags": [ "account" @@ -1011,7 +1011,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "tags": [ "account" @@ -1061,7 +1061,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "tags": [ "account" @@ -1111,7 +1111,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "tags": [ "account" @@ -4452,7 +4452,7 @@ }, "\/console\/assistant": { "post": { - "summary": "Ask Query", + "summary": "Ask query", "operationId": "assistantChat", "tags": [ "assistant" @@ -13257,7 +13257,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "tags": [ "locale" @@ -18060,7 +18060,7 @@ }, "\/migrations": { "get": { - "summary": "List Migrations", + "summary": "List migrations", "operationId": "migrationsList", "tags": [ "migrations" @@ -18136,7 +18136,7 @@ }, "\/migrations\/appwrite": { "post": { - "summary": "Migrate Appwrite Data", + "summary": "Migrate Appwrite data", "operationId": "migrationsCreateAppwriteMigration", "tags": [ "migrations" @@ -18226,7 +18226,7 @@ }, "\/migrations\/appwrite\/report": { "get": { - "summary": "Generate a report on Appwrite Data", + "summary": "Generate a report on Appwrite data", "operationId": "migrationsGetAppwriteReport", "tags": [ "migrations" @@ -18321,7 +18321,7 @@ }, "\/migrations\/firebase": { "post": { - "summary": "Migrate Firebase Data (Service Account)", + "summary": "Migrate Firebase data (Service Account)", "operationId": "migrationsCreateFirebaseMigration", "tags": [ "migrations" @@ -18399,7 +18399,7 @@ }, "\/migrations\/firebase\/deauthorize": { "get": { - "summary": "Revoke Appwrite's authorization to access Firebase Projects", + "summary": "Revoke Appwrite's authorization to access Firebase projects", "operationId": "migrationsDeleteFirebaseAuth", "tags": [ "migrations" @@ -18442,7 +18442,7 @@ }, "\/migrations\/firebase\/oauth": { "post": { - "summary": "Migrate Firebase Data (OAuth)", + "summary": "Migrate Firebase data (OAuth)", "operationId": "migrationsCreateFirebaseOAuthMigration", "tags": [ "migrations" @@ -18520,7 +18520,7 @@ }, "\/migrations\/firebase\/projects": { "get": { - "summary": "List Firebase Projects", + "summary": "List Firebase projects", "operationId": "migrationsListFirebaseProjects", "tags": [ "migrations" @@ -18570,7 +18570,7 @@ }, "\/migrations\/firebase\/report": { "get": { - "summary": "Generate a report on Firebase Data", + "summary": "Generate a report on Firebase data", "operationId": "migrationsGetFirebaseReport", "tags": [ "migrations" @@ -18644,7 +18644,7 @@ }, "\/migrations\/firebase\/report\/oauth": { "get": { - "summary": "Generate a report on Firebase Data using OAuth", + "summary": "Generate a report on Firebase data using OAuth", "operationId": "migrationsGetFirebaseReportOAuth", "tags": [ "migrations" @@ -18718,7 +18718,7 @@ }, "\/migrations\/nhost": { "post": { - "summary": "Migrate NHost Data", + "summary": "Migrate NHost data", "operationId": "migrationsCreateNHostMigration", "tags": [ "migrations" @@ -18966,7 +18966,7 @@ }, "\/migrations\/supabase": { "post": { - "summary": "Migrate Supabase Data", + "summary": "Migrate Supabase data", "operationId": "migrationsCreateSupabaseMigration", "tags": [ "migrations" @@ -19199,7 +19199,7 @@ }, "\/migrations\/{migrationId}": { "get": { - "summary": "Get Migration", + "summary": "Get migration", "operationId": "migrationsGet", "tags": [ "migrations" @@ -19259,7 +19259,7 @@ ] }, "patch": { - "summary": "Retry Migration", + "summary": "Retry migration", "operationId": "migrationsRetry", "tags": [ "migrations" @@ -19319,7 +19319,7 @@ ] }, "delete": { - "summary": "Delete Migration", + "summary": "Delete migration", "operationId": "migrationsDelete", "tags": [ "migrations" @@ -19464,7 +19464,7 @@ }, "\/project\/variables": { "get": { - "summary": "List Variables", + "summary": "List variables", "operationId": "projectListVariables", "tags": [ "project" @@ -19512,7 +19512,7 @@ ] }, "post": { - "summary": "Create Variable", + "summary": "Create variable", "operationId": "projectCreateVariable", "tags": [ "project" @@ -19587,7 +19587,7 @@ }, "\/project\/variables\/{variableId}": { "get": { - "summary": "Get Variable", + "summary": "Get variable", "operationId": "projectGetVariable", "tags": [ "project" @@ -19647,7 +19647,7 @@ ] }, "put": { - "summary": "Update Variable", + "summary": "Update variable", "operationId": "projectUpdateVariable", "tags": [ "project" @@ -19731,7 +19731,7 @@ } }, "delete": { - "summary": "Delete Variable", + "summary": "Delete variable", "operationId": "projectDeleteVariable", "tags": [ "project" @@ -21282,7 +21282,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "keys.read", "platforms": [ "console" ], @@ -21342,7 +21342,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -21437,7 +21437,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "keys.read", "platforms": [ "console" ], @@ -21507,7 +21507,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -21603,7 +21603,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -21814,7 +21814,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "platforms.read", "platforms": [ "console" ], @@ -21874,7 +21874,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -21995,7 +21995,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "platforms.read", "platforms": [ "console" ], @@ -22065,7 +22065,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -22162,7 +22162,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -24640,7 +24640,7 @@ }, "\/proxy\/rules": { "get": { - "summary": "List Rules", + "summary": "List rules", "operationId": "proxyListRules", "tags": [ "proxy" @@ -24714,7 +24714,7 @@ ] }, "post": { - "summary": "Create Rule", + "summary": "Create rule", "operationId": "proxyCreateRule", "tags": [ "proxy" @@ -24800,7 +24800,7 @@ }, "\/proxy\/rules\/{ruleId}": { "get": { - "summary": "Get Rule", + "summary": "Get rule", "operationId": "proxyGetRule", "tags": [ "proxy" @@ -24860,7 +24860,7 @@ ] }, "delete": { - "summary": "Delete Rule", + "summary": "Delete rule", "operationId": "proxyDeleteRule", "tags": [ "proxy" @@ -24915,7 +24915,7 @@ }, "\/proxy\/rules\/{ruleId}\/verification": { "patch": { - "summary": "Update Rule Verification Status", + "summary": "Update rule verification status", "operationId": "proxyUpdateRuleVerification", "tags": [ "proxy" @@ -25791,7 +25791,7 @@ } }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "tags": [ "storage" @@ -27842,7 +27842,7 @@ }, "\/users\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "usersListIdentities", "tags": [ "users" @@ -29141,7 +29141,7 @@ }, "\/users\/{userId}\/mfa\/authenticators\/{type}": { "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "usersDeleteMfaAuthenticator", "tags": [ "users" @@ -29219,7 +29219,7 @@ }, "\/users\/{userId}\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "usersListMfaFactors", "tags": [ "users" @@ -29282,7 +29282,7 @@ }, "\/users\/{userId}\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "usersGetMfaRecoveryCodes", "tags": [ "users" @@ -29343,7 +29343,7 @@ ] }, "put": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "usersUpdateMfaRecoveryCodes", "tags": [ "users" @@ -29404,7 +29404,7 @@ ] }, "patch": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "usersCreateMfaRecoveryCodes", "tags": [ "users" @@ -30182,7 +30182,7 @@ }, "\/users\/{userId}\/targets": { "get": { - "summary": "List User Targets", + "summary": "List user targets", "operationId": "usersListTargets", "tags": [ "users" @@ -30257,7 +30257,7 @@ ] }, "post": { - "summary": "Create User Target", + "summary": "Create user target", "operationId": "usersCreateTarget", "tags": [ "users" @@ -30369,7 +30369,7 @@ }, "\/users\/{userId}\/targets\/{targetId}": { "get": { - "summary": "Get User Target", + "summary": "Get user target", "operationId": "usersGetTarget", "tags": [ "users" @@ -30441,7 +30441,7 @@ ] }, "patch": { - "summary": "Update User target", + "summary": "Update user target", "operationId": "usersUpdateTarget", "tags": [ "users" @@ -30854,7 +30854,7 @@ }, "\/vcs\/github\/installations\/{installationId}\/providerRepositories": { "get": { - "summary": "List Repositories", + "summary": "List repositories", "operationId": "vcsListRepositories", "tags": [ "vcs" @@ -31084,7 +31084,7 @@ }, "\/vcs\/github\/installations\/{installationId}\/providerRepositories\/{providerRepositoryId}\/branches": { "get": { - "summary": "List Repository Branches", + "summary": "List repository branches", "operationId": "vcsListRepositoryBranches", "tags": [ "vcs" @@ -31547,7 +31547,7 @@ ] }, "delete": { - "summary": "Delete Installation", + "summary": "Delete installation", "operationId": "vcsDeleteInstallation", "tags": [ "vcs" @@ -36747,6 +36747,12 @@ "x-example": 0, "format": "int32" }, + "storageTotal": { + "type": "integer", + "description": "Total aggregated number of total databases storage in bytes.", + "x-example": 0, + "format": "int32" + }, "databases": { "type": "array", "description": "Aggregated number of databases per period.", @@ -36770,6 +36776,14 @@ "$ref": "#\/components\/schemas\/metric" }, "x-example": [] + }, + "storage": { + "type": "array", + "description": "An array of the aggregated number of databases storage in bytes per period.", + "items": { + "$ref": "#\/components\/schemas\/metric" + }, + "x-example": [] } }, "required": [ @@ -36777,9 +36791,11 @@ "databasesTotal", "collectionsTotal", "documentsTotal", + "storageTotal", "databases", "collections", - "documents" + "documents", + "storage" ] }, "usageDatabase": { @@ -36803,6 +36819,12 @@ "x-example": 0, "format": "int32" }, + "storageTotal": { + "type": "integer", + "description": "Total aggregated number of total storage used in bytes.", + "x-example": 0, + "format": "int32" + }, "collections": { "type": "array", "description": "Aggregated number of collections per period.", @@ -36818,14 +36840,24 @@ "$ref": "#\/components\/schemas\/metric" }, "x-example": [] + }, + "storage": { + "type": "array", + "description": "Aggregated storage used in bytes per period.", + "items": { + "$ref": "#\/components\/schemas\/metric" + }, + "x-example": [] } }, "required": [ "range", "collectionsTotal", "documentsTotal", + "storageTotal", "collections", - "documents" + "documents", + "storage" ] }, "usageCollection": { @@ -37366,6 +37398,12 @@ "x-example": 0, "format": "int32" }, + "databasesStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of databases storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, "usersTotal": { "type": "integer", "description": "Total aggregated number of users.", @@ -37462,6 +37500,14 @@ }, "x-example": [] }, + "databasesStorageBreakdown": { + "type": "array", + "description": "An array of the aggregated breakdown of storage usage by databases.", + "items": { + "$ref": "#\/components\/schemas\/metricBreakdown" + }, + "x-example": [] + }, "executionsMbSecondsBreakdown": { "type": "array", "description": "Aggregated breakdown in totals of execution mbSeconds by functions.", @@ -37491,6 +37537,7 @@ "executionsTotal", "documentsTotal", "databasesTotal", + "databasesStorageTotal", "usersTotal", "filesStorageTotal", "functionsStorageTotal", @@ -37505,6 +37552,7 @@ "executions", "executionsBreakdown", "bucketsBreakdown", + "databasesStorageBreakdown", "executionsMbSecondsBreakdown", "buildsMbSecondsBreakdown", "functionsStorageBreakdown" diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index ef41688ca8..7d199f1919 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -241,7 +241,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "tags": [ "account" @@ -562,7 +562,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "tags": [ "account" @@ -631,7 +631,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "tags": [ "account" @@ -719,7 +719,7 @@ } }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "tags": [ "account" @@ -783,7 +783,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "tags": [ "account" @@ -859,7 +859,7 @@ } }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "tags": [ "account" @@ -938,7 +938,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "tags": [ "account" @@ -992,7 +992,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "tags": [ "account" @@ -1044,7 +1044,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "tags": [ "account" @@ -1096,7 +1096,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "tags": [ "account" @@ -12097,7 +12097,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "tags": [ "locale" @@ -17789,7 +17789,7 @@ } }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "tags": [ "storage" @@ -19645,7 +19645,7 @@ }, "\/users\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "usersListIdentities", "tags": [ "users" @@ -20885,7 +20885,7 @@ }, "\/users\/{userId}\/mfa\/authenticators\/{type}": { "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "usersDeleteMfaAuthenticator", "tags": [ "users" @@ -20964,7 +20964,7 @@ }, "\/users\/{userId}\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "usersListMfaFactors", "tags": [ "users" @@ -21028,7 +21028,7 @@ }, "\/users\/{userId}\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "usersGetMfaRecoveryCodes", "tags": [ "users" @@ -21090,7 +21090,7 @@ ] }, "put": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "usersUpdateMfaRecoveryCodes", "tags": [ "users" @@ -21152,7 +21152,7 @@ ] }, "patch": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "usersCreateMfaRecoveryCodes", "tags": [ "users" @@ -21941,7 +21941,7 @@ }, "\/users\/{userId}\/targets": { "get": { - "summary": "List User Targets", + "summary": "List user targets", "operationId": "usersListTargets", "tags": [ "users" @@ -22017,7 +22017,7 @@ ] }, "post": { - "summary": "Create User Target", + "summary": "Create user target", "operationId": "usersCreateTarget", "tags": [ "users" @@ -22130,7 +22130,7 @@ }, "\/users\/{userId}\/targets\/{targetId}": { "get": { - "summary": "Get User Target", + "summary": "Get user target", "operationId": "usersGetTarget", "tags": [ "users" @@ -22203,7 +22203,7 @@ ] }, "patch": { - "summary": "Update User target", + "summary": "Update user target", "operationId": "usersUpdateTarget", "tags": [ "users" diff --git a/app/config/specs/swagger2-1.6.x-client.json b/app/config/specs/swagger2-1.6.x-client.json index ce9ea857bb..66b272bea7 100644 --- a/app/config/specs/swagger2-1.6.x-client.json +++ b/app/config/specs/swagger2-1.6.x-client.json @@ -293,7 +293,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "consumes": [ "application\/json" @@ -619,7 +619,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "consumes": [ "application\/json" @@ -687,7 +687,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "consumes": [ "application\/json" @@ -773,7 +773,7 @@ ] }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "consumes": [ "application\/json" @@ -838,7 +838,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "consumes": [ "application\/json" @@ -917,7 +917,7 @@ ] }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "consumes": [ "application\/json" @@ -994,7 +994,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "consumes": [ "application\/json" @@ -1049,7 +1049,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1102,7 +1102,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1155,7 +1155,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -5573,7 +5573,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "consumes": [ "application\/json" @@ -6476,7 +6476,7 @@ ] }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-1.6.x-console.json b/app/config/specs/swagger2-1.6.x-console.json index 51935a5e01..58c81422a9 100644 --- a/app/config/specs/swagger2-1.6.x-console.json +++ b/app/config/specs/swagger2-1.6.x-console.json @@ -348,7 +348,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "consumes": [ "application\/json" @@ -670,7 +670,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "consumes": [ "application\/json" @@ -737,7 +737,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "consumes": [ "application\/json" @@ -822,7 +822,7 @@ ] }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "consumes": [ "application\/json" @@ -886,7 +886,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "consumes": [ "application\/json" @@ -965,7 +965,7 @@ ] }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "consumes": [ "application\/json" @@ -1041,7 +1041,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "consumes": [ "application\/json" @@ -1095,7 +1095,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1147,7 +1147,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1199,7 +1199,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -4637,7 +4637,7 @@ }, "\/console\/assistant": { "post": { - "summary": "Ask Query", + "summary": "Ask query", "operationId": "assistantChat", "consumes": [ "application\/json" @@ -13464,7 +13464,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "consumes": [ "application\/json" @@ -18494,7 +18494,7 @@ }, "\/migrations": { "get": { - "summary": "List Migrations", + "summary": "List migrations", "operationId": "migrationsList", "consumes": [ "application\/json" @@ -18569,7 +18569,7 @@ }, "\/migrations\/appwrite": { "post": { - "summary": "Migrate Appwrite Data", + "summary": "Migrate Appwrite data", "operationId": "migrationsCreateAppwriteMigration", "consumes": [ "application\/json" @@ -18665,7 +18665,7 @@ }, "\/migrations\/appwrite\/report": { "get": { - "summary": "Generate a report on Appwrite Data", + "summary": "Generate a report on Appwrite data", "operationId": "migrationsGetAppwriteReport", "consumes": [ "application\/json" @@ -18755,7 +18755,7 @@ }, "\/migrations\/firebase": { "post": { - "summary": "Migrate Firebase Data (Service Account)", + "summary": "Migrate Firebase data (Service Account)", "operationId": "migrationsCreateFirebaseMigration", "consumes": [ "application\/json" @@ -18837,7 +18837,7 @@ }, "\/migrations\/firebase\/deauthorize": { "get": { - "summary": "Revoke Appwrite's authorization to access Firebase Projects", + "summary": "Revoke Appwrite's authorization to access Firebase projects", "operationId": "migrationsDeleteFirebaseAuth", "consumes": [ "application\/json" @@ -18889,7 +18889,7 @@ }, "\/migrations\/firebase\/oauth": { "post": { - "summary": "Migrate Firebase Data (OAuth)", + "summary": "Migrate Firebase data (OAuth)", "operationId": "migrationsCreateFirebaseOAuthMigration", "consumes": [ "application\/json" @@ -18971,7 +18971,7 @@ }, "\/migrations\/firebase\/projects": { "get": { - "summary": "List Firebase Projects", + "summary": "List Firebase projects", "operationId": "migrationsListFirebaseProjects", "consumes": [ "application\/json" @@ -19023,7 +19023,7 @@ }, "\/migrations\/firebase\/report": { "get": { - "summary": "Generate a report on Firebase Data", + "summary": "Generate a report on Firebase data", "operationId": "migrationsGetFirebaseReport", "consumes": [ "application\/json" @@ -19096,7 +19096,7 @@ }, "\/migrations\/firebase\/report\/oauth": { "get": { - "summary": "Generate a report on Firebase Data using OAuth", + "summary": "Generate a report on Firebase data using OAuth", "operationId": "migrationsGetFirebaseReportOAuth", "consumes": [ "application\/json" @@ -19169,7 +19169,7 @@ }, "\/migrations\/nhost": { "post": { - "summary": "Migrate NHost Data", + "summary": "Migrate NHost data", "operationId": "migrationsCreateNHostMigration", "consumes": [ "application\/json" @@ -19414,7 +19414,7 @@ }, "\/migrations\/supabase": { "post": { - "summary": "Migrate Supabase Data", + "summary": "Migrate Supabase data", "operationId": "migrationsCreateSupabaseMigration", "consumes": [ "application\/json" @@ -19645,7 +19645,7 @@ }, "\/migrations\/{migrationId}": { "get": { - "summary": "Get Migration", + "summary": "Get migration", "operationId": "migrationsGet", "consumes": [ "application\/json" @@ -19705,7 +19705,7 @@ ] }, "patch": { - "summary": "Retry Migration", + "summary": "Retry migration", "operationId": "migrationsRetry", "consumes": [ "application\/json" @@ -19765,7 +19765,7 @@ ] }, "delete": { - "summary": "Delete Migration", + "summary": "Delete migration", "operationId": "migrationsDelete", "consumes": [ "application\/json" @@ -19908,7 +19908,7 @@ }, "\/project\/variables": { "get": { - "summary": "List Variables", + "summary": "List variables", "operationId": "projectListVariables", "consumes": [ "application\/json" @@ -19958,7 +19958,7 @@ ] }, "post": { - "summary": "Create Variable", + "summary": "Create variable", "operationId": "projectCreateVariable", "consumes": [ "application\/json" @@ -20037,7 +20037,7 @@ }, "\/project\/variables\/{variableId}": { "get": { - "summary": "Get Variable", + "summary": "Get variable", "operationId": "projectGetVariable", "consumes": [ "application\/json" @@ -20097,7 +20097,7 @@ ] }, "put": { - "summary": "Update Variable", + "summary": "Update variable", "operationId": "projectUpdateVariable", "consumes": [ "application\/json" @@ -20181,7 +20181,7 @@ ] }, "delete": { - "summary": "Delete Variable", + "summary": "Delete variable", "operationId": "projectDeleteVariable", "consumes": [ "application\/json" @@ -21748,7 +21748,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "keys.read", "platforms": [ "console" ], @@ -21808,7 +21808,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -21904,7 +21904,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "keys.read", "platforms": [ "console" ], @@ -21972,7 +21972,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -22069,7 +22069,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -22280,7 +22280,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "platforms.read", "platforms": [ "console" ], @@ -22340,7 +22340,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -22464,7 +22464,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "platforms.read", "platforms": [ "console" ], @@ -22532,7 +22532,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -22631,7 +22631,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -25101,7 +25101,7 @@ }, "\/proxy\/rules": { "get": { - "summary": "List Rules", + "summary": "List rules", "operationId": "proxyListRules", "consumes": [ "application\/json" @@ -25174,7 +25174,7 @@ ] }, "post": { - "summary": "Create Rule", + "summary": "Create rule", "operationId": "proxyCreateRule", "consumes": [ "application\/json" @@ -25265,7 +25265,7 @@ }, "\/proxy\/rules\/{ruleId}": { "get": { - "summary": "Get Rule", + "summary": "Get rule", "operationId": "proxyGetRule", "consumes": [ "application\/json" @@ -25325,7 +25325,7 @@ ] }, "delete": { - "summary": "Delete Rule", + "summary": "Delete rule", "operationId": "proxyDeleteRule", "consumes": [ "application\/json" @@ -25382,7 +25382,7 @@ }, "\/proxy\/rules\/{ruleId}\/verification": { "patch": { - "summary": "Update Rule Verification Status", + "summary": "Update rule verification status", "operationId": "proxyUpdateRuleVerification", "consumes": [ "application\/json" @@ -26265,7 +26265,7 @@ ] }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "consumes": [ "application\/json" @@ -28324,7 +28324,7 @@ }, "\/users\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "usersListIdentities", "consumes": [ "application\/json" @@ -29661,7 +29661,7 @@ }, "\/users\/{userId}\/mfa\/authenticators\/{type}": { "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "usersDeleteMfaAuthenticator", "consumes": [ "application\/json" @@ -29737,7 +29737,7 @@ }, "\/users\/{userId}\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "usersListMfaFactors", "consumes": [ "application\/json" @@ -29800,7 +29800,7 @@ }, "\/users\/{userId}\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "usersGetMfaRecoveryCodes", "consumes": [ "application\/json" @@ -29861,7 +29861,7 @@ ] }, "put": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "usersUpdateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -29922,7 +29922,7 @@ ] }, "patch": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "usersCreateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -30697,7 +30697,7 @@ }, "\/users\/{userId}\/targets": { "get": { - "summary": "List User Targets", + "summary": "List user targets", "operationId": "usersListTargets", "consumes": [ "application\/json" @@ -30771,7 +30771,7 @@ ] }, "post": { - "summary": "Create User Target", + "summary": "Create user target", "operationId": "usersCreateTarget", "consumes": [ "application\/json" @@ -30886,7 +30886,7 @@ }, "\/users\/{userId}\/targets\/{targetId}": { "get": { - "summary": "Get User Target", + "summary": "Get user target", "operationId": "usersGetTarget", "consumes": [ "application\/json" @@ -30956,7 +30956,7 @@ ] }, "patch": { - "summary": "Update User target", + "summary": "Update user target", "operationId": "usersUpdateTarget", "consumes": [ "application\/json" @@ -31368,7 +31368,7 @@ }, "\/vcs\/github\/installations\/{installationId}\/providerRepositories": { "get": { - "summary": "List Repositories", + "summary": "List repositories", "operationId": "vcsListRepositories", "consumes": [ "application\/json" @@ -31594,7 +31594,7 @@ }, "\/vcs\/github\/installations\/{installationId}\/providerRepositories\/{providerRepositoryId}\/branches": { "get": { - "summary": "List Repository Branches", + "summary": "List repository branches", "operationId": "vcsListRepositoryBranches", "consumes": [ "application\/json" @@ -32046,7 +32046,7 @@ ] }, "delete": { - "summary": "Delete Installation", + "summary": "Delete installation", "operationId": "vcsDeleteInstallation", "consumes": [ "application\/json" @@ -37270,6 +37270,12 @@ "x-example": 0, "format": "int32" }, + "storageTotal": { + "type": "integer", + "description": "Total aggregated number of total databases storage in bytes.", + "x-example": 0, + "format": "int32" + }, "databases": { "type": "array", "description": "Aggregated number of databases per period.", @@ -37296,6 +37302,15 @@ "$ref": "#\/definitions\/metric" }, "x-example": [] + }, + "storage": { + "type": "array", + "description": "An array of the aggregated number of databases storage in bytes per period.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metric" + }, + "x-example": [] } }, "required": [ @@ -37303,9 +37318,11 @@ "databasesTotal", "collectionsTotal", "documentsTotal", + "storageTotal", "databases", "collections", - "documents" + "documents", + "storage" ] }, "usageDatabase": { @@ -37329,6 +37346,12 @@ "x-example": 0, "format": "int32" }, + "storageTotal": { + "type": "integer", + "description": "Total aggregated number of total storage used in bytes.", + "x-example": 0, + "format": "int32" + }, "collections": { "type": "array", "description": "Aggregated number of collections per period.", @@ -37346,14 +37369,25 @@ "$ref": "#\/definitions\/metric" }, "x-example": [] + }, + "storage": { + "type": "array", + "description": "Aggregated storage used in bytes per period.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metric" + }, + "x-example": [] } }, "required": [ "range", "collectionsTotal", "documentsTotal", + "storageTotal", "collections", - "documents" + "documents", + "storage" ] }, "usageCollection": { @@ -37921,6 +37955,12 @@ "x-example": 0, "format": "int32" }, + "databasesStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of databases storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, "usersTotal": { "type": "integer", "description": "Total aggregated number of users.", @@ -38023,6 +38063,15 @@ }, "x-example": [] }, + "databasesStorageBreakdown": { + "type": "array", + "description": "An array of the aggregated breakdown of storage usage by databases.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metricBreakdown" + }, + "x-example": [] + }, "executionsMbSecondsBreakdown": { "type": "array", "description": "Aggregated breakdown in totals of execution mbSeconds by functions.", @@ -38055,6 +38104,7 @@ "executionsTotal", "documentsTotal", "databasesTotal", + "databasesStorageTotal", "usersTotal", "filesStorageTotal", "functionsStorageTotal", @@ -38069,6 +38119,7 @@ "executions", "executionsBreakdown", "bucketsBreakdown", + "databasesStorageBreakdown", "executionsMbSecondsBreakdown", "buildsMbSecondsBreakdown", "functionsStorageBreakdown" diff --git a/app/config/specs/swagger2-1.6.x-server.json b/app/config/specs/swagger2-1.6.x-server.json index af6274226f..1e70715a7e 100644 --- a/app/config/specs/swagger2-1.6.x-server.json +++ b/app/config/specs/swagger2-1.6.x-server.json @@ -310,7 +310,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "consumes": [ "application\/json" @@ -640,7 +640,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "consumes": [ "application\/json" @@ -709,7 +709,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "consumes": [ "application\/json" @@ -796,7 +796,7 @@ ] }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "consumes": [ "application\/json" @@ -862,7 +862,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "consumes": [ "application\/json" @@ -941,7 +941,7 @@ ] }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "consumes": [ "application\/json" @@ -1019,7 +1019,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "consumes": [ "application\/json" @@ -1075,7 +1075,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1129,7 +1129,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1183,7 +1183,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -12312,7 +12312,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "consumes": [ "application\/json" @@ -18238,7 +18238,7 @@ ] }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "consumes": [ "application\/json" @@ -20105,7 +20105,7 @@ }, "\/users\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "usersListIdentities", "consumes": [ "application\/json" @@ -21383,7 +21383,7 @@ }, "\/users\/{userId}\/mfa\/authenticators\/{type}": { "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "usersDeleteMfaAuthenticator", "consumes": [ "application\/json" @@ -21460,7 +21460,7 @@ }, "\/users\/{userId}\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "usersListMfaFactors", "consumes": [ "application\/json" @@ -21524,7 +21524,7 @@ }, "\/users\/{userId}\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "usersGetMfaRecoveryCodes", "consumes": [ "application\/json" @@ -21586,7 +21586,7 @@ ] }, "put": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "usersUpdateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -21648,7 +21648,7 @@ ] }, "patch": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "usersCreateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -22434,7 +22434,7 @@ }, "\/users\/{userId}\/targets": { "get": { - "summary": "List User Targets", + "summary": "List user targets", "operationId": "usersListTargets", "consumes": [ "application\/json" @@ -22509,7 +22509,7 @@ ] }, "post": { - "summary": "Create User Target", + "summary": "Create user target", "operationId": "usersCreateTarget", "consumes": [ "application\/json" @@ -22625,7 +22625,7 @@ }, "\/users\/{userId}\/targets\/{targetId}": { "get": { - "summary": "Get User Target", + "summary": "Get user target", "operationId": "usersGetTarget", "consumes": [ "application\/json" @@ -22696,7 +22696,7 @@ ] }, "patch": { - "summary": "Update User target", + "summary": "Update user target", "operationId": "usersUpdateTarget", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index ce9ea857bb..66b272bea7 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -293,7 +293,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "consumes": [ "application\/json" @@ -619,7 +619,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "consumes": [ "application\/json" @@ -687,7 +687,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "consumes": [ "application\/json" @@ -773,7 +773,7 @@ ] }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "consumes": [ "application\/json" @@ -838,7 +838,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "consumes": [ "application\/json" @@ -917,7 +917,7 @@ ] }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "consumes": [ "application\/json" @@ -994,7 +994,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "consumes": [ "application\/json" @@ -1049,7 +1049,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1102,7 +1102,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1155,7 +1155,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -5573,7 +5573,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "consumes": [ "application\/json" @@ -6476,7 +6476,7 @@ ] }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "consumes": [ "application\/json" diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 51935a5e01..58c81422a9 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -348,7 +348,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "consumes": [ "application\/json" @@ -670,7 +670,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "consumes": [ "application\/json" @@ -737,7 +737,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "consumes": [ "application\/json" @@ -822,7 +822,7 @@ ] }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "consumes": [ "application\/json" @@ -886,7 +886,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "consumes": [ "application\/json" @@ -965,7 +965,7 @@ ] }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "consumes": [ "application\/json" @@ -1041,7 +1041,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "consumes": [ "application\/json" @@ -1095,7 +1095,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1147,7 +1147,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1199,7 +1199,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -4637,7 +4637,7 @@ }, "\/console\/assistant": { "post": { - "summary": "Ask Query", + "summary": "Ask query", "operationId": "assistantChat", "consumes": [ "application\/json" @@ -13464,7 +13464,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "consumes": [ "application\/json" @@ -18494,7 +18494,7 @@ }, "\/migrations": { "get": { - "summary": "List Migrations", + "summary": "List migrations", "operationId": "migrationsList", "consumes": [ "application\/json" @@ -18569,7 +18569,7 @@ }, "\/migrations\/appwrite": { "post": { - "summary": "Migrate Appwrite Data", + "summary": "Migrate Appwrite data", "operationId": "migrationsCreateAppwriteMigration", "consumes": [ "application\/json" @@ -18665,7 +18665,7 @@ }, "\/migrations\/appwrite\/report": { "get": { - "summary": "Generate a report on Appwrite Data", + "summary": "Generate a report on Appwrite data", "operationId": "migrationsGetAppwriteReport", "consumes": [ "application\/json" @@ -18755,7 +18755,7 @@ }, "\/migrations\/firebase": { "post": { - "summary": "Migrate Firebase Data (Service Account)", + "summary": "Migrate Firebase data (Service Account)", "operationId": "migrationsCreateFirebaseMigration", "consumes": [ "application\/json" @@ -18837,7 +18837,7 @@ }, "\/migrations\/firebase\/deauthorize": { "get": { - "summary": "Revoke Appwrite's authorization to access Firebase Projects", + "summary": "Revoke Appwrite's authorization to access Firebase projects", "operationId": "migrationsDeleteFirebaseAuth", "consumes": [ "application\/json" @@ -18889,7 +18889,7 @@ }, "\/migrations\/firebase\/oauth": { "post": { - "summary": "Migrate Firebase Data (OAuth)", + "summary": "Migrate Firebase data (OAuth)", "operationId": "migrationsCreateFirebaseOAuthMigration", "consumes": [ "application\/json" @@ -18971,7 +18971,7 @@ }, "\/migrations\/firebase\/projects": { "get": { - "summary": "List Firebase Projects", + "summary": "List Firebase projects", "operationId": "migrationsListFirebaseProjects", "consumes": [ "application\/json" @@ -19023,7 +19023,7 @@ }, "\/migrations\/firebase\/report": { "get": { - "summary": "Generate a report on Firebase Data", + "summary": "Generate a report on Firebase data", "operationId": "migrationsGetFirebaseReport", "consumes": [ "application\/json" @@ -19096,7 +19096,7 @@ }, "\/migrations\/firebase\/report\/oauth": { "get": { - "summary": "Generate a report on Firebase Data using OAuth", + "summary": "Generate a report on Firebase data using OAuth", "operationId": "migrationsGetFirebaseReportOAuth", "consumes": [ "application\/json" @@ -19169,7 +19169,7 @@ }, "\/migrations\/nhost": { "post": { - "summary": "Migrate NHost Data", + "summary": "Migrate NHost data", "operationId": "migrationsCreateNHostMigration", "consumes": [ "application\/json" @@ -19414,7 +19414,7 @@ }, "\/migrations\/supabase": { "post": { - "summary": "Migrate Supabase Data", + "summary": "Migrate Supabase data", "operationId": "migrationsCreateSupabaseMigration", "consumes": [ "application\/json" @@ -19645,7 +19645,7 @@ }, "\/migrations\/{migrationId}": { "get": { - "summary": "Get Migration", + "summary": "Get migration", "operationId": "migrationsGet", "consumes": [ "application\/json" @@ -19705,7 +19705,7 @@ ] }, "patch": { - "summary": "Retry Migration", + "summary": "Retry migration", "operationId": "migrationsRetry", "consumes": [ "application\/json" @@ -19765,7 +19765,7 @@ ] }, "delete": { - "summary": "Delete Migration", + "summary": "Delete migration", "operationId": "migrationsDelete", "consumes": [ "application\/json" @@ -19908,7 +19908,7 @@ }, "\/project\/variables": { "get": { - "summary": "List Variables", + "summary": "List variables", "operationId": "projectListVariables", "consumes": [ "application\/json" @@ -19958,7 +19958,7 @@ ] }, "post": { - "summary": "Create Variable", + "summary": "Create variable", "operationId": "projectCreateVariable", "consumes": [ "application\/json" @@ -20037,7 +20037,7 @@ }, "\/project\/variables\/{variableId}": { "get": { - "summary": "Get Variable", + "summary": "Get variable", "operationId": "projectGetVariable", "consumes": [ "application\/json" @@ -20097,7 +20097,7 @@ ] }, "put": { - "summary": "Update Variable", + "summary": "Update variable", "operationId": "projectUpdateVariable", "consumes": [ "application\/json" @@ -20181,7 +20181,7 @@ ] }, "delete": { - "summary": "Delete Variable", + "summary": "Delete variable", "operationId": "projectDeleteVariable", "consumes": [ "application\/json" @@ -21748,7 +21748,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "keys.read", "platforms": [ "console" ], @@ -21808,7 +21808,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -21904,7 +21904,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "keys.read", "platforms": [ "console" ], @@ -21972,7 +21972,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -22069,7 +22069,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "keys.write", "platforms": [ "console" ], @@ -22280,7 +22280,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "platforms.read", "platforms": [ "console" ], @@ -22340,7 +22340,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -22464,7 +22464,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.read", + "scope": "platforms.read", "platforms": [ "console" ], @@ -22532,7 +22532,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -22631,7 +22631,7 @@ "rate-limit": 0, "rate-time": 3600, "rate-key": "url:{url},ip:{ip}", - "scope": "projects.write", + "scope": "platforms.write", "platforms": [ "console" ], @@ -25101,7 +25101,7 @@ }, "\/proxy\/rules": { "get": { - "summary": "List Rules", + "summary": "List rules", "operationId": "proxyListRules", "consumes": [ "application\/json" @@ -25174,7 +25174,7 @@ ] }, "post": { - "summary": "Create Rule", + "summary": "Create rule", "operationId": "proxyCreateRule", "consumes": [ "application\/json" @@ -25265,7 +25265,7 @@ }, "\/proxy\/rules\/{ruleId}": { "get": { - "summary": "Get Rule", + "summary": "Get rule", "operationId": "proxyGetRule", "consumes": [ "application\/json" @@ -25325,7 +25325,7 @@ ] }, "delete": { - "summary": "Delete Rule", + "summary": "Delete rule", "operationId": "proxyDeleteRule", "consumes": [ "application\/json" @@ -25382,7 +25382,7 @@ }, "\/proxy\/rules\/{ruleId}\/verification": { "patch": { - "summary": "Update Rule Verification Status", + "summary": "Update rule verification status", "operationId": "proxyUpdateRuleVerification", "consumes": [ "application\/json" @@ -26265,7 +26265,7 @@ ] }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "consumes": [ "application\/json" @@ -28324,7 +28324,7 @@ }, "\/users\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "usersListIdentities", "consumes": [ "application\/json" @@ -29661,7 +29661,7 @@ }, "\/users\/{userId}\/mfa\/authenticators\/{type}": { "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "usersDeleteMfaAuthenticator", "consumes": [ "application\/json" @@ -29737,7 +29737,7 @@ }, "\/users\/{userId}\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "usersListMfaFactors", "consumes": [ "application\/json" @@ -29800,7 +29800,7 @@ }, "\/users\/{userId}\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "usersGetMfaRecoveryCodes", "consumes": [ "application\/json" @@ -29861,7 +29861,7 @@ ] }, "put": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "usersUpdateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -29922,7 +29922,7 @@ ] }, "patch": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "usersCreateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -30697,7 +30697,7 @@ }, "\/users\/{userId}\/targets": { "get": { - "summary": "List User Targets", + "summary": "List user targets", "operationId": "usersListTargets", "consumes": [ "application\/json" @@ -30771,7 +30771,7 @@ ] }, "post": { - "summary": "Create User Target", + "summary": "Create user target", "operationId": "usersCreateTarget", "consumes": [ "application\/json" @@ -30886,7 +30886,7 @@ }, "\/users\/{userId}\/targets\/{targetId}": { "get": { - "summary": "Get User Target", + "summary": "Get user target", "operationId": "usersGetTarget", "consumes": [ "application\/json" @@ -30956,7 +30956,7 @@ ] }, "patch": { - "summary": "Update User target", + "summary": "Update user target", "operationId": "usersUpdateTarget", "consumes": [ "application\/json" @@ -31368,7 +31368,7 @@ }, "\/vcs\/github\/installations\/{installationId}\/providerRepositories": { "get": { - "summary": "List Repositories", + "summary": "List repositories", "operationId": "vcsListRepositories", "consumes": [ "application\/json" @@ -31594,7 +31594,7 @@ }, "\/vcs\/github\/installations\/{installationId}\/providerRepositories\/{providerRepositoryId}\/branches": { "get": { - "summary": "List Repository Branches", + "summary": "List repository branches", "operationId": "vcsListRepositoryBranches", "consumes": [ "application\/json" @@ -32046,7 +32046,7 @@ ] }, "delete": { - "summary": "Delete Installation", + "summary": "Delete installation", "operationId": "vcsDeleteInstallation", "consumes": [ "application\/json" @@ -37270,6 +37270,12 @@ "x-example": 0, "format": "int32" }, + "storageTotal": { + "type": "integer", + "description": "Total aggregated number of total databases storage in bytes.", + "x-example": 0, + "format": "int32" + }, "databases": { "type": "array", "description": "Aggregated number of databases per period.", @@ -37296,6 +37302,15 @@ "$ref": "#\/definitions\/metric" }, "x-example": [] + }, + "storage": { + "type": "array", + "description": "An array of the aggregated number of databases storage in bytes per period.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metric" + }, + "x-example": [] } }, "required": [ @@ -37303,9 +37318,11 @@ "databasesTotal", "collectionsTotal", "documentsTotal", + "storageTotal", "databases", "collections", - "documents" + "documents", + "storage" ] }, "usageDatabase": { @@ -37329,6 +37346,12 @@ "x-example": 0, "format": "int32" }, + "storageTotal": { + "type": "integer", + "description": "Total aggregated number of total storage used in bytes.", + "x-example": 0, + "format": "int32" + }, "collections": { "type": "array", "description": "Aggregated number of collections per period.", @@ -37346,14 +37369,25 @@ "$ref": "#\/definitions\/metric" }, "x-example": [] + }, + "storage": { + "type": "array", + "description": "Aggregated storage used in bytes per period.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metric" + }, + "x-example": [] } }, "required": [ "range", "collectionsTotal", "documentsTotal", + "storageTotal", "collections", - "documents" + "documents", + "storage" ] }, "usageCollection": { @@ -37921,6 +37955,12 @@ "x-example": 0, "format": "int32" }, + "databasesStorageTotal": { + "type": "integer", + "description": "Total aggregated sum of databases storage size (in bytes).", + "x-example": 0, + "format": "int32" + }, "usersTotal": { "type": "integer", "description": "Total aggregated number of users.", @@ -38023,6 +38063,15 @@ }, "x-example": [] }, + "databasesStorageBreakdown": { + "type": "array", + "description": "An array of the aggregated breakdown of storage usage by databases.", + "items": { + "type": "object", + "$ref": "#\/definitions\/metricBreakdown" + }, + "x-example": [] + }, "executionsMbSecondsBreakdown": { "type": "array", "description": "Aggregated breakdown in totals of execution mbSeconds by functions.", @@ -38055,6 +38104,7 @@ "executionsTotal", "documentsTotal", "databasesTotal", + "databasesStorageTotal", "usersTotal", "filesStorageTotal", "functionsStorageTotal", @@ -38069,6 +38119,7 @@ "executions", "executionsBreakdown", "bucketsBreakdown", + "databasesStorageBreakdown", "executionsMbSecondsBreakdown", "buildsMbSecondsBreakdown", "functionsStorageBreakdown" diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index af6274226f..1e70715a7e 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -310,7 +310,7 @@ }, "\/account\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "accountListIdentities", "consumes": [ "application\/json" @@ -640,7 +640,7 @@ }, "\/account\/mfa\/authenticators\/{type}": { "post": { - "summary": "Create Authenticator", + "summary": "Create authenticator", "operationId": "accountCreateMfaAuthenticator", "consumes": [ "application\/json" @@ -709,7 +709,7 @@ ] }, "put": { - "summary": "Verify Authenticator", + "summary": "Verify authenticator", "operationId": "accountUpdateMfaAuthenticator", "consumes": [ "application\/json" @@ -796,7 +796,7 @@ ] }, "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "accountDeleteMfaAuthenticator", "consumes": [ "application\/json" @@ -862,7 +862,7 @@ }, "\/account\/mfa\/challenge": { "post": { - "summary": "Create MFA Challenge", + "summary": "Create MFA challenge", "operationId": "accountCreateMfaChallenge", "consumes": [ "application\/json" @@ -941,7 +941,7 @@ ] }, "put": { - "summary": "Create MFA Challenge (confirmation)", + "summary": "Create MFA challenge (confirmation)", "operationId": "accountUpdateMfaChallenge", "consumes": [ "application\/json" @@ -1019,7 +1019,7 @@ }, "\/account\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "accountListMfaFactors", "consumes": [ "application\/json" @@ -1075,7 +1075,7 @@ }, "\/account\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "accountGetMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1129,7 +1129,7 @@ ] }, "post": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "accountCreateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -1183,7 +1183,7 @@ ] }, "patch": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "accountUpdateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -12312,7 +12312,7 @@ }, "\/locale\/codes": { "get": { - "summary": "List Locale Codes", + "summary": "List locale codes", "operationId": "localeListCodes", "consumes": [ "application\/json" @@ -18238,7 +18238,7 @@ ] }, "delete": { - "summary": "Delete File", + "summary": "Delete file", "operationId": "storageDeleteFile", "consumes": [ "application\/json" @@ -20105,7 +20105,7 @@ }, "\/users\/identities": { "get": { - "summary": "List Identities", + "summary": "List identities", "operationId": "usersListIdentities", "consumes": [ "application\/json" @@ -21383,7 +21383,7 @@ }, "\/users\/{userId}\/mfa\/authenticators\/{type}": { "delete": { - "summary": "Delete Authenticator", + "summary": "Delete authenticator", "operationId": "usersDeleteMfaAuthenticator", "consumes": [ "application\/json" @@ -21460,7 +21460,7 @@ }, "\/users\/{userId}\/mfa\/factors": { "get": { - "summary": "List Factors", + "summary": "List factors", "operationId": "usersListMfaFactors", "consumes": [ "application\/json" @@ -21524,7 +21524,7 @@ }, "\/users\/{userId}\/mfa\/recovery-codes": { "get": { - "summary": "Get MFA Recovery Codes", + "summary": "Get MFA recovery codes", "operationId": "usersGetMfaRecoveryCodes", "consumes": [ "application\/json" @@ -21586,7 +21586,7 @@ ] }, "put": { - "summary": "Regenerate MFA Recovery Codes", + "summary": "Regenerate MFA recovery codes", "operationId": "usersUpdateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -21648,7 +21648,7 @@ ] }, "patch": { - "summary": "Create MFA Recovery Codes", + "summary": "Create MFA recovery codes", "operationId": "usersCreateMfaRecoveryCodes", "consumes": [ "application\/json" @@ -22434,7 +22434,7 @@ }, "\/users\/{userId}\/targets": { "get": { - "summary": "List User Targets", + "summary": "List user targets", "operationId": "usersListTargets", "consumes": [ "application\/json" @@ -22509,7 +22509,7 @@ ] }, "post": { - "summary": "Create User Target", + "summary": "Create user target", "operationId": "usersCreateTarget", "consumes": [ "application\/json" @@ -22625,7 +22625,7 @@ }, "\/users\/{userId}\/targets\/{targetId}": { "get": { - "summary": "Get User Target", + "summary": "Get user target", "operationId": "usersGetTarget", "consumes": [ "application\/json" @@ -22696,7 +22696,7 @@ ] }, "patch": { - "summary": "Update User target", + "summary": "Update user target", "operationId": "usersUpdateTarget", "consumes": [ "application\/json" diff --git a/app/controllers/api/databases.php b/app/controllers/api/databases.php index a9bb58df4b..b76548b725 100644 --- a/app/controllers/api/databases.php +++ b/app/controllers/api/databases.php @@ -5,6 +5,7 @@ use Appwrite\Detector\Detector; use Appwrite\Event\Database as EventDatabase; use Appwrite\Event\Delete; use Appwrite\Event\Event; +use Appwrite\Event\Usage; use Appwrite\Extend\Exception; use Appwrite\Network\Validator\Email; use Appwrite\Utopia\Database\Validator\CustomId; @@ -452,7 +453,8 @@ App::post('/v1/databases') ->inject('response') ->inject('dbForProject') ->inject('queueForEvents') - ->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents) { + ->inject('queueForUsage') + ->action(function (string $databaseId, string $name, bool $enabled, Response $response, Database $dbForProject, Event $queueForEvents, Usage $queueForUsage) { $databaseId = $databaseId == 'unique()' ? ID::unique() : $databaseId; @@ -502,6 +504,7 @@ App::post('/v1/databases') } $queueForEvents->setParam('databaseId', $database->getId()); + $queueForUsage->addMetric(str_replace(['{databaseInternalId}'], [$database->getInternalId()], METRIC_DATABASE_ID_STORAGE), 1); // per database $response ->setStatusCode(Response::STATUS_CODE_CREATED) @@ -733,7 +736,8 @@ App::delete('/v1/databases/:databaseId') ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('queueForUsage') + ->action(function (string $databaseId, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) { $database = $dbForProject->getDocument('databases', $databaseId); @@ -756,6 +760,9 @@ App::delete('/v1/databases/:databaseId') ->setParam('databaseId', $database->getId()) ->setPayload($response->output($database, Response::MODEL_DATABASE)); + $queueForUsage + ->addMetric(METRIC_DATABASES_STORAGE, 1); // Global, deletion forces full recalculation + $response->noContent(); }); @@ -2350,7 +2357,8 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->inject('dbForProject') ->inject('queueForDatabase') ->inject('queueForEvents') - ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents) { + ->inject('queueForUsage') + ->action(function (string $databaseId, string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Usage $queueForUsage) { $db = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); @@ -2435,6 +2443,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/attributes/:key ->setContext('database', $db) ->setPayload($response->output($attribute, $model)); + $queueForUsage + ->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$db->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection + $response->noContent(); }); @@ -2810,8 +2821,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->inject('dbForProject') ->inject('user') ->inject('queueForEvents') + ->inject('queueForUsage') ->inject('mode') - ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, string $mode) { + ->action(function (string $databaseId, string $documentId, string $collectionId, string|array $data, ?array $permissions, Response $response, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode) { $data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array @@ -3027,6 +3039,9 @@ App::post('/v1/databases/:databaseId/collections/:collectionId/documents') ->setContext('database', $database) ->setPayload($response->getPayload(), sensitive: $relationships); + + $queueForUsage + ->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection }); App::get('/v1/databases/:databaseId/collections/:collectionId/documents') @@ -3643,8 +3658,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->inject('dbForProject') ->inject('queueForDeletes') ->inject('queueForEvents') + ->inject('queueForUsage') ->inject('mode') - ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, string $mode) { + ->action(function (string $databaseId, string $collectionId, string $documentId, ?\DateTime $requestTimestamp, Response $response, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Usage $queueForUsage, string $mode) { $database = Authorization::skip(fn () => $dbForProject->getDocument('databases', $databaseId)); $isAPIKey = Auth::isAppUser(Authorization::getRoles()); @@ -3729,6 +3745,9 @@ App::delete('/v1/databases/:databaseId/collections/:collectionId/documents/:docu ->setContext('database', $database) ->setPayload($response->output($document, Response::MODEL_DOCUMENT), sensitive: $relationships); + $queueForUsage + ->addMetric(str_replace(['{databaseInternalId}', '{collectionInternalId}'], [$database->getInternalId(), $collection->getInternalId()], METRIC_DATABASE_ID_COLLECTION_ID_STORAGE), 1); // per collection + $response->noContent(); }); @@ -3754,6 +3773,7 @@ App::get('/v1/databases/usage') METRIC_DATABASES, METRIC_COLLECTIONS, METRIC_DOCUMENTS, + METRIC_DATABASES_STORAGE ]; Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { @@ -3804,9 +3824,11 @@ App::get('/v1/databases/usage') 'databasesTotal' => $usage[$metrics[0]]['total'], 'collectionsTotal' => $usage[$metrics[1]]['total'], 'documentsTotal' => $usage[$metrics[2]]['total'], + 'storageTotal' => $usage[$metrics[3]]['total'], 'databases' => $usage[$metrics[0]]['data'], 'collections' => $usage[$metrics[1]]['data'], 'documents' => $usage[$metrics[2]]['data'], + 'storage' => $usage[$metrics[3]]['data'], ]), Response::MODEL_USAGE_DATABASES); }); @@ -3838,6 +3860,7 @@ App::get('/v1/databases/:databaseId/usage') $metrics = [ str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_COLLECTIONS), str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_DOCUMENTS), + str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE) ]; Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats) { @@ -3888,8 +3911,10 @@ App::get('/v1/databases/:databaseId/usage') 'range' => $range, 'collectionsTotal' => $usage[$metrics[0]]['total'], 'documentsTotal' => $usage[$metrics[1]]['total'], + 'storageTotal' => $usage[$metrics[2]]['total'], 'collections' => $usage[$metrics[0]]['data'], 'documents' => $usage[$metrics[1]]['data'], + 'storage' => $usage[$metrics[2]]['data'], ]), Response::MODEL_USAGE_DATABASE); }); diff --git a/app/controllers/api/project.php b/app/controllers/api/project.php index 62a34bb5ce..6053326308 100644 --- a/app/controllers/api/project.php +++ b/app/controllers/api/project.php @@ -47,6 +47,7 @@ App::get('/v1/project/usage') METRIC_USERS, METRIC_BUCKETS, METRIC_FILES_STORAGE, + METRIC_DATABASES_STORAGE, METRIC_DEPLOYMENTS_STORAGE, METRIC_BUILDS_STORAGE ], @@ -56,6 +57,7 @@ App::get('/v1/project/usage') METRIC_NETWORK_OUTBOUND, METRIC_USERS, METRIC_EXECUTIONS, + METRIC_DATABASES_STORAGE, METRIC_EXECUTIONS_MB_SECONDS, METRIC_BUILDS_MB_SECONDS ] @@ -182,6 +184,23 @@ App::get('/v1/project/usage') ]; }, $dbForProject->find('buckets')); + $databasesStorageBreakdown = array_map(function ($database) use ($dbForProject) { + $id = $database->getId(); + $name = $database->getAttribute('name'); + $metric = str_replace('{databaseInternalId}', $database->getInternalId(), METRIC_DATABASE_ID_STORAGE); + + $value = $dbForProject->findOne('stats', [ + Query::equal('metric', [$metric]), + Query::equal('period', ['inf']) + ]); + + return [ + 'resourceId' => $id, + 'name' => $name, + 'value' => $value['value'] ?? 0, + ]; + }, $dbForProject->find('databases')); + $functionsStorageBreakdown = array_map(function ($function) use ($dbForProject) { $id = $function->getId(); $name = $function->getAttribute('name'); @@ -269,6 +288,7 @@ App::get('/v1/project/usage') 'buildsMbSecondsTotal' => $total[METRIC_BUILDS_MB_SECONDS], 'documentsTotal' => $total[METRIC_DOCUMENTS], 'databasesTotal' => $total[METRIC_DATABASES], + 'databasesStorageTotal' => $total[METRIC_DATABASES_STORAGE], 'usersTotal' => $total[METRIC_USERS], 'bucketsTotal' => $total[METRIC_BUCKETS], 'filesStorageTotal' => $total[METRIC_FILES_STORAGE], @@ -279,6 +299,7 @@ App::get('/v1/project/usage') 'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown, 'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown, 'bucketsBreakdown' => $bucketsBreakdown, + 'databasesStorageBreakdown' => $databasesStorageBreakdown, 'executionsMbSecondsBreakdown' => $executionsMbSecondsBreakdown, 'buildsMbSecondsBreakdown' => $buildsMbSecondsBreakdown, 'functionsStorageBreakdown' => $functionsStorageBreakdown, diff --git a/app/controllers/api/projects.php b/app/controllers/api/projects.php index 3a8c232195..aafd480398 100644 --- a/app/controllers/api/projects.php +++ b/app/controllers/api/projects.php @@ -1199,7 +1199,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId') App::post('/v1/projects/:projectId/keys') ->desc('Create key') ->groups(['api', 'projects']) - ->label('scope', 'projects.write') + ->label('scope', 'keys.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'createKey') @@ -1249,7 +1249,7 @@ App::post('/v1/projects/:projectId/keys') App::get('/v1/projects/:projectId/keys') ->desc('List keys') ->groups(['api', 'projects']) - ->label('scope', 'projects.read') + ->label('scope', 'keys.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'listKeys') @@ -1281,7 +1281,7 @@ App::get('/v1/projects/:projectId/keys') App::get('/v1/projects/:projectId/keys/:keyId') ->desc('Get key') ->groups(['api', 'projects']) - ->label('scope', 'projects.read') + ->label('scope', 'keys.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'getKey') @@ -1315,7 +1315,7 @@ App::get('/v1/projects/:projectId/keys/:keyId') App::put('/v1/projects/:projectId/keys/:keyId') ->desc('Update key') ->groups(['api', 'projects']) - ->label('scope', 'projects.write') + ->label('scope', 'keys.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'updateKey') @@ -1361,7 +1361,7 @@ App::put('/v1/projects/:projectId/keys/:keyId') App::delete('/v1/projects/:projectId/keys/:keyId') ->desc('Delete key') ->groups(['api', 'projects']) - ->label('scope', 'projects.write') + ->label('scope', 'keys.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'deleteKey') @@ -1436,7 +1436,7 @@ App::post('/v1/projects/:projectId/platforms') ->desc('Create platform') ->groups(['api', 'projects']) ->label('audits.event', 'platforms.create') - ->label('scope', 'projects.write') + ->label('scope', 'platforms.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'createPlatform') @@ -1486,7 +1486,7 @@ App::post('/v1/projects/:projectId/platforms') App::get('/v1/projects/:projectId/platforms') ->desc('List platforms') ->groups(['api', 'projects']) - ->label('scope', 'projects.read') + ->label('scope', 'platforms.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'listPlatforms') @@ -1518,7 +1518,7 @@ App::get('/v1/projects/:projectId/platforms') App::get('/v1/projects/:projectId/platforms/:platformId') ->desc('Get platform') ->groups(['api', 'projects']) - ->label('scope', 'projects.read') + ->label('scope', 'platforms.read') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'getPlatform') @@ -1552,7 +1552,7 @@ App::get('/v1/projects/:projectId/platforms/:platformId') App::put('/v1/projects/:projectId/platforms/:platformId') ->desc('Update platform') ->groups(['api', 'projects']) - ->label('scope', 'projects.write') + ->label('scope', 'platforms.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'updatePlatform') @@ -1600,7 +1600,7 @@ App::delete('/v1/projects/:projectId/platforms/:platformId') ->desc('Delete platform') ->groups(['api', 'projects']) ->label('audits.event', 'platforms.delete') - ->label('scope', 'projects.write') + ->label('scope', 'platforms.write') ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) ->label('sdk.namespace', 'projects') ->label('sdk.method', 'deletePlatform') diff --git a/app/controllers/api/teams.php b/app/controllers/api/teams.php index f98cdd721c..146b5d5f81 100644 --- a/app/controllers/api/teams.php +++ b/app/controllers/api/teams.php @@ -43,6 +43,7 @@ use Utopia\Validator\ArrayList; use Utopia\Validator\Assoc; use Utopia\Validator\Host; use Utopia\Validator\Text; +use Utopia\Validator\WhiteList; App::post('/v1/teams') ->desc('Create team') @@ -394,7 +395,17 @@ App::post('/v1/teams/:teamId/memberships') ->param('email', '', new Email(), 'Email of the new team member.', true) ->param('userId', '', new UID(), 'ID of the user to be added to a team.', true) ->param('phone', '', new Phone(), 'Phone number. Format this number with a leading \'+\' and a country code, e.g., +16175551212.', true) - ->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.') + ->param('roles', [], function (Document $project) { + if ($project->getId() === 'console') { + ; + $roles = array_keys(Config::getParam('roles', [])); + array_filter($roles, function ($role) { + return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]); + }); + return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE); + } + return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE); + }, 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project']) ->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. This parameter is not required when an API key is supplied. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients']) // TODO add our own built-in confirm page ->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true) ->inject('response') @@ -868,7 +879,17 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId') ->label('sdk.response.model', Response::MODEL_MEMBERSHIP) ->param('teamId', '', new UID(), 'Team ID.') ->param('membershipId', '', new UID(), 'Membership ID.') - ->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.') + ->param('roles', [], function (Document $project) { + if ($project->getId() === 'console') { + ; + $roles = array_keys(Config::getParam('roles', [])); + array_filter($roles, function ($role) { + return !in_array($role, [Auth::USER_ROLE_APPS, Auth::USER_ROLE_GUESTS, Auth::USER_ROLE_USERS]); + }); + return new ArrayList(new WhiteList($roles), APP_LIMIT_ARRAY_PARAMS_SIZE); + } + return new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE); + }, 'An array of strings. Use this param to set the user\'s roles in the team. A role can be any string. Learn more about [roles and permissions](https://appwrite.io/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.', false, ['project']) ->inject('request') ->inject('response') ->inject('user') diff --git a/app/controllers/general.php b/app/controllers/general.php index 0bbfa2b694..04554a940e 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -863,8 +863,12 @@ App::error() $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - $responseCode = $logger->addLog($log); - Console::info('Log pushed with status code: ' . $responseCode); + try { + $responseCode = $logger->addLog($log); + Console::info('Error log pushed with status code: ' . $responseCode); + } catch (Throwable $th) { + Console::error('Error pushing log: ' . $th->getMessage()); + } } /** Wrap all exceptions inside Appwrite\Extend\Exception */ diff --git a/app/controllers/mock.php b/app/controllers/mock.php index fdb1d80dcc..bc071fc885 100644 --- a/app/controllers/mock.php +++ b/app/controllers/mock.php @@ -158,7 +158,7 @@ App::patch('/v1/mock/functions-v2') App::post('/v1/mock/api-key-unprefixed') ->desc('Create API Key (without standard prefix)') ->groups(['mock', 'api', 'projects']) - ->label('scope', 'projects.write') + ->label('scope', 'public') ->label('docs', false) ->param('projectId', '', new UID(), 'Project ID.') ->inject('response') diff --git a/app/controllers/shared/api.php b/app/controllers/shared/api.php index 6d87940ff7..f0d896c95a 100644 --- a/app/controllers/shared/api.php +++ b/app/controllers/shared/api.php @@ -95,7 +95,7 @@ $databaseListener = function (string $event, Document $document, Document $proje $databaseInternalId = $parts[1] ?? 0; $queueForUsage ->addMetric(METRIC_COLLECTIONS, $value) // per project - ->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value) // per database + ->addMetric(str_replace('{databaseInternalId}', $databaseInternalId, METRIC_DATABASE_ID_COLLECTIONS), $value) ; if ($event === Database::EVENT_DOCUMENT_DELETE) { @@ -160,42 +160,22 @@ App::init() ->inject('session') ->inject('servers') ->inject('mode') - ->action(function (App $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode) { + ->inject('team') + ->action(function (App $utopia, Request $request, Database $dbForConsole, Document $project, Document $user, ?Document $session, array $servers, string $mode, Document $team) { $route = $utopia->getRoute(); if ($project->isEmpty()) { throw new Exception(Exception::PROJECT_NOT_FOUND); } - /** - * ACL Check - */ + /** Default role */ + $roles = Config::getParam('roles', []); $role = ($user->isEmpty()) ? Role::guests()->toString() : Role::users()->toString(); - // Add user roles - $memberships = $user->find('teamId', $project->getAttribute('teamId'), 'memberships'); - - if ($memberships) { - foreach ($memberships->getAttribute('roles', []) as $memberRole) { - switch ($memberRole) { - case 'owner': - $role = Auth::USER_ROLE_OWNER; - break; - case 'admin': - $role = Auth::USER_ROLE_ADMIN; - break; - case 'developer': - $role = Auth::USER_ROLE_DEVELOPER; - break; - } - } - } - - $roles = Config::getParam('roles', []); - $scope = $route->getLabel('scope', 'none'); // Allowed scope for chosen route - $scopes = $roles[$role]['scopes']; // Allowed scopes for user role + /** Allowed Scopes for the role */ + $scopes = $roles[$role]['scopes']; $apiKey = $request->getHeader('x-appwrite-key', ''); @@ -294,13 +274,38 @@ App::init() } } } + // Admin User Authentication + elseif (($project->getId() === 'console' && !$team->isEmpty() && !$user->isEmpty()) || ($project->getId() !== 'console' && !$user->isEmpty() && $mode === APP_MODE_ADMIN)) { + $teamId = $team->getId(); + $adminRoles = []; + $memberships = $user->getAttribute('memberships', []); + foreach ($memberships as $membership) { + if ($membership->getAttribute('confirm', false) === true && $membership->getAttribute('teamId') === $teamId) { + $adminRoles = $membership->getAttribute('roles', []); + break; + } + } + + if (empty($adminRoles)) { + throw new Exception(Exception::USER_UNAUTHORIZED); + } + + $scopes = []; // reset scope if admin + foreach ($adminRoles as $role) { + $scopes = \array_merge($scopes, $roles[$role]['scopes']); + } + + Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. + } + + $scopes = \array_unique($scopes); Authorization::setRole($role); - foreach (Auth::getRoles($user) as $authRole) { Authorization::setRole($authRole); } + /** Do not allow access to disabled services */ $service = $route->getLabel('sdk.namespace', ''); if (!empty($service)) { if ( @@ -311,14 +316,14 @@ App::init() throw new Exception(Exception::GENERAL_SERVICE_DISABLED); } } - if (!\in_array($scope, $scopes)) { - if ($project->isEmpty()) { // Check if permission is denied because project is missing - throw new Exception(Exception::PROJECT_NOT_FOUND); - } + /** Do now allow access if scope is not allowed */ + $scope = $route->getLabel('scope', 'none'); + if (!\in_array($scope, $scopes)) { throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, $user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')'); } + /** Do not allow access to blocked accounts */ if (false === $user->getAttribute('status')) { // Account is blocked throw new Exception(Exception::USER_BLOCKED); } diff --git a/app/http.php b/app/http.php index 7e1291142b..bec772c770 100644 --- a/app/http.php +++ b/app/http.php @@ -298,8 +298,12 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - $responseCode = $logger->addLog($log); - Console::info('Log pushed with status code: ' . $responseCode); + try { + $responseCode = $logger->addLog($log); + Console::info('Error log pushed with status code: ' . $responseCode); + } catch (Throwable $th) { + Console::error('Error pushing log: ' . $th->getMessage()); + } } Console::error('[Error] Type: ' . get_class($th)); diff --git a/app/init.php b/app/init.php index b4ab772e0e..cf0dbe44ee 100644 --- a/app/init.php +++ b/app/init.php @@ -41,6 +41,7 @@ use Appwrite\Network\Validator\Email; use Appwrite\Network\Validator\Origin; use Appwrite\OpenSSL\OpenSSL; use Appwrite\URL\URL as AppwriteURL; +use Appwrite\Utopia\Request; use MaxMind\Db\Reader; use PHPMailer\PHPMailer\PHPMailer; use Swoole\Database\PDOProxy; @@ -238,10 +239,13 @@ const METRIC_MESSAGES_TYPE_PROVIDER_FAILED = METRIC_MESSAGES . '.{type}.{provid const METRIC_SESSIONS = 'sessions'; const METRIC_DATABASES = 'databases'; const METRIC_COLLECTIONS = 'collections'; +const METRIC_DATABASES_STORAGE = 'databases.storage'; const METRIC_DATABASE_ID_COLLECTIONS = '{databaseInternalId}.collections'; +const METRIC_DATABASE_ID_STORAGE = '{databaseInternalId}.databases.storage'; const METRIC_DOCUMENTS = 'documents'; const METRIC_DATABASE_ID_DOCUMENTS = '{databaseInternalId}.documents'; const METRIC_DATABASE_ID_COLLECTION_ID_DOCUMENTS = '{databaseInternalId}.{collectionInternalId}.documents'; +const METRIC_DATABASE_ID_COLLECTION_ID_STORAGE = '{databaseInternalId}.{collectionInternalId}.databases.storage'; const METRIC_BUCKETS = 'buckets'; const METRIC_FILES = 'files'; const METRIC_FILES_STORAGE = 'files.storage'; @@ -1252,13 +1256,13 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons $user = new Document([]); } - if (APP_MODE_ADMIN === $mode) { - if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { - Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. - } else { - $user = new Document([]); - } - } + // if (APP_MODE_ADMIN === $mode) { + // if ($user->find('teamInternalId', $project->getAttribute('teamInternalId'), 'memberships')) { + // Authorization::setDefaultStatus(false); // Cancel security segmentation for admin users. + // } else { + // $user = new Document([]); + // } + // } $authJWT = $request->getHeader('x-appwrite-jwt', ''); @@ -1335,7 +1339,7 @@ App::setResource('console', function () { '$collection' => ID::custom('projects'), 'description' => 'Appwrite core engine', 'logo' => '', - 'teamId' => -1, + 'teamId' => null, 'webhooks' => [], 'keys' => [], 'platforms' => [ @@ -1763,3 +1767,35 @@ App::setResource('requestTimestamp', function ($request) { App::setResource('plan', function (array $plan = []) { return []; }); + + +App::setResource('team', function (Document $project, Database $dbForConsole, App $utopia, Request $request) { + $teamInternalId = ''; + if ($project->getId() !== 'console') { + $teamInternalId = $project->getAttribute('teamInternalId', ''); + } else { + $route = $utopia->match($request); + $path = $route->getPath(); + if (str_starts_with($path, '/v1/projects/:projectId')) { + $uri = $request->getURI(); + $pid = explode('/', $uri)[3]; + $p = Authorization::skip(fn () => $dbForConsole->getDocument('projects', $pid)); + $teamInternalId = $p->getAttribute('teamInternalId', ''); + } elseif ($path === '/v1/projects') { + $teamId = $request->getParam('teamId', ''); + $team = Authorization::skip(fn () => $dbForConsole->getDocument('teams', $teamId)); + return $team; + } + } + + $team = Authorization::skip(function () use ($dbForConsole, $teamInternalId) { + return $dbForConsole->findOne('teams', [ + Query::equal('$internalId', [$teamInternalId]), + ]); + }); + + if (!$team) { + $team = new Document([]); + } + return $team; +}, ['project', 'dbForConsole', 'utopia', 'request']); diff --git a/app/realtime.php b/app/realtime.php index b8fdb2cf21..39e00f7dda 100644 --- a/app/realtime.php +++ b/app/realtime.php @@ -184,8 +184,12 @@ $logError = function (Throwable $error, string $action) use ($register) { $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - $responseCode = $logger->addLog($log); - Console::info('Realtime log pushed with status code: ' . $responseCode); + try { + $responseCode = $logger->addLog($log); + Console::info('Error log pushed with status code: ' . $responseCode); + } catch (Throwable $th) { + Console::error('Error pushing log: ' . $th->getMessage()); + } } Console::error('[Error] Type: ' . get_class($error)); diff --git a/app/views/install/compose.phtml b/app/views/install/compose.phtml index 8f83fed544..ad35135a6f 100644 --- a/app/views/install/compose.phtml +++ b/app/views/install/compose.phtml @@ -531,6 +531,8 @@ $image = $this->getParam('image', ''); - _APP_SMTP_USERNAME - _APP_SMTP_PASSWORD - _APP_LOGGING_CONFIG + - _APP_DOMAIN + - _APP_OPTIONS_FORCE_HTTPS appwrite-worker-messaging: image: /: diff --git a/app/worker.php b/app/worker.php index 9bcdae78e6..6078a0127c 100644 --- a/app/worker.php +++ b/app/worker.php @@ -346,8 +346,12 @@ $worker $isProduction = System::getEnv('_APP_ENV', 'development') === 'production'; $log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING); - $responseCode = $logger->addLog($log); - Console::info('Usage stats log pushed with status code: ' . $responseCode); + try { + $responseCode = $logger->addLog($log); + Console::info('Error log pushed with status code: ' . $responseCode); + } catch (Throwable $th) { + Console::error('Error pushing log: ' . $th->getMessage()); + } } Console::error('[Error] Type: ' . get_class($error)); diff --git a/composer.json b/composer.json index 91ff1eeb92..be53cb0540 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "utopia-php/cache": "0.10.*", "utopia-php/cli": "0.15.*", "utopia-php/config": "0.2.*", - "utopia-php/database": "0.53.*", + "utopia-php/database": "0.53.5", "utopia-php/domains": "0.5.*", "utopia-php/dsn": "0.2.1", "utopia-php/framework": "0.33.*", diff --git a/composer.lock b/composer.lock index 147800df32..7611fb7032 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "b6820da26239716cf14a445697902a03", + "content-hash": "1981099f3c47f5536298222ba9b0b994", "packages": [ { "name": "adhocore/jwt", @@ -1723,16 +1723,16 @@ }, { "name": "utopia-php/database", - "version": "0.53.4", + "version": "0.53.5", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "36a0e89d983afc1368635282e04fa762220a1d2a" + "reference": "689ba22063bf46def385da8695ba7a921e81e38d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/36a0e89d983afc1368635282e04fa762220a1d2a", - "reference": "36a0e89d983afc1368635282e04fa762220a1d2a", + "url": "https://api.github.com/repos/utopia-php/database/zipball/689ba22063bf46def385da8695ba7a921e81e38d", + "reference": "689ba22063bf46def385da8695ba7a921e81e38d", "shasum": "" }, "require": { @@ -1773,9 +1773,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/0.53.4" + "source": "https://github.com/utopia-php/database/tree/0.53.5" }, - "time": "2024-09-10T10:19:57+00:00" + "time": "2024-09-24T08:43:10+00:00" }, { "name": "utopia-php/domains", @@ -2069,16 +2069,16 @@ }, { "name": "utopia-php/logger", - "version": "0.6.0", + "version": "0.6.1", "source": { "type": "git", "url": "https://github.com/utopia-php/logger.git", - "reference": "a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9" + "reference": "7e8ff512c6f04577aba1df67c7b9628971946f9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/logger/zipball/a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9", - "reference": "a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9", + "url": "https://api.github.com/repos/utopia-php/logger/zipball/7e8ff512c6f04577aba1df67c7b9628971946f9c", + "reference": "7e8ff512c6f04577aba1df67c7b9628971946f9c", "shasum": "" }, "require": { @@ -2117,9 +2117,9 @@ ], "support": { "issues": "https://github.com/utopia-php/logger/issues", - "source": "https://github.com/utopia-php/logger/tree/0.6.0" + "source": "https://github.com/utopia-php/logger/tree/0.6.1" }, - "time": "2024-05-23T13:37:54+00:00" + "time": "2024-09-20T14:02:12+00:00" }, { "name": "utopia-php/messaging", @@ -2174,16 +2174,16 @@ }, { "name": "utopia-php/migration", - "version": "0.5.2", + "version": "0.5.3", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "f18d44d4459f78c292dac9edde856fd156fe497a" + "reference": "b30e7834da69e25084b0c8e9ba29e4a7b54c6eb6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/f18d44d4459f78c292dac9edde856fd156fe497a", - "reference": "f18d44d4459f78c292dac9edde856fd156fe497a", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/b30e7834da69e25084b0c8e9ba29e4a7b54c6eb6", + "reference": "b30e7834da69e25084b0c8e9ba29e4a7b54c6eb6", "shasum": "" }, "require": { @@ -2216,9 +2216,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/0.5.2" + "source": "https://github.com/utopia-php/migration/tree/0.5.3" }, - "time": "2024-07-22T09:27:07+00:00" + "time": "2024-09-10T10:45:18+00:00" }, { "name": "utopia-php/mongo", @@ -2993,16 +2993,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.39.21", + "version": "0.39.22", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "9754b190d33aaad56fdb8defc94f90248184c5ac" + "reference": "bdbb1607527550e67283ff0533522d1410c2c0df" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9754b190d33aaad56fdb8defc94f90248184c5ac", - "reference": "9754b190d33aaad56fdb8defc94f90248184c5ac", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/bdbb1607527550e67283ff0533522d1410c2c0df", + "reference": "bdbb1607527550e67283ff0533522d1410c2c0df", "shasum": "" }, "require": { @@ -3038,9 +3038,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/0.39.21" + "source": "https://github.com/appwrite/sdk-generator/tree/0.39.22" }, - "time": "2024-09-10T08:49:29+00:00" + "time": "2024-10-01T16:16:26+00:00" }, { "name": "doctrine/annotations", @@ -3314,16 +3314,16 @@ }, { "name": "laravel/pint", - "version": "v1.17.3", + "version": "v1.18.1", "source": { "type": "git", "url": "https://github.com/laravel/pint.git", - "reference": "9d77be916e145864f10788bb94531d03e1f7b482" + "reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/pint/zipball/9d77be916e145864f10788bb94531d03e1f7b482", - "reference": "9d77be916e145864f10788bb94531d03e1f7b482", + "url": "https://api.github.com/repos/laravel/pint/zipball/35c00c05ec43e6b46d295efc0f4386ceb30d50d9", + "reference": "35c00c05ec43e6b46d295efc0f4386ceb30d50d9", "shasum": "" }, "require": { @@ -3376,7 +3376,7 @@ "issues": "https://github.com/laravel/pint/issues", "source": "https://github.com/laravel/pint" }, - "time": "2024-09-03T15:00:28+00:00" + "time": "2024-09-24T17:22:50+00:00" }, { "name": "matthiasmullie/minify", @@ -3564,16 +3564,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.2.0", + "version": "v5.3.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb" + "reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb", - "reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3abf7425cd284141dc5d8d14a9ee444de3345d1a", + "reference": "3abf7425cd284141dc5d8d14a9ee444de3345d1a", "shasum": "" }, "require": { @@ -3616,9 +3616,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.2.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.0" }, - "time": "2024-09-15T16:40:33+00:00" + "time": "2024-09-29T13:56:26+00:00" }, { "name": "phar-io/manifest", @@ -4185,16 +4185,16 @@ }, { "name": "phpstan/phpdoc-parser", - "version": "1.30.1", + "version": "1.32.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "51b95ec8670af41009e2b2b56873bad96682413e" + "reference": "6ca22b154efdd9e3c68c56f5d94670920a1c19a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/51b95ec8670af41009e2b2b56873bad96682413e", - "reference": "51b95ec8670af41009e2b2b56873bad96682413e", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6ca22b154efdd9e3c68c56f5d94670920a1c19a4", + "reference": "6ca22b154efdd9e3c68c56f5d94670920a1c19a4", "shasum": "" }, "require": { @@ -4226,9 +4226,9 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.30.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.32.0" }, - "time": "2024-09-07T20:13:05+00:00" + "time": "2024-09-26T07:23:32+00:00" }, { "name": "phpunit/php-code-coverage", @@ -5865,16 +5865,16 @@ }, { "name": "symfony/console", - "version": "v7.1.4", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "1eed7af6961d763e7832e874d7f9b21c3ea9c111" + "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/1eed7af6961d763e7832e874d7f9b21c3ea9c111", - "reference": "1eed7af6961d763e7832e874d7f9b21c3ea9c111", + "url": "https://api.github.com/repos/symfony/console/zipball/0fa539d12b3ccf068a722bbbffa07ca7079af9ee", + "reference": "0fa539d12b3ccf068a722bbbffa07ca7079af9ee", "shasum": "" }, "require": { @@ -5938,7 +5938,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.1.4" + "source": "https://github.com/symfony/console/tree/v7.1.5" }, "funding": [ { @@ -5954,7 +5954,7 @@ "type": "tidelift" } ], - "time": "2024-08-15T22:48:53+00:00" + "time": "2024-09-20T08:28:38+00:00" }, { "name": "symfony/deprecation-contracts", @@ -6025,16 +6025,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.1.2", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "92a91985250c251de9b947a14bb2c9390b1a562c" + "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/92a91985250c251de9b947a14bb2c9390b1a562c", - "reference": "92a91985250c251de9b947a14bb2c9390b1a562c", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/61fe0566189bf32e8cfee78335d8776f64a66f5a", + "reference": "61fe0566189bf32e8cfee78335d8776f64a66f5a", "shasum": "" }, "require": { @@ -6071,7 +6071,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.1.2" + "source": "https://github.com/symfony/filesystem/tree/v7.1.5" }, "funding": [ { @@ -6087,7 +6087,7 @@ "type": "tidelift" } ], - "time": "2024-06-28T10:03:55+00:00" + "time": "2024-09-17T09:16:35+00:00" }, { "name": "symfony/finder", @@ -6536,16 +6536,16 @@ }, { "name": "symfony/process", - "version": "v7.1.3", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca" + "reference": "5c03ee6369281177f07f7c68252a280beccba847" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/7f2f542c668ad6c313dc4a5e9c3321f733197eca", - "reference": "7f2f542c668ad6c313dc4a5e9c3321f733197eca", + "url": "https://api.github.com/repos/symfony/process/zipball/5c03ee6369281177f07f7c68252a280beccba847", + "reference": "5c03ee6369281177f07f7c68252a280beccba847", "shasum": "" }, "require": { @@ -6577,7 +6577,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.1.3" + "source": "https://github.com/symfony/process/tree/v7.1.5" }, "funding": [ { @@ -6593,7 +6593,7 @@ "type": "tidelift" } ], - "time": "2024-07-26T12:44:47+00:00" + "time": "2024-09-19T21:48:23+00:00" }, { "name": "symfony/service-contracts", @@ -6680,16 +6680,16 @@ }, { "name": "symfony/string", - "version": "v7.1.4", + "version": "v7.1.5", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "6cd670a6d968eaeb1c77c2e76091c45c56bc367b" + "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/6cd670a6d968eaeb1c77c2e76091c45c56bc367b", - "reference": "6cd670a6d968eaeb1c77c2e76091c45c56bc367b", + "url": "https://api.github.com/repos/symfony/string/zipball/d66f9c343fa894ec2037cc928381df90a7ad4306", + "reference": "d66f9c343fa894ec2037cc928381df90a7ad4306", "shasum": "" }, "require": { @@ -6747,7 +6747,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.1.4" + "source": "https://github.com/symfony/string/tree/v7.1.5" }, "funding": [ { @@ -6763,7 +6763,7 @@ "type": "tidelift" } ], - "time": "2024-08-12T09:59:40+00:00" + "time": "2024-09-20T08:28:38+00:00" }, { "name": "textalk/websocket", diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index c70d9ca11b..4f8953a270 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -503,7 +503,18 @@ class Deletes extends Action foreach ($collections as $collection) { if ($dsn->getHost() !== System::getEnv('_APP_DATABASE_SHARED_TABLES', '') || !\in_array($collection->getId(), $projectCollectionIds)) { - $dbForProject->deleteCollection($collection->getId()); + try { + $dbForProject->deleteCollection($collection->getId()); + } catch (Throwable $e) { + Console::error('Error deleting '.$collection->getId().' '.$e->getMessage()); + + /** + * Ignore junction tables; + */ + if (!preg_match('/^_\d+_\d+$/', $collection->getId())) { + throw $e; + } + } } else { $this->deleteByGroup($collection->getId(), [], database: $dbForProject); } diff --git a/src/Appwrite/Platform/Workers/UsageDump.php b/src/Appwrite/Platform/Workers/UsageDump.php index b7097dbb04..b5480e1dec 100644 --- a/src/Appwrite/Platform/Workers/UsageDump.php +++ b/src/Appwrite/Platform/Workers/UsageDump.php @@ -4,6 +4,7 @@ namespace Appwrite\Platform\Workers; use Appwrite\Extend\Exception; use Utopia\CLI\Console; +use Utopia\Database\Database; use Utopia\Database\DateTime; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate; @@ -11,6 +12,10 @@ use Utopia\Platform\Action; use Utopia\Queue\Message; use Utopia\System\System; +const METRIC_COLLECTION_LEVEL_STORAGE = 4; +const METRIC_DATABASE_LEVEL_STORAGE = 3; +const METRIC_PROJECT_LEVEL_STORAGE = 2; + class UsageDump extends Action { protected array $stats = []; @@ -70,6 +75,15 @@ class UsageDump extends Action continue; } + if (str_contains($key, METRIC_DATABASES_STORAGE)) { + try { + $this->handleDatabaseStorage($key, $dbForProject); + } catch (\Exception $e) { + console::error('[' . DateTime::now() . '] failed to calculate database storage for key [' . $key . '] ' . $e->getMessage()); + } + continue; + } + foreach ($this->periods as $period => $format) { $time = 'inf' === $period ? null : date($format, time()); $id = \md5("{$time}_{$period}_{$key}"); @@ -107,4 +121,162 @@ class UsageDump extends Action } } } + + private function handleDatabaseStorage(string $key, Database $dbForProject): void + { + $data = explode('.', $key); + $start = microtime(true); + + $updateMetric = function (Database $dbForProject, int $value, string $key, string $period, string|null $time) { + $id = \md5("{$time}_{$period}_{$key}"); + + try { + $dbForProject->createDocument('stats', new Document([ + '$id' => $id, + 'period' => $period, + 'time' => $time, + 'metric' => $key, + 'value' => $value, + 'region' => System::getEnv('_APP_REGION', 'default'), + ])); + } catch (Duplicate $th) { + if ($value < 0) { + $dbForProject->decreaseDocumentAttribute( + 'stats', + $id, + 'value', + abs($value) + ); + } else { + $dbForProject->increaseDocumentAttribute( + 'stats', + $id, + 'value', + $value + ); + } + } + }; + + foreach ($this->periods as $period => $format) { + $time = 'inf' === $period ? null : date($format, time()); + $id = \md5("{$time}_{$period}_{$key}"); + + $value = 0; + $previousValue = 0; + try { + $previousValue = ($dbForProject->getDocument('stats', $id))->getAttribute('value', 0); + } catch (\Exception $e) { + // No previous value + } + + switch (count($data)) { + // Collection Level + case METRIC_COLLECTION_LEVEL_STORAGE: + Console::log('[' . DateTime::now() . '] Collection Level Storage Calculation [' . $key . ']'); + $databaseInternalId = $data[0]; + $collectionInternalId = $data[1]; + + try { + $value = $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collectionInternalId); + } catch (\Exception $e) { + // Collection not found + if ($e->getMessage() !== 'Collection not found') { + throw $e; + } + } + + // Compare with previous value + $diff = $value - $previousValue; + + if ($diff === 0) { + break; + } + + // Update Collection + $updateMetric($dbForProject, $diff, $key, $period, $time); + + // Update Database + $databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE); + $updateMetric($dbForProject, $diff, $databaseKey, $period, $time); + + // Update Project + $projectKey = METRIC_DATABASES_STORAGE; + $updateMetric($dbForProject, $diff, $projectKey, $period, $time); + break; + // Database Level + case METRIC_DATABASE_LEVEL_STORAGE: + Console::log('[' . DateTime::now() . '] Database Level Storage Calculation [' . $key . ']'); + $databaseInternalId = $data[0]; + + $collections = []; + try { + $collections = $dbForProject->find('database_' . $databaseInternalId); + } catch (\Exception $e) { + // Database not found + if ($e->getMessage() !== 'Collection not found') { + throw $e; + } + } + + foreach ($collections as $collection) { + try { + $value += $dbForProject->getSizeOfCollection('database_' . $databaseInternalId . '_collection_' . $collection->getInternalId()); + } catch (\Exception $e) { + // Collection not found + if ($e->getMessage() !== 'Collection not found') { + throw $e; + } + } + } + + $diff = $value - $previousValue; + + if ($diff === 0) { + break; + } + + // Update Database + $databaseKey = str_replace(['{databaseInternalId}'], [$data[0]], METRIC_DATABASE_ID_STORAGE); + $updateMetric($dbForProject, $diff, $databaseKey, $period, $time); + + // Update Project + $projectKey = METRIC_DATABASES_STORAGE; + $updateMetric($dbForProject, $diff, $projectKey, $period, $time); + break; + // Project Level + case METRIC_PROJECT_LEVEL_STORAGE: + Console::log('[' . DateTime::now() . '] Project Level Storage Calculation [' . $key . ']'); + // Get all project databases + $databases = $dbForProject->find('database'); + + // Recalculate all databases + foreach ($databases as $database) { + $collections = $dbForProject->find('database_' . $database->getInternalId()); + + foreach ($collections as $collection) { + try { + $value += $dbForProject->getSizeOfCollection('database_' . $database->getInternalId() . '_collection_' . $collection->getInternalId()); + } catch (\Exception $e) { + // Collection not found + if ($e->getMessage() !== 'Collection not found') { + throw $e; + } + } + } + } + + $diff = $value - $previousValue; + + // Update Project + $projectKey = METRIC_DATABASES_STORAGE; + $updateMetric($dbForProject, $diff, $projectKey, $period, $time); + break; + } + } + + $end = microtime(true); + + console::log('[' . DateTime::now() . '] DB Storage Calculation [' . $key . '] took ' . (($end - $start) * 1000) . ' milliseconds'); + } } diff --git a/src/Appwrite/Utopia/Response.php b/src/Appwrite/Utopia/Response.php index a2c07d34b0..6cc2639f51 100644 --- a/src/Appwrite/Utopia/Response.php +++ b/src/Appwrite/Utopia/Response.php @@ -625,6 +625,11 @@ class Response extends SwooleResponse } } + if (!$data->isSet($key) && !$rule['required']) { // set output key null if data key is not set and required is false + $output[$key] = null; + continue; + } + if ($rule['array']) { if (!is_array($data[$key])) { throw new Exception($key . ' must be an array of type ' . $rule['type']); diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php index d4733f2568..eb985baabb 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabase.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabase.php @@ -28,6 +28,12 @@ class UsageDatabase extends Model 'default' => 0, 'example' => 0, ]) + ->addRule('storageTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of total storage used in bytes.', + 'default' => 0, + 'example' => 0, + ]) ->addRule('collections', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated number of collections per period.', @@ -42,6 +48,13 @@ class UsageDatabase extends Model 'example' => [], 'array' => true ]) + ->addRule('storage', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'Aggregated storage used in bytes per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php index f775f9489d..e0abba8ab8 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageDatabases.php +++ b/src/Appwrite/Utopia/Response/Model/UsageDatabases.php @@ -34,6 +34,12 @@ class UsageDatabases extends Model 'default' => 0, 'example' => 0, ]) + ->addRule('storageTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated number of total databases storage in bytes.', + 'default' => 0, + 'example' => 0, + ]) ->addRule('databases', [ 'type' => Response::MODEL_METRIC, 'description' => 'Aggregated number of databases per period.', @@ -55,6 +61,13 @@ class UsageDatabases extends Model 'example' => [], 'array' => true ]) + ->addRule('storage', [ + 'type' => Response::MODEL_METRIC, + 'description' => 'An array of the aggregated number of databases storage in bytes per period.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ; } diff --git a/src/Appwrite/Utopia/Response/Model/UsageProject.php b/src/Appwrite/Utopia/Response/Model/UsageProject.php index 2703691238..17d9271f04 100644 --- a/src/Appwrite/Utopia/Response/Model/UsageProject.php +++ b/src/Appwrite/Utopia/Response/Model/UsageProject.php @@ -28,6 +28,12 @@ class UsageProject extends Model 'default' => 0, 'example' => 0, ]) + ->addRule('databasesStorageTotal', [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Total aggregated sum of databases storage size (in bytes).', + 'default' => 0, + 'example' => 0, + ]) ->addRule('usersTotal', [ 'type' => self::TYPE_INTEGER, 'description' => 'Total aggregated number of users.', @@ -118,6 +124,13 @@ class UsageProject extends Model 'example' => [], 'array' => true ]) + ->addRule('databasesStorageBreakdown', [ + 'type' => Response::MODEL_METRIC_BREAKDOWN, + 'description' => 'An array of the aggregated breakdown of storage usage by databases.', + 'default' => [], + 'example' => [], + 'array' => true + ]) ->addRule('executionsMbSecondsBreakdown', [ 'type' => Response::MODEL_METRIC_BREAKDOWN, 'description' => 'Aggregated breakdown in totals of execution mbSeconds by functions.', diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 69460aa4cd..db75a5db45 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -143,7 +143,7 @@ class UsageTest extends Scope ); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(20, count($response['body'])); + $this->assertEquals(22, count($response['body'])); $this->validateDates($response['body']['network']); $this->validateDates($response['body']['requests']); $this->validateDates($response['body']['users']); @@ -324,7 +324,7 @@ class UsageTest extends Scope ] ); - $this->assertEquals(20, count($response['body'])); + $this->assertEquals(22, count($response['body'])); $this->assertEquals(1, count($response['body']['requests'])); $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); $this->validateDates($response['body']['requests']); @@ -545,7 +545,7 @@ class UsageTest extends Scope ] ); - $this->assertEquals(20, count($response['body'])); + $this->assertEquals(22, count($response['body'])); $this->assertEquals(1, count($response['body']['requests'])); $this->assertEquals(1, count($response['body']['network'])); $this->assertEquals($requestsTotal, $response['body']['requests'][array_key_last($response['body']['requests'])]['value']); @@ -590,6 +590,260 @@ class UsageTest extends Scope return $data; } + public function testDatabaseStoragePrepare(): array + { + $response = $this->client->call( + Client::METHOD_POST, + '/databases', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + [ + 'databaseId' => 'unique()', + 'name' => 'dbStorageStats', + ] + ); + + $this->assertNotEmpty($response['body']['$id']); + $databaseId = $response['body']['$id']; + + $response = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/collections', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + [ + 'collectionId' => 'unique()', + 'name' => 'collectionStorageStats', + 'documentSecurity' => false, + 'permissions' => [ + Permission::read(Role::any()), + Permission::create(Role::any()), + Permission::update(Role::any()), + Permission::delete(Role::any()), + ], + ] + ); + + $this->assertNotEmpty($response['body']['$id']); + $collectionId = $response['body']['$id']; + + $response = $this->client->call( + Client::METHOD_POST, + '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes' . '/string', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'] + ], $this->getHeaders()), + [ + 'key' => 'data', + 'size' => 100000, + 'required' => true, + ] + ); + + return [ + 'databaseId' => $databaseId, + 'collectionId' => $collectionId, + ]; + } + + // /** @depends testDatabaseStoragePrepare */ + // #[Retry(count: 1)] + // public function testDatabaseStorageStatsCreateDocument(array $data): array + // { + // $databaseId = $data['databaseId']; + // $collectionId = $data['collectionId']; + + // $originalProjectMetrics = $this->client->call( + // Client::METHOD_GET, + // '/project/usage', + // $this->getConsoleHeaders(), + // [ + // 'period' => '1d', + // 'startDate' => self::getToday(), + // 'endDate' => self::getTomorrow(), + // ] + // ); + + // $this->assertEquals(200, $originalProjectMetrics['headers']['status-code']); + // $this->assertArrayHasKey('databasesStorageTotal', $originalProjectMetrics['body']); + + // $originalProjectMetrics = $originalProjectMetrics['body']; + + // $originalDatabaseMetrics = $this->client->call( + // Client::METHOD_GET, + // '/databases/' . $databaseId . '/usage?range=30d', + // $this->getConsoleHeaders() + // ); + + // $this->assertEquals(200, $originalDatabaseMetrics['headers']['status-code']); + // $this->assertArrayHasKey('storageTotal', $originalDatabaseMetrics['body']); + // $originalDatabaseMetrics = $originalDatabaseMetrics['body']; + + // // Create documents + // for ($i = 0; $i < 100; $i++) { + // $response = $this->client->call( + // Client::METHOD_POST, + // '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', + // array_merge([ + // 'content-type' => 'application/json', + // 'x-appwrite-project' => $this->getProject()['$id'] + // ], $this->getHeaders()), + // [ + // 'documentId' => 'unique()', + // 'data' => ['data' => str_repeat('a', 10000)], + // ] + // ); + + // $this->assertEquals(201, $response['headers']['status-code']); + // } + + // sleep(self::WAIT); + + // for ($i = 0; $i < 3; $i++) { + // try { + // $newProjectMetrics = $this->client->call( + // Client::METHOD_GET, + // '/project/usage', + // $this->getConsoleHeaders(), + // [ + // 'period' => '1d', + // 'startDate' => self::getToday(), + // 'endDate' => self::getTomorrow(), + // ] + // ); + + // $this->assertEquals(200, $newProjectMetrics['headers']['status-code']); + // $this->assertArrayHasKey('databasesStorageTotal', $newProjectMetrics['body']); + // $this->assertGreaterThan($originalProjectMetrics['databasesStorageTotal'], $newProjectMetrics['body']['databasesStorageTotal']); + + // $newProjectMetrics = $newProjectMetrics['body']; + + // $newDatabaseMetrics = $this->client->call( + // Client::METHOD_GET, + // '/databases/' . $databaseId . '/usage?range=30d', + // $this->getConsoleHeaders() + // ); + + // $this->assertEquals(200, $newDatabaseMetrics['headers']['status-code']); + // $this->assertArrayHasKey('storageTotal', $newDatabaseMetrics['body']); + // $this->assertGreaterThan($originalDatabaseMetrics['storageTotal'], $newDatabaseMetrics['body']['storageTotal']); + + // $newDatabaseMetrics = $newDatabaseMetrics['body']; + + // return [ + // 'databaseId' => $databaseId, + // 'collectionId' => $collectionId, + // 'currentProjectMetrics' => $newProjectMetrics, + // 'currentDatabaseMetrics' => $newDatabaseMetrics, + // ]; + // } catch (ExpectationFailedException $e) { + // if ($i === 2) { + // throw $e; + // } + // sleep(self::WAIT); + // continue; + // } + // } + // } + + // /** @depends testDatabaseStorageStatsCreateDocument */ + // #[Retry(count: 1)] + // public function testDatabaseStorageStatsDeleteDocument(array $data): array + // { + // $databaseId = $data['databaseId']; + // $collectionId = $data['collectionId']; + // $currentProjectMetrics = $data['currentProjectMetrics']; + // $currentDatabaseMetrics = $data['currentDatabaseMetrics']; + + // $documents = $this->client->call( + // Client::METHOD_GET, + // '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents', + // array_merge([ + // 'x-appwrite-project' => $this->getProject()['$id'] + // ], $this->getHeaders()), + // [ + // 'queries' => [ + // Query::limit(50)->toString() + // ] + // ] + // ); + + // foreach ($documents['body']['documents'] as $document) { + // $response = $this->client->call( + // Client::METHOD_DELETE, + // '/databases/' . $databaseId . '/collections/' . $collectionId . '/documents/' . $document['$id'], + // array_merge([ + // 'x-appwrite-project' => $this->getProject()['$id'] + // ], $this->getHeaders()) + // ); + + // $this->assertEquals(204, $response['headers']['status-code']); + // } + + // sleep(self::WAIT); + + // for ($i = 0; $i < 3; $i++) { + // try { + // $newProjectMetrics = $this->client->call( + // Client::METHOD_GET, + // '/project/usage', + // $this->getConsoleHeaders(), + // [ + // 'period' => '1d', + // 'startDate' => self::getToday(), + // 'endDate' => self::getTomorrow(), + // ] + // ); + + // $this->assertEquals(200, $newProjectMetrics['headers']['status-code']); + // $this->assertArrayHasKey('databasesStorageTotal', $newProjectMetrics['body']); + // $this->assertLessThan($currentProjectMetrics['databasesStorageTotal'], $newProjectMetrics['body']['databasesStorageTotal']); + + // $newProjectMetrics = $newProjectMetrics['body']; + + // $newDatabaseMetrics = $this->client->call( + // Client::METHOD_GET, + // '/databases/' . $databaseId . '/usage?range=30d', + // $this->getConsoleHeaders() + // ); + + // $this->assertEquals(200, $newDatabaseMetrics['headers']['status-code']); + // $this->assertArrayHasKey('storageTotal', $newDatabaseMetrics['body']); + // $this->assertLessThan($currentDatabaseMetrics['storageTotal'], $newDatabaseMetrics['body']['storageTotal']); + + // $newDatabaseMetrics = $newDatabaseMetrics['body']; + + // return [ + // 'databaseId' => $databaseId, + // 'collectionId' => $collectionId, + // 'currentProjectMetrics' => $newProjectMetrics, + // 'currentDatabaseMetrics' => $newDatabaseMetrics, + // ]; + // } catch (ExpectationFailedException $e) { + // if ($i === 2) { + // throw $e; + // } + // sleep(self::WAIT); + // continue; + // } + // } + + // $newProjectMetrics = $this->client->call( + // Client::METHOD_GET, + // '/project/usage', + // $this->getConsoleHeaders(), + // [ + // 'period' => '1d', + // 'startDate' => self::getToday(), + // 'endDate' => self::getTomorrow(), + // ] + // ); + // } /** @depends testDatabaseStats */ public function testPrepareFunctionsStats(array $data): array diff --git a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php index ca77cf2581..96bb0b5609 100644 --- a/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php +++ b/tests/e2e/Services/Databases/DatabasesConsoleClientTest.php @@ -224,7 +224,7 @@ class DatabasesConsoleClientTest extends Scope ]); $this->assertEquals(200, $response['headers']['status-code']); - $this->assertEquals(5, count($response['body'])); + $this->assertEquals(7, count($response['body'])); $this->assertEquals('24h', $response['body']['range']); $this->assertIsNumeric($response['body']['documentsTotal']); $this->assertIsNumeric($response['body']['collectionsTotal']); diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index c41d861f1d..05893be3a8 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -101,7 +101,7 @@ class ProjectsConsoleClientTest extends Scope 'region' => 'default' ]); - $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals(401, $response['headers']['status-code']); return [ 'projectId' => $projectId, @@ -546,7 +546,7 @@ class ProjectsConsoleClientTest extends Scope 'name' => '', ]); - $this->assertEquals(400, $response['headers']['status-code']); + $this->assertEquals(401, $response['headers']['status-code']); return ['projectId' => $projectId]; } diff --git a/tests/e2e/Services/Teams/TeamsBaseClient.php b/tests/e2e/Services/Teams/TeamsBaseClient.php index 03dffac6aa..8a1fed028e 100644 --- a/tests/e2e/Services/Teams/TeamsBaseClient.php +++ b/tests/e2e/Services/Teams/TeamsBaseClient.php @@ -161,7 +161,7 @@ trait TeamsBaseClient $this->assertNotEmpty($response['body']['userEmail']); $this->assertNotEmpty($response['body']['teamId']); $this->assertNotEmpty($response['body']['teamName']); - $this->assertCount(2, $response['body']['roles']); + $this->assertCount(1, $response['body']['roles']); $this->assertEquals(false, (new DatetimeValidator())->isValid($response['body']['joined'])); // is null in DB $this->assertEquals(false, $response['body']['confirm']); @@ -203,7 +203,7 @@ trait TeamsBaseClient ], $this->getHeaders()), [ 'email' => $email, 'name' => $name, - 'roles' => ['admin', 'editor'], + 'roles' => ['developer'], 'url' => 'http://localhost:5000/join-us#title' ]); @@ -214,7 +214,7 @@ trait TeamsBaseClient $this->assertEquals($email, $response['body']['userEmail']); $this->assertNotEmpty($response['body']['teamId']); $this->assertNotEmpty($response['body']['teamName']); - $this->assertCount(2, $response['body']['roles']); + $this->assertCount(1, $response['body']['roles']); $this->assertEquals(false, (new DatetimeValidator())->isValid($response['body']['joined'])); // is null in DB $this->assertEquals(false, $response['body']['confirm']); @@ -255,7 +255,7 @@ trait TeamsBaseClient 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'userId' => 'abcdefdg', - 'roles' => ['admin', 'editor'], + 'roles' => ['developer'], 'url' => 'http://localhost:5000/join-us#title' ]); @@ -270,7 +270,7 @@ trait TeamsBaseClient 'x-appwrite-project' => $this->getProject()['$id'], ], $this->getHeaders()), [ 'userId' => $userId, - 'roles' => ['admin', 'editor'], + 'roles' => ['developer'], 'url' => 'http://localhost:5000/join-us#title' ]); @@ -281,7 +281,7 @@ trait TeamsBaseClient $this->assertEquals($secondEmail, $response['body']['userEmail']); $this->assertNotEmpty($response['body']['teamId']); $this->assertNotEmpty($response['body']['teamName']); - $this->assertCount(2, $response['body']['roles']); + $this->assertCount(1, $response['body']['roles']); $this->assertEquals(false, (new DateTimeValidator())->isValid($response['body']['joined'])); // is null in DB $this->assertEquals(false, $response['body']['confirm']); @@ -301,7 +301,7 @@ trait TeamsBaseClient ], $this->getHeaders()), [ 'email' => $email, 'name' => 'Friend User', - 'roles' => ['admin', 'editor'], + 'roles' => ['developer'], 'url' => 'http://localhost:5000/join-us#title' ]); @@ -313,7 +313,7 @@ trait TeamsBaseClient ], $this->getHeaders()), [ 'email' => 'dasdkaskdjaskdjasjkd', 'name' => $name, - 'roles' => ['admin', 'editor'], + 'roles' => ['developer'], 'url' => 'http://localhost:5000/join-us#title' ]); @@ -337,7 +337,7 @@ trait TeamsBaseClient ], $this->getHeaders()), [ 'email' => $email, 'name' => $name, - 'roles' => ['admin', 'editor'], + 'roles' => ['developer'], 'url' => 'http://example.com/join-us#title' // bad url ]); @@ -413,7 +413,7 @@ trait TeamsBaseClient $this->assertNotEmpty($response['body']['$id']); $this->assertNotEmpty($response['body']['userId']); $this->assertNotEmpty($response['body']['teamId']); - $this->assertCount(2, $response['body']['roles']); + $this->assertCount(1, $response['body']['roles']); $this->assertEquals(true, (new DatetimeValidator())->isValid($response['body']['joined'])); $this->assertEquals(true, $response['body']['confirm']); $session = $response['cookies']['a_session_' . $this->getProject()['$id']]; @@ -571,7 +571,7 @@ trait TeamsBaseClient /** * Test for SUCCESS */ - $roles = ['admin', 'editor', 'uncle']; + $roles = ['editor', 'uncle']; $response = $this->client->call(Client::METHOD_PATCH, '/teams/' . $teamUid . '/memberships/' . $membershipUid, array_merge([ 'origin' => 'http://localhost', 'content-type' => 'application/json', @@ -587,7 +587,6 @@ trait TeamsBaseClient $this->assertCount(count($roles), $response['body']['roles']); $this->assertEquals($roles[0], $response['body']['roles'][0]); $this->assertEquals($roles[1], $response['body']['roles'][1]); - $this->assertEquals($roles[2], $response['body']['roles'][2]); /** * Test for unknown team diff --git a/tests/e2e/Services/Teams/TeamsConsoleClientTest.php b/tests/e2e/Services/Teams/TeamsConsoleClientTest.php index 61d0f6a027..4b5ade7cbf 100644 --- a/tests/e2e/Services/Teams/TeamsConsoleClientTest.php +++ b/tests/e2e/Services/Teams/TeamsConsoleClientTest.php @@ -28,12 +28,13 @@ class TeamsConsoleClientTest extends Scope // Create a user account before we create a invite so we can check if the user has permissions when it shouldn't $user = $this->client->call(Client::METHOD_POST, '/account', [ 'content-type' => 'application/json', - 'x-appwrite-project' => 'console'], [ - 'userId' => 'unique()', - 'email' => $email, - 'password' => $password, - 'name' => $name, - ], false); + 'x-appwrite-project' => 'console' + ], [ + 'userId' => 'unique()', + 'email' => $email, + 'password' => $password, + 'name' => $name, + ], false); $this->assertEquals(201, $user['headers']['status-code']); @@ -46,7 +47,7 @@ class TeamsConsoleClientTest extends Scope ], $this->getHeaders()), [ 'email' => $email, 'name' => $name, - 'roles' => ['admin', 'editor'], + 'roles' => ['developer'], 'url' => 'http://localhost:5000/join-us#title' ]); @@ -76,4 +77,75 @@ class TeamsConsoleClientTest extends Scope return $data; } + + /** @depends testUpdateTeamMembership */ + public function testUpdateTeamMembershipRoles($data): array + { + $teamUid = $data['teamUid'] ?? ''; + $membershipUid = $data['membershipUid'] ?? ''; + $session = $data['session'] ?? ''; + + /** + * Test for SUCCESS + */ + $roles = ['developer']; + $response = $this->client->call(Client::METHOD_PATCH, '/teams/' . $teamUid . '/memberships/' . $membershipUid, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'roles' => $roles + ]); + + $this->assertEquals(200, $response['headers']['status-code']); + $this->assertNotEmpty($response['body']['$id']); + $this->assertNotEmpty($response['body']['userId']); + $this->assertNotEmpty($response['body']['teamId']); + $this->assertCount(count($roles), $response['body']['roles']); + $this->assertEquals($roles[0], $response['body']['roles'][0]); + + /** + * Test for unknown team + */ + $response = $this->client->call(Client::METHOD_PATCH, '/teams/' . 'abc' . '/memberships/' . $membershipUid, array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'roles' => $roles + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + /** + * Test for unknown membership ID + */ + $response = $this->client->call(Client::METHOD_PATCH, '/teams/' . $teamUid . '/memberships/' . 'abc', array_merge([ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'roles' => $roles + ]); + + $this->assertEquals(404, $response['headers']['status-code']); + + + /** + * Test for when a user other than the owner tries to update membership + */ + $response = $this->client->call(Client::METHOD_PATCH, '/teams/' . $teamUid . '/memberships/' . $membershipUid, [ + 'origin' => 'http://localhost', + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + 'cookie' => 'a_session_' . $this->getProject()['$id'] . '=' . $session, + ], [ + 'roles' => $roles + ]); + + $this->assertEquals(401, $response['headers']['status-code']); + $this->assertEquals('User is not allowed to modify roles', $response['body']['message']); + + return $data; + } } diff --git a/tests/unit/Utopia/ResponseTest.php b/tests/unit/Utopia/ResponseTest.php index cd111ec22c..452119fafb 100644 --- a/tests/unit/Utopia/ResponseTest.php +++ b/tests/unit/Utopia/ResponseTest.php @@ -55,12 +55,32 @@ class ResponseTest extends TestCase 'integer' => 123, 'boolean' => true, 'hidden' => 'secret', + 'array' => [ + 'string 1', + 'string 2' + ], ]), 'single'); $this->assertArrayHasKey('string', $output); $this->assertArrayHasKey('integer', $output); $this->assertArrayHasKey('boolean', $output); $this->assertArrayNotHasKey('hidden', $output); + $this->assertIsArray($output['array']); + + // test optional array + $output = $this->response->output(new Document([ + 'string' => 'lorem ipsum', + 'integer' => 123, + 'boolean' => true, + 'hidden' => 'secret', + ]), 'single'); + $this->assertArrayHasKey('string', $output); + $this->assertArrayHasKey('integer', $output); + $this->assertArrayHasKey('boolean', $output); + $this->assertArrayNotHasKey('hidden', $output); + $this->assertArrayHasKey('array', $output); + $this->assertNull($output['array']); + } public function testResponseModelRequired(): void diff --git a/tests/unit/Utopia/Single.php b/tests/unit/Utopia/Single.php index 3bd09ef6da..b7f36d10a8 100644 --- a/tests/unit/Utopia/Single.php +++ b/tests/unit/Utopia/Single.php @@ -28,6 +28,11 @@ class Single extends Model 'type' => self::TYPE_STRING, 'default' => 'default', 'required' => true + ]) + ->addRule('array', [ + 'type' => self::TYPE_STRING, + 'required' => false, + 'array' => true, ]); }