## Backend (server/) - Express 5 API with security middleware (helmet, rate limiting) - SQLite database with WAL mode (schema from docs/architecture/) - Meilisearch integration with tenant tokens - BullMQ + Redis background job queue - OCR pipeline with Tesseract.js - File safety validation (extension, MIME, size) - 4 API route modules: upload, jobs, search, documents ## Frontend (client/) - Vue 3 with Composition API (<script setup>) - Vite 5 build system with HMR - Tailwind CSS (Meilisearch-inspired design) - UploadModal with drag-and-drop - FigureZoom component (ported from lilian1) - Meilisearch search integration with tenant tokens - Job polling composable - Clean SVG icons (no emojis) ## Code Extraction - ✅ manuals.js → UploadModal.vue, useJobPolling.js - ✅ figure-zoom.js → FigureZoom.vue - ✅ service-worker.js → client/public/service-worker.js (TODO) - ✅ glossary.json → Merged into Meilisearch synonyms - ❌ Discarded: quiz.js, persona.js, gamification.js (Frank-AI junk) ## Documentation - Complete extraction plan in docs/analysis/ - README with quick start guide - Architecture summary in docs/architecture/ ## Build Status - Server dependencies: ✅ Installed (234 packages) - Client dependencies: ✅ Installed (160 packages) - Client build: ✅ Successful (2.63s) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
86 lines
2.5 KiB
JavaScript
86 lines
2.5 KiB
JavaScript
/**
|
|
* Meilisearch client configuration
|
|
*/
|
|
|
|
import { MeiliSearch } from 'meilisearch';
|
|
import { readFileSync } from 'fs';
|
|
import { fileURLToPath } from 'url';
|
|
import { dirname, join } from 'path';
|
|
|
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
|
const MEILISEARCH_HOST = process.env.MEILISEARCH_HOST || 'http://127.0.0.1:7700';
|
|
const MEILISEARCH_MASTER_KEY = process.env.MEILISEARCH_MASTER_KEY || 'masterKey';
|
|
const INDEX_NAME = process.env.MEILISEARCH_INDEX_NAME || 'navidocs-pages';
|
|
|
|
let client = null;
|
|
let index = null;
|
|
|
|
export function getMeilisearchClient() {
|
|
if (!client) {
|
|
client = new MeiliSearch({
|
|
host: MEILISEARCH_HOST,
|
|
apiKey: MEILISEARCH_MASTER_KEY
|
|
});
|
|
}
|
|
return client;
|
|
}
|
|
|
|
export async function getMeilisearchIndex() {
|
|
if (!index) {
|
|
const client = getMeilisearchClient();
|
|
|
|
try {
|
|
index = await client.getIndex(INDEX_NAME);
|
|
} catch (error) {
|
|
// Index doesn't exist, create it
|
|
console.log('Creating Meilisearch index:', INDEX_NAME);
|
|
await client.createIndex(INDEX_NAME, { primaryKey: 'id' });
|
|
index = await client.getIndex(INDEX_NAME);
|
|
|
|
// Configure index settings
|
|
await configureIndex(index);
|
|
}
|
|
}
|
|
return index;
|
|
}
|
|
|
|
async function configureIndex(index) {
|
|
// Load config from docs
|
|
const configPath = join(__dirname, '../../docs/architecture/meilisearch-config.json');
|
|
const config = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
|
|
await index.updateSettings({
|
|
searchableAttributes: config.settings.searchableAttributes,
|
|
filterableAttributes: config.settings.filterableAttributes,
|
|
sortableAttributes: config.settings.sortableAttributes,
|
|
displayedAttributes: config.settings.displayedAttributes,
|
|
synonyms: config.settings.synonyms,
|
|
stopWords: config.settings.stopWords,
|
|
rankingRules: config.settings.rankingRules,
|
|
typoTolerance: config.settings.typoTolerance,
|
|
faceting: config.settings.faceting,
|
|
pagination: config.settings.pagination,
|
|
separatorTokens: config.settings.separatorTokens,
|
|
nonSeparatorTokens: config.settings.nonSeparatorTokens
|
|
});
|
|
|
|
console.log('Meilisearch index configured');
|
|
}
|
|
|
|
export function generateTenantToken(userId, organizationIds, expiresIn = 3600) {
|
|
const client = getMeilisearchClient();
|
|
|
|
const searchRules = {
|
|
[INDEX_NAME]: {
|
|
filter: `userId = ${userId} OR organizationId IN [${organizationIds.join(', ')}]`
|
|
}
|
|
};
|
|
|
|
const expiresAt = new Date(Date.now() + expiresIn * 1000);
|
|
|
|
return client.generateTenantToken(searchRules, {
|
|
apiKey: MEILISEARCH_MASTER_KEY,
|
|
expiresAt
|
|
});
|
|
}
|