Security hardening and edge case fixes across frontend
Security Improvements: - Add input sanitization utilities (XSS, SQL injection prevention) - Add token validation with JWT structure verification - Add secure form validators with pattern enforcement - Implement proper token storage with encryption support Service Hardening: - Add timeout (30s) and retry logic (3 attempts) to all API calls - Add UUID validation for all ID parameters - Add null/undefined checks with defensive defaults - Proper error propagation with typed error handling Component Fixes: - Fix memory leaks with takeUntilDestroyed pattern - Remove mock data fallbacks in error handlers - Add proper loading/error state management - Add form field length limits and validation Files affected: 51 (6000+ lines added for security)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import { Component, OnInit, inject, signal } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy, inject, signal, DestroyRef } from '@angular/core';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule } from '@angular/router';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
@@ -151,12 +152,14 @@ import { ApprovalResponseDto } from '../../../api/models';
|
||||
`,
|
||||
],
|
||||
})
|
||||
export class PendingListComponent implements OnInit {
|
||||
export class PendingListComponent implements OnInit, OnDestroy {
|
||||
private readonly approvalService = inject(ApprovalService);
|
||||
private readonly notification = inject(NotificationService);
|
||||
private readonly dialog = inject(MatDialog);
|
||||
private readonly destroyRef = inject(DestroyRef);
|
||||
|
||||
readonly loading = signal(true);
|
||||
readonly hasError = signal(false);
|
||||
readonly approvals = signal<ApprovalResponseDto[]>([]);
|
||||
readonly totalItems = signal(0);
|
||||
readonly pageSize = signal(10);
|
||||
@@ -170,15 +173,19 @@ export class PendingListComponent implements OnInit {
|
||||
|
||||
loadApprovals(): void {
|
||||
this.loading.set(true);
|
||||
this.hasError.set(false);
|
||||
|
||||
this.approvalService
|
||||
.getPendingApprovals(this.pageIndex() + 1, this.pageSize())
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe({
|
||||
next: (response) => {
|
||||
this.approvals.set(response.data);
|
||||
this.totalItems.set(response.total);
|
||||
this.approvals.set(response?.data ?? []);
|
||||
this.totalItems.set(response?.total ?? 0);
|
||||
this.loading.set(false);
|
||||
},
|
||||
error: () => {
|
||||
this.hasError.set(true);
|
||||
this.loading.set(false);
|
||||
},
|
||||
});
|
||||
@@ -194,15 +201,28 @@ export class PendingListComponent implements OnInit {
|
||||
approval: ApprovalResponseDto,
|
||||
action: 'approve' | 'reject' | 'changes'
|
||||
): void {
|
||||
if (!approval) return;
|
||||
|
||||
const dialogRef = this.dialog.open(ApprovalActionComponent, {
|
||||
data: { approval, action },
|
||||
width: '500px',
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe((result) => {
|
||||
if (result) {
|
||||
this.loadApprovals();
|
||||
}
|
||||
});
|
||||
dialogRef.afterClosed()
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe((result) => {
|
||||
if (result) {
|
||||
this.loadApprovals();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
// Cleanup handled by DestroyRef/takeUntilDestroyed
|
||||
}
|
||||
|
||||
/** Retry loading data - clears error state */
|
||||
retryLoad(): void {
|
||||
this.loadApprovals();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user