195 lines
6.3 KiB
JavaScript
195 lines
6.3 KiB
JavaScript
const fs = require('fs');
|
|
const path = require('path');
|
|
require('dotenv').config();
|
|
|
|
async function bootstrap() {
|
|
console.log("=== STARTING N8N WORKFLOW BOOTSTRAP / KICKSTART ===");
|
|
|
|
// 1. Fetch n8n API configuration from .agents/mcp_config.json
|
|
const mcpConfigPath = path.join(__dirname, '../.agents/mcp_config.json');
|
|
if (!fs.existsSync(mcpConfigPath)) {
|
|
console.error("Error: .agents/mcp_config.json not found.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const mcpConfig = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf8'));
|
|
const n8nEnv = mcpConfig.mcpServers.n8n.env;
|
|
const n8nUrl = n8nEnv.N8N_API_URL || "https://n8n.gaboggamer.online";
|
|
const n8nApiKey = n8nEnv.N8N_API_KEY;
|
|
|
|
if (!n8nApiKey) {
|
|
console.error("Error: N8N_API_KEY is missing in mcp_config.json.");
|
|
process.exit(1);
|
|
}
|
|
|
|
// 2. Load the official local workflow JSON
|
|
const workflowPath = path.join(__dirname, '../n8n/sales_import_workflow.json');
|
|
if (!fs.existsSync(workflowPath)) {
|
|
console.error("Error: n8n/sales_import_workflow.json not found.");
|
|
process.exit(1);
|
|
}
|
|
|
|
const workflowJson = JSON.parse(fs.readFileSync(workflowPath, 'utf8'));
|
|
const targetName = workflowJson.name || "Sales Data Import & Validation";
|
|
|
|
// 2.5. Fetch credentials dynamically to resolve AI accounts
|
|
console.log("Fetching credentials from n8n to resolve AI accounts...");
|
|
let deepSeekCred = null;
|
|
let geminiCred = null;
|
|
try {
|
|
const credsRes = await fetch(`${n8nUrl}/api/v1/credentials`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-N8N-API-KEY': n8nApiKey
|
|
}
|
|
});
|
|
if (credsRes.ok) {
|
|
const credsData = await credsRes.json();
|
|
const credentials = Array.isArray(credsData.data) ? credsData.data : (credsData.data.credentials || []);
|
|
|
|
const deepSeekCreds = credentials.filter(c => c.type === 'deepSeekApi');
|
|
if (deepSeekCreds.length > 0) {
|
|
deepSeekCred = deepSeekCreds.find(c => c.name.toLowerCase().includes('deepseek')) || deepSeekCreds[0];
|
|
}
|
|
|
|
const geminiCreds = credentials.filter(c => c.type === 'googlePalmApi');
|
|
if (geminiCreds.length > 0) {
|
|
geminiCred = geminiCreds.find(c => c.name.toLowerCase().includes('gemini') || c.name.toLowerCase().includes('google')) || geminiCreds[0];
|
|
}
|
|
|
|
if (deepSeekCred) {
|
|
console.log(`Resolved DeepSeek credential: "${deepSeekCred.name}" (ID: ${deepSeekCred.id})`);
|
|
}
|
|
if (geminiCred) {
|
|
console.log(`Resolved Gemini credential: "${geminiCred.name}" (ID: ${geminiCred.id})`);
|
|
}
|
|
} else {
|
|
console.warn(`Warning: Failed to fetch credentials. Status: ${credsRes.status}`);
|
|
}
|
|
} catch (err) {
|
|
console.warn("Warning: Could not fetch credentials dynamically:", err.message);
|
|
}
|
|
|
|
// Inject resolved credentials dynamically into the workflow JSON
|
|
for (const node of workflowJson.nodes) {
|
|
if (node.type === '@n8n/n8n-nodes-langchain.lmChatDeepSeek' && deepSeekCred) {
|
|
node.credentials = {
|
|
deepSeekApi: {
|
|
id: deepSeekCred.id,
|
|
name: deepSeekCred.name
|
|
}
|
|
};
|
|
console.log(`Injected DeepSeek credential into node "${node.name}"`);
|
|
}
|
|
if (node.type === '@n8n/n8n-nodes-langchain.lmChatGoogleGemini' && geminiCred) {
|
|
node.credentials = {
|
|
googlePalmApi: {
|
|
id: geminiCred.id,
|
|
name: geminiCred.name
|
|
}
|
|
};
|
|
console.log(`Injected Gemini credential into node "${node.name}"`);
|
|
}
|
|
}
|
|
|
|
// 3. Check if workflow already exists in n8n
|
|
console.log(`Searching for existing workflow named "${targetName}"...`);
|
|
const listRes = await fetch(`${n8nUrl}/api/v1/workflows`, {
|
|
method: 'GET',
|
|
headers: {
|
|
'X-N8N-API-KEY': n8nApiKey
|
|
}
|
|
});
|
|
|
|
if (!listRes.ok) {
|
|
console.error(`Failed to list workflows from n8n. Status: ${listRes.status}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const listData = await listRes.json();
|
|
const existingWorkflow = listData.data.find(w => w.name === targetName);
|
|
|
|
// Retrieve project ID from existing workflows if available
|
|
let projectId;
|
|
const firstWf = listData.data.find(w => w.shared && w.shared.length > 0);
|
|
if (firstWf) {
|
|
projectId = firstWf.shared[0].projectId;
|
|
console.log(`Resolved project ID dynamically: ${projectId}`);
|
|
}
|
|
|
|
let workflowId;
|
|
if (existingWorkflow) {
|
|
workflowId = existingWorkflow.id;
|
|
console.log(`Found existing workflow. ID: ${workflowId}. Updating...`);
|
|
|
|
const updateRes = await fetch(`${n8nUrl}/api/v1/workflows/${workflowId}`, {
|
|
method: 'PUT',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-N8N-API-KEY': n8nApiKey
|
|
},
|
|
body: JSON.stringify({
|
|
name: targetName,
|
|
nodes: workflowJson.nodes,
|
|
connections: workflowJson.connections,
|
|
settings: workflowJson.settings || {}
|
|
})
|
|
});
|
|
|
|
if (!updateRes.ok) {
|
|
console.error(`Failed to update workflow. Status: ${updateRes.status}`);
|
|
process.exit(1);
|
|
}
|
|
console.log(`Workflow updated successfully!`);
|
|
} else {
|
|
console.log("No existing workflow found. Creating new workflow...");
|
|
|
|
const createRes = await fetch(`${n8nUrl}/api/v1/workflows`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-N8N-API-KEY': n8nApiKey
|
|
},
|
|
body: JSON.stringify({
|
|
name: targetName,
|
|
nodes: workflowJson.nodes,
|
|
connections: workflowJson.connections,
|
|
settings: workflowJson.settings || {},
|
|
projectId
|
|
})
|
|
});
|
|
|
|
if (!createRes.ok) {
|
|
console.error(`Failed to create workflow. Status: ${createRes.status}`);
|
|
process.exit(1);
|
|
}
|
|
|
|
const createData = await createRes.json();
|
|
workflowId = createData.id;
|
|
console.log(`Workflow created successfully! ID: ${workflowId}`);
|
|
}
|
|
|
|
// 4. Activate the workflow
|
|
console.log(`Activating workflow ${workflowId}...`);
|
|
const activateRes = await fetch(`${n8nUrl}/api/v1/workflows/${workflowId}/activate`, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-N8N-API-KEY': n8nApiKey
|
|
},
|
|
body: JSON.stringify({})
|
|
});
|
|
|
|
if (!activateRes.ok) {
|
|
console.error(`Warning: Failed to activate workflow. Status: ${activateRes.status}`);
|
|
} else {
|
|
console.log(`Workflow activated and ready!`);
|
|
}
|
|
|
|
console.log("=== N8N WORKFLOW BOOTSTRAP COMPLETED SUCCESSFULLY ===");
|
|
}
|
|
|
|
bootstrap().catch(err => {
|
|
console.error("Fatal bootstrap error:", err);
|
|
process.exit(1);
|
|
});
|