defect-inspection-tools-mcp-server
Version:
Defect inspection tools server handling defect detection, analysis, and reporting with AI/ML capabilities for quality control
350 lines • 14.1 kB
JavaScript
import { BaseService, ServiceHealthStatus } from './base-service.js';
import { SanitizationMiddleware } from '../middleware/sanitization-middleware.js';
import { ErrorHandler } from '../middleware/error-handler.js';
import { logger } from '../utils/logger.js';
import { MongoClient } from 'mongodb';
export class DatabaseService extends BaseService {
constructor(config) {
super('DatabaseService', config);
this.client = null;
this.db = null;
this.isConnected = false;
const baseConfig = super.getConfig();
this.config = {
...baseConfig,
uri: config.uri,
databaseName: config.databaseName,
maxPoolSize: config.maxPoolSize || 10,
minPoolSize: config.minPoolSize || 2,
maxIdleTimeMS: config.maxIdleTimeMS || 30000,
serverSelectionTimeoutMS: config.serverSelectionTimeoutMS || 5000
};
}
// Initialize database connection
async initialize() {
const context = ErrorHandler.createErrorContext('DatabaseService.initialize', {});
try {
if (this.isConnected) {
logger.warn('Database service already initialized');
return;
}
this.client = new MongoClient(this.config.uri, {
maxPoolSize: this.config.maxPoolSize,
minPoolSize: this.config.minPoolSize,
maxIdleTimeMS: this.config.maxIdleTimeMS,
serverSelectionTimeoutMS: this.config.serverSelectionTimeoutMS
});
await this.client.connect();
this.db = this.client.db(this.config.databaseName);
this.isConnected = true;
logger.info('Database service initialized successfully', {
databaseName: this.config.databaseName,
maxPoolSize: this.config.maxPoolSize
});
}
catch (error) {
logger.error('Failed to initialize database service', {
error: error.message,
uri: this.config.uri,
databaseName: this.config.databaseName
});
throw error;
}
}
// Ensure database connection
async ensureConnected() {
if (!this.isConnected || !this.client || !this.db) {
await this.initialize();
}
}
// Health check implementation
async healthCheck() {
try {
await this.ensureConnected();
if (!this.db) {
return ServiceHealthStatus.UNHEALTHY;
}
// Ping the database
await this.db.admin().ping();
// Check server status
const serverStatus = await this.db.admin().serverStatus();
if (serverStatus.ok === 1) {
return ServiceHealthStatus.HEALTHY;
}
else {
return ServiceHealthStatus.DEGRADED;
}
}
catch (error) {
logger.error('Database health check failed', {
error: error.message,
databaseName: this.config.databaseName
});
return ServiceHealthStatus.UNHEALTHY;
}
}
// Find documents with options
async find(collectionName, query, options = {}) {
const context = ErrorHandler.createErrorContext('DatabaseService.find', {
collectionName,
query,
options
});
return this.executeWithResilience(async () => {
await this.ensureConnected();
const collection = this.db.collection(collectionName);
// Sanitize query if requested
const sanitizedQuery = options.sanitizeQuery !== false ?
this.sanitizeQuery(query, options) : query;
let cursor = collection.find(sanitizedQuery);
// Apply options
if (options.projection) {
cursor = cursor.project(options.projection);
}
if (options.sort) {
cursor = cursor.sort(options.sort);
}
if (options.skip) {
cursor = cursor.skip(options.skip);
}
if (options.limit) {
cursor = cursor.limit(options.limit);
}
const results = await cursor.toArray();
logger.debug('Database find operation completed', {
collectionName,
resultCount: results.length,
query: SanitizationMiddleware.sanitizeForLogging(sanitizedQuery)
});
return results;
}, context);
}
// Find one document
async findOne(collectionName, query, options = {}) {
const context = ErrorHandler.createErrorContext('DatabaseService.findOne', {
collectionName,
query,
options
});
return this.executeWithResilience(async () => {
await this.ensureConnected();
const collection = this.db.collection(collectionName);
// Sanitize query if requested
const sanitizedQuery = options.sanitizeQuery !== false ?
this.sanitizeQuery(query, options) : query;
const result = await collection.findOne(sanitizedQuery, options.projection ? { projection: options.projection } : {});
logger.debug('Database findOne operation completed', {
collectionName,
found: !!result,
query: SanitizationMiddleware.sanitizeForLogging(sanitizedQuery)
});
return result;
}, context);
}
// Count documents
async count(collectionName, query, options = {}) {
const context = ErrorHandler.createErrorContext('DatabaseService.count', {
collectionName,
query,
options
});
return this.executeWithResilience(async () => {
await this.ensureConnected();
const collection = this.db.collection(collectionName);
// Sanitize query if requested
const sanitizedQuery = options.sanitizeQuery !== false ?
this.sanitizeQuery(query, options) : query;
const count = await collection.countDocuments(sanitizedQuery);
logger.debug('Database count operation completed', {
collectionName,
count,
query: SanitizationMiddleware.sanitizeForLogging(sanitizedQuery)
});
return count;
}, context);
}
// Insert one document
async insertOne(collectionName, document) {
const context = ErrorHandler.createErrorContext('DatabaseService.insertOne', {
collectionName,
document
});
return this.executeWithResilience(async () => {
await this.ensureConnected();
const collection = this.db.collection(collectionName);
// Sanitize document
const sanitizedDoc = SanitizationMiddleware.sanitizeToolArguments(document);
const result = await collection.insertOne(sanitizedDoc);
logger.debug('Database insertOne operation completed', {
collectionName,
insertedId: result.insertedId,
acknowledged: result.acknowledged
});
return result.insertedId;
}, context);
}
// Update one document
async updateOne(collectionName, filter, update, options = {}) {
const context = ErrorHandler.createErrorContext('DatabaseService.updateOne', {
collectionName,
filter,
update,
options
});
return this.executeWithResilience(async () => {
await this.ensureConnected();
const collection = this.db.collection(collectionName);
// Sanitize filter and update
const sanitizedFilter = this.sanitizeQuery(filter, options);
const sanitizedUpdate = options.sanitizeUpdate !== false ?
this.sanitizeUpdate(update, options) : update;
const result = await collection.updateOne(sanitizedFilter, sanitizedUpdate, { upsert: options.upsert || false });
logger.debug('Database updateOne operation completed', {
collectionName,
matched: result.matchedCount,
modified: result.modifiedCount,
upserted: result.upsertedCount
});
return result.modifiedCount > 0 || result.upsertedCount > 0;
}, context);
}
// Delete one document
async deleteOne(collectionName, filter, options = {}) {
const context = ErrorHandler.createErrorContext('DatabaseService.deleteOne', {
collectionName,
filter,
options
});
return this.executeWithResilience(async () => {
await this.ensureConnected();
const collection = this.db.collection(collectionName);
// Sanitize filter
const sanitizedFilter = this.sanitizeQuery(filter, options);
const result = await collection.deleteOne(sanitizedFilter);
logger.debug('Database deleteOne operation completed', {
collectionName,
deleted: result.deletedCount
});
return result.deletedCount > 0;
}, context);
}
// Aggregate documents
async aggregate(collectionName, pipeline, options = {}) {
const context = ErrorHandler.createErrorContext('DatabaseService.aggregate', {
collectionName,
pipeline,
options
});
return this.executeWithResilience(async () => {
await this.ensureConnected();
const collection = this.db.collection(collectionName);
// Sanitize pipeline if requested
const sanitizedPipeline = options.sanitizePipeline !== false ?
this.sanitizePipeline(pipeline, options) : pipeline;
const results = await collection.aggregate(sanitizedPipeline).toArray();
logger.debug('Database aggregate operation completed', {
collectionName,
stageCount: sanitizedPipeline.length,
resultCount: results.length
});
return results;
}, context);
}
// Get database statistics
async getStats() {
const context = ErrorHandler.createErrorContext('DatabaseService.getStats', {});
return this.executeWithResilience(async () => {
await this.ensureConnected();
const stats = await this.db.stats();
logger.debug('Database stats retrieved', {
collections: stats.collections,
objects: stats.objects,
dataSize: stats.dataSize
});
return stats;
}, context);
}
// List collections
async listCollections() {
const context = ErrorHandler.createErrorContext('DatabaseService.listCollections', {});
return this.executeWithResilience(async () => {
await this.ensureConnected();
const collections = await this.db.listCollections().toArray();
const collectionNames = collections.map(col => col.name);
logger.debug('Database collections listed', {
collectionCount: collectionNames.length,
collections: collectionNames
});
return collectionNames;
}, context);
}
// Private helper methods
sanitizeQuery(query, options) {
const sanitizationOptions = {
allowedFields: options.allowedFields,
maxStringLength: 1000,
allowObjectIds: true
};
return SanitizationMiddleware.sanitizeMongoQuery(query, sanitizationOptions);
}
sanitizeUpdate(update, options) {
const sanitizationOptions = {
allowedFields: options.allowedFields,
maxStringLength: 1000,
allowObjectIds: true
};
return SanitizationMiddleware.sanitizeMongoQuery(update, sanitizationOptions);
}
sanitizePipeline(pipeline, options) {
const maxLength = options.maxPipelineLength || 20;
const allowedStages = options.allowedStages || [
'$match', '$project', '$sort', '$limit', '$skip',
'$group', '$unwind', '$lookup', '$addFields',
'$count', '$facet', '$sample'
];
if (pipeline.length > maxLength) {
logger.warn('Aggregation pipeline too long, truncating', {
originalLength: pipeline.length,
maxLength
});
pipeline = pipeline.slice(0, maxLength);
}
return pipeline.map(stage => {
const stageKey = Object.keys(stage)[0];
if (!allowedStages.includes(stageKey)) {
logger.warn('Aggregation stage not allowed, skipping', {
stage: stageKey,
allowedStages
});
return { $match: {} }; // Replace with safe stage
}
const sanitizationOptions = {
maxStringLength: 1000,
allowObjectIds: true
};
return SanitizationMiddleware.sanitizeMongoQuery(stage, sanitizationOptions);
});
}
// Cleanup and close connection
async close() {
try {
if (this.client) {
await this.client.close();
this.client = null;
this.db = null;
this.isConnected = false;
logger.info('Database service closed successfully');
}
}
catch (error) {
logger.error('Error closing database service', {
error: error.message
});
}
}
// Get connection status
isConnectionHealthy() {
return this.isConnected && this.client !== null && this.db !== null;
}
}
//# sourceMappingURL=database-service.js.map