diff --git a/src/Migration/Destinations/Appwrite.php b/src/Migration/Destinations/Appwrite.php index 2b89e1be..94417254 100644 --- a/src/Migration/Destinations/Appwrite.php +++ b/src/Migration/Destinations/Appwrite.php @@ -64,6 +64,7 @@ use Utopia\Migration\Resources\Settings\ProjectVariable; use Utopia\Migration\Resources\Settings\Protocols; use Utopia\Migration\Resources\Settings\Services as ServicesResource; +use Utopia\Migration\Resources\Settings\SMTP; use Utopia\Migration\Resources\Settings\Webhook; use Utopia\Migration\Resources\Sites\Deployment as SiteDeployment; use Utopia\Migration\Resources\Sites\EnvVar as SiteEnvVar; @@ -291,6 +292,7 @@ public static function getSupportedResources(): array Resource::TYPE_PLATFORM, Resource::TYPE_API_KEY, Resource::TYPE_WEBHOOK, + Resource::TYPE_SMTP, // Project Resource::TYPE_PROJECT_VARIABLE, @@ -3113,6 +3115,10 @@ public function importIntegrationsResource(Resource $resource): Resource /** @var Webhook $resource */ $this->createWebhook($resource); break; + case Resource::TYPE_SMTP: + /** @var SMTP $resource */ + $this->createSMTP($resource); + break; } if ($resource->getStatus() !== Resource::STATUS_SKIPPED) { @@ -3244,6 +3250,36 @@ protected function createServices(ServicesResource $resource): bool return true; } + /** + * Password is intentionally not copied — source API never exposes it. + * Read-then-merge preserves the destination's existing password. + */ + protected function createSMTP(SMTP $resource): bool + { + $project = $this->dbForPlatform->getDocument('projects', $this->projectId); + $smtp = $project->getAttribute('smtp', []); + + $smtp['enabled'] = $resource->getEnabled(); + $smtp['senderName'] = $resource->getSenderName(); + $smtp['senderEmail'] = $resource->getSenderEmail(); + $smtp['replyToName'] = $resource->getReplyToName(); + $smtp['replyToEmail'] = $resource->getReplyToEmail(); + $smtp['host'] = $resource->getHost(); + $smtp['port'] = $resource->getPort(); + $smtp['username'] = $resource->getUsername(); + $smtp['secure'] = $resource->getSecure(); + + $this->dbForPlatform->getAuthorization()->skip(fn () => $this->dbForPlatform->updateDocument( + 'projects', + $this->projectId, + new UtopiaDocument(['smtp' => $smtp]), + )); + + $this->dbForPlatform->purgeCachedDocument('projects', $this->projectId); + + return true; + } + protected function createWebhook(Webhook $resource): bool { $existing = $this->dbForPlatform->findOne('webhooks', [ diff --git a/src/Migration/Resource.php b/src/Migration/Resource.php index 0edb63bd..0c56694b 100644 --- a/src/Migration/Resource.php +++ b/src/Migration/Resource.php @@ -79,6 +79,7 @@ abstract class Resource implements \JsonSerializable public const TYPE_PLATFORM = 'platform'; public const TYPE_API_KEY = 'api-key'; public const TYPE_WEBHOOK = 'webhook'; + public const TYPE_SMTP = 'smtp'; // Project (per-project singleton/settings resources) public const TYPE_PROJECT_VARIABLE = 'project-variable'; @@ -129,6 +130,7 @@ abstract class Resource implements \JsonSerializable self::TYPE_PLATFORM, self::TYPE_API_KEY, self::TYPE_WEBHOOK, + self::TYPE_SMTP, self::TYPE_PROJECT_VARIABLE, self::TYPE_PROJECT_PROTOCOLS, self::TYPE_PROJECT_LABELS, diff --git a/src/Migration/Resources/Settings/SMTP.php b/src/Migration/Resources/Settings/SMTP.php new file mode 100644 index 00000000..ea14895c --- /dev/null +++ b/src/Migration/Resources/Settings/SMTP.php @@ -0,0 +1,129 @@ +id = $id; + $this->createdAt = $createdAt; + $this->updatedAt = $updatedAt; + } + + /** + * @param array $array + */ + public static function fromArray(array $array): self + { + return new self( + $array['id'], + (bool) ($array['enabled'] ?? false), + (string) ($array['senderName'] ?? ''), + (string) ($array['senderEmail'] ?? ''), + (string) ($array['replyToName'] ?? ''), + (string) ($array['replyToEmail'] ?? ''), + (string) ($array['host'] ?? ''), + (int) ($array['port'] ?? 0), + (string) ($array['username'] ?? ''), + (string) ($array['secure'] ?? ''), + createdAt: $array['createdAt'] ?? '', + updatedAt: $array['updatedAt'] ?? '', + ); + } + + /** + * @return array + */ + public function jsonSerialize(): array + { + return [ + 'id' => $this->id, + 'enabled' => $this->enabled, + 'senderName' => $this->senderName, + 'senderEmail' => $this->senderEmail, + 'replyToName' => $this->replyToName, + 'replyToEmail' => $this->replyToEmail, + 'host' => $this->host, + 'port' => $this->port, + 'username' => $this->username, + 'secure' => $this->secure, + 'createdAt' => $this->createdAt, + 'updatedAt' => $this->updatedAt, + ]; + } + + public static function getName(): string + { + return Resource::TYPE_SMTP; + } + + public function getGroup(): string + { + return Transfer::GROUP_INTEGRATIONS; + } + + public function getEnabled(): bool + { + return $this->enabled; + } + + public function getSenderName(): string + { + return $this->senderName; + } + + public function getSenderEmail(): string + { + return $this->senderEmail; + } + + public function getReplyToName(): string + { + return $this->replyToName; + } + + public function getReplyToEmail(): string + { + return $this->replyToEmail; + } + + public function getHost(): string + { + return $this->host; + } + + public function getPort(): int + { + return $this->port; + } + + public function getUsername(): string + { + return $this->username; + } + + public function getSecure(): string + { + return $this->secure; + } +} diff --git a/src/Migration/Sources/Appwrite.php b/src/Migration/Sources/Appwrite.php index c3397ac8..72658951 100644 --- a/src/Migration/Sources/Appwrite.php +++ b/src/Migration/Sources/Appwrite.php @@ -71,6 +71,7 @@ use Utopia\Migration\Resources\Settings\ProjectVariable; use Utopia\Migration\Resources\Settings\Protocols; use Utopia\Migration\Resources\Settings\Services as ServicesResource; +use Utopia\Migration\Resources\Settings\SMTP; use Utopia\Migration\Resources\Settings\Webhook; use Utopia\Migration\Resources\Sites\Deployment as SiteDeployment; use Utopia\Migration\Resources\Sites\EnvVar as SiteEnvVar; @@ -226,6 +227,7 @@ public static function getSupportedResources(): array Resource::TYPE_PLATFORM, Resource::TYPE_API_KEY, Resource::TYPE_WEBHOOK, + Resource::TYPE_SMTP, // Backups Resource::TYPE_BACKUP_POLICY, @@ -1658,6 +1660,7 @@ protected function exportGroupProjects(int $batchSize, array $resources): void previous: $e )); } + } private function exportServices(): void @@ -1714,6 +1717,28 @@ private function exportProtocols(): void $this->callback([$protocols]); } + private function exportSMTP(): void + { + $project = $this->project->get(); + + $smtp = new SMTP( + $this->projectId, + $project->smtpEnabled, + $project->smtpSenderName, + $project->smtpSenderEmail, + $project->smtpReplyToName, + $project->smtpReplyToEmail, + $project->smtpHost, + $project->smtpPort, + $project->smtpUsername, + $project->smtpSecure, + createdAt: $project->createdAt, + updatedAt: $project->updatedAt, + ); + + $this->callback([$smtp]); + } + /** * @throws AppwriteException */ @@ -2630,6 +2655,11 @@ private function reportIntegrations(array $resources, array &$report, array $res $report[Resource::TYPE_WEBHOOK] = 0; } } + + if (\in_array(Resource::TYPE_SMTP, $resources)) { + // Singleton — one SMTP config per project. + $report[Resource::TYPE_SMTP] = 1; + } } /** @@ -2703,6 +2733,20 @@ protected function exportGroupIntegrations(int $batchSize, array $resources): vo )); } } + + try { + if (\in_array(Resource::TYPE_SMTP, $resources)) { + $this->exportSMTP(); + } + } catch (\Throwable $e) { + $this->addError(new Exception( + Resource::TYPE_SMTP, + Transfer::GROUP_INTEGRATIONS, + message: $e->getMessage(), + code: (int) $e->getCode() ?: Exception::CODE_INTERNAL, + previous: $e + )); + } } /** diff --git a/src/Migration/Transfer.php b/src/Migration/Transfer.php index f139196f..b446446b 100644 --- a/src/Migration/Transfer.php +++ b/src/Migration/Transfer.php @@ -68,6 +68,7 @@ class Transfer Resource::TYPE_PLATFORM, Resource::TYPE_API_KEY, Resource::TYPE_WEBHOOK, + Resource::TYPE_SMTP, ]; public const GROUP_DOCUMENTSDB_RESOURCES = [ Resource::TYPE_DATABASE_DOCUMENTSDB, @@ -144,6 +145,7 @@ class Transfer Resource::TYPE_PLATFORM, Resource::TYPE_API_KEY, Resource::TYPE_WEBHOOK, + Resource::TYPE_SMTP, // Project Resource::TYPE_PROJECT_VARIABLE,