* SPDX-License-Identifier: AGPL-3.0-or-later */ namespace KTXM\ProviderMailSystem\Stores; use KTXC\Db\DataStore; use KTXF\Utile\UUID; use KTXM\ProviderMailSystem\Providers\Service; /** * System Mail Route Store * * Persists system mail routes (one MongoDB document per route) in the * collection `provider_system_mail_services`. Routes are tenant scoped — * they carry no user association and contain no transport credentials, * only the mapping from a "@system" address to a backing provider/service. * * @since 2026.07.01 */ class ServiceStore { protected const COLLECTION_NAME = 'provider_system_mail_services'; public function __construct( protected readonly DataStore $dataStore, ) {} /** * List routes for a tenant, optionally filtered by route IDs */ public function list(string $tenantId, ?array $filter = null): array { $filterCondition = [ 'tid' => $tenantId, ]; if (!empty($filter)) { $filterCondition['sid'] = ['$in' => array_map('strval', $filter)]; } $cursor = $this->dataStore->selectCollection(self::COLLECTION_NAME)->find($filterCondition); $list = []; foreach ($cursor as $entry) { $list[$entry['sid']] = $entry; } return $list; } /** * Check existence of routes by IDs for a tenant */ public function extant(string $tenantId, array $identifiers): array { if (empty($identifiers)) { return []; } $cursor = $this->dataStore->selectCollection(self::COLLECTION_NAME)->find( [ 'tid' => $tenantId, 'sid' => ['$in' => array_map('strval', $identifiers)] ], ['projection' => ['sid' => 1]] ); $existingIds = []; foreach ($cursor as $document) { $existingIds[] = $document['sid']; } $result = []; foreach ($identifiers as $id) { $result[(string)$id] = in_array((string)$id, $existingIds, true); } return $result; } /** * Retrieve a single route by ID */ public function fetch(string $tenantId, string|int $serviceId): ?array { $document = $this->dataStore->selectCollection(self::COLLECTION_NAME)->findOne([ 'tid' => $tenantId, 'sid' => (string)$serviceId, ]); if (!$document) { return null; } return $document; } /** * Create a new route */ public function create(string $tenantId, Service $service): array { $document = $service->toStore(); $document['tid'] = $tenantId; $document['sid'] = UUID::v4(); $document['createdOn'] = new \MongoDB\BSON\UTCDateTime(); $document['modifiedOn'] = new \MongoDB\BSON\UTCDateTime(); $this->dataStore->selectCollection(self::COLLECTION_NAME)->insertOne($document); return $document; } /** * Modify an existing route */ public function modify(string $tenantId, Service $service): array { $serviceId = (string)$service->identifier(); if ($serviceId === '') { throw new \InvalidArgumentException('Service ID is required for update'); } $document = $service->toStore(); $document['modifiedOn'] = new \MongoDB\BSON\UTCDateTime(); unset($document['sid'], $document['tid'], $document['createdOn']); $this->dataStore->selectCollection(self::COLLECTION_NAME)->updateOne( [ 'tid' => $tenantId, 'sid' => $serviceId, ], ['$set' => $document] ); return $document; } /** * Delete a route */ public function delete(string $tenantId, string|int $serviceId): bool { $result = $this->dataStore->selectCollection(self::COLLECTION_NAME)->deleteOne([ 'tid' => $tenantId, 'sid' => (string)$serviceId, ]); return $result->getDeletedCount() > 0; } }