diff --git a/app/config/platforms.php b/app/config/platforms.php index e54fb0a073..167e55651c 100644 --- a/app/config/platforms.php +++ b/app/config/platforms.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '16.0.2', + 'version' => '16.1.0', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -59,7 +59,7 @@ return [ [ 'key' => 'flutter', 'name' => 'Flutter', - 'version' => '13.0.0', + 'version' => '13.1.1', 'url' => 'https://github.com/appwrite/sdk-for-flutter', 'package' => 'https://pub.dev/packages/appwrite', 'enabled' => true, @@ -77,7 +77,7 @@ return [ [ 'key' => 'apple', 'name' => 'Apple', - 'version' => '7.0.0', + 'version' => '7.1.0', 'url' => 'https://github.com/appwrite/sdk-for-apple', 'package' => 'https://github.com/appwrite/sdk-for-apple', 'enabled' => true, @@ -112,7 +112,7 @@ return [ [ 'key' => 'android', 'name' => 'Android', - 'version' => '6.0.0', + 'version' => '6.1.0', 'url' => 'https://github.com/appwrite/sdk-for-android', 'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android', 'enabled' => true, @@ -134,7 +134,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.5.0', + 'version' => '0.6.0', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index d948a101f2..af7303c985 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.6.0", + "version": "1.6.1", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -5766,7 +5766,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 382, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -5851,7 +5851,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 386, + "weight": 380, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index fb01509ecd..6878909f64 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.6.0", + "version": "1.6.1", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -9488,7 +9488,8 @@ "bun-1.0", "bun-1.1", "go-1.23", - "static-1" + "static-1", + "flutter-3.24" ], "x-enum-name": null, "x-enum-keys": [] @@ -10145,7 +10146,8 @@ "bun-1.0", "bun-1.1", "go-1.23", - "static-1" + "static-1", + "flutter-3.24" ], "x-enum-name": null, "x-enum-keys": [] @@ -13673,7 +13675,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 390, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -13751,7 +13753,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 387, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -13897,7 +13899,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 394, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -14045,7 +14047,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 389, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -14202,7 +14204,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 396, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -14361,7 +14363,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 388, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -14472,7 +14474,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 395, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -14586,7 +14588,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 393, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -14641,7 +14643,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 397, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -14705,7 +14707,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 391, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -14782,7 +14784,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 392, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -14859,7 +14861,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 362, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -14937,7 +14939,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 361, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -15044,7 +15046,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 374, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -15154,7 +15156,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 360, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -15241,7 +15243,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 373, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -15331,7 +15333,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 352, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -15448,7 +15450,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 365, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -15568,7 +15570,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 355, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -15665,7 +15667,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 368, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -15765,7 +15767,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 353, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -15872,7 +15874,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 366, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -15982,7 +15984,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 354, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -16127,7 +16129,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 367, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -16274,7 +16276,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 356, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -16371,7 +16373,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 369, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -16471,7 +16473,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 357, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -16568,7 +16570,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 370, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -16668,7 +16670,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 358, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -16765,7 +16767,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 371, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -16865,7 +16867,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 359, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -16962,7 +16964,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 372, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -17062,7 +17064,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 364, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -17117,7 +17119,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 375, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -17181,7 +17183,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 363, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -17258,7 +17260,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 384, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -17335,7 +17337,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 377, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -17411,7 +17413,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 376, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -17496,7 +17498,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 379, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -17558,7 +17560,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 380, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -17637,7 +17639,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 381, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -17701,7 +17703,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 378, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -17778,7 +17780,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 383, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -17864,7 +17866,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 382, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -17956,7 +17958,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 385, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -18021,7 +18023,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 386, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -18098,7 +18100,7 @@ }, "x-appwrite": { "method": "list", - "weight": 339, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -18264,7 +18266,7 @@ }, "x-appwrite": { "method": "getAppwriteReport", - "weight": 341, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -18339,7 +18341,7 @@ }, "\/migrations\/firebase": { "post": { - "summary": "Migrate Firebase data (Service Account)", + "summary": "Migrate Firebase data", "operationId": "migrationsCreateFirebaseMigration", "tags": [ "migrations" @@ -18359,7 +18361,7 @@ }, "x-appwrite": { "method": "createFirebaseMigration", - "weight": 336, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -18415,177 +18417,6 @@ } } }, - "\/migrations\/firebase\/deauthorize": { - "get": { - "summary": "Revoke Appwrite's authorization to access Firebase projects", - "operationId": "migrationsDeleteFirebaseAuth", - "tags": [ - "migrations" - ], - "description": "", - "responses": { - "200": { - "description": "File" - } - }, - "x-appwrite": { - "method": "deleteFirebaseAuth", - "weight": 347, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "migrations\/delete-firebase-auth.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "migrations.write", - "platforms": [ - "console" - ], - "packaging": false, - "offline-model": "", - "offline-key": "", - "offline-response-key": "$id", - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [] - } - ] - } - }, - "\/migrations\/firebase\/oauth": { - "post": { - "summary": "Migrate Firebase data (OAuth)", - "operationId": "migrationsCreateFirebaseOAuthMigration", - "tags": [ - "migrations" - ], - "description": "", - "responses": { - "202": { - "description": "Migration", - "content": { - "application\/json": { - "schema": { - "$ref": "#\/components\/schemas\/migration" - } - } - } - } - }, - "x-appwrite": { - "method": "createFirebaseOAuthMigration", - "weight": 335, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "migrations\/create-firebase-o-auth-migration.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/migrations\/migration-firebase.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "migrations.write", - "platforms": [ - "console" - ], - "packaging": false, - "offline-model": "", - "offline-key": "", - "offline-response-key": "$id", - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [] - } - ], - "requestBody": { - "content": { - "application\/json": { - "schema": { - "type": "object", - "properties": { - "resources": { - "type": "array", - "description": "List of resources to migrate", - "x-example": null, - "items": { - "type": "string" - } - }, - "projectId": { - "type": "string", - "description": "Project ID of the Firebase Project", - "x-example": "" - } - }, - "required": [ - "resources", - "projectId" - ] - } - } - } - } - } - }, - "\/migrations\/firebase\/projects": { - "get": { - "summary": "List Firebase projects", - "operationId": "migrationsListFirebaseProjects", - "tags": [ - "migrations" - ], - "description": "", - "responses": { - "200": { - "description": "Migrations Firebase Projects List", - "content": { - "application\/json": { - "schema": { - "$ref": "#\/components\/schemas\/firebaseProjectList" - } - } - } - } - }, - "x-appwrite": { - "method": "listFirebaseProjects", - "weight": 346, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "migrations\/list-firebase-projects.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "migrations.read", - "platforms": [ - "console" - ], - "packaging": false, - "offline-model": "", - "offline-key": "", - "offline-response-key": "$id", - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [] - } - ] - } - }, "\/migrations\/firebase\/report": { "get": { "summary": "Generate a report on Firebase data", @@ -18608,7 +18439,7 @@ }, "x-appwrite": { "method": "getFirebaseReport", - "weight": 342, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -18660,80 +18491,6 @@ ] } }, - "\/migrations\/firebase\/report\/oauth": { - "get": { - "summary": "Generate a report on Firebase data using OAuth", - "operationId": "migrationsGetFirebaseReportOAuth", - "tags": [ - "migrations" - ], - "description": "", - "responses": { - "200": { - "description": "Migration Report", - "content": { - "application\/json": { - "schema": { - "$ref": "#\/components\/schemas\/migrationReport" - } - } - } - } - }, - "x-appwrite": { - "method": "getFirebaseReportOAuth", - "weight": 343, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "migrations\/get-firebase-report-o-auth.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/migrations\/migration-firebase-report.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "migrations.write", - "platforms": [ - "console" - ], - "packaging": false, - "offline-model": "", - "offline-key": "", - "offline-response-key": "$id", - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [] - } - ], - "parameters": [ - { - "name": "resources", - "description": "List of resources to migrate", - "required": true, - "schema": { - "type": "array", - "items": { - "type": "string" - } - }, - "in": "query" - }, - { - "name": "projectId", - "description": "Project ID", - "required": true, - "schema": { - "type": "string", - "x-example": "" - }, - "in": "query" - } - ] - } - }, "\/migrations\/nhost": { "post": { "summary": "Migrate NHost data", @@ -18756,7 +18513,7 @@ }, "x-appwrite": { "method": "createNHostMigration", - "weight": 338, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -18869,7 +18626,7 @@ }, "x-appwrite": { "method": "getNHostReport", - "weight": 349, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -19004,7 +18761,7 @@ }, "x-appwrite": { "method": "createSupabaseMigration", - "weight": 337, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -19111,7 +18868,7 @@ }, "x-appwrite": { "method": "getSupabaseReport", - "weight": 348, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -19237,7 +18994,7 @@ }, "x-appwrite": { "method": "get", - "weight": 340, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -19297,7 +19054,7 @@ }, "x-appwrite": { "method": "retry", - "weight": 350, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -19350,7 +19107,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 351, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -32749,30 +32506,6 @@ "migrations" ] }, - "firebaseProjectList": { - "description": "Migrations Firebase Projects List", - "type": "object", - "properties": { - "total": { - "type": "integer", - "description": "Total number of projects documents that matched your query.", - "x-example": 5, - "format": "int32" - }, - "projects": { - "type": "array", - "description": "List of projects.", - "items": { - "$ref": "#\/components\/schemas\/firebaseProject" - }, - "x-example": "" - } - }, - "required": [ - "total", - "projects" - ] - }, "specificationList": { "description": "Specifications List", "type": "object", @@ -35109,7 +34842,7 @@ }, "schedule": { "type": "string", - "description": "Function execution schedult in CRON format.", + "description": "Function execution schedule in CRON format.", "x-example": "5 4 * * *" }, "timeout": { @@ -38707,26 +38440,6 @@ "size", "version" ] - }, - "firebaseProject": { - "description": "MigrationFirebaseProject", - "type": "object", - "properties": { - "projectId": { - "type": "string", - "description": "Project ID.", - "x-example": "my-project" - }, - "displayName": { - "type": "string", - "description": "Project display name.", - "x-example": "My Project" - } - }, - "required": [ - "projectId", - "displayName" - ] } }, "securitySchemes": { diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index 1504322456..8900d6ddf2 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -1,7 +1,7 @@ { "openapi": "3.0.0", "info": { - "version": "1.6.0", + "version": "1.6.1", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -8596,7 +8596,8 @@ "bun-1.0", "bun-1.1", "go-1.23", - "static-1" + "static-1", + "flutter-3.24" ], "x-enum-name": null, "x-enum-keys": [] @@ -9019,7 +9020,8 @@ "bun-1.0", "bun-1.1", "go-1.23", - "static-1" + "static-1", + "flutter-3.24" ], "x-enum-name": null, "x-enum-keys": [] @@ -12527,7 +12529,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 390, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -12606,7 +12608,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 387, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -12753,7 +12755,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 394, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -12902,7 +12904,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 389, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -13060,7 +13062,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 396, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -13220,7 +13222,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 388, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -13332,7 +13334,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 395, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -13447,7 +13449,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 393, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -13503,7 +13505,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 397, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -13568,7 +13570,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 391, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -13646,7 +13648,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 392, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -13724,7 +13726,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 362, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -13803,7 +13805,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 361, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -13911,7 +13913,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 374, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -14022,7 +14024,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 360, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -14110,7 +14112,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 373, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -14201,7 +14203,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 352, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -14319,7 +14321,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 365, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -14440,7 +14442,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 355, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -14538,7 +14540,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 368, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -14639,7 +14641,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 353, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -14747,7 +14749,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 366, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -14858,7 +14860,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 354, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -15004,7 +15006,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 367, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -15152,7 +15154,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 356, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -15250,7 +15252,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 369, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -15351,7 +15353,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 357, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -15449,7 +15451,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 370, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -15550,7 +15552,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 358, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -15648,7 +15650,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 371, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -15749,7 +15751,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 359, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -15847,7 +15849,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 372, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -15948,7 +15950,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 364, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -16004,7 +16006,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 375, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -16069,7 +16071,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 363, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -16147,7 +16149,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 384, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -16225,7 +16227,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 377, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -16302,7 +16304,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 376, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -16388,7 +16390,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 379, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -16451,7 +16453,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 380, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -16531,7 +16533,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 381, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -16596,7 +16598,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 378, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -16674,7 +16676,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 383, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -16761,7 +16763,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 382, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -16855,7 +16857,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 385, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -16921,7 +16923,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 386, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -25746,7 +25748,7 @@ }, "schedule": { "type": "string", - "description": "Function execution schedult in CRON format.", + "description": "Function execution schedule in CRON format.", "x-example": "5 4 * * *" }, "timeout": { diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 3ac70ffe28..77025ec042 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.6.0", + "version": "1.6.1", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -5981,7 +5981,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 382, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -6070,7 +6070,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 386, + "weight": 380, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index 25c0c289b3..0ef937050c 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.6.0", + "version": "1.6.1", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -9610,7 +9610,8 @@ "bun-1.0", "bun-1.1", "go-1.23", - "static-1" + "static-1", + "flutter-3.24" ], "x-enum-name": null, "x-enum-keys": [] @@ -10286,7 +10287,8 @@ "bun-1.0", "bun-1.1", "go-1.23", - "static-1" + "static-1", + "flutter-3.24" ], "x-enum-name": null, "x-enum-keys": [] @@ -13890,7 +13892,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 390, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -13967,7 +13969,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 387, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -14127,7 +14129,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 394, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -14284,7 +14286,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 389, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -14459,7 +14461,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 396, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -14631,7 +14633,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 388, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -14751,7 +14753,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 395, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -14869,7 +14871,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 393, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -14928,7 +14930,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 397, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -14992,7 +14994,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 391, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -15068,7 +15070,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 392, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -15144,7 +15146,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 362, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -15221,7 +15223,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 361, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -15338,7 +15340,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 374, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -15453,7 +15455,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 360, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -15546,7 +15548,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 373, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -15637,7 +15639,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 352, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -15766,7 +15768,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 365, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -15893,7 +15895,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 355, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -15998,7 +16000,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 368, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -16101,7 +16103,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 353, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -16218,7 +16220,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 366, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -16333,7 +16335,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 354, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -16494,7 +16496,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 367, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -16652,7 +16654,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 356, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -16757,7 +16759,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 369, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -16860,7 +16862,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 357, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -16965,7 +16967,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 370, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -17068,7 +17070,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 358, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -17173,7 +17175,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 371, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -17276,7 +17278,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 359, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -17381,7 +17383,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 372, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -17484,7 +17486,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 364, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -17543,7 +17545,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 375, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -17607,7 +17609,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 363, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -17683,7 +17685,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 384, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -17759,7 +17761,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 377, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -17834,7 +17836,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 376, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -17926,7 +17928,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 379, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -17988,7 +17990,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 380, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -18071,7 +18073,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 381, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -18135,7 +18137,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 378, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -18211,7 +18213,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 383, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -18294,7 +18296,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 382, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -18386,7 +18388,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 385, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -18453,7 +18455,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 386, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -18528,7 +18530,7 @@ }, "x-appwrite": { "method": "list", - "weight": 339, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -18699,7 +18701,7 @@ }, "x-appwrite": { "method": "getAppwriteReport", - "weight": 341, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -18767,7 +18769,7 @@ }, "\/migrations\/firebase": { "post": { - "summary": "Migrate Firebase data (Service Account)", + "summary": "Migrate Firebase data", "operationId": "migrationsCreateFirebaseMigration", "consumes": [ "application\/json" @@ -18789,7 +18791,7 @@ }, "x-appwrite": { "method": "createFirebaseMigration", - "weight": 336, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -18847,192 +18849,6 @@ ] } }, - "\/migrations\/firebase\/deauthorize": { - "get": { - "summary": "Revoke Appwrite's authorization to access Firebase projects", - "operationId": "migrationsDeleteFirebaseAuth", - "consumes": [ - "application\/json" - ], - "produces": [ - "application\/json" - ], - "tags": [ - "migrations" - ], - "description": "", - "responses": { - "200": { - "description": "File", - "schema": { - "type": "file" - } - } - }, - "x-appwrite": { - "method": "deleteFirebaseAuth", - "weight": 347, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "migrations\/delete-firebase-auth.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "migrations.write", - "platforms": [ - "console" - ], - "packaging": false, - "offline-model": "", - "offline-key": "", - "offline-response-key": "$id", - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [] - } - ] - } - }, - "\/migrations\/firebase\/oauth": { - "post": { - "summary": "Migrate Firebase data (OAuth)", - "operationId": "migrationsCreateFirebaseOAuthMigration", - "consumes": [ - "application\/json" - ], - "produces": [ - "application\/json" - ], - "tags": [ - "migrations" - ], - "description": "", - "responses": { - "202": { - "description": "Migration", - "schema": { - "$ref": "#\/definitions\/migration" - } - } - }, - "x-appwrite": { - "method": "createFirebaseOAuthMigration", - "weight": 335, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "migrations\/create-firebase-o-auth-migration.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/migrations\/migration-firebase.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "migrations.write", - "platforms": [ - "console" - ], - "packaging": false, - "offline-model": "", - "offline-key": "", - "offline-response-key": "$id", - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [] - } - ], - "parameters": [ - { - "name": "payload", - "in": "body", - "schema": { - "type": "object", - "properties": { - "resources": { - "type": "array", - "description": "List of resources to migrate", - "default": null, - "x-example": null, - "items": { - "type": "string" - } - }, - "projectId": { - "type": "string", - "description": "Project ID of the Firebase Project", - "default": null, - "x-example": "" - } - }, - "required": [ - "resources", - "projectId" - ] - } - } - ] - } - }, - "\/migrations\/firebase\/projects": { - "get": { - "summary": "List Firebase projects", - "operationId": "migrationsListFirebaseProjects", - "consumes": [ - "application\/json" - ], - "produces": [ - "application\/json" - ], - "tags": [ - "migrations" - ], - "description": "", - "responses": { - "200": { - "description": "Migrations Firebase Projects List", - "schema": { - "$ref": "#\/definitions\/firebaseProjectList" - } - } - }, - "x-appwrite": { - "method": "listFirebaseProjects", - "weight": 346, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "migrations\/list-firebase-projects.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "migrations.read", - "platforms": [ - "console" - ], - "packaging": false, - "offline-model": "", - "offline-key": "", - "offline-response-key": "$id", - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [] - } - ] - } - }, "\/migrations\/firebase\/report": { "get": { "summary": "Generate a report on Firebase data", @@ -19057,7 +18873,7 @@ }, "x-appwrite": { "method": "getFirebaseReport", - "weight": 342, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -19106,79 +18922,6 @@ ] } }, - "\/migrations\/firebase\/report\/oauth": { - "get": { - "summary": "Generate a report on Firebase data using OAuth", - "operationId": "migrationsGetFirebaseReportOAuth", - "consumes": [ - "application\/json" - ], - "produces": [ - "application\/json" - ], - "tags": [ - "migrations" - ], - "description": "", - "responses": { - "200": { - "description": "Migration Report", - "schema": { - "$ref": "#\/definitions\/migrationReport" - } - } - }, - "x-appwrite": { - "method": "getFirebaseReportOAuth", - "weight": 343, - "cookies": false, - "type": "", - "deprecated": false, - "demo": "migrations\/get-firebase-report-o-auth.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/migrations\/migration-firebase-report.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", - "scope": "migrations.write", - "platforms": [ - "console" - ], - "packaging": false, - "offline-model": "", - "offline-key": "", - "offline-response-key": "$id", - "auth": { - "Project": [] - } - }, - "security": [ - { - "Project": [] - } - ], - "parameters": [ - { - "name": "resources", - "description": "List of resources to migrate", - "required": true, - "type": "array", - "collectionFormat": "multi", - "items": { - "type": "string" - }, - "in": "query" - }, - { - "name": "projectId", - "description": "Project ID", - "required": true, - "type": "string", - "x-example": "", - "in": "query" - } - ] - } - }, "\/migrations\/nhost": { "post": { "summary": "Migrate NHost data", @@ -19203,7 +18946,7 @@ }, "x-appwrite": { "method": "createNHostMigration", - "weight": 338, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -19326,7 +19069,7 @@ }, "x-appwrite": { "method": "getNHostReport", - "weight": 349, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -19448,7 +19191,7 @@ }, "x-appwrite": { "method": "createSupabaseMigration", - "weight": 337, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -19564,7 +19307,7 @@ }, "x-appwrite": { "method": "getSupabaseReport", - "weight": 348, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -19679,7 +19422,7 @@ }, "x-appwrite": { "method": "get", - "weight": 340, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -19739,7 +19482,7 @@ }, "x-appwrite": { "method": "retry", - "weight": 350, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -19794,7 +19537,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 351, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -33250,31 +32993,6 @@ "migrations" ] }, - "firebaseProjectList": { - "description": "Migrations Firebase Projects List", - "type": "object", - "properties": { - "total": { - "type": "integer", - "description": "Total number of projects documents that matched your query.", - "x-example": 5, - "format": "int32" - }, - "projects": { - "type": "array", - "description": "List of projects.", - "items": { - "type": "object", - "$ref": "#\/definitions\/firebaseProject" - }, - "x-example": "" - } - }, - "required": [ - "total", - "projects" - ] - }, "specificationList": { "description": "Specifications List", "type": "object", @@ -35618,7 +35336,7 @@ }, "schedule": { "type": "string", - "description": "Function execution schedult in CRON format.", + "description": "Function execution schedule in CRON format.", "x-example": "5 4 * * *" }, "timeout": { @@ -39274,26 +38992,6 @@ "size", "version" ] - }, - "firebaseProject": { - "description": "MigrationFirebaseProject", - "type": "object", - "properties": { - "projectId": { - "type": "string", - "description": "Project ID.", - "x-example": "my-project" - }, - "displayName": { - "type": "string", - "description": "Project display name.", - "x-example": "My Project" - } - }, - "required": [ - "projectId", - "displayName" - ] } }, "externalDocs": { diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 68bcf268fb..f5b1f24a22 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -1,7 +1,7 @@ { "swagger": "2.0", "info": { - "version": "1.6.0", + "version": "1.6.1", "title": "Appwrite", "description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)", "termsOfService": "https:\/\/appwrite.io\/policy\/terms", @@ -8720,7 +8720,8 @@ "bun-1.0", "bun-1.1", "go-1.23", - "static-1" + "static-1", + "flutter-3.24" ], "x-enum-name": null, "x-enum-keys": [] @@ -9166,7 +9167,8 @@ "bun-1.0", "bun-1.1", "go-1.23", - "static-1" + "static-1", + "flutter-3.24" ], "x-enum-name": null, "x-enum-keys": [] @@ -12752,7 +12754,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 390, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -12830,7 +12832,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 387, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -12991,7 +12993,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 394, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -13149,7 +13151,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 389, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -13325,7 +13327,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 396, + "weight": 390, "cookies": false, "type": "", "deprecated": false, @@ -13498,7 +13500,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 388, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -13619,7 +13621,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 395, + "weight": 389, "cookies": false, "type": "", "deprecated": false, @@ -13738,7 +13740,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 393, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -13798,7 +13800,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 397, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -13863,7 +13865,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 391, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -13940,7 +13942,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 392, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -14017,7 +14019,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 362, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -14095,7 +14097,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 361, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -14213,7 +14215,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 374, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -14329,7 +14331,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 360, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -14423,7 +14425,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 373, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -14515,7 +14517,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 352, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -14645,7 +14647,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 365, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -14773,7 +14775,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 355, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -14879,7 +14881,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 368, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -14983,7 +14985,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 353, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -15101,7 +15103,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 366, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -15217,7 +15219,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 354, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -15379,7 +15381,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 367, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -15538,7 +15540,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 356, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -15644,7 +15646,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 369, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -15748,7 +15750,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 357, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -15854,7 +15856,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 370, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -15958,7 +15960,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 358, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -16064,7 +16066,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 371, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -16168,7 +16170,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 359, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -16274,7 +16276,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 372, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -16378,7 +16380,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 364, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -16438,7 +16440,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 375, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -16503,7 +16505,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 363, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -16580,7 +16582,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 384, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -16657,7 +16659,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 377, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -16733,7 +16735,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 376, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -16826,7 +16828,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 379, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -16889,7 +16891,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 380, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -16973,7 +16975,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 381, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -17038,7 +17040,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 378, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -17115,7 +17117,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 383, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -17199,7 +17201,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 382, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -17293,7 +17295,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 385, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -17361,7 +17363,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 386, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -26233,7 +26235,7 @@ }, "schedule": { "type": "string", - "description": "Function execution schedult in CRON format.", + "description": "Function execution schedule in CRON format.", "x-example": "5 4 * * *" }, "timeout": { diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 3abd1db016..29e46505f5 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -1,17 +1,12 @@ dynamic($migration, Response::MODEL_MIGRATION); }); -App::post('/v1/migrations/firebase/oauth') - ->groups(['api', 'migrations']) - ->desc('Migrate Firebase data (OAuth)') - ->label('scope', 'migrations.write') - ->label('event', 'migrations.[migrationId].create') - ->label('audits.event', 'migration.create') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'migrations') - ->label('sdk.method', 'createFirebaseOAuthMigration') - ->label('sdk.description', '/docs/references/migrations/migration-firebase.md') - ->label('sdk.response.code', Response::STATUS_CODE_ACCEPTED) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_MIGRATION) - ->param('resources', [], new ArrayList(new WhiteList(Firebase::getSupportedResources())), 'List of resources to migrate') - ->param('projectId', '', new Text(65536), 'Project ID of the Firebase Project') - ->inject('response') - ->inject('dbForProject') - ->inject('dbForPlatform') - ->inject('project') - ->inject('user') - ->inject('queueForEvents') - ->inject('queueForMigrations') - ->inject('request') - ->action(function (array $resources, string $projectId, Response $response, Database $dbForProject, Database $dbForPlatform, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations, Request $request) { - $firebase = new OAuth2Firebase( - System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), - System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), - $request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect' - ); - - $identity = $dbForPlatform->findOne('identities', [ - Query::equal('provider', ['firebase']), - Query::equal('userInternalId', [$user->getInternalId()]), - ]); - if ($identity->isEmpty()) { - throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); - } - - $accessToken = $identity->getAttribute('providerAccessToken'); - $refreshToken = $identity->getAttribute('providerRefreshToken'); - $accessTokenExpiry = $identity->getAttribute('providerAccessTokenExpiry'); - - $isExpired = new \DateTime($accessTokenExpiry) < new \DateTime('now'); - if ($isExpired) { - $firebase->refreshTokens($refreshToken); - - $accessToken = $firebase->getAccessToken(''); - $refreshToken = $firebase->getRefreshToken(''); - - $verificationId = $firebase->getUserID($accessToken); - - if (empty($verificationId)) { - throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED, 'Another request is currently refreshing OAuth token. Please try again.'); - } - - $identity = $identity - ->setAttribute('providerAccessToken', $accessToken) - ->setAttribute('providerRefreshToken', $refreshToken) - ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$firebase->getAccessTokenExpiry(''))); - - $dbForPlatform->updateDocument('identities', $identity->getId(), $identity); - } - - if ($identity->getAttribute('secrets')) { - $serviceAccount = $identity->getAttribute('secrets'); - } else { - $firebase->cleanupServiceAccounts($accessToken, $projectId); - $serviceAccount = $firebase->createServiceAccount($accessToken, $projectId); - $identity = $identity - ->setAttribute('secrets', json_encode($serviceAccount)); - - $dbForPlatform->updateDocument('identities', $identity->getId(), $identity); - } - - $migration = $dbForProject->createDocument('migrations', new Document([ - '$id' => ID::unique(), - 'status' => 'pending', - 'stage' => 'init', - 'source' => Firebase::getName(), - 'destination' => Appwrite::getName(), - 'credentials' => [ - 'serviceAccount' => json_encode($serviceAccount), - ], - 'resources' => $resources, - 'statusCounters' => '{}', - 'resourceData' => '{}', - 'errors' => [] - ])); - - $queueForEvents->setParam('migrationId', $migration->getId()); - - // Trigger Transfer - $queueForMigrations - ->setMigration($migration) - ->setProject($project) - ->setUser($user) - ->trigger(); - - $response - ->setStatusCode(Response::STATUS_CODE_ACCEPTED) - ->dynamic($migration, Response::MODEL_MIGRATION); - }); - App::post('/v1/migrations/firebase') ->groups(['api', 'migrations']) - ->desc('Migrate Firebase data (Service Account)') + ->desc('Migrate Firebase data') ->label('scope', 'migrations.write') ->label('event', 'migrations.[migrationId].create') ->label('audits.event', 'migration.create') @@ -547,368 +437,6 @@ App::get('/v1/migrations/firebase/report') ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); -App::get('/v1/migrations/firebase/report/oauth') - ->groups(['api', 'migrations']) - ->desc('Generate a report on Firebase data using OAuth') - ->label('scope', 'migrations.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'migrations') - ->label('sdk.method', 'getFirebaseReportOAuth') - ->label('sdk.description', '/docs/references/migrations/migration-firebase-report.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_MIGRATION_REPORT) - ->param('resources', [], new ArrayList(new WhiteList(Firebase::getSupportedResources())), 'List of resources to migrate') - ->param('projectId', '', new Text(65536), 'Project ID') - ->inject('response') - ->inject('request') - ->inject('user') - ->inject('dbForPlatform') - ->action(function (array $resources, string $projectId, Response $response, Request $request, Document $user, Database $dbForPlatform) { - $firebase = new OAuth2Firebase( - System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), - System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), - $request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect' - ); - - $identity = $dbForPlatform->findOne('identities', [ - Query::equal('provider', ['firebase']), - Query::equal('userInternalId', [$user->getInternalId()]), - ]); - - if ($identity->isEmpty()) { - throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); - } - - $accessToken = $identity->getAttribute('providerAccessToken'); - $refreshToken = $identity->getAttribute('providerRefreshToken'); - $accessTokenExpiry = $identity->getAttribute('providerAccessTokenExpiry'); - - if (empty($accessToken) || empty($refreshToken) || empty($accessTokenExpiry)) { - throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); - } - - if (System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', '') === '' || System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', '') === '') { - throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); - } - - $isExpired = new \DateTime($accessTokenExpiry) < new \DateTime('now'); - if ($isExpired) { - $firebase->refreshTokens($refreshToken); - - $accessToken = $firebase->getAccessToken(''); - $refreshToken = $firebase->getRefreshToken(''); - - $verificationId = $firebase->getUserID($accessToken); - - if (empty($verificationId)) { - throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED, 'Another request is currently refreshing OAuth token. Please try again.'); - } - - $identity = $identity - ->setAttribute('providerAccessToken', $accessToken) - ->setAttribute('providerRefreshToken', $refreshToken) - ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$firebase->getAccessTokenExpiry(''))); - - $dbForPlatform->updateDocument('identities', $identity->getId(), $identity); - } - - // Get Service Account - if ($identity->getAttribute('secrets')) { - $serviceAccount = $identity->getAttribute('secrets'); - } else { - $firebase->cleanupServiceAccounts($accessToken, $projectId); - $serviceAccount = $firebase->createServiceAccount($accessToken, $projectId); - $identity = $identity - ->setAttribute('secrets', json_encode($serviceAccount)); - - $dbForPlatform->updateDocument('identities', $identity->getId(), $identity); - } - - $firebase = new Firebase($serviceAccount); - - try { - $report = $firebase->report($resources); - } catch (\Throwable $e) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Source Error: ' . $e->getMessage()); - } - - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); - }); - -App::get('/v1/migrations/firebase/connect') - ->desc('Authorize with Firebase') - ->groups(['api', 'migrations']) - ->label('scope', 'migrations.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'migrations') - ->label('sdk.method', 'createFirebaseAuth') - ->label('sdk.description', '') - ->label('sdk.response.code', Response::STATUS_CODE_MOVED_PERMANENTLY) - ->label('sdk.response.type', Response::CONTENT_TYPE_HTML) - ->label('sdk.methodType', 'webAuth') - ->label('sdk.hide', true) - ->param('redirect', '', fn ($clients) => new Host($clients), 'URL to redirect back to your Firebase authorization. Only console hostnames are allowed.', true, ['clients']) - ->param('projectId', '', new UID(), 'Project ID') - ->inject('response') - ->inject('request') - ->inject('user') - ->inject('dbForPlatform') - ->action(function (string $redirect, string $projectId, Response $response, Request $request, Document $user, Database $dbForPlatform) { - $state = \json_encode([ - 'projectId' => $projectId, - 'redirect' => $redirect, - ]); - - $prefs = $user->getAttribute('prefs', []); - $prefs['migrationState'] = $state; - $user->setAttribute('prefs', $prefs); - $dbForPlatform->updateDocument('users', $user->getId(), $user); - - $oauth2 = new OAuth2Firebase( - System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), - System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), - $request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect' - ); - $url = $oauth2->getLoginURL(); - - $response - ->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') - ->addHeader('Pragma', 'no-cache') - ->redirect($url); - }); - -App::get('/v1/migrations/firebase/redirect') - ->desc('Capture and receive data on Firebase authorization') - ->groups(['api', 'migrations']) - ->label('scope', 'public') - ->label('error', __DIR__ . '/../../views/general/error.phtml') - ->param('code', '', new Text(2048), 'OAuth2 code. This is a temporary code that the will be later exchanged for an access token.', true) - ->inject('user') - ->inject('project') - ->inject('request') - ->inject('response') - ->inject('dbForPlatform') - ->action(function (string $code, Document $user, Document $project, Request $request, Response $response, Database $dbForPlatform) { - $state = $user['prefs']['migrationState'] ?? '{}'; - $prefs['migrationState'] = ''; - $user->setAttribute('prefs', $prefs); - $dbForPlatform->updateDocument('users', $user->getId(), $user); - - if (empty($state)) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Installation requests from organisation members for the Appwrite Google App are currently unsupported.'); - } - - $state = \json_decode($state, true); - $redirect = $state['redirect'] ?? ''; - $projectId = $state['projectId'] ?? ''; - - $project = $dbForPlatform->getDocument('projects', $projectId); - - if (empty($redirect)) { - $redirect = $request->getProtocol() . '://' . $request->getHostname() . '/console/project-$projectId/settings/migrations'; - } - - if ($project->isEmpty()) { - $response - ->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') - ->addHeader('Pragma', 'no-cache') - ->redirect($redirect); - - return; - } - - // OAuth Authroization - if (!empty($code)) { - $oauth2 = new OAuth2Firebase( - System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), - System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), - $request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect' - ); - - $accessToken = $oauth2->getAccessToken($code); - $refreshToken = $oauth2->getRefreshToken($code); - $accessTokenExpiry = $oauth2->getAccessTokenExpiry($code); - $email = $oauth2->getUserEmail($accessToken); - $oauth2ID = $oauth2->getUserID($accessToken); - - if (empty($accessToken)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to get access token.'); - } - - if (empty($refreshToken)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to get refresh token.'); - } - - if (empty($accessTokenExpiry)) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to get access token expiry.'); - } - - // Makes sure this email is not already used in another identity - $identity = $dbForPlatform->findOne('identities', [ - Query::equal('providerEmail', [$email]), - ]); - - if (!$identity->isEmpty()) { - if ($identity->getAttribute('userInternalId', '') !== $user->getInternalId()) { - throw new Exception(Exception::USER_EMAIL_ALREADY_EXISTS); - } - } - - if (!$identity->isEmpty()) { - $identity = $identity - ->setAttribute('providerAccessToken', $accessToken) - ->setAttribute('providerRefreshToken', $refreshToken) - ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry)); - - $dbForPlatform->updateDocument('identities', $identity->getId(), $identity); - } else { - $identity = $dbForPlatform->createDocument('identities', new Document([ - '$id' => ID::unique(), - '$permissions' => [ - Permission::read(Role::any()), - Permission::update(Role::user($user->getId())), - Permission::delete(Role::user($user->getId())), - ], - 'userInternalId' => $user->getInternalId(), - 'userId' => $user->getId(), - 'provider' => 'firebase', - 'providerUid' => $oauth2ID, - 'providerEmail' => $email, - 'providerAccessToken' => $accessToken, - 'providerRefreshToken' => $refreshToken, - 'providerAccessTokenExpiry' => DateTime::addSeconds(new \DateTime(), (int)$accessTokenExpiry), - ])); - } - } else { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Missing OAuth2 code.'); - } - - $response - ->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0') - ->addHeader('Pragma', 'no-cache') - ->redirect($redirect); - }); - -App::get('/v1/migrations/firebase/projects') - ->desc('List Firebase projects') - ->groups(['api', 'migrations']) - ->label('scope', 'migrations.read') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'migrations') - ->label('sdk.method', 'listFirebaseProjects') - ->label('sdk.description', '') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_MIGRATION_FIREBASE_PROJECT_LIST) - ->inject('user') - ->inject('response') - ->inject('project') - ->inject('dbForPlatform') - ->inject('request') - ->action(function (Document $user, Response $response, Document $project, Database $dbForPlatform, Request $request) { - $firebase = new OAuth2Firebase( - System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', ''), - System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', ''), - $request->getProtocol() . '://' . $request->getHostname() . '/v1/migrations/firebase/redirect' - ); - - $identity = $dbForPlatform->findOne('identities', [ - Query::equal('provider', ['firebase']), - Query::equal('userInternalId', [$user->getInternalId()]), - ]); - - if ($identity->isEmpty()) { - throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); - } - - $accessToken = $identity->getAttribute('providerAccessToken'); - $refreshToken = $identity->getAttribute('providerRefreshToken'); - $accessTokenExpiry = $identity->getAttribute('providerAccessTokenExpiry'); - - if (empty($accessToken) || empty($refreshToken) || empty($accessTokenExpiry)) { - throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); - } - - if (System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_ID', '') === '' || System::getEnv('_APP_MIGRATIONS_FIREBASE_CLIENT_SECRET', '') === '') { - throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); - } - - try { - $isExpired = new \DateTime($accessTokenExpiry) < new \DateTime('now'); - if ($isExpired) { - try { - $firebase->refreshTokens($refreshToken); - } catch (\Throwable $e) { - throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); - } - - $accessToken = $firebase->getAccessToken(''); - $refreshToken = $firebase->getRefreshToken(''); - - $verificationId = $firebase->getUserID($accessToken); - - if (empty($verificationId)) { - throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED, 'Another request is currently refreshing OAuth token. Please try again.'); - } - - $identity = $identity - ->setAttribute('providerAccessToken', $accessToken) - ->setAttribute('providerRefreshToken', $refreshToken) - ->setAttribute('providerAccessTokenExpiry', DateTime::addSeconds(new \DateTime(), (int)$firebase->getAccessTokenExpiry(''))); - - $dbForPlatform->updateDocument('identities', $identity->getId(), $identity); - } - - $projects = $firebase->getProjects($accessToken); - - $output = []; - foreach ($projects as $project) { - $output[] = [ - 'displayName' => $project['displayName'], - 'projectId' => $project['projectId'], - ]; - } - } catch (\Throwable $e) { - throw new Exception(Exception::USER_IDENTITY_NOT_FOUND); - } - - $response->dynamic(new Document([ - 'projects' => $output, - 'total' => count($output), - ]), Response::MODEL_MIGRATION_FIREBASE_PROJECT_LIST); - }); - -App::get('/v1/migrations/firebase/deauthorize') - ->desc('Revoke Appwrite\'s authorization to access Firebase projects') - ->groups(['api', 'migrations']) - ->label('scope', 'migrations.write') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'migrations') - ->label('sdk.method', 'deleteFirebaseAuth') - ->label('sdk.description', '') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->inject('user') - ->inject('response') - ->inject('dbForPlatform') - ->action(function (Document $user, Response $response, Database $dbForPlatform) { - $identity = $dbForPlatform->findOne('identities', [ - Query::equal('provider', ['firebase']), - Query::equal('userInternalId', [$user->getInternalId()]), - ]); - - if ($identity->isEmpty()) { - throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN, 'Not authenticated with Firebase'); //TODO: Replace with USER_IDENTITY_NOT_FOUND - } - - $dbForPlatform->deleteDocument('identities', $identity->getId()); - - $response->noContent(); - }); - App::get('/v1/migrations/supabase/report') ->groups(['api', 'migrations']) ->desc('Generate a report on Supabase Data') diff --git a/composer.json b/composer.json index e49948977b..e44dd0bffc 100644 --- a/composer.json +++ b/composer.json @@ -84,7 +84,7 @@ }, "require-dev": { "ext-fileinfo": "*", - "appwrite/sdk-generator": "0.39.*", + "appwrite/sdk-generator": "0.39.28", "phpunit/phpunit": "9.5.20", "swoole/ide-helper": "5.1.2", "textalk/websocket": "1.5.7", diff --git a/composer.lock b/composer.lock index 083456a0d4..ce5f358619 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": "786a6c2f9aa130c673f8a39a18a28e3c", + "content-hash": "6b136b5490c0d5d331eac0d70bb3e198", "packages": [ { "name": "adhocore/jwt", @@ -4807,16 +4807,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "0.39.27", + "version": "0.39.28", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "27d8ecde30e40cbfe1124cc0430c406d3e144849" + "reference": "6ff467858fe418e364460da905139216570a5d5e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/27d8ecde30e40cbfe1124cc0430c406d3e144849", - "reference": "27d8ecde30e40cbfe1124cc0430c406d3e144849", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6ff467858fe418e364460da905139216570a5d5e", + "reference": "6ff467858fe418e364460da905139216570a5d5e", "shasum": "" }, "require": { @@ -4852,9 +4852,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.27" + "source": "https://github.com/appwrite/sdk-generator/tree/0.39.28" }, - "time": "2024-12-16T11:32:02+00:00" + "time": "2024-12-30T11:17:25+00:00" }, { "name": "doctrine/annotations", @@ -5376,16 +5376,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.3.1", + "version": "v5.4.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b" + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/8eea230464783aa9671db8eea6f8c6ac5285794b", - "reference": "8eea230464783aa9671db8eea6f8c6ac5285794b", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", "shasum": "" }, "require": { @@ -5428,9 +5428,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.3.1" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" }, - "time": "2024-10-08T18:51:32+00:00" + "time": "2024-12-30T11:07:19+00:00" }, { "name": "phar-io/manifest", diff --git a/docs/examples/1.6.x/client-graphql/examples/account/create-push-target.md b/docs/examples/1.6.x/client-graphql/examples/account/create-push-target.md index 8a0fad387c..63802a782e 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/create-push-target.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/create-push-target.md @@ -12,5 +12,6 @@ mutation { providerId providerType identifier + expired } } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/create.md b/docs/examples/1.6.x/client-graphql/examples/account/create.md index 3f8e3c3cf7..0d39394a3d 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/create.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/create.md @@ -33,6 +33,7 @@ mutation { providerId providerType identifier + expired } accessedAt } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/get.md b/docs/examples/1.6.x/client-graphql/examples/account/get.md index e4db8f0e41..f4f07c187f 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/get.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/get.md @@ -28,6 +28,7 @@ query { providerId providerType identifier + expired } accessedAt } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/update-email.md b/docs/examples/1.6.x/client-graphql/examples/account/update-email.md index b207bad4bb..c879e24a43 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/update-email.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/update-email.md @@ -31,6 +31,7 @@ mutation { providerId providerType identifier + expired } accessedAt } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/update-m-f-a.md b/docs/examples/1.6.x/client-graphql/examples/account/update-m-f-a.md index d2cd3d6ae5..787c2e0860 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/update-m-f-a.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/update-m-f-a.md @@ -30,6 +30,7 @@ mutation { providerId providerType identifier + expired } accessedAt } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/update-mfa-authenticator.md b/docs/examples/1.6.x/client-graphql/examples/account/update-mfa-authenticator.md index c74062c7d4..9cfe9150be 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/update-mfa-authenticator.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/update-mfa-authenticator.md @@ -31,6 +31,7 @@ mutation { providerId providerType identifier + expired } accessedAt } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/update-name.md b/docs/examples/1.6.x/client-graphql/examples/account/update-name.md index 850b5760a0..8ba2c99d9c 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/update-name.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/update-name.md @@ -30,6 +30,7 @@ mutation { providerId providerType identifier + expired } accessedAt } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/update-password.md b/docs/examples/1.6.x/client-graphql/examples/account/update-password.md index 5904da0842..f3619a10d2 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/update-password.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/update-password.md @@ -31,6 +31,7 @@ mutation { providerId providerType identifier + expired } accessedAt } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/update-phone.md b/docs/examples/1.6.x/client-graphql/examples/account/update-phone.md index 408a203300..adecb71168 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/update-phone.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/update-phone.md @@ -31,6 +31,7 @@ mutation { providerId providerType identifier + expired } accessedAt } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/update-prefs.md b/docs/examples/1.6.x/client-graphql/examples/account/update-prefs.md index 40db7b43db..57280247e4 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/update-prefs.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/update-prefs.md @@ -30,6 +30,7 @@ mutation { providerId providerType identifier + expired } accessedAt } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/update-push-target.md b/docs/examples/1.6.x/client-graphql/examples/account/update-push-target.md index 059702dc97..3c402cdf12 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/update-push-target.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/update-push-target.md @@ -11,5 +11,6 @@ mutation { providerId providerType identifier + expired } } diff --git a/docs/examples/1.6.x/client-graphql/examples/account/update-status.md b/docs/examples/1.6.x/client-graphql/examples/account/update-status.md index aca8c098e7..c17f556842 100644 --- a/docs/examples/1.6.x/client-graphql/examples/account/update-status.md +++ b/docs/examples/1.6.x/client-graphql/examples/account/update-status.md @@ -28,6 +28,7 @@ mutation { providerId providerType identifier + expired } accessedAt } diff --git a/docs/examples/1.6.x/client-graphql/examples/messaging/create-subscriber.md b/docs/examples/1.6.x/client-graphql/examples/messaging/create-subscriber.md index b2712ebb48..bab53612b7 100644 --- a/docs/examples/1.6.x/client-graphql/examples/messaging/create-subscriber.md +++ b/docs/examples/1.6.x/client-graphql/examples/messaging/create-subscriber.md @@ -17,6 +17,7 @@ mutation { providerId providerType identifier + expired } userId userName diff --git a/src/Appwrite/Auth/OAuth2/Firebase.php b/src/Appwrite/Auth/OAuth2/Firebase.php deleted file mode 100644 index 0e2859e32c..0000000000 --- a/src/Appwrite/Auth/OAuth2/Firebase.php +++ /dev/null @@ -1,389 +0,0 @@ - 'offline', - 'client_id' => $this->appID, - 'redirect_uri' => $this->callback, - 'scope' => \implode(' ', $this->getScopes()), - 'state' => \json_encode($this->state), - 'response_type' => 'code', - 'prompt' => 'consent', - ]); - } - - /** - * @param string $code - * - * @return array - */ - protected function getTokens(string $code): array - { - if (empty($this->tokens)) { - $response = $this->request( - 'POST', - 'https://oauth2.googleapis.com/token', - [], - \http_build_query([ - 'client_id' => $this->appID, - 'redirect_uri' => $this->callback, - 'client_secret' => $this->appSecret, - 'code' => $code, - 'grant_type' => 'authorization_code' - ]) - ); - - $this->tokens = \json_decode($response, true); - } - - return $this->tokens; - } - - /** - * @param string $refreshToken - * - * @return array - */ - public function refreshTokens(string $refreshToken): array - { - $response = $this->request( - 'POST', - 'https://oauth2.googleapis.com/token', - [], - \http_build_query([ - 'client_id' => $this->appID, - 'client_secret' => $this->appSecret, - 'grant_type' => 'refresh_token', - 'refresh_token' => $refreshToken - ]) - ); - - $output = []; - \parse_str($response, $output); - $this->tokens = $output; - - if (empty($this->tokens['refresh_token'])) { - $this->tokens['refresh_token'] = $refreshToken; - } - - return $this->tokens; - } - - - /** - * @param string $accessToken - * - * @return string - */ - public function getUserID(string $accessToken): string - { - $user = $this->getUser($accessToken); - - return $user['id'] ?? ''; - } - - /** - * @param string $accessToken - * - * @return string - */ - public function getUserEmail(string $accessToken): string - { - $user = $this->getUser($accessToken); - - return $user['email'] ?? ''; - } - - - /** - * Check if the OAuth email is verified - * - * @link https://docs.github.com/en/rest/users/emails#list-email-addresses-for-the-authenticated-user - * - * @param string $accessToken - * - * @return bool - */ - public function isEmailVerified(string $accessToken): bool - { - $user = $this->getUser($accessToken); - - if ($user['verified'] ?? false) { - return true; - } - - return false; - } - - /** - * @param string $accessToken - * - * @return string - */ - public function getUserName(string $accessToken): string - { - $user = $this->getUser($accessToken); - - return $user['name'] ?? ''; - } - - /** - * @param string $accessToken - * - * @return array - */ - protected function getUser(string $accessToken) - { - if (empty($this->user)) { - $response = $this->request( - 'GET', - 'https://www.googleapis.com/oauth2/v1/userinfo?access_token=' . \urlencode($accessToken), - [], - ); - - $this->user = \json_decode($response, true); - } - - return $this->user; - } - - public function getProjects(string $accessToken): array - { - $projects = $this->request('GET', 'https://firebase.googleapis.com/v1beta1/projects', ['Authorization: Bearer ' . \urlencode($accessToken)]); - - $projects = \json_decode($projects, true); - - return $projects['results']; - } - - /* - Be careful with the setIAMPolicy method, it will overwrite all existing policies - **/ - public function assignIAMRole(string $accessToken, string $email, string $projectId, array $role) - { - // Get IAM Roles - $iamRoles = $this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/' . $projectId . ':getIamPolicy', [ - 'Authorization: Bearer ' . \urlencode($accessToken), - 'Content-Type: application/json' - ]); - - $iamRoles = \json_decode($iamRoles, true); - - $iamRoles['bindings'][] = [ - 'role' => $role['name'], - 'members' => [ - 'serviceAccount:' . $email - ] - ]; - - // Set IAM Roles - $this->request('POST', 'https://cloudresourcemanager.googleapis.com/v1/projects/' . $projectId . ':setIamPolicy', [ - 'Authorization: Bearer ' . \urlencode($accessToken), - 'Content-Type: application/json' - ], \json_encode([ - 'policy' => $iamRoles - ])); - } - - private function generateRandomString($length = 10): string - { - $characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - $charactersLength = strlen($characters); - $randomString = ''; - for ($i = 0; $i < $length; $i++) { - $randomString .= $characters[random_int(0, $charactersLength - 1)]; - } - return $randomString; - } - - private function createCustomRole(string $accessToken, string $projectId): array - { - // Check if role already exists - try { - $role = $this->request('GET', 'https://iam.googleapis.com/v1/projects/' . $projectId . '/roles/appwriteMigrations', [ - 'Content-Type: application/json', - 'Authorization: Bearer ' . \urlencode($accessToken), - ]); - - $role = \json_decode($role, true); - - return $role; - } catch (\Throwable $e) { - if ($e->getCode() !== 404) { - throw $e; - } - } - - // Create role if doesn't exist or isn't correct - $role = $this->request( - 'POST', - 'https://iam.googleapis.com/v1/projects/' . $projectId . '/roles/', - [ - 'Content-Type: application/json', - 'Authorization: Bearer ' . \urlencode($accessToken), - ], - \json_encode( - [ - 'roleId' => 'appwriteMigrations', - 'role' => [ - 'title' => 'Appwrite Migrations', - 'description' => 'A helper role for Appwrite Migrations', - 'includedPermissions' => $this->iamPermissions, - 'stage' => 'GA' - ] - ] - ) - ); - - return json_decode($role, true); - } - - public function createServiceAccount(string $accessToken, string $projectId): array - { - // Create Service Account - $uid = $this->generateRandomString(); - - $response = $this->request( - 'POST', - 'https://iam.googleapis.com/v1/projects/' . $projectId . '/serviceAccounts', - [ - 'Authorization: Bearer ' . \urlencode($accessToken), - 'Content-Type: application/json' - ], - \json_encode([ - 'accountId' => 'appwrite-' . $uid, - 'serviceAccount' => [ - 'displayName' => 'Appwrite Migrations ' . $uid - ] - ]) - ); - - $response = json_decode($response, true); - - // Create and assign IAM Roles - $role = $this->createCustomRole($accessToken, $projectId); - - \sleep(1); // Wait for IAM to propagate changes. - - $this->assignIAMRole($accessToken, $response['email'], $projectId, $role); - - // Create Service Account Key - $responseKey = $this->request( - 'POST', - 'https://iam.googleapis.com/v1/projects/' . $projectId . '/serviceAccounts/' . $response['email'] . '/keys', - [ - 'Authorization: Bearer ' . \urlencode($accessToken), - 'Content-Type: application/json' - ] - ); - - $responseKey = json_decode($responseKey, true); - - return json_decode(base64_decode($responseKey['privateKeyData']), true); - } - - public function cleanupServiceAccounts(string $accessToken, string $projectId) - { - // List Service Accounts - $response = $this->request( - 'GET', - 'https://iam.googleapis.com/v1/projects/' . $projectId . '/serviceAccounts', - [ - 'Authorization: Bearer ' . \urlencode($accessToken), - 'Content-Type: application/json' - ] - ); - - $response = json_decode($response, true); - - if (empty($response['accounts'])) { - return false; - } - - foreach ($response['accounts'] as $account) { - if (strpos($account['email'], 'appwrite-') !== false) { - $this->request( - 'DELETE', - 'https://iam.googleapis.com/v1/projects/' . $projectId . '/serviceAccounts/' . $account['email'], - [ - 'Authorization: Bearer ' . \urlencode($accessToken), - 'Content-Type: application/json' - ] - ); - } - } - - return true; - } -}