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,83 @@
import { createCipheriv, createDecipheriv, randomBytes, scryptSync } from 'crypto';
import * as bcrypt from 'bcrypt';
import * as crypto from 'crypto';
export async function hash(data: string): Promise<string> {
return bcrypt.hash(data, 10);
}
export async function generateApiKey(): Promise<{
apiKey: string;
apiSecret: string;
apiKeyHash: string;
apiSecretHash: string;
}> {
const apiKey = `goa_${crypto.randomBytes(16).toString('hex')}`;
const apiSecret = crypto.randomBytes(32).toString('hex');
const [apiKeyHash, apiSecretHash] = await Promise.all([
hash(apiKey),
hash(apiSecret),
]);
return {
apiKey,
apiSecret,
apiKeyHash,
apiSecretHash,
};
}
export class CryptoUtil {
private static readonly ALGORITHM = 'aes-256-gcm';
private static readonly SALT_LENGTH = 16;
private static readonly TAG_LENGTH = 16;
private static readonly IV_LENGTH = 16;
static encrypt(data: string, password: string): string {
const salt = randomBytes(CryptoUtil.SALT_LENGTH);
const key = scryptSync(password, salt, 32);
const iv = randomBytes(CryptoUtil.IV_LENGTH);
const cipher = createCipheriv(CryptoUtil.ALGORITHM, key, iv);
const encrypted = Buffer.concat([
cipher.update(data, 'utf8'),
cipher.final(),
]);
const authTag = cipher.getAuthTag();
return Buffer.concat([salt, iv, authTag, encrypted]).toString('hex');
}
static decrypt(encryptedData: string, password: string): string {
const buffer = Buffer.from(encryptedData, 'hex');
const salt = buffer.subarray(0, CryptoUtil.SALT_LENGTH);
const iv = buffer.subarray(
CryptoUtil.SALT_LENGTH,
CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH,
);
const authTag = buffer.subarray(
CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH,
CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH + CryptoUtil.TAG_LENGTH,
);
const encrypted = buffer.subarray(
CryptoUtil.SALT_LENGTH + CryptoUtil.IV_LENGTH + CryptoUtil.TAG_LENGTH,
);
const key = scryptSync(password, salt, 32);
const decipher = createDecipheriv(CryptoUtil.ALGORITHM, key, iv);
decipher.setAuthTag(authTag);
return decipher.update(encrypted) + decipher.final('utf8');
}
static generateKey(length: number = 32): string {
return randomBytes(length).toString('hex');
}
static generateIV(length: number = 16): string {
return randomBytes(length).toString('hex');
}
}