✨ 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>
198 lines
No EOL
6.3 KiB
TypeScript
198 lines
No EOL
6.3 KiB
TypeScript
import { Request, Response } from 'express';
|
|
import { pool } from '../db/connection';
|
|
import { AuthRequest } from '../middleware/auth';
|
|
import { n8nService } from '../services/n8nService';
|
|
|
|
export const getTemplates = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const { category, search } = req.query;
|
|
const userRole = req.user?.role || 'free';
|
|
|
|
// Build query for local templates
|
|
let queryText = `
|
|
SELECT id, name, description, category, trigger_type, tags, is_featured, install_count, created_at
|
|
FROM templates WHERE 1=1
|
|
`;
|
|
const params: any[] = [];
|
|
let paramCount = 0;
|
|
|
|
if (category) {
|
|
paramCount++;
|
|
queryText += ` AND category = $${paramCount}`;
|
|
params.push(category);
|
|
}
|
|
|
|
if (search) {
|
|
paramCount++;
|
|
queryText += ` AND (name ILIKE $${paramCount} OR description ILIKE $${paramCount})`;
|
|
params.push(`%${search}%`);
|
|
}
|
|
|
|
queryText += ' ORDER BY is_featured DESC, install_count DESC, created_at DESC';
|
|
|
|
// Get local templates
|
|
const localResult = await pool.query(queryText, params);
|
|
let templates = localResult.rows.map(template => ({
|
|
...template,
|
|
source: 'local'
|
|
}));
|
|
|
|
// For admin users, also fetch templates from n8n
|
|
if (userRole === 'admin') {
|
|
try {
|
|
const n8nWorkflows = await n8nService.getWorkflows();
|
|
|
|
const n8nTemplates = n8nWorkflows.map(workflow => ({
|
|
id: `n8n_${workflow.id}`,
|
|
name: workflow.name,
|
|
description: `N8n workflow: ${workflow.name}`,
|
|
category: 'n8n',
|
|
trigger_type: 'n8n_workflow',
|
|
tags: ['n8n', 'automation'],
|
|
is_featured: false,
|
|
install_count: 0,
|
|
created_at: workflow.createdAt,
|
|
source: 'n8n',
|
|
n8n_workflow_id: workflow.id,
|
|
n8n_data: workflow
|
|
}));
|
|
|
|
// Filter n8n templates if search is provided
|
|
if (search) {
|
|
const searchTerm = search.toString().toLowerCase();
|
|
const filteredN8nTemplates = n8nTemplates.filter(template =>
|
|
template.name.toLowerCase().includes(searchTerm) ||
|
|
template.description.toLowerCase().includes(searchTerm)
|
|
);
|
|
templates = [...templates, ...filteredN8nTemplates];
|
|
} else {
|
|
templates = [...templates, ...n8nTemplates];
|
|
}
|
|
} catch (n8nError) {
|
|
console.error('Error fetching n8n workflows:', n8nError);
|
|
// Continue with local templates only
|
|
}
|
|
}
|
|
|
|
res.json({
|
|
success: true,
|
|
templates: templates
|
|
});
|
|
} catch (error) {
|
|
console.error('Get templates error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Internal server error'
|
|
});
|
|
}
|
|
};
|
|
|
|
export const installTemplate = async (req: AuthRequest, res: Response) => {
|
|
try {
|
|
const { id } = req.params;
|
|
const { workflow_name } = req.body;
|
|
|
|
// Check if it's an n8n template
|
|
if (id.startsWith('n8n_')) {
|
|
const n8nWorkflowId = id.replace('n8n_', '');
|
|
|
|
try {
|
|
// Get the n8n workflow
|
|
const n8nWorkflow = await n8nService.getWorkflow(n8nWorkflowId);
|
|
|
|
// Create workflow in our database that references the n8n workflow
|
|
const workflowResult = await pool.query(
|
|
`INSERT INTO workflows (user_id, name, description, trigger_type, n8n_workflow_id, status, is_active)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7)
|
|
RETURNING id`,
|
|
[
|
|
req.user?.id,
|
|
workflow_name || n8nWorkflow.name,
|
|
`N8n workflow: ${n8nWorkflow.name}`,
|
|
'n8n_workflow',
|
|
n8nWorkflowId,
|
|
'connected',
|
|
n8nWorkflow.active
|
|
]
|
|
);
|
|
|
|
const workflowId = workflowResult.rows[0].id;
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: 'N8n workflow connected successfully',
|
|
workflow_id: workflowId,
|
|
n8n_workflow_id: n8nWorkflowId
|
|
});
|
|
|
|
} catch (n8nError) {
|
|
console.error('Error connecting n8n workflow:', n8nError);
|
|
return res.status(500).json({
|
|
success: false,
|
|
message: 'Failed to connect n8n workflow'
|
|
});
|
|
}
|
|
} else {
|
|
// Handle local template installation
|
|
const template = await pool.query('SELECT * FROM templates WHERE id = $1', [id]);
|
|
if (template.rows.length === 0) {
|
|
return res.status(404).json({
|
|
success: false,
|
|
message: 'Template not found'
|
|
});
|
|
}
|
|
|
|
const templateData = template.rows[0];
|
|
const workflowName = workflow_name || templateData.name;
|
|
|
|
// Check user's workflow limit
|
|
const userQuery = await pool.query('SELECT workflow_limit FROM users WHERE id = $1', [req.user?.id]);
|
|
const userWorkflowLimit = userQuery.rows[0]?.workflow_limit || 3;
|
|
|
|
const currentWorkflowsQuery = await pool.query(
|
|
'SELECT COUNT(*) as count FROM workflows WHERE user_id = $1',
|
|
[req.user?.id]
|
|
);
|
|
const currentWorkflowCount = parseInt(currentWorkflowsQuery.rows[0].count);
|
|
|
|
if (userWorkflowLimit !== -1 && currentWorkflowCount >= userWorkflowLimit) {
|
|
return res.status(403).json({
|
|
success: false,
|
|
message: 'Workflow limit reached. Please upgrade your plan to create more workflows.'
|
|
});
|
|
}
|
|
|
|
const workflowResult = await pool.query(
|
|
`INSERT INTO workflows (user_id, name, description, trigger_type, trigger_config, actions_config)
|
|
VALUES ($1, $2, $3, $4, $5, $6)
|
|
RETURNING id`,
|
|
[req.user?.id, workflowName, templateData.description, templateData.trigger_type,
|
|
templateData.trigger_config, templateData.actions_config]
|
|
);
|
|
|
|
const workflowId = workflowResult.rows[0].id;
|
|
|
|
await pool.query(
|
|
'INSERT INTO user_templates (user_id, template_id, workflow_id) VALUES ($1, $2, $3)',
|
|
[req.user?.id, id, workflowId]
|
|
);
|
|
|
|
await pool.query(
|
|
'UPDATE templates SET install_count = install_count + 1 WHERE id = $1',
|
|
[id]
|
|
);
|
|
|
|
res.status(201).json({
|
|
success: true,
|
|
message: 'Template installed successfully',
|
|
workflow_id: workflowId
|
|
});
|
|
}
|
|
} catch (error) {
|
|
console.error('Install template error:', error);
|
|
res.status(500).json({
|
|
success: false,
|
|
message: 'Internal server error'
|
|
});
|
|
}
|
|
}; |