Spaces:
Running
Running
Update db.js
Browse files
db.js
CHANGED
|
@@ -1,53 +1,74 @@
|
|
| 1 |
const admin = require('firebase-admin');
|
| 2 |
const MiniSearch = require('minisearch');
|
| 3 |
-
// require('dotenv').config();
|
| 4 |
-
|
| 5 |
-
// 1. Initialize Firestore
|
| 6 |
-
// if
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 13 |
const db = admin.firestore();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
const TOOLS_COLLECTION = 'mcp_tools';
|
| 15 |
|
| 16 |
-
//
|
| 17 |
-
// We index 'name', 'desc', 'keywords' for fuzzy search
|
| 18 |
const searchEngine = new MiniSearch({
|
| 19 |
fields: ['name', 'description', 'keywords'],
|
| 20 |
storeFields: ['id', 'name', 'description', 'url', 'install_command', 'stats', 'reputation', 'analytics'],
|
| 21 |
searchOptions: {
|
| 22 |
-
boost: { name: 2, keywords: 1.5 },
|
| 23 |
fuzzy: 0.2,
|
| 24 |
prefix: true
|
| 25 |
}
|
| 26 |
});
|
| 27 |
|
| 28 |
-
//
|
| 29 |
async function hydrateMemory() {
|
| 30 |
console.log("💧 Hydrating In-Memory Index...");
|
| 31 |
try {
|
| 32 |
const snapshot = await db.collection(TOOLS_COLLECTION).get();
|
| 33 |
-
const tools = [];
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
snapshot.forEach(doc => {
|
| 36 |
tools.push(doc.data());
|
| 37 |
});
|
| 38 |
|
| 39 |
searchEngine.removeAll();
|
| 40 |
-
|
| 41 |
-
searchEngine.addAll(tools);
|
| 42 |
-
}
|
| 43 |
console.log(`✅ Loaded ${tools.length} MCP tools into RAM.`);
|
| 44 |
} catch (error) {
|
| 45 |
-
console.error("❌ Hydration Failed:", error);
|
|
|
|
| 46 |
}
|
| 47 |
}
|
| 48 |
|
| 49 |
-
// 4. Fast Search Wrapper
|
| 50 |
function searchTools(query) {
|
|
|
|
|
|
|
| 51 |
return searchEngine.search(query);
|
| 52 |
}
|
| 53 |
|
|
|
|
| 1 |
const admin = require('firebase-admin');
|
| 2 |
const MiniSearch = require('minisearch');
|
| 3 |
+
// require('dotenv').config(); // Good, handled by platform
|
| 4 |
+
|
| 5 |
+
// 1. Initialize Firestore (The Singleton Pattern Fix)
|
| 6 |
+
// We MUST check if the app is already initialized to prevent crashes on hot-reloads
|
| 7 |
+
if (!admin.apps.length) {
|
| 8 |
+
try {
|
| 9 |
+
// Ensure the ENV variable exists to prevent JSON.parse error
|
| 10 |
+
if (!process.env.FIREBASE_CREDENTIALS) {
|
| 11 |
+
throw new Error('Missing FIREBASE_CREDENTIALS env var');
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
const svc = JSON.parse(process.env.FIREBASE_CREDENTIALS);
|
| 15 |
+
admin.initializeApp({ credential: admin.credential.cert(svc) });
|
| 16 |
+
console.log("🔥 Firebase Admin Initialized");
|
| 17 |
+
} catch (error) {
|
| 18 |
+
console.error("❌ Firebase Init Error:", error.message);
|
| 19 |
+
process.exit(1); // Kill app if DB fails to start
|
| 20 |
+
}
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
const db = admin.firestore();
|
| 24 |
+
|
| 25 |
+
// 2. FORCE HTTP (The Fix for '14 UNAVAILABLE')
|
| 26 |
+
db.settings({
|
| 27 |
+
preferRest: true, // Bypass gRPC, use standard HTTP
|
| 28 |
+
ignoreUndefinedProperties: true // Good safety setting
|
| 29 |
+
});
|
| 30 |
+
|
| 31 |
const TOOLS_COLLECTION = 'mcp_tools';
|
| 32 |
|
| 33 |
+
// 3. Initialize In-Memory Search Engine
|
|
|
|
| 34 |
const searchEngine = new MiniSearch({
|
| 35 |
fields: ['name', 'description', 'keywords'],
|
| 36 |
storeFields: ['id', 'name', 'description', 'url', 'install_command', 'stats', 'reputation', 'analytics'],
|
| 37 |
searchOptions: {
|
| 38 |
+
boost: { name: 2, keywords: 1.5 },
|
| 39 |
fuzzy: 0.2,
|
| 40 |
prefix: true
|
| 41 |
}
|
| 42 |
});
|
| 43 |
|
| 44 |
+
// 4. Hydrate RAM from Disk
|
| 45 |
async function hydrateMemory() {
|
| 46 |
console.log("💧 Hydrating In-Memory Index...");
|
| 47 |
try {
|
| 48 |
const snapshot = await db.collection(TOOLS_COLLECTION).get();
|
|
|
|
| 49 |
|
| 50 |
+
if (snapshot.empty) {
|
| 51 |
+
console.log("⚠️ Firestore is empty. Run scraper to populate.");
|
| 52 |
+
return;
|
| 53 |
+
}
|
| 54 |
+
|
| 55 |
+
const tools = [];
|
| 56 |
snapshot.forEach(doc => {
|
| 57 |
tools.push(doc.data());
|
| 58 |
});
|
| 59 |
|
| 60 |
searchEngine.removeAll();
|
| 61 |
+
searchEngine.addAll(tools);
|
|
|
|
|
|
|
| 62 |
console.log(`✅ Loaded ${tools.length} MCP tools into RAM.`);
|
| 63 |
} catch (error) {
|
| 64 |
+
console.error("❌ Hydration Failed:", error.message);
|
| 65 |
+
// Do not throw here, or the server will crash. Just log it.
|
| 66 |
}
|
| 67 |
}
|
| 68 |
|
|
|
|
| 69 |
function searchTools(query) {
|
| 70 |
+
// Add safety check in case searchEngine is empty
|
| 71 |
+
if (searchEngine.documentCount === 0) return [];
|
| 72 |
return searchEngine.search(query);
|
| 73 |
}
|
| 74 |
|