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:
Mahi
2026-02-07 10:23:29 -04:00
commit 80566bf0a2
441 changed files with 102418 additions and 0 deletions

View File

@@ -0,0 +1,100 @@
import { ethers } from 'hardhat';
import * as fs from 'fs';
import * as path from 'path';
async function main() {
console.log('Starting deployment to Besu network...\n');
const [deployer] = await ethers.getSigners();
console.log('Deploying contracts with account:', deployer.address);
const balance = await ethers.provider.getBalance(deployer.address);
console.log('Account balance:', ethers.formatEther(balance), 'ETH\n');
// Deploy LicenseNFT
console.log('Deploying LicenseNFT...');
const LicenseNFT = await ethers.getContractFactory('LicenseNFT');
const licenseNFT = await LicenseNFT.deploy();
await licenseNFT.waitForDeployment();
const licenseNFTAddress = await licenseNFT.getAddress();
console.log('LicenseNFT deployed to:', licenseNFTAddress);
// Deploy ApprovalManager
console.log('\nDeploying ApprovalManager...');
const ApprovalManager = await ethers.getContractFactory('ApprovalManager');
const approvalManager = await ApprovalManager.deploy();
await approvalManager.waitForDeployment();
const approvalManagerAddress = await approvalManager.getAddress();
console.log('ApprovalManager deployed to:', approvalManagerAddress);
// Deploy DocumentChain
console.log('\nDeploying DocumentChain...');
const DocumentChain = await ethers.getContractFactory('DocumentChain');
const documentChain = await DocumentChain.deploy();
await documentChain.waitForDeployment();
const documentChainAddress = await documentChain.getAddress();
console.log('DocumentChain deployed to:', documentChainAddress);
// Deploy WorkflowRegistry
console.log('\nDeploying WorkflowRegistry...');
const WorkflowRegistry = await ethers.getContractFactory('WorkflowRegistry');
const workflowRegistry = await WorkflowRegistry.deploy();
await workflowRegistry.waitForDeployment();
const workflowRegistryAddress = await workflowRegistry.getAddress();
console.log('WorkflowRegistry deployed to:', workflowRegistryAddress);
// Summary
console.log('\n========================================');
console.log('Deployment Complete!');
console.log('========================================');
console.log('Contract Addresses:');
console.log(' LicenseNFT:', licenseNFTAddress);
console.log(' ApprovalManager:', approvalManagerAddress);
console.log(' DocumentChain:', documentChainAddress);
console.log(' WorkflowRegistry:', workflowRegistryAddress);
console.log('========================================\n');
// Save deployment info
const deploymentInfo = {
network: 'besu',
chainId: 1337,
deployer: deployer.address,
timestamp: new Date().toISOString(),
contracts: {
LicenseNFT: licenseNFTAddress,
ApprovalManager: approvalManagerAddress,
DocumentChain: documentChainAddress,
WorkflowRegistry: workflowRegistryAddress,
},
};
const deploymentPath = path.join(__dirname, '../deployments');
if (!fs.existsSync(deploymentPath)) {
fs.mkdirSync(deploymentPath, { recursive: true });
}
fs.writeFileSync(
path.join(deploymentPath, 'deployment.json'),
JSON.stringify(deploymentInfo, null, 2)
);
console.log('Deployment info saved to deployments/deployment.json');
// Generate .env updates
console.log('\n========================================');
console.log('Add these to your backend/.env file:');
console.log('========================================');
console.log(`CONTRACT_ADDRESS_LICENSE_NFT=${licenseNFTAddress}`);
console.log(`CONTRACT_ADDRESS_APPROVAL_MANAGER=${approvalManagerAddress}`);
console.log(`CONTRACT_ADDRESS_DOCUMENT_CHAIN=${documentChainAddress}`);
console.log(`CONTRACT_ADDRESS_WORKFLOW_REGISTRY=${workflowRegistryAddress}`);
console.log('========================================\n');
return deploymentInfo;
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});

View File

@@ -0,0 +1,60 @@
import * as fs from 'fs';
import * as path from 'path';
/**
* Updates the backend .env file with deployed contract addresses
*/
async function main() {
const deploymentPath = path.join(__dirname, '../deployments/deployment.json');
if (!fs.existsSync(deploymentPath)) {
console.error('Deployment file not found. Run deploy.ts first.');
process.exit(1);
}
const deployment = JSON.parse(fs.readFileSync(deploymentPath, 'utf8'));
const backendEnvPath = path.join(__dirname, '../../backend/.env');
if (!fs.existsSync(backendEnvPath)) {
console.error('Backend .env file not found at:', backendEnvPath);
process.exit(1);
}
let envContent = fs.readFileSync(backendEnvPath, 'utf8');
// Contract address mappings
const envUpdates: Record<string, string> = {
CONTRACT_ADDRESS_LICENSE_NFT: deployment.contracts.LicenseNFT,
CONTRACT_ADDRESS_APPROVAL_MANAGER: deployment.contracts.ApprovalManager,
CONTRACT_ADDRESS_DOCUMENT_CHAIN: deployment.contracts.DocumentChain,
CONTRACT_ADDRESS_WORKFLOW_REGISTRY: deployment.contracts.WorkflowRegistry,
};
// Update or append each variable
for (const [key, value] of Object.entries(envUpdates)) {
const regex = new RegExp(`^${key}=.*$`, 'm');
if (regex.test(envContent)) {
envContent = envContent.replace(regex, `${key}=${value}`);
console.log(`Updated ${key}`);
} else {
envContent += `\n${key}=${value}`;
console.log(`Added ${key}`);
}
}
fs.writeFileSync(backendEnvPath, envContent);
console.log('\nBackend .env file updated successfully!');
console.log('Updated contract addresses:');
for (const [key, value] of Object.entries(envUpdates)) {
console.log(` ${key}=${value}`);
}
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exit(1);
});