diff --git a/.env.dist b/.env.dist index 54e74965c..276f42eac 100644 --- a/.env.dist +++ b/.env.dist @@ -14,6 +14,10 @@ SEARCH_DRIVER=${SEARCH_DRIVER} MANTICORESEARCH_HOST=${MANTICORESEARCH_HOST} MANTICORESEARCH_PORT=${MANTICORESEARCH_PORT} +MANTICORESEARCH_SCHEME=${MANTICORESEARCH_SCHEME} +MANTICORESEARCH_USERNAME=${MANTICORESEARCH_USERNAME} +MANTICORESEARCH_PASSWORD=${MANTICORESEARCH_PASSWORD} +MANTICORESEARCH_TOKEN=${MANTICORESEARCH_TOKEN} # ManticoreSearch Fuzzy Search Settings MANTICORESEARCH_FUZZY_ENABLED=${MANTICORESEARCH_FUZZY_ENABLED} MANTICORESEARCH_FUZZY_MAX_DISTANCE=${MANTICORESEARCH_FUZZY_MAX_DISTANCE} diff --git a/.env.example b/.env.example index 97f7b2573..fe504eec8 100644 --- a/.env.example +++ b/.env.example @@ -14,6 +14,13 @@ SEARCH_DRIVER=manticore # ManticoreSearch Configuration MANTICORESEARCH_HOST=localhost MANTICORESEARCH_PORT=9308 +# Use http by default. Set https when Manticore HTTP(S) is served over TLS. +MANTICORESEARCH_SCHEME=http +# Optional for Manticore 27.x built-in auth/authz. Leave blank for anonymous local Docker. +MANTICORESEARCH_USERNAME= +MANTICORESEARCH_PASSWORD= +# Optional bearer token auth. If set, this takes precedence over username/password for HTTP clients. +MANTICORESEARCH_TOKEN= # fuzzy search settings MANTICORESEARCH_FUZZY_ENABLED=false # For high availability, set multiple hosts (comma-separated): "host1:9308,host2:9308" diff --git a/DOCKER.md b/DOCKER.md index d1f44ee44..e13b1d7a1 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -53,6 +53,27 @@ Set `COMPOSE_PROFILES` in `.env` to match your `SEARCH_DRIVER`: Only the selected search service container will start. The other remains dormant and consumes no resources. +### ManticoreSearch Image Updates + +Both `docker-compose.yml` and `docker-compose.yml.prod-dist` intentionally use +the unpinned `manticoresearch/manticore` image. This keeps ManticoreSearch on +the latest published Docker image whenever images are pulled, but it also means +production can receive ManticoreSearch changes without a repository diff. + +Before pulling and restarting production, validate the new image in staging by +creating indexes, indexing a small batch, and running representative searches, +filters, sorting, pagination, deletes, suggestions, fuzzy searches, and any raw +SQL diagnostics/reconciliation commands. + +Manticore Search 27.1.5 is part of the 25.x-27.x release line that introduced +built-in authentication/authorization, sharded tables, conversational search, +vector-search improvements, and replication layout changes. The app does not +enable Manticore auth by default, but if auth is enabled on the server set either +`MANTICORESEARCH_USERNAME`/`MANTICORESEARCH_PASSWORD` or +`MANTICORESEARCH_TOKEN` before running the app, index-creation commands, or raw +HTTP diagnostics. Roll auth out in staging first; anonymous local Docker usage +continues to work with these variables blank. + ## Make Targets Run `make` or `make help` to see all targets, grouped by section. diff --git a/app/Console/Commands/CreateManticoreIndexes.php b/app/Console/Commands/CreateManticoreIndexes.php index cd47fbc56..62281384b 100644 --- a/app/Console/Commands/CreateManticoreIndexes.php +++ b/app/Console/Commands/CreateManticoreIndexes.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Console\Commands; use App\Enums\SecondarySearchIndex; +use App\Services\Search\Support\ManticoreClientFactory; use Illuminate\Console\Command; use Manticoresearch\Client; use Manticoresearch\Exceptions\ResponseException; @@ -37,15 +38,7 @@ class CreateManticoreIndexes extends Command $dropExisting = $this->option('drop'); - // Get connection details from config - $host = config('manticoresearch.host', '127.0.0.1'); - $port = config('manticoresearch.port', 9308); - - // Create client - $this->client = new Client([ - 'host' => $host, - 'port' => $port, - ]); + $this->client = ManticoreClientFactory::make(config('search.drivers.manticore', config('manticoresearch', []))); // We'll skip checking for data_dir this way since it may not be accessible via API // but instead provide better error handling during index creation diff --git a/app/Console/Commands/CreateMediaIndexes.php b/app/Console/Commands/CreateMediaIndexes.php index d90360d10..53c7dd3f1 100644 --- a/app/Console/Commands/CreateMediaIndexes.php +++ b/app/Console/Commands/CreateMediaIndexes.php @@ -6,6 +6,7 @@ namespace App\Console\Commands; use App\Services\Search\Support\ElasticsearchClientFactory; use App\Services\Search\Support\ElasticsearchResponseHelper; +use App\Services\Search\Support\ManticoreClientFactory; use Elastic\Elasticsearch\Client as ElasticsearchClient; use Illuminate\Console\Command; use Manticoresearch\Client as ManticoreClient; @@ -53,13 +54,7 @@ class CreateMediaIndexes extends Command */ private function createManticoreIndexes(): int { - $host = config('search.drivers.manticore.host', '127.0.0.1'); - $port = config('search.drivers.manticore.port', 9308); - - $client = new ManticoreClient([ - 'host' => $host, - 'port' => $port, - ]); + $client = ManticoreClientFactory::make(config('search.drivers.manticore', [])); // Test connection try { diff --git a/app/Console/Commands/NntmuxCheckIndex.php b/app/Console/Commands/NntmuxCheckIndex.php index 0ad8d20d3..fb3f6261e 100644 --- a/app/Console/Commands/NntmuxCheckIndex.php +++ b/app/Console/Commands/NntmuxCheckIndex.php @@ -6,6 +6,7 @@ namespace App\Console\Commands; use App\Facades\Elasticsearch; use App\Services\Search\Support\ElasticsearchResponseHelper; +use App\Services\Search\Support\ManticoreClientFactory; use Elastic\Elasticsearch\Client as ElasticsearchClient; use Exception; use GuzzleHttp\Client; @@ -140,18 +141,20 @@ class NntmuxCheckIndex extends Command 'anime' => 'anime_rt', default => 'releases_rt', }; - $host = (string) config('search.drivers.manticore.host', '127.0.0.1'); - $port = (int) config('search.drivers.manticore.port', 9308); + $manticoreConfig = config('search.drivers.manticore', []); + $host = (string) ($manticoreConfig['host'] ?? '127.0.0.1'); + $port = (int) ($manticoreConfig['port'] ?? 9308); + $scheme = (string) ($manticoreConfig['scheme'] ?? 'http'); // Use HTTP API endpoint - $baseUrl = "http://{$host}:{$port}"; + $baseUrl = "{$scheme}://{$host}:{$port}"; // Check if table exists using HTTP API - $client = new Client([ + $client = new Client(ManticoreClientFactory::guzzleOptions($manticoreConfig, [ 'base_uri' => $baseUrl, 'timeout' => 10, 'http_errors' => false, // Don't throw on HTTP errors - ]); + ])); // Use raw mode which seems to work $query = "SELECT COUNT(*) FROM {$indexName}"; diff --git a/app/Console/Commands/UpdateReleasesIndexSchema.php b/app/Console/Commands/UpdateReleasesIndexSchema.php index 483a4d709..6033d2369 100644 --- a/app/Console/Commands/UpdateReleasesIndexSchema.php +++ b/app/Console/Commands/UpdateReleasesIndexSchema.php @@ -6,6 +6,7 @@ namespace App\Console\Commands; use App\Facades\Search; use App\Models\Release; +use App\Services\Search\Support\ManticoreClientFactory; use Illuminate\Console\Command; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Log; @@ -75,14 +76,7 @@ class UpdateReleasesIndexSchema extends Command $this->info('Releases index schema update utility'); $this->newLine(); - // Initialize ManticoreSearch client - $host = config('search.drivers.manticore.host', '127.0.0.1'); - $port = config('search.drivers.manticore.port', 9308); - - $this->client = new Client([ - 'host' => $host, - 'port' => $port, - ]); + $this->client = ManticoreClientFactory::make(config('search.drivers.manticore', [])); // Test connection try { diff --git a/app/Services/Search/Drivers/ManticoreSearchDriver.php b/app/Services/Search/Drivers/ManticoreSearchDriver.php index f349b5ad8..343614036 100644 --- a/app/Services/Search/Drivers/ManticoreSearchDriver.php +++ b/app/Services/Search/Drivers/ManticoreSearchDriver.php @@ -15,6 +15,7 @@ use App\Models\Release; use App\Models\SteamApp; use App\Models\Video; use App\Services\Search\Contracts\SearchDriverInterface; +use App\Services\Search\Support\ManticoreClientFactory; use App\Support\ReleaseSearchIndexDocument; use App\Support\SecondaryIndexDocuments; use Illuminate\Support\Facades\Cache; @@ -55,8 +56,8 @@ class ManticoreSearchDriver implements SearchDriverInterface public function __construct(array $config = []) { $this->config = ! empty($config) ? $config : config('search.drivers.manticore'); - $this->connection = ['host' => $this->config['host'], 'port' => $this->config['port']]; - $this->manticoreSearch = new Client($this->connection); + $this->connection = ManticoreClientFactory::clientConfig($this->config); + $this->manticoreSearch = ManticoreClientFactory::make($this->config); $this->search = new Search($this->manticoreSearch); } @@ -2336,7 +2337,6 @@ class ManticoreSearchDriver implements SearchDriverInterface * * @param array $categoryIds Array of category IDs to filter by * @param int $limit Maximum number of results - * @return list * @return array */ public function searchReleasesByCategory(array $categoryIds, int $limit = 1000): array @@ -2397,7 +2397,6 @@ class ManticoreSearchDriver implements SearchDriverInterface * @param string $searchTerm Search text * @param array $categoryIds Array of category IDs to filter by (empty for all categories) * @param int $limit Maximum number of results - * @return list * @return array */ public function searchReleasesWithCategoryFilter(string $searchTerm, array $categoryIds = [], int $limit = 1000): array diff --git a/config/manticoresearch.php b/config/manticoresearch.php index 4babff68c..9c0e2ffb0 100644 --- a/config/manticoresearch.php +++ b/config/manticoresearch.php @@ -12,6 +12,10 @@ return [ */ 'host' => env('MANTICORESEARCH_HOST', '127.0.0.1'), 'port' => env('MANTICORESEARCH_PORT', 9308), + 'scheme' => env('MANTICORESEARCH_SCHEME', 'http'), + 'username' => env('MANTICORESEARCH_USERNAME'), + 'password' => env('MANTICORESEARCH_PASSWORD'), + 'token' => env('MANTICORESEARCH_TOKEN'), /* |-------------------------------------------------------------------------- diff --git a/config/search.php b/config/search.php index 427e10bef..baa0af103 100644 --- a/config/search.php +++ b/config/search.php @@ -27,6 +27,10 @@ return [ 'driver' => 'manticore', 'host' => env('MANTICORESEARCH_HOST', '127.0.0.1'), 'port' => env('MANTICORESEARCH_PORT', 9308), + 'scheme' => env('MANTICORESEARCH_SCHEME', 'http'), + 'username' => env('MANTICORESEARCH_USERNAME'), + 'password' => env('MANTICORESEARCH_PASSWORD'), + 'token' => env('MANTICORESEARCH_TOKEN'), 'hosts' => env('MANTICORESEARCH_HOSTS', ''), 'retries' => env('MANTICORESEARCH_RETRIES', 2), 'indexes' => [ diff --git a/docker-compose.yml.prod-dist b/docker-compose.yml.prod-dist index 0120ae6ec..685ac1e97 100644 --- a/docker-compose.yml.prod-dist +++ b/docker-compose.yml.prod-dist @@ -111,7 +111,7 @@ services: retries: 3 timeout: 5s manticore: - image: manticoresearch/manticore:7.4.6 + image: manticoresearch/manticore profiles: - manticore environment: