fix(search): fallback to search API key when tenant token fails; use direct HTTP for server-side search with master key

This commit is contained in:
ggq-admin 2025-10-19 17:24:55 +02:00
parent 607e379dee
commit dfdadcdf77

View file

@ -40,29 +40,30 @@ router.post('/token', async (req, res) => {
const organizationIds = orgs.map(org => org.organization_id); const organizationIds = orgs.map(org => org.organization_id);
if (organizationIds.length === 0) { if (organizationIds.length === 0) {
return res.status(403).json({ return res.status(403).json({ error: 'No organizations found for user' });
error: 'No organizations found for user'
});
} }
// Generate tenant token with user and organization filters
const token = await generateTenantToken(userId, organizationIds, tokenExpiry);
const expiresAt = new Date(Date.now() + tokenExpiry * 1000); const expiresAt = new Date(Date.now() + tokenExpiry * 1000);
const searchUrl = process.env.MEILISEARCH_HOST || 'http://127.0.0.1:7700';
res.json({ // Preferred: tenant token
token, try {
expiresAt: expiresAt.toISOString(), const token = await generateTenantToken(userId, organizationIds, tokenExpiry);
expiresIn: tokenExpiry, return res.json({ token, expiresAt: expiresAt.toISOString(), expiresIn: tokenExpiry, indexName: INDEX_NAME, searchUrl, mode: 'tenant' });
indexName: INDEX_NAME, } catch (err) {
searchUrl: process.env.MEILISEARCH_HOST || 'http://127.0.0.1:7700' console.warn('Tenant token generation failed, falling back to search API key:', err?.message || err);
}); }
// Fallback: use default search API key
const client = getMeilisearchClient();
const keys = await client.getKeys();
const searchKey = keys.results?.find(k => k.actions?.includes('search') && (k.indexes?.includes('*') || k.indexes?.includes(INDEX_NAME)));
if (!searchKey?.key) throw new Error('No search API key available for fallback');
return res.json({ token: searchKey.key, expiresAt: null, expiresIn: null, indexName: INDEX_NAME, searchUrl, mode: 'search-key' });
} catch (error) { } catch (error) {
console.error('Token generation error:', error); console.error('Token generation error:', error);
res.status(500).json({ res.status(500).json({ error: 'Failed to generate search token', message: error.message });
error: 'Failed to generate search token',
message: error.message
});
} }
}); });
@ -124,24 +125,27 @@ router.post('/', async (req, res) => {
const filterString = filterParts.join(' AND '); const filterString = filterParts.join(' AND ');
// Get Meilisearch client and search // Use direct HTTP call with master key to avoid client cache issues
const client = getMeilisearchClient(); const host = process.env.MEILISEARCH_HOST || 'http://127.0.0.1:7700';
const index = client.index(INDEX_NAME); const apiKey = process.env.MEILISEARCH_MASTER_KEY;
const resp = await fetch(`${host}/indexes/${INDEX_NAME}/search`, {
const searchResults = await index.search(q, { method: 'POST',
filter: filterString, headers: {
limit: parseInt(limit), 'Content-Type': 'application/json',
offset: parseInt(offset), ...(apiKey ? { Authorization: `Bearer ${apiKey}` } : {})
attributesToHighlight: ['text'], },
attributesToCrop: ['text'], body: JSON.stringify({ q, filter: filterString, limit: parseInt(limit), offset: parseInt(offset), attributesToHighlight: ['text'], attributesToCrop: ['text'], cropLength: 200 })
cropLength: 200
}); });
if (!resp.ok) {
res.json({ const txt = await resp.text();
hits: searchResults.hits, throw new Error(`Meilisearch HTTP ${resp.status}: ${txt}`);
estimatedTotalHits: searchResults.estimatedTotalHits, }
query: searchResults.query, const searchResults = await resp.json();
processingTimeMs: searchResults.processingTimeMs, return res.json({
hits: searchResults.hits || [],
estimatedTotalHits: searchResults.estimatedTotalHits || 0,
query: searchResults.query || q,
processingTimeMs: searchResults.processingTimeMs || 0,
limit: parseInt(limit), limit: parseInt(limit),
offset: parseInt(offset) offset: parseInt(offset)
}); });