diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 5a864ab928..370acb8da8 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -1,5 +1,6 @@ dynamic($migration, Response::MODEL_MIGRATION); }); +Http::get('/v1/migrations/appwrite/console-key') + ->groups(['api', 'migrations']) + ->desc('Generate console API key for migration') + ->label('scope', 'migrations.read') + ->label('sdk', new Method( + namespace: 'migrations', + group: null, + name: 'getAppwriteConsoleKey', + description: '/docs/references/migrations/migration-appwrite-console-key.md', + auth: [AuthType::KEY], + responses: [ + new SDKResponse( + code: Response::STATUS_CODE_OK, + model: Response::MODEL_MIGRATION_KEY, + ) + ] + )) + ->param('resources', [], new ArrayList(new WhiteList(\array_keys(Config::getParam('consoleProjectScopes')))), 'List of resource types to request access for.', true) + ->inject('response') + ->inject('project') + ->action(function (array $resources, Response $response, Document $project) { + $consoleProjectScopes = Config::getParam('consoleProjectScopes'); + + $scopes = empty($resources) + ? \array_values($consoleProjectScopes) + : \array_values(\array_intersect_key($consoleProjectScopes, \array_flip($resources))); + + if (empty($scopes)) { + throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE); + } + + $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', APP_CONSOLE_KEY_TTL, 0); + $consoleKey = $jwt->encode([ + 'projectId' => 'console', + 'name' => 'Migration Settings Key', + 'source' => KEY_SOURCE_MIGRATION, + 'scopes' => $scopes, + 'disabledMetrics' => [ + METRIC_DATABASES_OPERATIONS_READS, + METRIC_DATABASES_OPERATIONS_WRITES, + METRIC_NETWORK_REQUESTS, + METRIC_NETWORK_INBOUND, + METRIC_NETWORK_OUTBOUND, + ], + 'scopedProjectId' => $project->getId(), + ]); + + $response->dynamic(new Document([ + 'key' => API_KEY_DYNAMIC . '_' . $consoleKey, + ]), Response::MODEL_MIGRATION_KEY); + }); + Http::get('/v1/migrations/appwrite/report') ->groups(['api', 'migrations']) ->desc('Get Appwrite migration report') diff --git a/app/init/configs.php b/app/init/configs.php index 35c8e3899d..cdca9a7632 100644 --- a/app/init/configs.php +++ b/app/init/configs.php @@ -25,6 +25,7 @@ Config::load('roles', __DIR__ . '/../config/roles.php', $configAdapter); // Use Config::load('projectScopes', __DIR__ . '/../config/scopes/project.php', $configAdapter); Config::load('organizationScopes', __DIR__ . '/../config/scopes/organization.php', $configAdapter); Config::load('accountScopes', __DIR__ . '/../config/scopes/account.php', $configAdapter); +Config::load('consoleProjectScopes', __DIR__ . '/../config/scopes/consoleProject.php', $configAdapter); Config::load('services', __DIR__ . '/../config/services.php', $configAdapter); // List of services Config::load('variables', __DIR__ . '/../config/variables.php', $configAdapter); // List of env variables Config::load('regions', __DIR__ . '/../config/regions.php', $configAdapter); // List of available regions diff --git a/app/init/models.php b/app/init/models.php index d432852660..3016d6f8cd 100644 --- a/app/init/models.php +++ b/app/init/models.php @@ -98,6 +98,7 @@ use Appwrite\Utopia\Response\Model\MFARecoveryCodes; use Appwrite\Utopia\Response\Model\MFAType; use Appwrite\Utopia\Response\Model\Migration; use Appwrite\Utopia\Response\Model\MigrationFirebaseProject; +use Appwrite\Utopia\Response\Model\MigrationKey; use Appwrite\Utopia\Response\Model\MigrationReport; use Appwrite\Utopia\Response\Model\Mock; use Appwrite\Utopia\Response\Model\MockNumber; @@ -357,6 +358,7 @@ Response::setModel(new Subscriber()); Response::setModel(new Target()); Response::setModel(new Migration()); Response::setModel(new MigrationReport()); +Response::setModel(new MigrationKey()); Response::setModel(new MigrationFirebaseProject()); // Tests (keep last) diff --git a/composer.json b/composer.json index 3dd24aa29f..37f7ad7442 100644 --- a/composer.json +++ b/composer.json @@ -69,7 +69,7 @@ "utopia-php/locale": "0.8.*", "utopia-php/logger": "0.6.*", "utopia-php/messaging": "0.20.*", - "utopia-php/migration": "1.6.*", + "utopia-php/migration": "dev-feat-platform-db-access as 1.5.0", "utopia-php/platform": "0.7.*", "utopia-php/pools": "1.*", "utopia-php/span": "1.1.*", diff --git a/composer.lock b/composer.lock index ccf03ce835..4461a8d2d9 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": "1fb043a556550f62ab27a0aad0b9332a", + "content-hash": "44e4887b0166d881da56e99dfcd284a4", "packages": [ { "name": "adhocore/jwt", @@ -9043,7 +9043,9 @@ ], "aliases": [], "minimum-stability": "dev", - "stability-flags": [], + "stability-flags": { + "utopia-php/migration": 20 + }, "prefer-stable": true, "prefer-lowest": false, "platform": { diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index 7bc6fe8d32..9e4f76957f 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -246,6 +246,8 @@ class Migrations extends Action $credentials['destinationApiKey'], $this->dbForProject, Config::getParam('collections', [])['databases']['collections'], + $this->dbForPlatform, + $this->project->getSequence(), ), DestinationCSV::getName() => new DestinationCSV( $this->deviceForFiles, @@ -286,9 +288,6 @@ class Migrations extends Action ); } - /** - * @throws Exception - */ protected function generateAPIKey(Document $project): string { $jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400, 0); diff --git a/src/Appwrite/Utopia/Response/Model/MigrationReport.php b/src/Appwrite/Utopia/Response/Model/MigrationReport.php index 7ebc22d22e..36d77044bd 100644 --- a/src/Appwrite/Utopia/Response/Model/MigrationReport.php +++ b/src/Appwrite/Utopia/Response/Model/MigrationReport.php @@ -53,6 +53,12 @@ class MigrationReport extends Model 'default' => 0, 'example' => 20, ]) + ->addRule(Resource::TYPE_PLATFORM, [ + 'type' => self::TYPE_INTEGER, + 'description' => 'Number of platforms to be migrated.', + 'default' => 0, + 'example' => 5, + ]) ->addRule(Resource::TYPE_SITE, [ 'type' => self::TYPE_INTEGER, 'description' => 'Number of sites to be migrated.',