Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions src/Migration/Destinations/Appwrite.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,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;
Expand Down Expand Up @@ -298,6 +299,7 @@ public static function getSupportedResources(): array
Resource::TYPE_PROTOCOLS,
Resource::TYPE_LABELS,
Resource::TYPE_SERVICES,
Resource::TYPE_SMTP,

// Backups
Resource::TYPE_BACKUP_POLICY,
Expand Down Expand Up @@ -3142,6 +3144,10 @@ public function importSettingsResource(Resource $resource): Resource
/** @var ServicesResource $resource */
$this->createServices($resource);
break;
case Resource::TYPE_SMTP:
/** @var SMTP $resource */
$this->createSMTP($resource);
break;
}

if ($resource->getStatus() !== Resource::STATUS_SKIPPED) {
Expand Down Expand Up @@ -3259,6 +3265,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();
Comment thread
premtsd-code marked this conversation as resolved.
$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', [
Expand Down
2 changes: 2 additions & 0 deletions src/Migration/Resource.php
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ abstract class Resource implements \JsonSerializable
public const TYPE_PROTOCOLS = 'protocols';
public const TYPE_LABELS = 'labels';
public const TYPE_SERVICES = 'services';
public const TYPE_SMTP = 'smtp';

// Messaging
public const TYPE_SUBSCRIBER = 'subscriber';
Expand Down Expand Up @@ -133,6 +134,7 @@ abstract class Resource implements \JsonSerializable
self::TYPE_PROTOCOLS,
self::TYPE_LABELS,
self::TYPE_SERVICES,
self::TYPE_SMTP,
self::TYPE_PROVIDER,
self::TYPE_TOPIC,
self::TYPE_SUBSCRIBER,
Expand Down
129 changes: 129 additions & 0 deletions src/Migration/Resources/Settings/SMTP.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
<?php

namespace Utopia\Migration\Resources\Settings;

use Utopia\Migration\Resource;
use Utopia\Migration\Transfer;

/**
* Singleton resource representing the project's custom SMTP configuration.
* Password is not migrated — the source API never exposes it.
*/
class SMTP extends Resource
{
public function __construct(
string $id,
private readonly bool $enabled = false,
private readonly string $senderName = '',
private readonly string $senderEmail = '',
private readonly string $replyToName = '',
private readonly string $replyToEmail = '',
private readonly string $host = '',
private readonly int $port = 0,
private readonly string $username = '',
private readonly string $secure = '',
string $createdAt = '',
string $updatedAt = '',
) {
$this->id = $id;
$this->createdAt = $createdAt;
$this->updatedAt = $updatedAt;
}

/**
* @param array<string, mixed> $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<string, mixed>
*/
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_SETTINGS;
}

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;
}
}
43 changes: 43 additions & 0 deletions src/Migration/Sources/Appwrite.php
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,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;
Expand Down Expand Up @@ -236,6 +237,7 @@ public static function getSupportedResources(): array
Resource::TYPE_PROTOCOLS,
Resource::TYPE_LABELS,
Resource::TYPE_SERVICES,
Resource::TYPE_SMTP,
];
}

Expand Down Expand Up @@ -1609,6 +1611,11 @@ private function reportSettings(array $resources, array &$report, array $resourc
// Singleton — one services config per project.
$report[Resource::TYPE_SERVICES] = 1;
}

if (\in_array(Resource::TYPE_SMTP, $resources)) {
// Singleton — one SMTP config per project.
$report[Resource::TYPE_SMTP] = 1;
}
}

/**
Expand Down Expand Up @@ -1686,6 +1693,20 @@ protected function exportGroupSettings(int $batchSize, array $resources): void
previous: $e
));
}

try {
if (\in_array(Resource::TYPE_SMTP, $resources)) {
$this->exportSMTP();
}
} catch (\Throwable $e) {
$this->addError(new Exception(
Resource::TYPE_SMTP,
Transfer::GROUP_SETTINGS,
message: $e->getMessage(),
code: (int) $e->getCode() ?: Exception::CODE_INTERNAL,
previous: $e
));
}
}

private function exportServices(): void
Expand Down Expand Up @@ -1758,6 +1779,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
*/
Expand Down
2 changes: 2 additions & 0 deletions src/Migration/Transfer.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class Transfer
Resource::TYPE_PROTOCOLS,
Resource::TYPE_LABELS,
Resource::TYPE_SERVICES,
Resource::TYPE_SMTP,
];

public const GROUP_BACKUPS_RESOURCES = [
Expand Down Expand Up @@ -150,6 +151,7 @@ class Transfer
Resource::TYPE_PROTOCOLS,
Resource::TYPE_LABELS,
Resource::TYPE_SERVICES,
Resource::TYPE_SMTP,

// legacy
Resource::TYPE_DOCUMENT,
Expand Down
Loading