diff --git a/app/controllers/api/users.php b/app/controllers/api/users.php index a212b23448..bf2435936a 100644 --- a/app/controllers/api/users.php +++ b/app/controllers/api/users.php @@ -9,6 +9,8 @@ use Appwrite\Auth\Validator\PasswordDictionary; use Appwrite\Auth\Validator\PasswordHistory; use Appwrite\Auth\Validator\PersonalData; use Appwrite\Auth\Validator\Phone; +use Appwrite\Deletes\Identities as DeleteIdentities; +use Appwrite\Deletes\Targets as DeleteTargets; use Appwrite\Detector\Detector; use Appwrite\Event\Delete; use Appwrite\Event\Event; @@ -2275,6 +2277,8 @@ App::delete('/v1/users/:userId') $clone = clone $user; $dbForProject->deleteDocument('users', $userId); + DeleteIdentities::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()])); + DeleteTargets::delete($dbForProject, Query::equal('userInternalId', [$user->getInternalId()])); $queueForDeletes ->setType(DELETE_TYPE_DOCUMENT) diff --git a/src/Appwrite/Deletes/Identities.php b/src/Appwrite/Deletes/Identities.php new file mode 100644 index 0000000000..09dac185a1 --- /dev/null +++ b/src/Appwrite/Deletes/Identities.php @@ -0,0 +1,22 @@ +deleteDocuments( + 'identities', + [ + $query, + Query::orderAsc() + ], + Database::DELETE_BATCH_SIZE + ); + } + +} diff --git a/src/Appwrite/Deletes/Targets.php b/src/Appwrite/Deletes/Targets.php new file mode 100644 index 0000000000..95e744ddf1 --- /dev/null +++ b/src/Appwrite/Deletes/Targets.php @@ -0,0 +1,56 @@ +deleteDocuments( + 'targets', + [ + $query, + Query::orderAsc() + ], + Database::DELETE_BATCH_SIZE, + fn (Document $target) => self::deleteSubscribers($database, $target) + ); + } + + public static function deleteSubscribers(Database $database, Document $target): void + { + $database->deleteDocuments( + 'subscribers', + [ + Query::equal('targetInternalId', [$target->getInternalId()]), + Query::orderAsc(), + ], + Database::DELETE_BATCH_SIZE, + function (Document $subscriber) use ($database, $target) { + $topicId = $subscriber->getAttribute('topicId'); + $topicInternalId = $subscriber->getAttribute('topicInternalId'); + $topic = $database->getDocument('topics', $topicId); + if (!$topic->isEmpty() && $topic->getInternalId() === $topicInternalId) { + $totalAttribute = match ($target->getAttribute('providerType')) { + MESSAGE_TYPE_EMAIL => 'emailTotal', + MESSAGE_TYPE_SMS => 'smsTotal', + MESSAGE_TYPE_PUSH => 'pushTotal', + default => throw new Exception('Invalid target provider type'), + }; + $database->decreaseDocumentAttribute( + 'topics', + $topicId, + $totalAttribute, + min: 0 + ); + } + } + ); + } + +} diff --git a/src/Appwrite/Platform/Workers/Deletes.php b/src/Appwrite/Platform/Workers/Deletes.php index f789e8a146..427772a6e0 100644 --- a/src/Appwrite/Platform/Workers/Deletes.php +++ b/src/Appwrite/Platform/Workers/Deletes.php @@ -4,6 +4,8 @@ namespace Appwrite\Platform\Workers; use Appwrite\Auth\Auth; use Appwrite\Certificates\Adapter as CertificatesAdapter; +use Appwrite\Deletes\Identities; +use Appwrite\Deletes\Targets; use Appwrite\Extend\Exception; use Executor\Executor; use Throwable; @@ -146,7 +148,7 @@ class Deletes extends Action $this->deleteTopic($project, $getProjectDB, $document); break; case DELETE_TYPE_TARGET: - $this->deleteTargetSubscribers($project, $getProjectDB, $document); + Targets::deleteSubscribers($getProjectDB($project), $document); break; case DELETE_TYPE_EXPIRED_TARGETS: $this->deleteExpiredTargets($project, $getProjectDB); @@ -262,47 +264,6 @@ class Deletes extends Action ); } - /** - * @param Document $project - * @param callable $getProjectDB - * @param Document $target - * @throws Exception - */ - private function deleteTargetSubscribers(Document $project, callable $getProjectDB, Document $target): void - { - /** @var Database */ - $dbForProject = $getProjectDB($project); - - // Delete subscribers and decrement topic counts - $this->deleteByGroup( - 'subscribers', - [ - Query::equal('targetInternalId', [$target->getInternalId()]), - Query::orderAsc(), - ], - $dbForProject, - function (Document $subscriber) use ($dbForProject, $target) { - $topicId = $subscriber->getAttribute('topicId'); - $topicInternalId = $subscriber->getAttribute('topicInternalId'); - $topic = $dbForProject->getDocument('topics', $topicId); - if (!$topic->isEmpty() && $topic->getInternalId() === $topicInternalId) { - $totalAttribute = match ($target->getAttribute('providerType')) { - MESSAGE_TYPE_EMAIL => 'emailTotal', - MESSAGE_TYPE_SMS => 'smsTotal', - MESSAGE_TYPE_PUSH => 'pushTotal', - default => throw new Exception('Invalid target CertificatesAdapter type'), - }; - $dbForProject->decreaseDocumentAttribute( - 'topics', - $topicId, - $totalAttribute, - min: 0 - ); - } - } - ); - } - /** * @param Document $project * @param callable $getProjectDB @@ -312,32 +273,12 @@ class Deletes extends Action */ private function deleteExpiredTargets(Document $project, callable $getProjectDB): void { - $this->deleteByGroup( - 'targets', - [ - Query::equal('expired', [true]), - Query::orderAsc(), - ], - $getProjectDB($project), - function (Document $target) use ($getProjectDB, $project) { - $this->deleteTargetSubscribers($project, $getProjectDB, $target); - } - ); + Targets::delete($getProjectDB($project), Query::equal('expired', [true])); } private function deleteSessionTargets(Document $project, callable $getProjectDB, Document $session): void { - $this->deleteByGroup( - 'targets', - [ - Query::equal('sessionInternalId', [$session->getInternalId()]), - Query::orderAsc(), - ], - $getProjectDB($project), - function (Document $target) use ($getProjectDB, $project) { - $this->deleteTargetSubscribers($project, $getProjectDB, $target); - } - ); + Targets::delete($getProjectDB($project), Query::equal('sessionInternalId', [$session->getInternalId()])); } /** @@ -720,23 +661,10 @@ class Deletes extends Action ], $dbForProject); // Delete identities - $this->deleteByGroup('identities', [ - Query::equal('userInternalId', [$userInternalId]), - Query::orderAsc() - ], $dbForProject); + Identities::delete($dbForProject, Query::equal('userInternalId', [$userInternalId])); // Delete targets - $this->deleteByGroup( - 'targets', - [ - Query::equal('userInternalId', [$userInternalId]), - Query::orderAsc() - ], - $dbForProject, - function (Document $target) use ($getProjectDB, $project) { - $this->deleteTargetSubscribers($project, $getProjectDB, $target); - } - ); + Targets::delete($dbForProject, Query::equal('userInternalId', [$userInternalId])); } /**