From 681a66862e2d32ba66ebaf40af6c94a9da0b20f4 Mon Sep 17 00:00:00 2001 From: Sebastian Krupinski Date: Sat, 26 Sep 2026 08:35:50 -0400 Subject: [PATCH] chore: remove obsolete code Signed-off-by: Sebastian Krupinski --- lib/Listeners/UserEventListener.php | 11 +- lib/Service/Cache/CacheMailService.php | 115 ------------- lib/Service/Cache/CacheService.php | 193 --------------------- lib/Stores/MessageStore.php | 224 ------------------------- 4 files changed, 1 insertion(+), 542 deletions(-) delete mode 100644 lib/Service/Cache/CacheMailService.php delete mode 100644 lib/Service/Cache/CacheService.php delete mode 100644 lib/Stores/MessageStore.php diff --git a/lib/Listeners/UserEventListener.php b/lib/Listeners/UserEventListener.php index c657859..a7c3039 100644 --- a/lib/Listeners/UserEventListener.php +++ b/lib/Listeners/UserEventListener.php @@ -6,14 +6,12 @@ namespace KTXM\ProviderImap\Listeners; use KTXC\User\Event\UserCreatedEvent; use KTXC\User\Event\UserDeletingEvent; -use KTXM\ProviderImap\Stores\MessageStore; use KTXM\ProviderImap\Stores\ServiceStore; final class UserEventListener { public function __construct( private readonly ServiceStore $serviceStore, - private readonly MessageStore $messageStore, ) { } @@ -24,13 +22,6 @@ final class UserEventListener public function onUserDeleting(UserDeletingEvent $event): void { - $tenantId = $event->tenantIdentifier(); - $userId = $event->userIdentifier(); - - foreach (array_keys($this->serviceStore->list($tenantId, $userId)) as $serviceId) { - $this->messageStore->deleteByServiceId((string) $serviceId); - } - - $this->serviceStore->deleteByUser($tenantId, $userId); + $this->serviceStore->deleteByUser($event->tenantIdentifier(), $event->userIdentifier()); } } diff --git a/lib/Service/Cache/CacheMailService.php b/lib/Service/Cache/CacheMailService.php deleted file mode 100644 index 62446d9..0000000 --- a/lib/Service/Cache/CacheMailService.php +++ /dev/null @@ -1,115 +0,0 @@ - - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -namespace KTXM\ProviderImap\Service\Cache; - -use KTXM\ProviderImap\Providers\CollectionResource; -use KTXM\ProviderImap\Providers\EntityResource; -use KTXM\ProviderImap\Stores\MessageStore; - -/** - * Cache Mail Service - * - * Provides read-only access to locally cached IMAP data stored in MongoDB by - * CacheService. Call CacheService::syncMailboxes() / syncMessages() to keep - * the cache up to date before reading from here. - */ -class CacheMailService -{ - public function __construct( - private readonly MessageStore $messageStore, - private readonly string $provider, - private readonly string|int $service, - ) {} - - // ── Collection (mailbox) reads ──────────────────────────────────────────── - - /** - * Return all cached mailboxes for this service. - * - * @return CollectionResource[] keyed by mailbox name - */ - public function collectionList(): array - { - $docs = $this->messageStore->listMailboxes((string) $this->service); - $result = []; - foreach ($docs as $doc) { - $resource = new CollectionResource($this->provider, $this->service); - $resource->fromStore($doc); - $result[$resource->identifier()] = $resource; - } - return $result; - } - - /** - * Fetch a single cached mailbox by name. - */ - public function collectionFetch(string $name): ?CollectionResource - { - $doc = $this->messageStore->fetchMailbox((string) $this->service, $name); - if ($doc === null) { - return null; - } - $resource = new CollectionResource($this->provider, $this->service); - $resource->fromStore($doc); - return $resource; - } - - // ── Entity (message) reads ──────────────────────────────────────────────── - - /** - * Return all cached UIDs for a mailbox. - * - * @return int[] - */ - public function entityList(string $collection): array - { - return $this->messageStore->listUids((string) $this->service, $collection); - } - - /** - * Fetch one or more cached messages by UID. - * - * @param int ...$uids - * @return EntityResource[] keyed by UID - */ - public function entityFetch(string $collection, int ...$uids): array - { - if (empty($uids)) { - return []; - } - - $docs = $this->messageStore->fetchMessages( - (string) $this->service, - $collection, - array_values($uids), - ); - $result = []; - foreach ($docs as $uid => $doc) { - $resource = new EntityResource($this->provider, $this->service); - $resource->fromStore($doc); - $result[$uid] = $resource; - } - return $result; - } - - /** - * Fetch a single cached message. - */ - public function entityFetchOne(string $collection, int $uid): ?EntityResource - { - $doc = $this->messageStore->fetchMessage((string) $this->service, $collection, $uid); - if ($doc === null) { - return null; - } - $resource = new EntityResource($this->provider, $this->service); - $resource->fromStore($doc); - return $resource; - } -} diff --git a/lib/Service/Cache/CacheService.php b/lib/Service/Cache/CacheService.php deleted file mode 100644 index 31d2033..0000000 --- a/lib/Service/Cache/CacheService.php +++ /dev/null @@ -1,193 +0,0 @@ - - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -namespace KTXM\ProviderImap\Service\Cache; - -use KTXM\ProviderImap\Providers\CollectionResource; -use KTXM\ProviderImap\Providers\EntityResource; -use KTXM\ProviderImap\Service\Remote\RemoteMailService; -use KTXM\ProviderImap\Stores\MessageStore; - -/** - * Cache Service — IMAP Sync Orchestrator - * - * Keeps the local MongoDB cache in sync with a remote IMAP server. - * - * Strategy (UID-based): - * 1. Fetch the complete remote UID set. - * 2. Compare against cached UIDs. - * 3. Fetch and store new UIDs; remove stale UIDs from the cache. - * - * This is always a full-range UID comparison. A CONDSTORE/HIGHESTMODSEQ - * strategy can be layered on later for servers that support RFC 7162. - */ -class CacheService -{ - public function __construct( - private readonly RemoteMailService $remoteMailService, - private readonly MessageStore $messageStore, - private readonly string $provider, - private readonly string|int $service, - ) {} - - // ── Mailbox sync ────────────────────────────────────────────────────────── - - /** - * Synchronise the mailbox list for this service. - * - * Inserts/updates every selectable mailbox returned by the remote server - * and removes cached mailboxes that no longer exist remotely. - * - * @return array{added: string[], removed: string[]} - */ - public function syncMailboxes(): array - { - $remoteCollections = $this->remoteMailService->collectionList(); - - $added = []; - $removed = []; - - // Upsert every remote mailbox - foreach ($remoteCollections as $name => $resource) { - $doc = array_merge( - $resource->toStore(), - ['sid' => (string) $this->service], - ); - $this->messageStore->upsertMailbox($doc); - $added[] = $name; - } - - // Remove cached mailboxes that no longer exist on the server - $cached = $this->messageStore->listMailboxes((string) $this->service); - foreach ($cached as $cachedDoc) { - $name = $cachedDoc['name'] ?? ($cachedDoc['identifier'] ?? null); - if ($name === null) { - continue; - } - if (!isset($remoteCollections[$name])) { - $this->messageStore->deleteMailbox((string) $this->service, $name); - $removed[] = $name; - } - } - - return ['added' => $added, 'removed' => $removed]; - } - - // ── Message sync ────────────────────────────────────────────────────────── - - /** - * Synchronise all messages in one mailbox. - * - * Fetches new UIDs from the remote server, stores them in MongoDB, and - * removes UIDs from the cache that no longer exist on the server. - * - * @return array{added: int[], removed: int[]} - */ - public function syncMessages(string $mailbox): array - { - $remoteUids = $this->remoteMailService->entityList($mailbox); - $cachedUids = $this->messageStore->listUids((string) $this->service, $mailbox); - - $remoteSet = array_fill_keys($remoteUids, true); - $cachedSet = array_fill_keys($cachedUids, true); - - $newUids = array_keys(array_diff_key($remoteSet, $cachedSet)); - $removedUids = array_keys(array_diff_key($cachedSet, $remoteSet)); - - // Stream-fetch new messages one at a time and store each - foreach ($this->remoteMailService->entitySyncStream($mailbox, $newUids) as $uid => $resource) { - /** @var EntityResource $resource */ - $doc = array_merge( - $resource->toStore(), - [ - 'sid' => (string) $this->service, - 'mailbox' => $mailbox, - 'uid' => $uid, - ], - ); - $this->messageStore->upsertMessage($doc); - } - - // Purge stale UIDs from cache - if (!empty($removedUids)) { - $this->messageStore->deleteMessages((string) $this->service, $mailbox, $removedUids); - } - - return [ - 'added' => $newUids, - 'removed' => $removedUids, - ]; - } - - /** - * Perform a full sync: mailboxes first, then all messages in each selectable mailbox. - * - * @return array{mailboxes: array, messages: array} - */ - public function syncAll(): array - { - $mailboxResult = $this->syncMailboxes(); - $messagesResults = []; - - foreach ($this->remoteMailService->collectionList() as $name => $collection) { - try { - $messagesResults[$name] = $this->syncMessages($name); - } catch (\Throwable $e) { - $messagesResults[$name] = ['error' => $e->getMessage()]; - } - } - - return [ - 'mailboxes' => $mailboxResult, - 'messages' => $messagesResults, - ]; - } - - // ── Partial helpers ─────────────────────────────────────────────────────── - - /** - * Sync only the flags of already-cached messages in a mailbox. - * - * This is cheaper than a full message sync: it fetches FLAGS only from - * the remote server and updates cached documents in place. - */ - public function syncFlags(string $mailbox): void - { - $cachedUids = $this->messageStore->listUids((string) $this->service, $mailbox); - - if (empty($cachedUids)) { - return; - } - - // Fetch only FLAGS + UID from remote - $items = ['FLAGS', 'UID']; - - foreach ($this->remoteMailService->entitySyncStream($mailbox, $cachedUids, $items) as $uid => $resource) { - /** @var EntityResource $resource */ - $existing = $this->messageStore->fetchMessage((string) $this->service, $mailbox, $uid); - if ($existing === null) { - continue; - } - - // Merge updated flags into the cached document - $existingProperties = $existing['properties'] ?? []; - $updatedProperties = $resource->toStore()['properties'] ?? []; - - // Only overwrite flag-related fields - foreach (['seen', 'flagged', 'answered', 'draft', 'deleted', 'junk'] as $field) { - if (isset($updatedProperties[$field])) { - $existingProperties[$field] = $updatedProperties[$field]; - } - } - - $existing['properties'] = $existingProperties; - $this->messageStore->upsertMessage($existing); - } - } -} diff --git a/lib/Stores/MessageStore.php b/lib/Stores/MessageStore.php deleted file mode 100644 index 54c28a9..0000000 --- a/lib/Stores/MessageStore.php +++ /dev/null @@ -1,224 +0,0 @@ - - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -namespace KTXM\ProviderImap\Stores; - -use KTXC\Db\DataStore; - -/** - * MongoDB cache for IMAP messages and mailboxes. - * - * Collections: - * provider_imap_mail_messages — one document per (sid, mailbox, uid) - * provider_imap_mail_mailboxes — one document per (sid, name) - * - * Documents are stored **pre-formatted** in the same internal shape used by - * EntityResource / MessageProperties and CollectionResource / CollectionProperties - * so the read path does zero IMAP parsing. - */ -class MessageStore -{ - protected const MESSAGES_COLLECTION = 'provider_imap_mail_messages'; - protected const MAILBOXES_COLLECTION = 'provider_imap_mail_mailboxes'; - - public function __construct( - protected readonly DataStore $store, - ) { - $this->ensureIndexes(); - } - - // ── Index creation ─────────────────────────────────────────────────────── - - private function ensureIndexes(): void - { - $messages = $this->store->selectCollection(self::MESSAGES_COLLECTION); - $mailboxes = $this->store->selectCollection(self::MAILBOXES_COLLECTION); - - $messages->createIndex(['sid' => 1, 'mailbox' => 1, 'uid' => 1], ['unique' => true]); - $mailboxes->createIndex(['sid' => 1, 'name' => 1], ['unique' => true]); - } - - // ── Messages ───────────────────────────────────────────────────────────── - - /** - * Insert or replace a cached message document. - * - * @param array $data Result of EntityResource::toStore() — must include - * sid, mailbox, uid at the top level. - */ - public function upsertMessage(array $data): void - { - $filter = [ - 'sid' => $data['sid'], - 'mailbox' => $data['mailbox'], - 'uid' => (int)$data['uid'], - ]; - $data['syncedAt'] = (new \DateTime())->format(\DateTimeInterface::ATOM); - - $this->store->selectCollection(self::MESSAGES_COLLECTION)->updateOne( - $filter, - ['$set' => $data], - ['upsert' => true], - ); - } - - /** - * Retrieve a single cached message by service ID, mailbox and UID. - */ - public function fetchMessage(string $serviceId, string $mailbox, int $uid): ?array - { - $doc = $this->store->selectCollection(self::MESSAGES_COLLECTION)->findOne([ - 'sid' => $serviceId, - 'mailbox' => $mailbox, - 'uid' => $uid, - ]); - - return $doc ? (array)$doc : null; - } - - /** - * Return all cached UIDs for a mailbox/service combination. - * - * @return int[] - */ - public function listUids(string $serviceId, string $mailbox): array - { - $cursor = $this->store->selectCollection(self::MESSAGES_COLLECTION)->find( - ['sid' => $serviceId, 'mailbox' => $mailbox], - ['projection' => ['uid' => 1]], - ); - - $uids = []; - foreach ($cursor as $doc) { - $uids[] = (int)$doc['uid']; - } - - return $uids; - } - - /** - * Fetch multiple cached messages by UID. - * - * @param int[] $uids - * @return array[] - */ - public function fetchMessages(string $serviceId, string $mailbox, array $uids): array - { - $cursor = $this->store->selectCollection(self::MESSAGES_COLLECTION)->find([ - 'sid' => $serviceId, - 'mailbox' => $mailbox, - 'uid' => ['$in' => $uids], - ]); - - $result = []; - foreach ($cursor as $doc) { - $doc = (array)$doc; - $result[$doc['uid']] = $doc; - } - - return $result; - } - - /** - * Delete a cached message entry by UID. - */ - public function deleteMessage(string $serviceId, string $mailbox, int $uid): void - { - $this->store->selectCollection(self::MESSAGES_COLLECTION)->deleteOne([ - 'sid' => $serviceId, - 'mailbox' => $mailbox, - 'uid' => $uid, - ]); - } - - /** - * Delete multiple cached message entries. - * - * @param int[] $uids - */ - public function deleteMessages(string $serviceId, string $mailbox, array $uids): void - { - if (empty($uids)) { - return; - } - - $this->store->selectCollection(self::MESSAGES_COLLECTION)->deleteMany([ - 'sid' => $serviceId, - 'mailbox' => $mailbox, - 'uid' => ['$in' => $uids], - ]); - } - - // ── Mailboxes ──────────────────────────────────────────────────────────── - - /** - * Insert or replace a cached mailbox document. - * - * @param array $data Result of CollectionResource::toStore() — must include - * sid and name at the top level. - */ - public function upsertMailbox(array $data): void - { - $filter = [ - 'sid' => $data['sid'], - 'name' => $data['name'], - ]; - $data['syncedAt'] = (new \DateTime())->format(\DateTimeInterface::ATOM); - - $this->store->selectCollection(self::MAILBOXES_COLLECTION)->updateOne( - $filter, - ['$set' => $data], - ['upsert' => true], - ); - } - - /** - * Retrieve a cached mailbox by service ID and name. - */ - public function fetchMailbox(string $serviceId, string $name): ?array - { - $doc = $this->store->selectCollection(self::MAILBOXES_COLLECTION)->findOne([ - 'sid' => $serviceId, - 'name' => $name, - ]); - - return $doc ? (array)$doc : null; - } - - /** - * List all cached mailboxes for a service. - * - * @return array[] - */ - public function listMailboxes(string $serviceId): array - { - $cursor = $this->store->selectCollection(self::MAILBOXES_COLLECTION)->find( - ['sid' => $serviceId], - ); - - $result = []; - foreach ($cursor as $doc) { - $doc = (array)$doc; - $result[$doc['name']] = $doc; - } - - return $result; - } - - /** - * Delete a cached mailbox entry by service ID and name. - */ - public function deleteMailbox(string $serviceId, string $name): void - { - $this->store->selectCollection(self::MAILBOXES_COLLECTION)->deleteOne([ - 'sid' => $serviceId, - 'name' => $name, - ]); - } -}