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,95 @@
import { Injectable, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { ApiService, UploadProgress } from '../../../core/services/api.service';
import {
DocumentResponseDto,
DocumentVersionResponseDto,
DownloadUrlResponseDto,
DocumentType,
} from '../../../api/models';
@Injectable({
providedIn: 'root',
})
export class DocumentService {
private readonly api = inject(ApiService);
getDocuments(requestId: string): Observable<DocumentResponseDto[]> {
return this.api.get<DocumentResponseDto[]>(`/requests/${requestId}/documents`);
}
getDocument(requestId: string, documentId: string): Observable<DocumentResponseDto> {
return this.api.get<DocumentResponseDto>(`/requests/${requestId}/documents/${documentId}`);
}
getDocumentVersions(
requestId: string,
documentId: string
): Observable<DocumentVersionResponseDto[]> {
return this.api.get<DocumentVersionResponseDto[]>(
`/requests/${requestId}/documents/${documentId}/versions`
);
}
uploadDocument(
requestId: string,
file: File,
docType: DocumentType,
description?: string
): Observable<DocumentResponseDto> {
const formData = new FormData();
formData.append('file', file);
formData.append('docType', docType);
if (description) {
formData.append('description', description);
}
return this.api.upload<DocumentResponseDto>(`/requests/${requestId}/documents`, formData);
}
/**
* Upload document with progress tracking
*/
uploadDocumentWithProgress(
requestId: string,
file: File,
docType: DocumentType,
description?: string
): Observable<UploadProgress<DocumentResponseDto>> {
const formData = new FormData();
formData.append('file', file);
formData.append('docType', docType);
if (description) {
formData.append('description', description);
}
return this.api.uploadWithProgress<DocumentResponseDto>(`/requests/${requestId}/documents`, formData);
}
updateDocument(
requestId: string,
documentId: string,
file: File
): Observable<DocumentResponseDto> {
const formData = new FormData();
formData.append('file', file);
return this.api.upload<DocumentResponseDto>(
`/requests/${requestId}/documents/${documentId}`,
formData
);
}
deleteDocument(requestId: string, documentId: string): Observable<void> {
return this.api.delete<void>(`/requests/${requestId}/documents/${documentId}`);
}
getDownloadUrl(requestId: string, documentId: string): Observable<DownloadUrlResponseDto> {
return this.api.get<DownloadUrlResponseDto>(
`/requests/${requestId}/documents/${documentId}/download`
);
}
verifyDocument(requestId: string, documentId: string): Observable<{ verified: boolean }> {
return this.api.get<{ verified: boolean }>(
`/requests/${requestId}/documents/${documentId}/verify`
);
}
}