UNPKG

nodejs-cloud-taskmq

Version:

Node.js TypeScript library for integrating Google Cloud Tasks with MongoDB/Redis/Memory/Custom for a BullMQ-like queue system. Compatible with NestJS but framework-agnostic.

203 lines (202 loc) 5.57 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getClientIp = getClientIp; exports.isCloudTasksRequest = isCloudTasksRequest; exports.validateCloudTasksHeaders = validateCloudTasksHeaders; exports.getRequestBodySize = getRequestBodySize; exports.isJsonRequest = isJsonRequest; exports.getBearerToken = getBearerToken; exports.createErrorResponse = createErrorResponse; exports.createSuccessResponse = createSuccessResponse; exports.parseQueryParam = parseQueryParam; exports.parseNumericQueryParam = parseNumericQueryParam; exports.parseBooleanQueryParam = parseBooleanQueryParam; exports.parseArrayQueryParam = parseArrayQueryParam; exports.sanitizeFileName = sanitizeFileName; exports.generateRequestId = generateRequestId; /** * HTTP utility functions */ /** * Extract IP address from Express request */ function getClientIp(req) { const forwarded = req.headers['x-forwarded-for']; const realIp = req.headers['x-real-ip']; const cloudFlareIp = req.headers['cf-connecting-ip']; if (forwarded) { return forwarded.split(',')[0].trim(); } if (realIp) { return realIp; } if (cloudFlareIp) { return cloudFlareIp; } return req.connection.remoteAddress || req.socket.remoteAddress || 'unknown'; } /** * Check if request is from Google Cloud Tasks */ function isCloudTasksRequest(req) { const taskName = req.headers['x-cloudtasks-taskname']; const queueName = req.headers['x-cloudtasks-queuename']; const userAgent = req.headers['user-agent']; // Check for Cloud Tasks headers if (taskName && queueName) { return true; } // Check for Cloud Tasks user agent if (userAgent && userAgent.includes('Google-Cloud-Tasks')) { return true; } return false; } /** * Validate Cloud Tasks request headers */ function validateCloudTasksHeaders(req) { const errors = []; const taskName = req.headers['x-cloudtasks-taskname']; const queueName = req.headers['x-cloudtasks-queuename']; const userAgent = req.headers['user-agent']; if (!taskName) { errors.push('Missing x-cloudtasks-taskname header'); } if (!queueName) { errors.push('Missing x-cloudtasks-queuename header'); } if (!userAgent || !userAgent.includes('Google-Cloud-Tasks')) { errors.push('Invalid or missing user-agent header'); } return { valid: errors.length === 0, taskName, queueName, errors, }; } /** * Get request body size in bytes */ function getRequestBodySize(req) { const contentLength = req.headers['content-length']; return contentLength ? parseInt(contentLength, 10) : 0; } /** * Check if request contains JSON */ function isJsonRequest(req) { const contentType = req.headers['content-type']; return contentType ? contentType.includes('application/json') : false; } /** * Extract bearer token from Authorization header */ function getBearerToken(req) { const authHeader = req.headers.authorization; if (!authHeader) { return null; } const matches = authHeader.match(/^Bearer\s+(.+)$/i); return matches ? matches[1] : null; } /** * Create standardized error response */ function createErrorResponse(message, code, details) { return { success: false, error: { message, code, details, timestamp: new Date().toISOString(), }, }; } /** * Create standardized success response */ function createSuccessResponse(data, message) { return { success: true, data, message, timestamp: new Date().toISOString(), }; } /** * Parse query string parameters safely */ function parseQueryParam(value, defaultValue) { if (Array.isArray(value)) { return value[0] || defaultValue; } return value || defaultValue; } /** * Parse numeric query parameter */ function parseNumericQueryParam(value, defaultValue, min, max) { const stringValue = parseQueryParam(value); if (!stringValue) { return defaultValue; } const numericValue = parseInt(stringValue, 10); if (isNaN(numericValue)) { return defaultValue; } let result = numericValue; if (min !== undefined && result < min) { result = min; } if (max !== undefined && result > max) { result = max; } return result; } /** * Parse boolean query parameter */ function parseBooleanQueryParam(value, defaultValue) { const stringValue = parseQueryParam(value); if (!stringValue) { return defaultValue; } const lowerValue = stringValue.toLowerCase(); if (lowerValue === 'true' || lowerValue === '1' || lowerValue === 'yes') { return true; } if (lowerValue === 'false' || lowerValue === '0' || lowerValue === 'no') { return false; } return defaultValue; } /** * Parse array query parameter */ function parseArrayQueryParam(value, separator = ',') { if (Array.isArray(value)) { return value; } if (!value) { return []; } return value.split(separator).map(item => item.trim()).filter(Boolean); } /** * Sanitize file name for safe usage */ function sanitizeFileName(fileName) { return fileName .replace(/[^a-zA-Z0-9.-]/g, '_') .replace(/_{2,}/g, '_') .replace(/^_+|_+$/g, ''); } /** * Generate unique request ID */ function generateRequestId() { return `req_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; }