tryaii-mcp-server
Version:
TryAII MCP Server - 15+ AI models with comparison, cost tracking, and collective intelligence
199 lines • 8.4 kB
JavaScript
import { databaseService } from './database.js';
import { logger } from '../utils/logger.js';
import { ObjectId } from 'mongodb';
import { verifyApiKey, isValidApiKeyFormat, sanitizeApiKeyForLog } from '../utils/apiKeyUtils.js';
export class CrossDatabaseService {
/**
* Validate API key and return authenticated user information
*/
async validateApiKey(apiKey) {
try {
const startTime = Date.now();
// Validate API key format first
if (!isValidApiKeyFormat(apiKey)) {
logger.warn('CrossDatabaseService: Invalid API key format', { keyPrefix: sanitizeApiKeyForLog(apiKey) });
return null;
}
// Extract key prefix for faster lookup
const keyPrefix = apiKey.substring(0, 8);
// Get connection to main database (tryaii_dev)
const mainConnection = databaseService.getMainConnection();
// Find ALL active API keys with matching prefix
const ApiKeyCollection = mainConnection.collection('userapikeys');
const potentialKeys = await ApiKeyCollection.find({
keyPrefix,
isActive: true,
$or: [
{ expiresAt: { $exists: false } },
{ expiresAt: { $gt: new Date() } }
]
}).toArray();
if (potentialKeys.length === 0) {
logger.warn('CrossDatabaseService: No active API keys found with prefix', { keyPrefix });
return null;
}
// Verify hash against each potential key (there should normally be only one)
let authenticatedKey = null;
for (const keyDoc of potentialKeys) {
if (verifyApiKey(apiKey, keyDoc.keyHash)) {
authenticatedKey = keyDoc;
break;
}
}
if (!authenticatedKey) {
logger.warn('CrossDatabaseService: API key hash verification failed', {
keyPrefix,
potentialMatches: potentialKeys.length
});
return null;
}
// Update last used timestamp (fire and forget)
ApiKeyCollection.updateOne({ _id: authenticatedKey._id }, { $set: { lastUsed: new Date() } }).catch((error) => logger.warn('CrossDatabaseService: Failed to update lastUsed', { error, keyId: authenticatedKey.keyId }));
// Get user details from the users collection
const UserCollection = mainConnection.collection('users');
const user = await UserCollection.findOne({
_id: authenticatedKey.userId
});
if (!user) {
logger.warn('CrossDatabaseService: User not found', { userId: authenticatedKey.userId });
return null;
}
const authTime = Date.now() - startTime;
logger.debug('CrossDatabaseService: API key authenticated successfully', {
userId: authenticatedKey.userId,
keyId: authenticatedKey.keyId,
keyPrefix,
authTime: `${authTime}ms`
});
return {
keyId: authenticatedKey.keyId,
userId: authenticatedKey.userId,
keyPrefix: authenticatedKey.keyPrefix,
permissions: authenticatedKey.permissions || [],
isActive: authenticatedKey.isActive,
userName: user?.name,
userEmail: user?.email,
keyDocument: authenticatedKey
};
}
catch (error) {
logger.error('CrossDatabaseService: Error validating API key', {
error: error instanceof Error ? error.message : String(error),
keyPrefix: sanitizeApiKeyForLog(apiKey)
});
return null;
}
}
/**
* Update usage statistics for an API key
*/
async updateUsageStats(keyId, updates) {
try {
const mainConnection = databaseService.getMainConnection();
const ApiKeyCollection = mainConnection.collection('userapikeys');
const updateDoc = {
updatedAt: new Date()
};
const incDoc = {};
if (updates.totalRequests !== undefined) {
incDoc['usageStats.totalRequests'] = updates.totalRequests;
}
if (updates.totalCost !== undefined) {
incDoc['usageStats.totalCost'] = updates.totalCost;
}
if (updates.lastUsed !== undefined) {
updateDoc.lastUsed = updates.lastUsed;
}
const result = await ApiKeyCollection.updateOne({ keyId }, {
...(Object.keys(incDoc).length > 0 ? { $inc: incDoc } : {}),
$set: updateDoc
});
if (result.matchedCount === 0) {
logger.warn('CrossDatabaseService: API key not found for usage update', { keyId });
return false;
}
logger.debug('CrossDatabaseService: Usage stats updated', { keyId, updates });
return true;
}
catch (error) {
logger.error('CrossDatabaseService: Error updating usage stats', { error, keyId });
return false;
}
}
/**
* Create or update session in MCP database with real user data
*/
async createOrUpdateSession(sessionData) {
try {
const mcpConnection = databaseService.getMcpConnection();
const collectionName = `mcp_${sessionData.sessionType}sessions`;
const SessionCollection = mcpConnection.collection(collectionName);
const sessionDoc = {
userId: sessionData.userId,
apiKeyId: sessionData.apiKeyId,
createdAt: new Date(),
updatedAt: new Date(),
status: 'active',
...sessionData.metadata
};
const result = await SessionCollection.insertOne(sessionDoc);
logger.debug('CrossDatabaseService: Session created', {
sessionId: result.insertedId,
userId: sessionData.userId,
apiKeyId: sessionData.apiKeyId,
sessionType: sessionData.sessionType
});
return result.insertedId.toString();
}
catch (error) {
logger.error('CrossDatabaseService: Error creating session', { error, sessionData });
return null;
}
}
/**
* Get session by ID from MCP database
*/
async getSession(sessionId, sessionType) {
try {
const mcpConnection = databaseService.getMcpConnection();
const collectionName = `mcp_${sessionType}sessions`;
const SessionCollection = mcpConnection.collection(collectionName);
const session = await SessionCollection.findOne({
_id: new ObjectId(sessionId)
});
return session;
}
catch (error) {
logger.error('CrossDatabaseService: Error getting session', { error, sessionId, sessionType });
return null;
}
}
/**
* Test connections to both databases
*/
async testConnections() {
const results = { main: false, mcp: false };
try {
const mainConnection = databaseService.getMainConnection();
await mainConnection.db.admin().ping();
results.main = true;
logger.debug('CrossDatabaseService: Main database connection OK');
}
catch (error) {
logger.error('CrossDatabaseService: Main database connection failed', { error });
}
try {
const mcpConnection = databaseService.getMcpConnection();
await mcpConnection.db.admin().ping();
results.mcp = true;
logger.debug('CrossDatabaseService: MCP database connection OK');
}
catch (error) {
logger.error('CrossDatabaseService: MCP database connection failed', { error });
}
return results;
}
}
// Create singleton instance
export const crossDatabaseService = new CrossDatabaseService();
//# sourceMappingURL=CrossDatabaseService.js.map