SaaS/backend/src/controllers/workflowController.ts
Aimpress Team bda23a773f 🚀 Initial commit: Aimpress AutomationHub
 Features:
- Modern SaaS automation platform
- Next.js 15 + TypeScript frontend
- Node.js + Express backend
- PostgreSQL database with full schema
- Docker Compose setup
- Admin panel with analytics
- Template marketplace (6 templates)
- Integrations hub (10+ services)
- Authentication & role-based access
- Responsive n8n-style design

🎯 Ready for demo and deployment

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-28 21:58:33 +01:00

456 lines
No EOL
12 KiB
TypeScript

import { Response } from 'express';
import { query } from '../utils/db';
import { AuthRequest } from '../middleware/auth';
import n8nService from '../services/n8nService';
export const getWorkflows = async (req: AuthRequest, res: Response) => {
try {
// Get workflows from n8n
const n8nWorkflows = await n8nService.getWorkflows();
// Get local workflow metadata
const localResult = await query(
`SELECT id, name, n8n_workflow_id, description, status, trigger_type, is_active, created_at, updated_at
FROM workflows WHERE user_id = $1 ORDER BY created_at DESC`,
[req.user?.id]
);
// Merge n8n data with local metadata
const workflows = localResult.rows.map(localWorkflow => {
const n8nWorkflow = n8nWorkflows.find(w => w.id === localWorkflow.n8n_workflow_id);
return {
...localWorkflow,
n8n_data: n8nWorkflow,
active: n8nWorkflow?.active || false,
status: n8nWorkflow?.active ? 'active' : 'inactive'
};
});
res.json({
success: true,
workflows,
total_n8n_workflows: n8nWorkflows.length
});
} catch (error) {
console.error('Get workflows error:', error);
res.status(500).json({
success: false,
message: 'Failed to fetch workflows'
});
}
};
export const getWorkflow = async (req: AuthRequest, res: Response) => {
try {
const { id } = req.params;
// Get local workflow
const localResult = await query(
`SELECT * FROM workflows WHERE id = $1 AND user_id = $2`,
[id, req.user?.id]
);
if (localResult.rows.length === 0) {
return res.status(404).json({
success: false,
message: 'Workflow not found'
});
}
const localWorkflow = localResult.rows[0];
// Get n8n workflow if exists
let n8nWorkflow = null;
if (localWorkflow.n8n_workflow_id) {
try {
n8nWorkflow = await n8nService.getWorkflow(localWorkflow.n8n_workflow_id);
} catch (error) {
console.error('Failed to fetch n8n workflow:', error);
}
}
res.json({
success: true,
workflow: {
...localWorkflow,
n8n_data: n8nWorkflow
}
});
} catch (error) {
console.error('Get workflow error:', error);
res.status(500).json({
success: false,
message: 'Internal server error'
});
}
};
export const createWorkflow = async (req: AuthRequest, res: Response) => {
try {
const { name, description, trigger_type, trigger_config, actions_config, n8n_workflow_data } = req.body;
if (!name) {
return res.status(400).json({
success: false,
message: 'Name is required'
});
}
let n8nWorkflowId = null;
// Create workflow in n8n if workflow data provided
if (n8n_workflow_data) {
try {
const n8nWorkflow = await n8nService.createWorkflow({
name,
active: false,
nodes: n8n_workflow_data.nodes || [],
connections: n8n_workflow_data.connections || {},
settings: n8n_workflow_data.settings,
staticData: n8n_workflow_data.staticData
});
n8nWorkflowId = n8nWorkflow.id;
} catch (error) {
console.error('Failed to create n8n workflow:', error);
return res.status(500).json({
success: false,
message: 'Failed to create workflow in n8n'
});
}
}
// Save to local database
const result = await query(
`INSERT INTO workflows (user_id, name, description, trigger_type, trigger_config, actions_config, n8n_workflow_id)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, name, description, status, trigger_type, is_active, created_at`,
[req.user?.id, name, description, trigger_type, trigger_config, actions_config, n8nWorkflowId]
);
res.status(201).json({
success: true,
workflow: {
...result.rows[0],
n8n_workflow_id: n8nWorkflowId
}
});
} catch (error) {
console.error('Create workflow error:', error);
res.status(500).json({
success: false,
message: 'Internal server error'
});
}
};
export const updateWorkflow = async (req: AuthRequest, res: Response) => {
try {
const { id } = req.params;
const { name, description, trigger_type, trigger_config, actions_config, n8n_workflow_data } = req.body;
// Get existing workflow
const existingResult = await query(
`SELECT * FROM workflows WHERE id = $1 AND user_id = $2`,
[id, req.user?.id]
);
if (existingResult.rows.length === 0) {
return res.status(404).json({
success: false,
message: 'Workflow not found'
});
}
const existingWorkflow = existingResult.rows[0];
// Update n8n workflow if exists
if (existingWorkflow.n8n_workflow_id && n8n_workflow_data) {
try {
await n8nService.updateWorkflow(existingWorkflow.n8n_workflow_id, {
name,
nodes: n8n_workflow_data.nodes,
connections: n8n_workflow_data.connections,
settings: n8n_workflow_data.settings,
staticData: n8n_workflow_data.staticData
});
} catch (error) {
console.error('Failed to update n8n workflow:', error);
return res.status(500).json({
success: false,
message: 'Failed to update workflow in n8n'
});
}
}
// Update local database
const result = await query(
`UPDATE workflows
SET name = $1, description = $2, trigger_type = $3, trigger_config = $4, actions_config = $5, updated_at = CURRENT_TIMESTAMP
WHERE id = $6 AND user_id = $7
RETURNING id, name, description, status, trigger_type, is_active, created_at, updated_at`,
[name, description, trigger_type, trigger_config, actions_config, id, req.user?.id]
);
res.json({
success: true,
workflow: result.rows[0]
});
} catch (error) {
console.error('Update workflow error:', error);
res.status(500).json({
success: false,
message: 'Internal server error'
});
}
};
export const activateWorkflow = async (req: AuthRequest, res: Response) => {
try {
const { id } = req.params;
// Get workflow
const workflowResult = await query(
`SELECT * FROM workflows WHERE id = $1 AND user_id = $2`,
[id, req.user?.id]
);
if (workflowResult.rows.length === 0) {
return res.status(404).json({
success: false,
message: 'Workflow not found'
});
}
const workflow = workflowResult.rows[0];
// Activate in n8n
if (workflow.n8n_workflow_id) {
try {
await n8nService.activateWorkflow(workflow.n8n_workflow_id);
} catch (error) {
console.error('Failed to activate n8n workflow:', error);
return res.status(500).json({
success: false,
message: 'Failed to activate workflow in n8n'
});
}
}
// Update local status
await query(
`UPDATE workflows SET is_active = true, status = 'active', updated_at = CURRENT_TIMESTAMP WHERE id = $1 AND user_id = $2`,
[id, req.user?.id]
);
res.json({
success: true,
message: 'Workflow activated successfully'
});
} catch (error) {
console.error('Activate workflow error:', error);
res.status(500).json({
success: false,
message: 'Internal server error'
});
}
};
export const deactivateWorkflow = async (req: AuthRequest, res: Response) => {
try {
const { id } = req.params;
// Get workflow
const workflowResult = await query(
`SELECT * FROM workflows WHERE id = $1 AND user_id = $2`,
[id, req.user?.id]
);
if (workflowResult.rows.length === 0) {
return res.status(404).json({
success: false,
message: 'Workflow not found'
});
}
const workflow = workflowResult.rows[0];
// Deactivate in n8n
if (workflow.n8n_workflow_id) {
try {
await n8nService.deactivateWorkflow(workflow.n8n_workflow_id);
} catch (error) {
console.error('Failed to deactivate n8n workflow:', error);
return res.status(500).json({
success: false,
message: 'Failed to deactivate workflow in n8n'
});
}
}
// Update local status
await query(
`UPDATE workflows SET is_active = false, status = 'inactive', updated_at = CURRENT_TIMESTAMP WHERE id = $1 AND user_id = $2`,
[id, req.user?.id]
);
res.json({
success: true,
message: 'Workflow deactivated successfully'
});
} catch (error) {
console.error('Deactivate workflow error:', error);
res.status(500).json({
success: false,
message: 'Internal server error'
});
}
};
export const executeWorkflow = async (req: AuthRequest, res: Response) => {
try {
const { id } = req.params;
const { inputData } = req.body;
// Get workflow
const workflowResult = await query(
`SELECT * FROM workflows WHERE id = $1 AND user_id = $2`,
[id, req.user?.id]
);
if (workflowResult.rows.length === 0) {
return res.status(404).json({
success: false,
message: 'Workflow not found'
});
}
const workflow = workflowResult.rows[0];
if (!workflow.n8n_workflow_id) {
return res.status(400).json({
success: false,
message: 'Workflow is not connected to n8n'
});
}
// Execute in n8n
try {
const execution = await n8nService.executeWorkflow(workflow.n8n_workflow_id, inputData);
// Log execution in database
await query(
`INSERT INTO workflow_executions (workflow_id, user_id, n8n_execution_id, status, trigger_data, started_at)
VALUES ($1, $2, $3, $4, $5, $6)`,
[id, req.user?.id, execution.id, execution.finished ? 'completed' : 'running', inputData, new Date()]
);
res.json({
success: true,
execution
});
} catch (error) {
console.error('Failed to execute workflow:', error);
res.status(500).json({
success: false,
message: 'Failed to execute workflow'
});
}
} catch (error) {
console.error('Execute workflow error:', error);
res.status(500).json({
success: false,
message: 'Internal server error'
});
}
};
export const deleteWorkflow = async (req: AuthRequest, res: Response) => {
try {
const { id } = req.params;
// Get workflow
const workflowResult = await query(
`SELECT * FROM workflows WHERE id = $1 AND user_id = $2`,
[id, req.user?.id]
);
if (workflowResult.rows.length === 0) {
return res.status(404).json({
success: false,
message: 'Workflow not found'
});
}
const workflow = workflowResult.rows[0];
// Delete from n8n first
if (workflow.n8n_workflow_id) {
try {
await n8nService.deleteWorkflow(workflow.n8n_workflow_id);
} catch (error) {
console.error('Failed to delete n8n workflow:', error);
// Continue with local deletion even if n8n deletion fails
}
}
// Delete from local database
await query(
'DELETE FROM workflows WHERE id = $1 AND user_id = $2',
[id, req.user?.id]
);
res.json({
success: true,
message: 'Workflow deleted successfully'
});
} catch (error) {
console.error('Delete workflow error:', error);
res.status(500).json({
success: false,
message: 'Internal server error'
});
}
};
export const getWorkflowExecutions = async (req: AuthRequest, res: Response) => {
try {
const { id } = req.params;
const limit = parseInt(req.query.limit as string) || 20;
// Get workflow
const workflowResult = await query(
`SELECT * FROM workflows WHERE id = $1 AND user_id = $2`,
[id, req.user?.id]
);
if (workflowResult.rows.length === 0) {
return res.status(404).json({
success: false,
message: 'Workflow not found'
});
}
const workflow = workflowResult.rows[0];
if (!workflow.n8n_workflow_id) {
return res.status(400).json({
success: false,
message: 'Workflow is not connected to n8n'
});
}
// Get executions from n8n
const executions = await n8nService.getExecutions(workflow.n8n_workflow_id, limit);
res.json({
success: true,
executions
});
} catch (error) {
console.error('Get workflow executions error:', error);
res.status(500).json({
success: false,
message: 'Internal server error'
});
}
};