mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Move settings migration to source adapter via console API key
This commit is contained in:
@@ -28,8 +28,6 @@ use Utopia\Migration\Destinations\CSV as DestinationCSV;
|
||||
use Utopia\Migration\Exception as MigrationException;
|
||||
use Utopia\Migration\Resource;
|
||||
use Utopia\Migration\Resources\Database\Database as ResourceDatabase;
|
||||
use Utopia\Migration\Resources\Settings\Key as ResourceKey;
|
||||
use Utopia\Migration\Resources\Settings\Platform as ResourcePlatform;
|
||||
use Utopia\Migration\Resources\Database\Row as ResourceRow;
|
||||
use Utopia\Migration\Resources\Database\Table as ResourceTable;
|
||||
use Utopia\Migration\Source;
|
||||
@@ -216,6 +214,8 @@ class Migrations extends Action
|
||||
$dataSource,
|
||||
$database,
|
||||
$queries,
|
||||
$credentials['consoleApiKey'] ?? '',
|
||||
$credentials['projectId'] ?? '',
|
||||
),
|
||||
CSV::getName() => new CSV(
|
||||
$resourceId,
|
||||
@@ -325,6 +325,29 @@ class Migrations extends Action
|
||||
return API_KEY_DYNAMIC . '_' . $apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a console-scoped dynamic API key for settings migration.
|
||||
*
|
||||
* This key allows the source adapter to read platforms and keys
|
||||
* via the console API endpoints (same-instance only).
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
protected function generateConsoleAPIKey(): string
|
||||
{
|
||||
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 86400, 0);
|
||||
|
||||
$apiKey = $jwt->encode([
|
||||
'projectId' => 'console',
|
||||
'scopes' => [
|
||||
'platforms.read',
|
||||
'keys.read',
|
||||
],
|
||||
]);
|
||||
|
||||
return API_KEY_DYNAMIC . '_' . $apiKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws AuthorizationException
|
||||
* @throws Conflict
|
||||
@@ -344,6 +367,7 @@ class Migrations extends Action
|
||||
$project = $this->project;
|
||||
|
||||
$tempAPIKey = $this->generateAPIKey($project);
|
||||
$tempConsoleAPIKey = $this->generateConsoleAPIKey();
|
||||
|
||||
$transfer = $source = $destination = null;
|
||||
|
||||
@@ -361,6 +385,7 @@ class Migrations extends Action
|
||||
$credentials['projectId'] = $credentials['projectId'] ?? $project->getId();
|
||||
$credentials['apiKey'] = $credentials['apiKey'] ?? $tempAPIKey;
|
||||
$credentials['endpoint'] = $credentials['endpoint'] ?? $endpoint;
|
||||
$credentials['consoleApiKey'] = $credentials['consoleApiKey'] ?? $tempConsoleAPIKey;
|
||||
}
|
||||
|
||||
if ($migration->getAttribute('destination') === DestinationAppwrite::getName()) {
|
||||
@@ -430,8 +455,6 @@ class Migrations extends Action
|
||||
$migration->getAttribute('resourceType')
|
||||
);
|
||||
|
||||
$this->migrateSettings($migration, $destination);
|
||||
|
||||
$destination->shutdown();
|
||||
$source->shutdown();
|
||||
}
|
||||
@@ -520,142 +543,6 @@ class Migrations extends Action
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate platforms and API keys at the worker level.
|
||||
*
|
||||
* Settings (platforms, keys) live in dbForPlatform, not dbForProject.
|
||||
* Instead of passing dbForPlatform to the source adapter, the worker
|
||||
* reads from the source project and writes via the destination adapter.
|
||||
*/
|
||||
protected function migrateSettings(Document $migration, Destination $destination): void
|
||||
{
|
||||
if (!($destination instanceof DestinationAppwrite)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$resources = $migration->getAttribute('resources', []);
|
||||
$credentials = $migration->getAttribute('credentials', []);
|
||||
|
||||
$hasPlatforms = \in_array(Resource::TYPE_PLATFORM, $resources);
|
||||
$hasKeys = \in_array(Resource::TYPE_KEY, $resources);
|
||||
|
||||
if (!$hasPlatforms && !$hasKeys) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sourceProjectId = $credentials['projectId'] ?? '';
|
||||
if (empty($sourceProjectId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sourceProject = $this->dbForPlatform->getDocument('projects', $sourceProjectId);
|
||||
if ($sourceProject->isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$sourceInternalId = $sourceProject->getSequence();
|
||||
|
||||
if ($hasPlatforms) {
|
||||
$this->migratePlatforms($sourceInternalId, $destination);
|
||||
}
|
||||
|
||||
if ($hasKeys) {
|
||||
$this->migrateKeys($sourceInternalId, $destination);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read platforms from source project and import via destination.
|
||||
*/
|
||||
protected function migratePlatforms(string $sourceInternalId, DestinationAppwrite $destination): void
|
||||
{
|
||||
$lastDocument = null;
|
||||
$batchSize = 100;
|
||||
|
||||
while (true) {
|
||||
$queries = [
|
||||
Query::equal('projectInternalId', [$sourceInternalId]),
|
||||
Query::limit($batchSize),
|
||||
];
|
||||
|
||||
if ($lastDocument !== null) {
|
||||
$queries[] = Query::cursorAfter($lastDocument);
|
||||
}
|
||||
|
||||
$platforms = $this->dbForPlatform->find('platforms', $queries);
|
||||
|
||||
if (empty($platforms)) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($platforms as $platform) {
|
||||
$resource = new ResourcePlatform(
|
||||
'unique()',
|
||||
$platform->getAttribute('type', ''),
|
||||
$platform->getAttribute('name', ''),
|
||||
$platform->getAttribute('key', ''),
|
||||
$platform->getAttribute('store', ''),
|
||||
$platform->getAttribute('hostname', ''),
|
||||
);
|
||||
$resource->setPermissions($platform->getPermissions());
|
||||
$destination->importSettingsResource($resource);
|
||||
}
|
||||
|
||||
$lastDocument = \end($platforms);
|
||||
|
||||
if (\count($platforms) < $batchSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read API keys from source project and import via destination.
|
||||
*/
|
||||
protected function migrateKeys(string $sourceInternalId, DestinationAppwrite $destination): void
|
||||
{
|
||||
$lastDocument = null;
|
||||
$batchSize = 100;
|
||||
|
||||
while (true) {
|
||||
$queries = [
|
||||
Query::equal('resourceType', ['projects']),
|
||||
Query::equal('resourceInternalId', [$sourceInternalId]),
|
||||
Query::limit($batchSize),
|
||||
];
|
||||
|
||||
if ($lastDocument !== null) {
|
||||
$queries[] = Query::cursorAfter($lastDocument);
|
||||
}
|
||||
|
||||
$keys = $this->dbForPlatform->find('keys', $queries);
|
||||
|
||||
if (empty($keys)) {
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($keys as $key) {
|
||||
$resource = new ResourceKey(
|
||||
'unique()',
|
||||
$key->getAttribute('name', ''),
|
||||
$key->getAttribute('scopes', []),
|
||||
$key->getAttribute('secret', ''),
|
||||
$key->getAttribute('expire', null),
|
||||
$key->getAttribute('accessedAt', null),
|
||||
$key->getAttribute('sdks', []),
|
||||
);
|
||||
$resource->setPermissions($key->getPermissions());
|
||||
$destination->importSettingsResource($resource);
|
||||
}
|
||||
|
||||
$lastDocument = \end($keys);
|
||||
|
||||
if (\count($keys) < $batchSize) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle actions to be performed when a CSV export migration is successfully completed
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user