feat: Goa GEL Blockchain e-Licensing Platform - Full Stack Implementation
Complete implementation of the Goa Government e-Licensing platform with: Backend: - NestJS API with JWT authentication - PostgreSQL database with Knex ORM - Redis caching and session management - MinIO document storage - Hyperledger Besu blockchain integration - Multi-department workflow system - Comprehensive API tests (266/282 passing) Frontend: - Angular 21 with standalone components - Angular Material + TailwindCSS UI - Visual workflow builder - Document upload with progress tracking - Blockchain explorer integration - Role-based dashboards (Admin, Department, Citizen) - E2E tests with Playwright (37 tests) Infrastructure: - Docker Compose orchestration - Blockscout blockchain explorer - Development and production configurations
This commit is contained in:
304
backend/scripts/create-all-tables.sql
Normal file
304
backend/scripts/create-all-tables.sql
Normal file
@@ -0,0 +1,304 @@
|
||||
-- Enable UUID extension
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
|
||||
-- =============================================
|
||||
-- MIGRATION 1: Initial Schema
|
||||
-- =============================================
|
||||
|
||||
-- Applicants table
|
||||
CREATE TABLE IF NOT EXISTS applicants (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
digilocker_id VARCHAR(255) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
phone VARCHAR(20),
|
||||
wallet_address VARCHAR(42),
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_applicant_digilocker ON applicants(digilocker_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_applicant_email ON applicants(email);
|
||||
|
||||
-- Departments table
|
||||
CREATE TABLE IF NOT EXISTS departments (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
code VARCHAR(50) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
wallet_address VARCHAR(42) UNIQUE,
|
||||
api_key_hash VARCHAR(255),
|
||||
api_secret_hash VARCHAR(255),
|
||||
webhook_url VARCHAR(500),
|
||||
webhook_secret_hash VARCHAR(255),
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
description TEXT,
|
||||
contact_email VARCHAR(255),
|
||||
contact_phone VARCHAR(20),
|
||||
last_webhook_at TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_department_code ON departments(code);
|
||||
CREATE INDEX IF NOT EXISTS idx_department_active ON departments(is_active);
|
||||
|
||||
-- Workflows table
|
||||
CREATE TABLE IF NOT EXISTS workflows (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
workflow_type VARCHAR(100) NOT NULL UNIQUE,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
version INTEGER NOT NULL DEFAULT 1,
|
||||
definition JSONB NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_by UUID,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_workflow_type ON workflows(workflow_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_workflow_active ON workflows(is_active);
|
||||
|
||||
-- License Requests table
|
||||
CREATE TABLE IF NOT EXISTS license_requests (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
request_number VARCHAR(50) NOT NULL UNIQUE,
|
||||
token_id BIGINT,
|
||||
applicant_id UUID NOT NULL REFERENCES applicants(id) ON DELETE CASCADE,
|
||||
request_type VARCHAR(100) NOT NULL,
|
||||
workflow_id UUID REFERENCES workflows(id) ON DELETE SET NULL,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'DRAFT',
|
||||
metadata JSONB,
|
||||
current_stage_id VARCHAR(100),
|
||||
blockchain_tx_hash VARCHAR(66),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
submitted_at TIMESTAMP,
|
||||
approved_at TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_request_number ON license_requests(request_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_request_applicant ON license_requests(applicant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_request_status ON license_requests(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_request_type ON license_requests(request_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_request_created ON license_requests(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_request_status_type ON license_requests(status, request_type);
|
||||
|
||||
-- Documents table
|
||||
CREATE TABLE IF NOT EXISTS documents (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
request_id UUID NOT NULL REFERENCES license_requests(id) ON DELETE CASCADE,
|
||||
doc_type VARCHAR(100) NOT NULL,
|
||||
original_filename VARCHAR(255) NOT NULL,
|
||||
current_version INTEGER NOT NULL DEFAULT 1,
|
||||
current_hash VARCHAR(66) NOT NULL,
|
||||
minio_bucket VARCHAR(100) NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_document_request ON documents(request_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_document_type ON documents(doc_type);
|
||||
|
||||
-- Document Versions table
|
||||
CREATE TABLE IF NOT EXISTS document_versions (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
document_id UUID NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
|
||||
version INTEGER NOT NULL,
|
||||
hash VARCHAR(66) NOT NULL,
|
||||
minio_path VARCHAR(500) NOT NULL,
|
||||
file_size BIGINT NOT NULL,
|
||||
mime_type VARCHAR(100) NOT NULL,
|
||||
uploaded_by UUID NOT NULL,
|
||||
blockchain_tx_hash VARCHAR(66),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(document_id, version)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_docversion_document ON document_versions(document_id);
|
||||
|
||||
-- Approvals table
|
||||
CREATE TABLE IF NOT EXISTS approvals (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
request_id UUID NOT NULL REFERENCES license_requests(id) ON DELETE CASCADE,
|
||||
department_id UUID NOT NULL REFERENCES departments(id) ON DELETE CASCADE,
|
||||
status VARCHAR(50) NOT NULL DEFAULT 'PENDING',
|
||||
remarks TEXT,
|
||||
remarks_hash VARCHAR(66),
|
||||
reviewed_documents JSONB,
|
||||
blockchain_tx_hash VARCHAR(66),
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
invalidated_at TIMESTAMP,
|
||||
invalidation_reason VARCHAR(255),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_approval_request ON approvals(request_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_approval_department ON approvals(department_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_approval_status ON approvals(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_approval_request_dept ON approvals(request_id, department_id);
|
||||
|
||||
-- Workflow States table
|
||||
CREATE TABLE IF NOT EXISTS workflow_states (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
request_id UUID NOT NULL UNIQUE REFERENCES license_requests(id) ON DELETE CASCADE,
|
||||
current_stage_id VARCHAR(100) NOT NULL,
|
||||
completed_stages JSONB NOT NULL DEFAULT '[]',
|
||||
pending_approvals JSONB NOT NULL DEFAULT '[]',
|
||||
execution_log JSONB NOT NULL DEFAULT '[]',
|
||||
stage_started_at TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wfstate_request ON workflow_states(request_id);
|
||||
|
||||
-- Webhooks table
|
||||
CREATE TABLE IF NOT EXISTS webhooks (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
department_id UUID NOT NULL REFERENCES departments(id) ON DELETE CASCADE,
|
||||
url VARCHAR(500) NOT NULL,
|
||||
events JSONB NOT NULL,
|
||||
secret_hash VARCHAR(255) NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_webhook_department ON webhooks(department_id);
|
||||
|
||||
-- Webhook Logs table
|
||||
CREATE TABLE IF NOT EXISTS webhook_logs (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
webhook_id UUID NOT NULL REFERENCES webhooks(id) ON DELETE CASCADE,
|
||||
event_type VARCHAR(100) NOT NULL,
|
||||
payload JSONB NOT NULL,
|
||||
response_status INTEGER,
|
||||
response_body TEXT,
|
||||
response_time INTEGER,
|
||||
retry_count INTEGER NOT NULL DEFAULT 0,
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_webhooklog_webhook ON webhook_logs(webhook_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_webhooklog_event ON webhook_logs(event_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_webhooklog_status ON webhook_logs(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_webhooklog_created ON webhook_logs(created_at);
|
||||
|
||||
-- Audit Logs table
|
||||
CREATE TABLE IF NOT EXISTS audit_logs (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
entity_type VARCHAR(50) NOT NULL,
|
||||
entity_id UUID NOT NULL,
|
||||
action VARCHAR(50) NOT NULL,
|
||||
actor_type VARCHAR(50) NOT NULL,
|
||||
actor_id UUID,
|
||||
old_value JSONB,
|
||||
new_value JSONB,
|
||||
ip_address VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
correlation_id VARCHAR(100),
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_entity ON audit_logs(entity_type, entity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_entitytype ON audit_logs(entity_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_action ON audit_logs(action);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_created ON audit_logs(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_audit_correlation ON audit_logs(correlation_id);
|
||||
|
||||
-- Blockchain Transactions table
|
||||
CREATE TABLE IF NOT EXISTS blockchain_transactions (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
tx_hash VARCHAR(66) NOT NULL UNIQUE,
|
||||
tx_type VARCHAR(50) NOT NULL,
|
||||
related_entity_type VARCHAR(50) NOT NULL,
|
||||
related_entity_id UUID NOT NULL,
|
||||
from_address VARCHAR(42) NOT NULL,
|
||||
to_address VARCHAR(42),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'PENDING',
|
||||
block_number BIGINT,
|
||||
gas_used BIGINT,
|
||||
error_message TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
confirmed_at TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_bctx_hash ON blockchain_transactions(tx_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_bctx_type ON blockchain_transactions(tx_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_bctx_status ON blockchain_transactions(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_bctx_entity ON blockchain_transactions(related_entity_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_bctx_created ON blockchain_transactions(created_at);
|
||||
|
||||
-- =============================================
|
||||
-- MIGRATION 2: Users, Wallets, Events, Logs
|
||||
-- =============================================
|
||||
|
||||
-- Users table for email/password authentication
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password_hash VARCHAR(255) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
role VARCHAR(20) NOT NULL CHECK (role IN ('ADMIN', 'DEPARTMENT', 'CITIZEN')),
|
||||
department_id UUID REFERENCES departments(id) ON DELETE SET NULL,
|
||||
wallet_address VARCHAR(42),
|
||||
wallet_encrypted_key TEXT,
|
||||
phone VARCHAR(20),
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
last_login_at TIMESTAMP,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_email ON users(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_role ON users(role);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_department ON users(department_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_user_active ON users(is_active);
|
||||
|
||||
-- Wallets table for storing encrypted private keys
|
||||
CREATE TABLE IF NOT EXISTS wallets (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
address VARCHAR(42) NOT NULL UNIQUE,
|
||||
encrypted_private_key TEXT NOT NULL,
|
||||
owner_type VARCHAR(20) NOT NULL CHECK (owner_type IN ('USER', 'DEPARTMENT')),
|
||||
owner_id UUID NOT NULL,
|
||||
is_active BOOLEAN NOT NULL DEFAULT true,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_wallet_address ON wallets(address);
|
||||
CREATE INDEX IF NOT EXISTS idx_wallet_owner ON wallets(owner_type, owner_id);
|
||||
|
||||
-- Blockchain events table
|
||||
CREATE TABLE IF NOT EXISTS blockchain_events (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
tx_hash VARCHAR(66) NOT NULL,
|
||||
event_name VARCHAR(100) NOT NULL,
|
||||
contract_address VARCHAR(42) NOT NULL,
|
||||
block_number BIGINT NOT NULL,
|
||||
log_index INTEGER NOT NULL,
|
||||
args JSONB NOT NULL,
|
||||
decoded_args JSONB,
|
||||
related_entity_type VARCHAR(50),
|
||||
related_entity_id UUID,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
UNIQUE(tx_hash, log_index)
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_event_tx ON blockchain_events(tx_hash);
|
||||
CREATE INDEX IF NOT EXISTS idx_event_name ON blockchain_events(event_name);
|
||||
CREATE INDEX IF NOT EXISTS idx_event_contract ON blockchain_events(contract_address);
|
||||
CREATE INDEX IF NOT EXISTS idx_event_block ON blockchain_events(block_number);
|
||||
CREATE INDEX IF NOT EXISTS idx_event_created ON blockchain_events(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_event_entity ON blockchain_events(related_entity_type, related_entity_id);
|
||||
|
||||
-- Application logs table
|
||||
CREATE TABLE IF NOT EXISTS application_logs (
|
||||
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
level VARCHAR(10) NOT NULL CHECK (level IN ('DEBUG', 'INFO', 'WARN', 'ERROR')),
|
||||
module VARCHAR(100) NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
context JSONB,
|
||||
stack_trace TEXT,
|
||||
user_id UUID,
|
||||
correlation_id VARCHAR(100),
|
||||
ip_address VARCHAR(45),
|
||||
user_agent TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_applog_level ON application_logs(level);
|
||||
CREATE INDEX IF NOT EXISTS idx_applog_module ON application_logs(module);
|
||||
CREATE INDEX IF NOT EXISTS idx_applog_user ON application_logs(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_applog_correlation ON application_logs(correlation_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_applog_created ON application_logs(created_at);
|
||||
Reference in New Issue
Block a user