godcrm/backend/routes/v3/labs/edges.js
GOD CRM Release f89e074dd1
Some checks failed
CI / Lint / Typecheck / Test / Build (push) Has been cancelled
CI / PostgreSQL Integration Tests (push) Has been cancelled
GOD CRM — public scrubbed snapshot
Governed substrate for autonomous agents: scoped identity (passports),
audited actions, MCP workspace. Infra IPs and secrets redacted for public release.
2026-08-10 04:01:45 +03:00

161 lines
5.5 KiB
JavaScript

/**
* Labs Edges Routes
* CRUD for edges between nodes (both v4 and legacy)
*/
import { Router } from 'express';
import { dbGet, dbRun, dbAll } from '../../../database/connection.js';
import { apiLogger } from '../../../utils/logger.js';
import { success, created, badRequest } from '../../../utils/response.js';
import { generateId } from './_helpers.js';
const router = Router();
// POST /labs/:id/edges - Create edge (supports both GOD CRM and MindWorkflow formats)
router.post('/:id/edges', async (req, res) => {
try {
const { id } = req.params;
// Support both formats: GOD CRM (source_node_id) and MindWorkflow (from)
const source_node_id = req.body.source_node_id || req.body.from;
const target_node_id = req.body.target_node_id || req.body.to;
const source_handle = req.body.source_handle || req.body.sourceHandle;
const target_handle = req.body.target_handle || req.body.targetHandle;
const label = req.body.label;
if (!source_node_id || !target_node_id) return badRequest(res, 'source and target required');
const edge_id = generateId('edge');
await dbRun(
`INSERT INTO labs_edges (lab_id, edge_id, source_node_id, target_node_id, source_handle, target_handle, created_at)
VALUES ($1, $2, $3, $4, $5, $6, NOW())`,
[id, edge_id, source_node_id, target_node_id, source_handle || null, target_handle || null]
);
// Also update the source node's edges array if using JSON storage
try {
const sourceNode = await dbGet(
`SELECT * FROM "${id}" WHERE node_id = $1`,
[source_node_id]
);
if (sourceNode) {
let edges = [];
try {
edges = sourceNode.edges ? (typeof sourceNode.edges === 'string' ? JSON.parse(sourceNode.edges) : sourceNode.edges) : [];
} catch (e) {
edges = [];
}
// Add target to edges array if not already present
if (!edges.includes(target_node_id)) {
edges.push(target_node_id);
await dbRun(
`UPDATE "${id}" SET edges = $1 WHERE node_id = $2`,
[JSON.stringify(edges), source_node_id]
);
}
}
} catch (tableErr) {
// Table might not exist yet, that's ok
apiLogger.debug({ tableErr }, 'Could not update node edges array');
}
const edge = await dbGet('SELECT * FROM labs_edges WHERE edge_id = $1', [edge_id]);
// Return in MindWorkflow format
const allEdges = await dbAll('SELECT * FROM labs_edges WHERE lab_id = $1', [id]);
created(res, {
edge,
edges: allEdges.map(e => ({
id: e.edge_id,
from: e.source_node_id,
to: e.target_node_id,
sourceHandle: e.source_handle,
targetHandle: e.target_handle
}))
}, 'Edge created');
} catch (err) {
apiLogger.error({ err }, 'Failed to create edge');
badRequest(res, 'Failed to create edge');
}
});
// POST /projects/:id/edges (backward compatibility alias)
router.post('/projects/:id/edges', async (req, res) => {
try {
const { id } = req.params;
const { source_node_id, target_node_id, source_handle, target_handle } = req.body;
if (!source_node_id || !target_node_id) return badRequest(res, 'source and target required');
const edge_id = generateId('edge');
await dbRun(
`INSERT INTO labs_edges (lab_id, edge_id, source_node_id, target_node_id, source_handle, target_handle, created_at)
VALUES ($1, $2, $3, $4, $5, $6, NOW())`,
[id, edge_id, source_node_id, target_node_id, source_handle || null, target_handle || null]
);
const edge = await dbGet('SELECT * FROM labs_edges WHERE edge_id = $1', [edge_id]);
created(res, edge, 'Edge created');
} catch (err) {
apiLogger.error({ err }, 'Failed to create edge');
badRequest(res, 'Failed to create edge');
}
});
// DELETE /edges/:edgeId
router.delete('/edges/:edgeId', async (req, res) => {
try {
const { edgeId } = req.params;
await dbRun('DELETE FROM labs_edges WHERE edge_id = $1', [edgeId]);
success(res, { deleted: true });
} catch (err) {
apiLogger.error({ err }, 'Failed to delete edge');
badRequest(res, 'Failed to delete edge');
}
});
// DELETE /:labTableId/edges/:from/:to - Delete edge by source and target node IDs
router.delete('/:labTableId/edges/:from/:to', async (req, res) => {
try {
const { labTableId, from, to } = req.params;
// Delete from labs_edges table
await dbRun(
'DELETE FROM labs_edges WHERE lab_id = $1 AND source_node_id = $2 AND target_node_id = $3',
[labTableId, from, to]
);
// Also update the source node's edges array if using JSON storage
const sourceNode = await dbGet(
`SELECT * FROM "${labTableId}" WHERE node_id = $1`,
[from]
);
if (sourceNode && sourceNode.edges) {
let edges = [];
try {
edges = typeof sourceNode.edges === 'string' ? JSON.parse(sourceNode.edges) : sourceNode.edges;
} catch (e) {
edges = [];
}
// Remove the target from edges array
const updatedEdges = edges.filter(e => e !== to && e.to !== to);
await dbRun(
`UPDATE "${labTableId}" SET edges = $1 WHERE node_id = $2`,
[JSON.stringify(updatedEdges), from]
);
}
// Return updated edges list
const remainingEdges = await dbAll(
'SELECT * FROM labs_edges WHERE lab_id = $1',
[labTableId]
);
success(res, { edges: remainingEdges });
} catch (err) {
apiLogger.error({ err, labTableId: req.params.labTableId }, 'Failed to delete edge');
badRequest(res, 'Failed to delete edge');
}
});
export default router;