Files
Goa-gel-fullstack/backend/src/database/migrations/20240101000000_initial_schema.ts

283 lines
12 KiB
TypeScript
Raw Normal View History

import type { Knex } from 'knex';
export async function up(knex: Knex): Promise<void> {
// Enable UUID extension
await knex.raw('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"');
// Applicants table
await knex.schema.createTable('applicants', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table.string('digilocker_id', 255).notNullable().unique();
table.string('name', 255).notNullable();
table.string('email', 255).notNullable();
table.string('phone', 20);
table.string('wallet_address', 42);
table.boolean('is_active').notNullable().defaultTo(true);
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.timestamp('updated_at').notNullable().defaultTo(knex.fn.now());
table.index('digilocker_id', 'idx_applicant_digilocker');
table.index('email', 'idx_applicant_email');
});
// Departments table
await knex.schema.createTable('departments', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table.string('code', 50).notNullable().unique();
table.string('name', 255).notNullable();
table.string('wallet_address', 42).unique();
table.string('api_key_hash', 255);
table.string('api_secret_hash', 255);
table.string('webhook_url', 500);
table.string('webhook_secret_hash', 255);
table.boolean('is_active').notNullable().defaultTo(true);
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.timestamp('updated_at').notNullable().defaultTo(knex.fn.now());
table.index('code', 'idx_department_code');
table.index('is_active', 'idx_department_active');
});
// Workflows table
await knex.schema.createTable('workflows', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table.string('workflow_type', 100).notNullable().unique();
table.string('name', 255).notNullable();
table.text('description');
table.integer('version').notNullable().defaultTo(1);
table.jsonb('definition').notNullable();
table.boolean('is_active').notNullable().defaultTo(true);
table.uuid('created_by');
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.timestamp('updated_at').notNullable().defaultTo(knex.fn.now());
table.index('workflow_type', 'idx_workflow_type');
table.index('is_active', 'idx_workflow_active');
});
// License Requests table
await knex.schema.createTable('license_requests', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table.string('request_number', 50).notNullable().unique();
table.bigInteger('token_id');
table
.uuid('applicant_id')
.notNullable()
.references('id')
.inTable('applicants')
.onDelete('CASCADE');
table.string('request_type', 100).notNullable();
table.uuid('workflow_id').references('id').inTable('workflows').onDelete('SET NULL');
table.string('status', 50).notNullable().defaultTo('DRAFT');
table.jsonb('metadata');
table.string('current_stage_id', 100);
table.string('blockchain_tx_hash', 66);
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.timestamp('updated_at').notNullable().defaultTo(knex.fn.now());
table.timestamp('submitted_at');
table.timestamp('approved_at');
table.index('request_number', 'idx_request_number');
table.index('applicant_id', 'idx_request_applicant');
table.index('status', 'idx_request_status');
table.index('request_type', 'idx_request_type');
table.index('created_at', 'idx_request_created');
table.index(['status', 'request_type'], 'idx_request_status_type');
});
// Documents table
await knex.schema.createTable('documents', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table
.uuid('request_id')
.notNullable()
.references('id')
.inTable('license_requests')
.onDelete('CASCADE');
table.string('doc_type', 100).notNullable();
table.string('original_filename', 255).notNullable();
table.integer('current_version').notNullable().defaultTo(1);
table.string('current_hash', 66).notNullable();
table.string('minio_bucket', 100).notNullable();
table.boolean('is_active').notNullable().defaultTo(true);
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.timestamp('updated_at').notNullable().defaultTo(knex.fn.now());
table.index('request_id', 'idx_document_request');
table.index('doc_type', 'idx_document_type');
});
// Document Versions table
await knex.schema.createTable('document_versions', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table
.uuid('document_id')
.notNullable()
.references('id')
.inTable('documents')
.onDelete('CASCADE');
table.integer('version').notNullable();
table.string('hash', 66).notNullable();
table.string('minio_path', 500).notNullable();
table.bigInteger('file_size').notNullable();
table.string('mime_type', 100).notNullable();
table.uuid('uploaded_by').notNullable();
table.string('blockchain_tx_hash', 66);
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.unique(['document_id', 'version'], { indexName: 'uq_document_version' });
table.index('document_id', 'idx_docversion_document');
});
// Approvals table
await knex.schema.createTable('approvals', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table
.uuid('request_id')
.notNullable()
.references('id')
.inTable('license_requests')
.onDelete('CASCADE');
table
.uuid('department_id')
.notNullable()
.references('id')
.inTable('departments')
.onDelete('CASCADE');
table.string('status', 50).notNullable().defaultTo('PENDING');
table.text('remarks');
table.string('remarks_hash', 66);
table.jsonb('reviewed_documents');
table.string('blockchain_tx_hash', 66);
table.boolean('is_active').notNullable().defaultTo(true);
table.timestamp('invalidated_at');
table.string('invalidation_reason', 255);
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.timestamp('updated_at').notNullable().defaultTo(knex.fn.now());
table.index('request_id', 'idx_approval_request');
table.index('department_id', 'idx_approval_department');
table.index('status', 'idx_approval_status');
table.index(['request_id', 'department_id'], 'idx_approval_request_dept');
});
// Workflow States table
await knex.schema.createTable('workflow_states', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table
.uuid('request_id')
.notNullable()
.unique()
.references('id')
.inTable('license_requests')
.onDelete('CASCADE');
table.string('current_stage_id', 100).notNullable();
table.jsonb('completed_stages').notNullable().defaultTo('[]');
table.jsonb('pending_approvals').notNullable().defaultTo('[]');
table.jsonb('execution_log').notNullable().defaultTo('[]');
table.timestamp('stage_started_at');
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.timestamp('updated_at').notNullable().defaultTo(knex.fn.now());
table.index('request_id', 'idx_wfstate_request');
});
// Webhooks table
await knex.schema.createTable('webhooks', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table
.uuid('department_id')
.notNullable()
.references('id')
.inTable('departments')
.onDelete('CASCADE');
table.string('url', 500).notNullable();
table.jsonb('events').notNullable();
table.string('secret_hash', 255).notNullable();
table.boolean('is_active').notNullable().defaultTo(true);
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.timestamp('updated_at').notNullable().defaultTo(knex.fn.now());
table.index('department_id', 'idx_webhook_department');
});
// Webhook Logs table
await knex.schema.createTable('webhook_logs', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table.uuid('webhook_id').notNullable().references('id').inTable('webhooks').onDelete('CASCADE');
table.string('event_type', 100).notNullable();
table.jsonb('payload').notNullable();
table.integer('response_status');
table.text('response_body');
table.integer('response_time');
table.integer('retry_count').notNullable().defaultTo(0);
table.string('status', 20).notNullable().defaultTo('PENDING');
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.index('webhook_id', 'idx_webhooklog_webhook');
table.index('event_type', 'idx_webhooklog_event');
table.index('status', 'idx_webhooklog_status');
table.index('created_at', 'idx_webhooklog_created');
});
// Audit Logs table
await knex.schema.createTable('audit_logs', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table.string('entity_type', 50).notNullable();
table.uuid('entity_id').notNullable();
table.string('action', 50).notNullable();
table.string('actor_type', 50).notNullable();
table.uuid('actor_id');
table.jsonb('old_value');
table.jsonb('new_value');
table.string('ip_address', 45);
table.text('user_agent');
table.string('correlation_id', 100);
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.index(['entity_type', 'entity_id'], 'idx_audit_entity');
table.index('entity_type', 'idx_audit_entitytype');
table.index('action', 'idx_audit_action');
table.index('created_at', 'idx_audit_created');
table.index('correlation_id', 'idx_audit_correlation');
});
// Blockchain Transactions table
await knex.schema.createTable('blockchain_transactions', table => {
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
table.string('tx_hash', 66).notNullable().unique();
table.string('tx_type', 50).notNullable();
table.string('related_entity_type', 50).notNullable();
table.uuid('related_entity_id').notNullable();
table.string('from_address', 42).notNullable();
table.string('to_address', 42);
table.string('status', 20).notNullable().defaultTo('PENDING');
table.bigInteger('block_number');
table.bigInteger('gas_used');
table.text('error_message');
table.timestamp('created_at').notNullable().defaultTo(knex.fn.now());
table.timestamp('confirmed_at');
table.index('tx_hash', 'idx_bctx_hash');
table.index('tx_type', 'idx_bctx_type');
table.index('status', 'idx_bctx_status');
table.index('related_entity_id', 'idx_bctx_entity');
table.index('created_at', 'idx_bctx_created');
});
}
export async function down(knex: Knex): Promise<void> {
await knex.schema.dropTableIfExists('blockchain_transactions');
await knex.schema.dropTableIfExists('audit_logs');
await knex.schema.dropTableIfExists('webhook_logs');
await knex.schema.dropTableIfExists('webhooks');
await knex.schema.dropTableIfExists('workflow_states');
await knex.schema.dropTableIfExists('approvals');
await knex.schema.dropTableIfExists('document_versions');
await knex.schema.dropTableIfExists('documents');
await knex.schema.dropTableIfExists('license_requests');
await knex.schema.dropTableIfExists('workflows');
await knex.schema.dropTableIfExists('departments');
await knex.schema.dropTableIfExists('applicants');
}