/** * @param { import("knex").Knex } knex * @returns { Promise } */ exports.up = async function (knex) { // Create calls table for tracking voice calls await knex.schema.createTable('calls', (table) => { table.string('id', 36).primary(); table.string('call_sid', 100).unique().notNullable().comment('Twilio call SID'); table.enum('direction', ['inbound', 'outbound']).notNullable(); table.string('from_number', 20).notNullable(); table.string('to_number', 20).notNullable(); table.enum('status', [ 'queued', 'ringing', 'in-progress', 'completed', 'busy', 'failed', 'no-answer', 'canceled' ]).notNullable().defaultTo('queued'); table.integer('duration_seconds').unsigned().nullable(); table.string('recording_url', 500).nullable(); table.text('ai_transcript').nullable().comment('Full transcript from OpenAI'); table.text('ai_summary').nullable().comment('AI-generated summary'); table.json('ai_insights').nullable().comment('Structured insights from AI'); table.string('user_id', 36).notNullable().comment('User who handled the call'); table.timestamp('started_at').nullable(); table.timestamp('ended_at').nullable(); table.timestamp('created_at').defaultTo(knex.fn.now()); table.timestamp('updated_at').defaultTo(knex.fn.now()); // Indexes table.index('call_sid'); table.index('user_id'); table.index('status'); table.index('direction'); table.index(['created_at', 'user_id']); // Foreign key to users table table.foreign('user_id').references('id').inTable('users').onDelete('CASCADE'); }); console.log('✅ Created calls table'); }; /** * @param { import("knex").Knex } knex * @returns { Promise } */ exports.down = async function (knex) { await knex.schema.dropTableIfExists('calls'); console.log('✅ Dropped calls table'); };