UNPKG

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
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