99use Appwrite \Enums \Compression ;
1010use Appwrite \Enums \Framework ;
1111use Appwrite \Enums \PasswordHash ;
12+ use Appwrite \Enums \ProjectProtocolId ;
1213use Appwrite \Enums \Runtime ;
1314use Appwrite \Enums \SmtpEncryption ;
1415use Appwrite \InputFile ;
1516use Appwrite \Services \Functions ;
1617use Appwrite \Services \Messaging ;
18+ use Appwrite \Services \Project ;
1719use Appwrite \Services \Sites ;
1820use Appwrite \Services \Storage ;
1921use Appwrite \Services \Teams ;
3739use Utopia \Migration \Destination ;
3840use Utopia \Migration \Exception ;
3941use Utopia \Migration \Resource ;
42+ use Utopia \Migration \Resources \Auth \AuthMethods ;
4043use Utopia \Migration \Resources \Auth \Hash ;
4144use Utopia \Migration \Resources \Auth \Membership ;
45+ use Utopia \Migration \Resources \Auth \Policies ;
4246use Utopia \Migration \Resources \Auth \Team ;
4347use Utopia \Migration \Resources \Auth \User ;
4448use Utopia \Migration \Resources \Database \Attribute ;
5660use Utopia \Migration \Resources \Messaging \Provider ;
5761use Utopia \Migration \Resources \Messaging \Subscriber ;
5862use Utopia \Migration \Resources \Messaging \Topic ;
63+ use Utopia \Migration \Resources \Settings \Labels ;
5964use Utopia \Migration \Resources \Settings \ProjectVariable ;
65+ use Utopia \Migration \Resources \Settings \Protocols ;
66+ use Utopia \Migration \Resources \Settings \Services as ServicesResource ;
6067use Utopia \Migration \Resources \Settings \Webhook ;
6168use Utopia \Migration \Resources \Sites \Deployment as SiteDeployment ;
6269use Utopia \Migration \Resources \Sites \EnvVar as SiteEnvVar ;
@@ -91,12 +98,13 @@ class Appwrite extends Destination
9198 ];
9299
93100 protected Client $ client ;
94- protected string $ project ;
101+ protected string $ projectId ;
95102
96103 protected string $ key ;
97104
98105 private Functions $ functions ;
99106 private Messaging $ messaging ;
107+ private Project $ project ;
100108 private Sites $ sites ;
101109 private Storage $ storage ;
102110 private Teams $ teams ;
@@ -170,7 +178,7 @@ public function __construct(
170178 protected OnDuplicate $ onDuplicate = OnDuplicate::Fail,
171179 ?callable $ getDatabaseDSN = null ,
172180 ) {
173- $ this ->project = $ project ;
181+ $ this ->projectId = $ project ;
174182 $ this ->endpoint = $ endpoint ;
175183 $ this ->key = $ key ;
176184
@@ -181,6 +189,7 @@ public function __construct(
181189
182190 $ this ->functions = new Functions ($ this ->client );
183191 $ this ->messaging = new Messaging ($ this ->client );
192+ $ this ->project = new Project ($ this ->client );
184193 $ this ->sites = new Sites ($ this ->client );
185194 $ this ->storage = new Storage ($ this ->client );
186195 $ this ->teams = new Teams ($ this ->client );
@@ -241,6 +250,8 @@ public static function getSupportedResources(): array
241250 Resource::TYPE_USER ,
242251 Resource::TYPE_TEAM ,
243252 Resource::TYPE_MEMBERSHIP ,
253+ Resource::TYPE_AUTH_METHODS ,
254+ Resource::TYPE_POLICIES ,
244255
245256 // Database
246257 Resource::TYPE_DATABASE ,
@@ -281,8 +292,11 @@ public static function getSupportedResources(): array
281292 Resource::TYPE_API_KEY ,
282293 Resource::TYPE_WEBHOOK ,
283294
284- // Settings
295+ // Project
285296 Resource::TYPE_PROJECT_VARIABLE ,
297+ Resource::TYPE_PROJECT_PROTOCOLS ,
298+ Resource::TYPE_PROJECT_LABELS ,
299+ Resource::TYPE_PROJECT_SERVICES ,
286300
287301 // Backups
288302 Resource::TYPE_BACKUP_POLICY ,
@@ -444,7 +458,7 @@ protected function import(array $resources, callable $callback): void
444458 Transfer::GROUP_SITES => $ this ->importSiteResource ($ resource ),
445459 Transfer::GROUP_INTEGRATIONS => $ this ->importIntegrationsResource ($ resource ),
446460 Transfer::GROUP_BACKUPS => $ this ->importBackupResource ($ resource ),
447- Transfer::GROUP_SETTINGS => $ this ->importSettingsResource ($ resource ),
461+ Transfer::GROUP_PROJECTS => $ this ->importProjectsResource ($ resource ),
448462 default => throw new \Exception ('Invalid resource group ' , Exception::CODE_VALIDATION ),
449463 };
450464 } catch (\Throwable $ e ) {
@@ -2160,6 +2174,14 @@ public function importAuthResource(Resource $resource): Resource
21602174 userId: $ user ->getId (),
21612175 );
21622176 break ;
2177+ case Resource::TYPE_AUTH_METHODS :
2178+ /** @var AuthMethods $resource */
2179+ $ this ->createAuthMethods ($ resource );
2180+ break ;
2181+ case Resource::TYPE_POLICIES :
2182+ /** @var Policies $resource */
2183+ $ this ->createPolicies ($ resource );
2184+ break ;
21632185 }
21642186
21652187 $ resource ->setStatus (Resource::STATUS_SUCCESS );
@@ -3100,13 +3122,25 @@ public function importIntegrationsResource(Resource $resource): Resource
31003122 return $ resource ;
31013123 }
31023124
3103- public function importSettingsResource (Resource $ resource ): Resource
3125+ public function importProjectsResource (Resource $ resource ): Resource
31043126 {
31053127 switch ($ resource ->getName ()) {
31063128 case Resource::TYPE_PROJECT_VARIABLE :
31073129 /** @var ProjectVariable $resource */
31083130 $ this ->createProjectVariable ($ resource );
31093131 break ;
3132+ case Resource::TYPE_PROJECT_PROTOCOLS :
3133+ /** @var Protocols $resource */
3134+ $ this ->createProtocols ($ resource );
3135+ break ;
3136+ case Resource::TYPE_PROJECT_LABELS :
3137+ /** @var Labels $resource */
3138+ $ this ->createLabels ($ resource );
3139+ break ;
3140+ case Resource::TYPE_PROJECT_SERVICES :
3141+ /** @var ServicesResource $resource */
3142+ $ this ->createServices ($ resource );
3143+ break ;
31103144 }
31113145
31123146 if ($ resource ->getStatus () !== Resource::STATUS_SKIPPED ) {
@@ -3155,6 +3189,61 @@ protected function createProjectVariable(ProjectVariable $resource): bool
31553189 return true ;
31563190 }
31573191
3192+ protected function createProtocols (Protocols $ resource ): bool
3193+ {
3194+ $ project = $ this ->dbForPlatform ->getDocument ('projects ' , $ this ->projectId );
3195+ $ apis = $ project ->getAttribute ('apis ' , []);
3196+
3197+ $ apis [(string ) ProjectProtocolId::REST ()] = $ resource ->getRest ();
3198+ $ apis [(string ) ProjectProtocolId::GRAPHQL ()] = $ resource ->getGraphql ();
3199+ $ apis [(string ) ProjectProtocolId::WEBSOCKET ()] = $ resource ->getWebsocket ();
3200+
3201+ $ this ->dbForPlatform ->getAuthorization ()->skip (fn () => $ this ->dbForPlatform ->updateDocument (
3202+ 'projects ' ,
3203+ $ this ->projectId ,
3204+ new UtopiaDocument (['apis ' => $ apis ]),
3205+ ));
3206+
3207+ $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->projectId );
3208+
3209+ return true ;
3210+ }
3211+
3212+ protected function createLabels (Labels $ resource ): bool
3213+ {
3214+ $ labels = \array_values (\array_unique ($ resource ->getLabels ()));
3215+
3216+ $ this ->dbForPlatform ->getAuthorization ()->skip (fn () => $ this ->dbForPlatform ->updateDocument (
3217+ 'projects ' ,
3218+ $ this ->projectId ,
3219+ new UtopiaDocument (['labels ' => $ labels ]),
3220+ ));
3221+
3222+ $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->projectId );
3223+
3224+ return true ;
3225+ }
3226+
3227+ protected function createServices (ServicesResource $ resource ): bool
3228+ {
3229+ $ project = $ this ->dbForPlatform ->getDocument ('projects ' , $ this ->projectId );
3230+ $ services = $ project ->getAttribute ('services ' , []);
3231+
3232+ foreach ($ resource ->getServices () as $ serviceId => $ enabled ) {
3233+ $ services [$ serviceId ] = (bool ) $ enabled ;
3234+ }
3235+
3236+ $ this ->dbForPlatform ->getAuthorization ()->skip (fn () => $ this ->dbForPlatform ->updateDocument (
3237+ 'projects ' ,
3238+ $ this ->projectId ,
3239+ new UtopiaDocument (['services ' => $ services ]),
3240+ ));
3241+
3242+ $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->projectId );
3243+
3244+ return true ;
3245+ }
3246+
31583247 protected function createWebhook (Webhook $ resource ): bool
31593248 {
31603249 $ existing = $ this ->dbForPlatform ->findOne ('webhooks ' , [
@@ -3175,7 +3264,7 @@ protected function createWebhook(Webhook $resource): bool
31753264 '$id ' => ID ::unique (),
31763265 '$permissions ' => $ resource ->getPermissions (),
31773266 'projectInternalId ' => $ this ->projectInternalId ,
3178- 'projectId ' => $ this ->project ,
3267+ 'projectId ' => $ this ->projectId ,
31793268 'name ' => $ resource ->getWebhookName (),
31803269 'events ' => $ resource ->getEvents (),
31813270 'url ' => $ resource ->getUrl (),
@@ -3194,7 +3283,7 @@ protected function createWebhook(Webhook $resource): bool
31943283 return false ;
31953284 }
31963285
3197- $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->project );
3286+ $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->projectId );
31983287
31993288 return true ;
32003289 }
@@ -3205,7 +3294,7 @@ protected function createWebhook(Webhook $resource): bool
32053294 protected function createPlatform (Platform $ resource ): bool
32063295 {
32073296 $ existing = $ this ->dbForPlatform ->findOne ('platforms ' , [
3208- Query::equal ('projectId ' , [$ this ->project ]),
3297+ Query::equal ('projectId ' , [$ this ->projectId ]),
32093298 Query::equal ('type ' , [$ resource ->getType ()]),
32103299 Query::equal ('name ' , [$ resource ->getPlatformName ()]),
32113300 ]);
@@ -3223,7 +3312,7 @@ protected function createPlatform(Platform $resource): bool
32233312 '$id ' => ID ::unique (),
32243313 '$permissions ' => $ resource ->getPermissions (),
32253314 'projectInternalId ' => $ this ->projectInternalId ,
3226- 'projectId ' => $ this ->project ,
3315+ 'projectId ' => $ this ->projectId ,
32273316 'type ' => $ resource ->getType (),
32283317 'name ' => $ resource ->getPlatformName (),
32293318 'key ' => $ resource ->getKey (),
@@ -3237,7 +3326,72 @@ protected function createPlatform(Platform $resource): bool
32373326 return false ;
32383327 }
32393328
3240- $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->project );
3329+ $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->projectId );
3330+
3331+ return true ;
3332+ }
3333+
3334+ /**
3335+ * Storage keys mirror app/config/auth.php, not the SDK enum values.
3336+ * Shares the `auths` map with createPolicies — read-then-merge.
3337+ */
3338+ protected function createAuthMethods (AuthMethods $ resource ): bool
3339+ {
3340+ $ project = $ this ->dbForPlatform ->getDocument ('projects ' , $ this ->projectId );
3341+ $ auths = $ project ->getAttribute ('auths ' , []);
3342+
3343+ $ auths ['emailPassword ' ] = $ resource ->getEmailPassword ();
3344+ $ auths ['usersAuthMagicURL ' ] = $ resource ->getMagicURL ();
3345+ $ auths ['emailOtp ' ] = $ resource ->getEmailOtp ();
3346+ $ auths ['anonymous ' ] = $ resource ->getAnonymous ();
3347+ $ auths ['invites ' ] = $ resource ->getInvites ();
3348+ $ auths ['JWT ' ] = $ resource ->getJwt ();
3349+ $ auths ['phone ' ] = $ resource ->getPhone ();
3350+
3351+ $ this ->dbForPlatform ->getAuthorization ()->skip (fn () => $ this ->dbForPlatform ->updateDocument (
3352+ 'projects ' ,
3353+ $ this ->projectId ,
3354+ new UtopiaDocument (['auths ' => $ auths ]),
3355+ ));
3356+
3357+ $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->projectId );
3358+
3359+ return true ;
3360+ }
3361+
3362+ /**
3363+ * Direct DB write — SDK policy setters reject `total: 0` but `0` is the
3364+ * storage value for "disabled". Shares the `auths` map with
3365+ * createAuthMethods — read-then-merge.
3366+ */
3367+ protected function createPolicies (Policies $ resource ): bool
3368+ {
3369+ $ project = $ this ->dbForPlatform ->getDocument ('projects ' , $ this ->projectId );
3370+ $ auths = $ project ->getAttribute ('auths ' , []);
3371+
3372+ $ auths ['passwordHistory ' ] = $ resource ->getPasswordHistory ();
3373+ $ auths ['duration ' ] = $ resource ->getSessionDuration ();
3374+ $ auths ['maxSessions ' ] = $ resource ->getSessionsLimit ();
3375+ $ auths ['limit ' ] = $ resource ->getUserLimit ();
3376+
3377+ $ auths ['passwordDictionary ' ] = $ resource ->getPasswordDictionary ();
3378+ $ auths ['personalDataCheck ' ] = $ resource ->getPersonalDataCheck ();
3379+ $ auths ['sessionAlerts ' ] = $ resource ->getSessionAlerts ();
3380+ $ auths ['invalidateSessions ' ] = $ resource ->getSessionInvalidation ();
3381+
3382+ $ auths ['membershipsUserId ' ] = $ resource ->getMembershipsUserId ();
3383+ $ auths ['membershipsUserEmail ' ] = $ resource ->getMembershipsUserEmail ();
3384+ $ auths ['membershipsUserName ' ] = $ resource ->getMembershipsUserName ();
3385+ $ auths ['membershipsMfa ' ] = $ resource ->getMembershipsUserMfa ();
3386+ $ auths ['membershipsUserPhone ' ] = $ resource ->getMembershipsUserPhone ();
3387+
3388+ $ this ->dbForPlatform ->getAuthorization ()->skip (fn () => $ this ->dbForPlatform ->updateDocument (
3389+ 'projects ' ,
3390+ $ this ->projectId ,
3391+ new UtopiaDocument (['auths ' => $ auths ]),
3392+ ));
3393+
3394+ $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->projectId );
32413395
32423396 return true ;
32433397 }
@@ -3264,7 +3418,7 @@ protected function createApiKey(ApiKey $resource): bool
32643418 '$id ' => ID ::unique (),
32653419 '$permissions ' => $ resource ->getPermissions (),
32663420 'resourceInternalId ' => $ this ->projectInternalId ,
3267- 'resourceId ' => $ this ->project ,
3421+ 'resourceId ' => $ this ->projectId ,
32683422 'resourceType ' => 'projects ' ,
32693423 'name ' => $ resource ->getApiKeyName (),
32703424 'scopes ' => $ resource ->getScopes (),
@@ -3280,7 +3434,7 @@ protected function createApiKey(ApiKey $resource): bool
32803434 return false ;
32813435 }
32823436
3283- $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->project );
3437+ $ this ->dbForPlatform ->purgeCachedDocument ('projects ' , $ this ->projectId );
32843438
32853439 return true ;
32863440 }
0 commit comments