@andrewlwn77/s3-upload-mcp-server
Version:
Pure Node.js MCP server for uploading images to AWS S3 with high-performance validation using Sharp and file-type
174 lines • 6.76 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ImageValidator = void 0;
const sharp_1 = __importDefault(require("sharp"));
const file_type_1 = require("file-type");
const fs_1 = require("fs");
const logger_1 = require("../utils/logger");
class ImageValidator {
constructor(maxFileSize = 10 * 1024 * 1024) {
this.logger = logger_1.Logger.getInstance();
this.maxFileSize = maxFileSize;
}
async validateImageFile(filePath) {
try {
// Check if file exists
try {
await fs_1.promises.access(filePath);
}
catch {
return {
success: true,
is_valid: false,
validation_errors: ['File does not exist']
};
}
// Get file stats and read buffer
const stats = await fs_1.promises.stat(filePath);
const fileSize = stats.size;
// Check file size
if (fileSize > this.maxFileSize) {
return {
success: true,
is_valid: false,
file_size: fileSize,
validation_errors: [`File size ${fileSize} exceeds maximum allowed size ${this.maxFileSize}`]
};
}
// Read file buffer for validation
const buffer = await fs_1.promises.readFile(filePath);
// Use Node.js validation
const result = await this.validateImageBuffer(buffer, fileSize);
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown validation error';
this.logger.error('Image validation failed', { error: errorMessage, filePath });
return {
success: false,
error: {
code: 'VALIDATION_ERROR',
message: errorMessage
}
};
}
}
async validateBase64Image(base64Data, filename) {
try {
// Basic base64 validation
const base64Pattern = /^data:image\/[a-z]+;base64,/;
const hasDataUrl = base64Pattern.test(base64Data);
if (!hasDataUrl && !this.isValidBase64(base64Data)) {
return {
success: true,
is_valid: false,
validation_errors: ['Invalid base64 data format']
};
}
// Extract base64 data
const base64String = base64Data.replace(/^data:image\/[a-z]+;base64,/, '');
const buffer = Buffer.from(base64String, 'base64');
// Check size
if (buffer.length > this.maxFileSize) {
return {
success: true,
is_valid: false,
file_size: buffer.length,
validation_errors: [`File size ${buffer.length} exceeds maximum allowed size ${this.maxFileSize}`]
};
}
// Use Node.js validation directly on buffer
const result = await this.validateImageBuffer(buffer, buffer.length);
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown base64 validation error';
this.logger.error('Base64 image validation failed', { error: errorMessage, filename });
return {
success: false,
error: {
code: 'VALIDATION_ERROR',
message: errorMessage
}
};
}
}
async validateImageBuffer(buffer, fileSize) {
try {
// Detect MIME type using file-type
const fileType = await (0, file_type_1.fileTypeFromBuffer)(buffer);
const mimeType = fileType?.mime || 'application/octet-stream';
// Define supported image formats
const supportedFormats = [
'image/jpeg', 'image/png', 'image/gif',
'image/webp', 'image/bmp'
];
const validationErrors = [];
// Check if it's a supported image format
if (!supportedFormats.includes(mimeType)) {
validationErrors.push(`Unsupported file format: ${mimeType}`);
}
let dimensions;
let fileFormat;
// Use Sharp to extract metadata and validate image integrity
try {
const metadata = await (0, sharp_1.default)(buffer).metadata();
dimensions = {
width: metadata.width || 0,
height: metadata.height || 0
};
fileFormat = metadata.format?.toLowerCase();
// Additional validation - Sharp will throw if image is corrupt
await (0, sharp_1.default)(buffer).stats();
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
validationErrors.push(`Invalid image file: ${errorMessage}`);
}
const isValid = validationErrors.length === 0;
const result = {
success: true,
is_valid: isValid,
file_format: fileFormat,
file_size: fileSize,
content_type: mimeType
};
if (dimensions) {
result.dimensions = dimensions;
}
if (!isValid) {
result.validation_errors = validationErrors;
}
this.logger.info('Image validation completed', {
isValid,
fileFormat,
mimeType,
dimensions,
fileSize
});
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown validation error';
this.logger.error('Image buffer validation failed', { error: errorMessage });
return {
success: true,
is_valid: false,
validation_errors: [`Image validation error: ${errorMessage}`]
};
}
}
isValidBase64(str) {
try {
return btoa(atob(str)) === str;
}
catch {
return false;
}
}
}
exports.ImageValidator = ImageValidator;
//# sourceMappingURL=image-validator.js.map