chore: remove obsolete code

Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
2026-09-26 08:35:50 -04:00
parent 8e30b8de97
commit 681a66862e
4 changed files with 1 additions and 542 deletions
+1 -10
View File
@@ -6,14 +6,12 @@ namespace KTXM\ProviderImap\Listeners;
use KTXC\User\Event\UserCreatedEvent; use KTXC\User\Event\UserCreatedEvent;
use KTXC\User\Event\UserDeletingEvent; use KTXC\User\Event\UserDeletingEvent;
use KTXM\ProviderImap\Stores\MessageStore;
use KTXM\ProviderImap\Stores\ServiceStore; use KTXM\ProviderImap\Stores\ServiceStore;
final class UserEventListener final class UserEventListener
{ {
public function __construct( public function __construct(
private readonly ServiceStore $serviceStore, private readonly ServiceStore $serviceStore,
private readonly MessageStore $messageStore,
) { ) {
} }
@@ -24,13 +22,6 @@ final class UserEventListener
public function onUserDeleting(UserDeletingEvent $event): void public function onUserDeleting(UserDeletingEvent $event): void
{ {
$tenantId = $event->tenantIdentifier(); $this->serviceStore->deleteByUser($event->tenantIdentifier(), $event->userIdentifier());
$userId = $event->userIdentifier();
foreach (array_keys($this->serviceStore->list($tenantId, $userId)) as $serviceId) {
$this->messageStore->deleteByServiceId((string) $serviceId);
}
$this->serviceStore->deleteByUser($tenantId, $userId);
} }
} }
-115
View File
@@ -1,115 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* 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;
}
}
-193
View File
@@ -1,193 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* 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<string, 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);
}
}
}
-224
View File
@@ -1,224 +0,0 @@
<?php
declare(strict_types=1);
/**
* SPDX-FileCopyrightText: Sebastian Krupinski <krupinski01@gmail.com>
* 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,
]);
}
}