@channel.io/be-user-chat-mcp
Version:
Backend UserChat Model Context Protocol server for Channel.io
97 lines • 4.5 kB
JavaScript
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