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
JavaScript
;
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)}`;
}