gmail-mcp-server
Version:
Gmail MCP Server with on-demand authentication for SIYA/Claude Desktop. Complete Gmail integration with multi-user support and OAuth2 security.
184 lines • 5.45 kB
JavaScript
import { promises as fs } from 'fs';
import * as path from 'path';
/**
* Utility functions for the Gmail MCP server
*/
/**
* Format file size in human-readable format
*/
export function formatFileSize(bytes) {
const units = ['B', 'KB', 'MB', 'GB', 'TB'];
let size = bytes;
let unitIndex = 0;
while (size >= 1024 && unitIndex < units.length - 1) {
size /= 1024;
unitIndex++;
}
return `${size.toFixed(2)} ${units[unitIndex]}`;
}
/**
* Check if a string is a valid email address
*/
export function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
/**
* Validate email addresses in an array
*/
export function validateEmailAddresses(emails) {
const invalidEmails = emails.filter(email => !isValidEmail(email));
return {
valid: invalidEmails.length === 0,
invalidEmails
};
}
/**
* Check if a file exists and is accessible
*/
export async function fileExists(filePath) {
try {
await fs.access(filePath);
return true;
}
catch {
return false;
}
}
/**
* Get file size
*/
export async function getFileSize(filePath) {
try {
const stats = await fs.stat(filePath);
return stats.size;
}
catch (error) {
throw new Error(`Cannot get file size for ${filePath}: ${error}`);
}
}
/**
* Create directory if it doesn't exist
*/
export async function ensureDirectory(dirPath) {
try {
await fs.access(dirPath);
}
catch {
await fs.mkdir(dirPath, { recursive: true });
}
}
/**
* Clean up old temporary files
*/
export async function cleanupOldTempFiles(tempDir, maxAgeMs = 24 * 60 * 60 * 1000) {
try {
const files = await fs.readdir(tempDir);
const now = Date.now();
for (const file of files) {
const filePath = path.join(tempDir, file);
const stats = await fs.stat(filePath);
if (now - stats.mtime.getTime() > maxAgeMs) {
await fs.unlink(filePath);
console.log(`Cleaned up old temp file: ${filePath}`);
}
}
}
catch (error) {
console.warn(`Failed to cleanup temp files: ${error}`);
}
}
/**
* Generate a unique filename with timestamp
*/
export function generateUniqueFilename(originalName) {
const ext = path.extname(originalName);
const name = path.basename(originalName, ext);
const timestamp = Date.now();
const random = Math.random().toString(36).substr(2, 6);
return `${name}_${timestamp}_${random}${ext}`;
}
/**
* Extract MIME type from file extension
*/
export function getMimeTypeFromExtension(filename) {
const ext = path.extname(filename).toLowerCase();
const mimeTypes = {
'.txt': 'text/plain',
'.html': 'text/html',
'.css': 'text/css',
'.js': 'application/javascript',
'.json': 'application/json',
'.pdf': 'application/pdf',
'.doc': 'application/msword',
'.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'.xls': 'application/vnd.ms-excel',
'.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'.ppt': 'application/vnd.ms-powerpoint',
'.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'.zip': 'application/zip',
'.rar': 'application/x-rar-compressed',
'.7z': 'application/x-7z-compressed',
'.tar': 'application/x-tar',
'.gz': 'application/gzip',
'.jpg': 'image/jpeg',
'.jpeg': 'image/jpeg',
'.png': 'image/png',
'.gif': 'image/gif',
'.bmp': 'image/bmp',
'.svg': 'image/svg+xml',
'.webp': 'image/webp',
'.mp3': 'audio/mpeg',
'.wav': 'audio/wav',
'.ogg': 'audio/ogg',
'.mp4': 'video/mp4',
'.avi': 'video/x-msvideo',
'.mov': 'video/quicktime',
'.wmv': 'video/x-ms-wmv',
'.flv': 'video/x-flv',
'.webm': 'video/webm'
};
return mimeTypes[ext] || 'application/octet-stream';
}
/**
* Throttle function to limit API calls
*/
export function throttle(func, limit) {
let inThrottle;
return async (...args) => {
if (!inThrottle) {
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
return func(...args);
}
else {
return new Promise((resolve) => {
setTimeout(async () => {
resolve(await func(...args));
}, limit);
});
}
};
}
/**
* Retry function with exponential backoff
*/
export async function retryWithBackoff(operation, maxRetries = 3, baseDelay = 1000) {
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation();
}
catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
if (attempt === maxRetries) {
break;
}
const delay = baseDelay * Math.pow(2, attempt);
console.warn(`Operation failed (attempt ${attempt + 1}/${maxRetries + 1}), retrying in ${delay}ms:`, error);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw lastError;
}
//# sourceMappingURL=utils.js.map