feat: Runtime configuration and Docker deployment improvements
Frontend: - Add runtime configuration service for deployment-time API URL injection - Create docker-entrypoint.sh to generate config.json from environment variables - Update ApiService, ApprovalService, and DocumentViewer to use RuntimeConfigService - Add APP_INITIALIZER to load runtime config before app starts Backend: - Fix init-blockchain.js to properly quote mnemonic phrases in .env file - Improve docker-entrypoint.sh with health checks and better error handling Docker: - Add API_BASE_URL environment variable to frontend container - Update docker-compose.yml with clear documentation for remote deployment - Reorganize .env.example with clear categories (REQUIRED FOR REMOTE, PRODUCTION, AUTO-GENERATED) Workflow fixes: - Fix DepartmentApproval interface to match backend schema - Fix stage transformation for 0-indexed stageOrder - Fix workflow list to show correct stage count from definition.stages Cleanup: - Move development artifacts to .trash directory - Remove root-level package.json (was only for utility scripts) - Add .trash/ to .gitignore
This commit is contained in:
@@ -5,7 +5,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
await knex.raw('CREATE EXTENSION IF NOT EXISTS "uuid-ossp"');
|
||||
|
||||
// Applicants table
|
||||
await knex.schema.createTable('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();
|
||||
@@ -21,7 +21,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Departments table
|
||||
await knex.schema.createTable('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();
|
||||
@@ -39,7 +39,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Workflows table
|
||||
await knex.schema.createTable('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();
|
||||
@@ -56,11 +56,16 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// License Requests table
|
||||
await knex.schema.createTable('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
|
||||
.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');
|
||||
@@ -81,9 +86,14 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Documents table
|
||||
await knex.schema.createTable('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
|
||||
.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);
|
||||
@@ -98,9 +108,14 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Document Versions table
|
||||
await knex.schema.createTable('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
|
||||
.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();
|
||||
@@ -115,10 +130,20 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Approvals table
|
||||
await knex.schema.createTable('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
|
||||
.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);
|
||||
@@ -137,9 +162,15 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Workflow States table
|
||||
await knex.schema.createTable('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
|
||||
.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('[]');
|
||||
@@ -152,9 +183,14 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Webhooks table
|
||||
await knex.schema.createTable('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
|
||||
.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();
|
||||
@@ -166,7 +202,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Webhook Logs table
|
||||
await knex.schema.createTable('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();
|
||||
@@ -185,7 +221,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Audit Logs table
|
||||
await knex.schema.createTable('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();
|
||||
@@ -207,7 +243,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Blockchain Transactions table
|
||||
await knex.schema.createTable('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();
|
||||
|
||||
@@ -2,7 +2,7 @@ import type { Knex } from 'knex';
|
||||
|
||||
export async function up(knex: Knex): Promise<void> {
|
||||
// Users table for email/password authentication
|
||||
await knex.schema.createTable('users', (table) => {
|
||||
await knex.schema.createTable('users', table => {
|
||||
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
|
||||
table.string('email', 255).notNullable().unique();
|
||||
table.string('password_hash', 255).notNullable();
|
||||
@@ -24,7 +24,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Wallets table for storing encrypted private keys
|
||||
await knex.schema.createTable('wallets', (table) => {
|
||||
await knex.schema.createTable('wallets', table => {
|
||||
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
|
||||
table.string('address', 42).notNullable().unique();
|
||||
table.text('encrypted_private_key').notNullable();
|
||||
@@ -39,7 +39,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Blockchain events table
|
||||
await knex.schema.createTable('blockchain_events', (table) => {
|
||||
await knex.schema.createTable('blockchain_events', table => {
|
||||
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
|
||||
table.string('tx_hash', 66).notNullable();
|
||||
table.string('event_name', 100).notNullable();
|
||||
@@ -62,7 +62,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Application logs table
|
||||
await knex.schema.createTable('application_logs', (table) => {
|
||||
await knex.schema.createTable('application_logs', table => {
|
||||
table.uuid('id').primary().defaultTo(knex.raw('uuid_generate_v4()'));
|
||||
table.enum('level', ['DEBUG', 'INFO', 'WARN', 'ERROR']).notNullable();
|
||||
table.string('module', 100).notNullable();
|
||||
@@ -83,7 +83,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
});
|
||||
|
||||
// Add additional fields to departments table
|
||||
await knex.schema.alterTable('departments', (table) => {
|
||||
await knex.schema.alterTable('departments', table => {
|
||||
table.text('description');
|
||||
table.string('contact_email', 255);
|
||||
table.string('contact_phone', 20);
|
||||
@@ -93,7 +93,7 @@ export async function up(knex: Knex): Promise<void> {
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
// Remove additional fields from departments
|
||||
await knex.schema.alterTable('departments', (table) => {
|
||||
await knex.schema.alterTable('departments', table => {
|
||||
table.dropColumn('description');
|
||||
table.dropColumn('contact_email');
|
||||
table.dropColumn('contact_phone');
|
||||
|
||||
Reference in New Issue
Block a user