godcrm/backend/database/migrations/knex/021_create_labs_tables.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

175 lines
No EOL
5.7 KiB
JavaScript

/**
* Migration 021: Create Labs Tables (ADR-043)
*
* Laboratories feature for AI workflow management.
* Creates tables for:
* - labs_projects: Lab project configurations
* - labs_nodes: Workflow nodes (AI agents, tools, etc.)
* - labs_edges: Connections between nodes
* - labs_ai_templates: Reusable AI workflow templates
*/
export async function up(knex) {
const isPostgres = knex.client.config.client === 'pg' || knex.client.config.client === 'postgresql';
// ========================================
// LABS_PROJECTS TABLE
// ========================================
await knex.schema.createTable('labs_projects', (table) => {
table.increments('id').primary();
// Reference to space (optional - can be null for global projects)
table.integer('space_id').unsigned()
.references('id').inTable('spaces').onDelete('SET NULL');
// Unique project identifier
table.string('project_id', 255).unique().notNullable();
// Project metadata
table.string('title', 255).notNullable();
table.text('description');
// Configuration stored as JSON
if (isPostgres) {
table.jsonb('settings').defaultTo('{}');
} else {
table.text('settings').defaultTo('{}');
}
// Default AI configuration
table.integer('ai_default_provider_id');
table.integer('ai_default_agent_id');
// Timestamps
table.timestamp('created_at').defaultTo(knex.fn.now());
table.timestamp('updated_at').defaultTo(knex.fn.now());
});
// Indexes for labs_projects
await knex.schema.alterTable('labs_projects', (table) => {
table.index('space_id', 'idx_labs_projects_space');
table.index('project_id', 'idx_labs_projects_project_id');
});
// ========================================
// LABS_NODES TABLE
// ========================================
await knex.schema.createTable('labs_nodes', (table) => {
table.increments('id').primary();
// Project and node identification
table.string('project_id', 255).notNullable();
table.string('node_id', 255).notNullable();
// Node type and metadata
table.string('type', 100).notNullable(); // 'ai-agent', 'tool', 'input', 'output', etc.
table.string('title', 255).notNullable();
table.text('content');
// Configuration stored as JSON
if (isPostgres) {
table.jsonb('meta').defaultTo('{}');
table.jsonb('ai_config').defaultTo('{}');
table.jsonb('ai_routing_config').defaultTo('{}');
table.jsonb('ui_config').defaultTo('{}');
} else {
table.text('meta').defaultTo('{}');
table.text('ai_config').defaultTo('{}');
table.text('ai_routing_config').defaultTo('{}');
table.text('ui_config').defaultTo('{}');
}
// AI configuration
table.integer('ai_agent_id');
table.integer('ai_provider_id');
table.boolean('ai_visible').defaultTo(true);
// Timestamps
table.timestamp('created_at').defaultTo(knex.fn.now());
table.timestamp('updated_at').defaultTo(knex.fn.now());
// Unique constraint: one node per (project, node_id)
table.unique(['project_id', 'node_id']);
});
// Indexes for labs_nodes
await knex.schema.alterTable('labs_nodes', (table) => {
table.index('project_id', 'idx_labs_nodes_project');
});
// ========================================
// LABS_EDGES TABLE
// ========================================
await knex.schema.createTable('labs_edges', (table) => {
table.increments('id').primary();
// Project and edge identification
table.string('project_id', 255).notNullable();
table.string('edge_id', 255).notNullable();
// Connection details
table.string('source_node_id', 255).notNullable();
table.string('target_node_id', 255).notNullable();
table.string('source_handle', 100);
table.string('target_handle', 100);
// Timestamps
table.timestamp('created_at').defaultTo(knex.fn.now());
// Unique constraint: one edge per (project, edge_id)
table.unique(['project_id', 'edge_id']);
});
// Indexes for labs_edges
await knex.schema.alterTable('labs_edges', (table) => {
table.index('project_id', 'idx_labs_edges_project');
});
// ========================================
// LABS_AI_TEMPLATES TABLE
// ========================================
await knex.schema.createTable('labs_ai_templates', (table) => {
table.increments('id').primary();
// Unique template identifier for MindWorkflow integration
table.string('mindworkflow_id', 100).unique().notNullable();
// Template metadata
table.string('name', 255).notNullable();
table.string('category', 100).notNullable();
table.text('description');
// AI prompts
table.text('system_prompt');
table.text('user_prompt_example');
// Configuration stored as JSON
if (isPostgres) {
table.jsonb('inputs').defaultTo('[]');
table.jsonb('settings').defaultTo('{}');
table.jsonb('routing_config').defaultTo('{}');
} else {
table.text('inputs').defaultTo('[]');
table.text('settings').defaultTo('{}');
table.text('routing_config').defaultTo('{}');
}
// Default AI agent
table.integer('ai_agent_id');
// Timestamps
table.timestamp('created_at').defaultTo(knex.fn.now());
});
console.log('✅ Created Labs tables: labs_projects, labs_nodes, labs_edges, labs_ai_templates');
}
export async function down(knex) {
// Drop tables in reverse order to handle dependencies
await knex.schema.dropTableIfExists('labs_ai_templates');
await knex.schema.dropTableIfExists('labs_edges');
await knex.schema.dropTableIfExists('labs_nodes');
await knex.schema.dropTableIfExists('labs_projects');
console.log('✅ Dropped Labs tables');
}