diff --git a/app/config/errors.php b/app/config/errors.php index ed1d97115c..2a61b74270 100644 --- a/app/config/errors.php +++ b/app/config/errors.php @@ -749,4 +749,9 @@ return [ 'description' => 'Too many messages.', 'code' => 1013, ], + Exception::MIGRATION_PROVIDER_ERROR => [ + 'name' => Exception::MIGRATION_PROVIDER_ERROR, + 'description' => 'An error occurred on the provider\'s side. Please try again later.', + 'code' => 400, + ], ]; diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index d51c6adbf7..65a0520d1c 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -208,6 +208,16 @@ App::post('/v1/migrations/firebase') ->inject('queueForEvents') ->inject('queueForMigrations') ->action(function (array $resources, string $serviceAccount, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Migration $queueForMigrations) { + $serviceAccountData = json_decode($serviceAccount, true); + + if (empty($serviceAccountData)) { + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Invalid Service Account JSON'); + } + + if (!isset($serviceAccountData['project_id']) || !isset($serviceAccountData['client_email']) || !isset($serviceAccountData['private_key'])) { + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Invalid Service Account JSON'); + } + $migration = $dbForProject->createDocument('migrations', new Document([ '$id' => ID::unique(), 'status' => 'pending', @@ -449,15 +459,26 @@ App::get('/v1/migrations/appwrite/report') ->inject('project') ->inject('user') ->action(function (array $resources, string $endpoint, string $projectID, string $key, Response $response) { - try { - $appwrite = new Appwrite($projectID, $endpoint, $key); + $appwrite = new Appwrite($projectID, $endpoint, $key); - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic(new Document($appwrite->report($resources)), Response::MODEL_MIGRATION_REPORT); + try { + $report = $appwrite->report($resources); } catch (\Throwable $e) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Source Error: ' . $e->getMessage()); + switch ($e->getCode()) { + case 401: + throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, 'Source Error: ' . $e->getMessage()); + case 429: + throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED, 'Source Error: Rate Limit Exceeded, Is your Cloud Provider blocking Appwrite\'s IP?'); + case 500: + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Source Error: ' . $e->getMessage()); + } + + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Source Error: ' . $e->getMessage()); } + + $response + ->setStatusCode(Response::STATUS_CODE_OK) + ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); App::get('/v1/migrations/firebase/report') @@ -475,15 +496,36 @@ App::get('/v1/migrations/firebase/report') ->param('serviceAccount', '', new Text(65536), 'JSON of the Firebase service account credentials') ->inject('response') ->action(function (array $resources, string $serviceAccount, Response $response) { - try { - $firebase = new Firebase(json_decode($serviceAccount, true)); + $serviceAccount = json_decode($serviceAccount, true); - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic(new Document($firebase->report($resources)), Response::MODEL_MIGRATION_REPORT); - } catch (\Exception $e) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Source Error: ' . $e->getMessage()); + if (empty($serviceAccount)) { + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Invalid Service Account JSON'); } + + if (!isset($serviceAccount['project_id']) || !isset($serviceAccount['client_email']) || !isset($serviceAccount['private_key'])) { + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Invalid Service Account JSON'); + } + + $firebase = new Firebase($serviceAccount); + + try { + $report = $firebase->report($resources); + } catch (\Throwable $e) { + switch ($e->getCode()) { + case 401: + throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, 'Source Error: ' . $e->getMessage()); + case 429: + throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED, 'Source Error: Rate Limit Exceeded, Is your Cloud Provider blocking Appwrite\'s IP?'); + case 500: + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Source Error: ' . $e->getMessage()); + } + + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Source Error: ' . $e->getMessage()); + } + + $response + ->setStatusCode(Response::STATUS_CODE_OK) + ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); App::get('/v1/migrations/firebase/report/oauth') @@ -869,15 +911,26 @@ App::get('/v1/migrations/supabase/report') ->inject('response') ->inject('dbForProject') ->action(function (array $resources, string $endpoint, string $apiKey, string $databaseHost, string $username, string $password, int $port, Response $response) { - try { - $supabase = new Supabase($endpoint, $apiKey, $databaseHost, 'postgres', $username, $password, $port); + $supabase = new Supabase($endpoint, $apiKey, $databaseHost, 'postgres', $username, $password, $port); - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic(new Document($supabase->report($resources)), Response::MODEL_MIGRATION_REPORT); - } catch (\Exception $e) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Source Error: ' . $e->getMessage()); + try { + $report = $supabase->report($resources); + } catch (\Throwable $e) { + switch ($e->getCode()) { + case 401: + throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, 'Source Error: ' . $e->getMessage()); + case 429: + throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED, 'Source Error: Rate Limit Exceeded, Is your Cloud Provider blocking Appwrite\'s IP?'); + case 500: + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Source Error: ' . $e->getMessage()); + } + + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Source Error: ' . $e->getMessage()); } + + $response + ->setStatusCode(Response::STATUS_CODE_OK) + ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); App::get('/v1/migrations/nhost/report') @@ -901,15 +954,26 @@ App::get('/v1/migrations/nhost/report') ->param('port', 5432, new Integer(true), 'Source\'s Database Port.', true) ->inject('response') ->action(function (array $resources, string $subdomain, string $region, string $adminSecret, string $database, string $username, string $password, int $port, Response $response) { - try { - $nhost = new NHost($subdomain, $region, $adminSecret, $database, $username, $password, $port); + $nhost = new NHost($subdomain, $region, $adminSecret, $database, $username, $password, $port); - $response - ->setStatusCode(Response::STATUS_CODE_OK) - ->dynamic(new Document($nhost->report($resources)), Response::MODEL_MIGRATION_REPORT); - } catch (\Exception $e) { - throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Source Error: ' . $e->getMessage()); + try { + $report = $nhost->report($resources); + } catch (\Throwable $e) { + switch ($e->getCode()) { + case 401: + throw new Exception(Exception::GENERAL_UNAUTHORIZED_SCOPE, 'Source Error: ' . $e->getMessage()); + case 429: + throw new Exception(Exception::GENERAL_RATE_LIMIT_EXCEEDED, 'Source Error: Rate Limit Exceeded, Is your Cloud Provider blocking Appwrite\'s IP?'); + case 500: + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Source Error: ' . $e->getMessage()); + } + + throw new Exception(Exception::MIGRATION_PROVIDER_ERROR, 'Source Error: ' . $e->getMessage()); } + + $response + ->setStatusCode(Response::STATUS_CODE_OK) + ->dynamic(new Document($report), Response::MODEL_MIGRATION_REPORT); }); App::patch('/v1/migrations/:migrationId') diff --git a/src/Appwrite/Extend/Exception.php b/src/Appwrite/Extend/Exception.php index dc56932cde..285b8ea239 100644 --- a/src/Appwrite/Extend/Exception.php +++ b/src/Appwrite/Extend/Exception.php @@ -224,6 +224,7 @@ class Exception extends \Exception public const MIGRATION_NOT_FOUND = 'migration_not_found'; public const MIGRATION_ALREADY_EXISTS = 'migration_already_exists'; public const MIGRATION_IN_PROGRESS = 'migration_in_progress'; + public const MIGRATION_PROVIDER_ERROR = 'migration_provider_error'; /** Realtime */ public const REALTIME_MESSAGE_FORMAT_INVALID = 'realtime_message_format_invalid';