UNPKG

@channel.io/be-user-chat-mcp

Version:

Backend UserChat Model Context Protocol server for Channel.io

97 lines 4.5 kB
import { queryDynamoDB } from "../services/dynamodb.service.js"; import { executeRawRedashQuery } from "../services/redash.service.js"; // --- Constants --- // Use hard-coded default values instead of environment variables const DYNAMODB_TABLE_USER_CHAT_WORKFLOW_SECTION_PATHS = "user_chat_workflow_section_paths"; const REDASH_DATA_SOURCE_ID_CHANNEL_PROD_PSQL = "1"; const TABLE_WORKFLOWS = "workflows"; const TABLE_WORKFLOW_REVISIONS = "workflow_revisions"; // --- Utility Functions --- /** * Validates a workflow ID format (basic alphanumeric, underscore, hyphen). * Throws an error for invalid formats to prevent potential SQL issues. */ function validateWorkflowId(workflowId, context) { // Stricter regex: Only allow alphanumeric, underscore, hyphen if (!/^[a-zA-Z0-9_-]+$/.test(workflowId)) { console.error(`Invalid workflowId format detected in ${context}: ${workflowId}`); throw new Error(`Invalid workflowId format provided for ${context}.`); } // Optional: Add length check if applicable // if (workflowId.length > 50) { ... } } // --- Data Fetching Functions --- /** * Fetches a workflow by its ID using a raw Redash query. * Throws an error if the query fails. */ export async function getWorkflow(workflowId, context) { const operationContext = `getWorkflow ${workflowId}`; validateWorkflowId(workflowId, operationContext); // Validate ID // WARNING: Constructing SQL like this is vulnerable to SQL injection // if validateWorkflowId is bypassed or insufficient. // Consider using a safer query method if possible. const sql = `SELECT * FROM ${TABLE_WORKFLOWS} WHERE id = '${workflowId}' LIMIT 1;`; const dataSourceId = parseInt(REDASH_DATA_SOURCE_ID_CHANNEL_PROD_PSQL, 10); if (isNaN(dataSourceId)) { throw new Error(`Invalid REDASH_DATA_SOURCE_ID_PSQL: ${REDASH_DATA_SOURCE_ID_CHANNEL_PROD_PSQL}`); } try { const results = await executeRawRedashQuery(dataSourceId, sql, operationContext, context); return results.length > 0 ? results[0] : null; } catch (error) { console.error(`[Workflow Feature] Error in ${operationContext}:`, error); throw error; // Re-throw the error handled by redash.ts } } /** * Fetches all workflow revisions for a given workflow ID using a raw Redash query. * Throws an error if the query fails. */ export async function getWorkflowRevisions(workflowId, context) { // Return empty array instead of null for consistency const operationContext = `getWorkflowRevisions ${workflowId}`; validateWorkflowId(workflowId, operationContext); // Validate ID // WARNING: SQL Injection Risk const sql = `SELECT * FROM ${TABLE_WORKFLOW_REVISIONS} WHERE workflow_id = '${workflowId}' ORDER BY created_at DESC;`; const dataSourceId = parseInt(REDASH_DATA_SOURCE_ID_CHANNEL_PROD_PSQL, 10); if (isNaN(dataSourceId)) { throw new Error(`Invalid REDASH_DATA_SOURCE_ID_PSQL: ${REDASH_DATA_SOURCE_ID_CHANNEL_PROD_PSQL}`); } try { // executeRawRedashQuery now throws on error, so it won't return null const results = await executeRawRedashQuery(dataSourceId, sql, operationContext, context); return results; // Can be an empty array if no revisions found } catch (error) { console.error(`[Workflow Feature] Error in ${operationContext}:`, error); throw error; // Re-throw the error handled by redash.ts } } /** * Fetches all UserChatWorkflowSectionPath items for a given userChatId from DynamoDB. * Throws an error if the query fails. */ export async function getWorkflowSectionPaths(userChatId, context) { const operationContext = `getUserChatWorkflowSectionPaths ${userChatId}`; try { const results = await queryDynamoDB(DYNAMODB_TABLE_USER_CHAT_WORKFLOW_SECTION_PATHS, { KeyConditionExpression: "userChatId = :ucid", ExpressionAttributeValues: { ":ucid": userChatId, }, }, context); // Convert epoch milliseconds to ISO string return results.map((item) => ({ ...item, createdAt: new Date(item.createdAt).toISOString(), updatedAt: new Date(item.updatedAt).toISOString(), })); } catch (error) { console.error(`[Workflow Feature] Error in ${operationContext}:`, error); throw error; // Re-throw the error handled by dynamodb.ts } } //# sourceMappingURL=workflow.repository.js.map