feat(search): Meilisearch message search — indexer + permission-scoped query
New search module: a message index (Meilisearch, MEILI_URL-gated; no-op when unset), a SearchProjector that indexes on message.sent, and a SearchService whose permission fence runs server-side — a query only touches the caller's own scope AND the threads they currently belong to (resolved live from participant rows, so membership changes reflect immediately). Every conversation kind (chat/mail/sms) is a message with a TEXT part, so all are searchable through one index. POST /v1/search + /reindex (backfill). 5 fence tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { OutboxModule } from '../outbox/outbox.module';
|
||||
import { SearchService } from './search.service';
|
||||
import { SearchProjector } from './search.projector';
|
||||
import { SearchController } from './search.controller';
|
||||
import { MESSAGE_SEARCH_PORT } from './message-search.port';
|
||||
import { messageSearchFromEnv } from './meili.adapter';
|
||||
|
||||
/**
|
||||
* Message search (Meilisearch). The engine binds from MEILI_URL; unset → a no-op port so search
|
||||
* returns empty rather than erroring. The projector indexes on `message.sent`; the service enforces
|
||||
* the permission fence (scope + caller's threads). SessionVerifier + ActorResolver are global.
|
||||
*/
|
||||
@Module({
|
||||
imports: [OutboxModule],
|
||||
controllers: [SearchController],
|
||||
providers: [
|
||||
SearchService,
|
||||
SearchProjector,
|
||||
{ provide: MESSAGE_SEARCH_PORT, useFactory: () => messageSearchFromEnv() },
|
||||
],
|
||||
exports: [SearchService],
|
||||
})
|
||||
export class SearchModule {}
|
||||
Reference in New Issue
Block a user