feature correction

This commit is contained in:
David 2025-10-21 21:18:01 +02:00
parent 7184a23f5d
commit 2cb43c08e3
10 changed files with 40 additions and 44 deletions

View File

@ -1,38 +1,11 @@
{ {
"permissions": { "permissions": {
"allow": [ "allow": [
"Bash(npx tsc:*)", "Bash(PGPASSWORD=xpeditis_dev_password psql -h localhost -p 5432 -U xpeditis -d xpeditis_dev -c \"\\d organizations\")",
"Bash(npm test)", "Bash(PGPASSWORD=xpeditis_dev_password psql -h localhost -p 5432 -U xpeditis -d xpeditis_dev -c \"\nINSERT INTO organizations (id, name, type, address_street, address_city, address_postal_code, address_country, is_active)\nVALUES (\n ''00000000-0000-0000-0000-000000000001'',\n ''Default Organization'',\n ''FREIGHT_FORWARDER'',\n ''123 Main Street'',\n ''New York'',\n ''10001'',\n ''US'',\n true\n);\nSELECT id, name FROM organizations;\")",
"Bash(npm test:*)", "Bash(PGPASSWORD=xpeditis_dev_password psql -h localhost -p 5432 -U xpeditis -d xpeditis_dev -c \"SELECT id, name FROM organizations WHERE id = ''00000000-0000-0000-0000-000000000001'';\")",
"Bash(git add:*)", "Bash(PGPASSWORD=xpeditis_dev_password psql -h localhost -p 5432 -U xpeditis -d xpeditis_dev -c \"\nINSERT INTO organizations (id, name, type, address_street, address_city, address_postal_code, address_country, is_active)\nVALUES (\n ''a1234567-0000-4000-8000-000000000001'',\n ''Test Organization'',\n ''FREIGHT_FORWARDER'',\n ''123 Main Street'',\n ''New York'',\n ''10001'',\n ''US'',\n true\n)\nON CONFLICT (id) DO NOTHING;\nSELECT id, name FROM organizations LIMIT 2;\")",
"Bash(git commit -m \"$(cat <<''EOF''\nfix: resolve all test failures and TypeScript errors (100% test success)\n\n✅ Fixed WebhookService Tests (2 tests failing → 100% passing)\n- Increased timeout to 20s for retry test (handles 3 retries × 5s delays)\n- Fixed signature verification test with correct 64-char hex signature\n- All 7 webhook tests now passing\n\n✅ Fixed Frontend TypeScript Errors\n- Updated tsconfig.json with complete path aliases (@/types/*, @/hooks/*, @/utils/*, @/pages/*)\n- Added explicit type annotations in useBookings.ts (prev: Set<string>)\n- Fixed BookingFilters.tsx with proper type casts (s: BookingStatus)\n- Fixed CarrierMonitoring.tsx with error callback types\n- Zero TypeScript compilation errors\n\n📊 Test Results\n- Test Suites: 8 passed, 8 total (100%)\n- Tests: 92 passed, 92 total (100%)\n- Coverage: ~82% for Phase 3 services, 100% for domain entities\n\n📝 Documentation Updated\n- TEST_COVERAGE_REPORT.md: Updated to reflect 100% success rate\n- IMPLEMENTATION_SUMMARY.md: Marked all issues as resolved\n\n🎯 Phase 3 Status: COMPLETE\n- All 13/13 features implemented\n- All tests passing\n- Production ready\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")", "Bash(ACCESS_TOKEN=\"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMzg1MDVkMi1hMmVlLTQ5NmMtOWNjZC1iNjUyN2FjMzcxODgiLCJlbWFpbCI6InRlc3Q0QHhwZWRpdGlzLmNvbSIsInJvbGUiOiJ1c2VyIiwib3JnYW5pemF0aW9uSWQiOiJhMTIzNDU2Ny0wMDAwLTQwMDAtODAwMC0wMDAwMDAwMDAwMDEiLCJ0eXBlIjoiYWNjZXNzIiwiaWF0IjoxNzYxMDczOTg4LCJleHAiOjE3NjEwNzQ4ODh9.-kmaFPj8vbhyEKQJr-kuM-WR_HvrYt6547BfLg0-HQs\")"
"Bash(git log:*)",
"Bash(git commit -m \"$(cat <<''EOF''\nfeat: Phase 4 - Production-ready security, monitoring & testing infrastructure\n\n🛡 Security Hardening (OWASP Top 10 Compliant)\n- Helmet.js: CSP, HSTS, XSS protection, frame denial\n- Rate Limiting: User-based throttling (100 global, 5 auth, 30 search, 20 booking req/min)\n- Brute-Force Protection: Exponential backoff (3 attempts → 5-60min blocks)\n- File Upload Security: MIME validation, magic number checking, sanitization\n- Password Policy: 12+ chars with complexity requirements\n\n📊 Monitoring & Observability\n- Sentry Integration: Error tracking + APM (10% traces, 5% profiles)\n- Performance Interceptor: Request duration tracking, slow request alerts\n- Breadcrumb Tracking: Context enrichment for debugging\n- Error Filtering: Ignore client errors (ECONNREFUSED, ETIMEDOUT)\n\n🧪 Testing Infrastructure\n- K6 Load Tests: Rate search endpoint (100 users, p95 < 2s threshold)\n- Playwright E2E: Complete booking workflow (8 scenarios, 5 browsers)\n- Postman Collection: 12+ automated API tests with assertions\n- Test Coverage: 82% Phase 3 services, 100% domain entities\n\n📖 Comprehensive Documentation\n- ARCHITECTURE.md: 5,800 words (system design, hexagonal architecture, ADRs)\n- DEPLOYMENT.md: 4,500 words (setup, Docker, AWS, CI/CD, troubleshooting)\n- PHASE4_SUMMARY.md: Complete implementation summary with checklists\n\n🏗 Infrastructure Components\nBackend (10 files):\n - security.config.ts: Helmet, CORS, rate limits, file upload, password policy\n - security.module.ts: Global security module with throttler\n - throttle.guard.ts: Custom user/IP-based rate limiting\n - file-validation.service.ts: MIME, signature, size validation\n - brute-force-protection.service.ts: Exponential backoff with stats\n - sentry.config.ts: Error tracking + APM configuration\n - performance-monitoring.interceptor.ts: Request tracking\n\nTesting (3 files):\n - load-tests/rate-search.test.js: K6 load test (5 trade lanes)\n - e2e/booking-workflow.spec.ts: Playwright E2E (8 test scenarios)\n - postman/xpeditis-api.postman_collection.json: API test suite\n\n📈 Build Status\n✅ Backend Build: SUCCESS (TypeScript 0 errors)\n✅ Tests: 92/92 passing (100%)\n✅ Security: OWASP Top 10 compliant\n✅ Documentation: Architecture + Deployment guides complete\n\n🎯 Production Readiness\n- Security headers configured\n- Rate limiting enabled globally\n- Error tracking active (Sentry)\n- Load tests ready\n- E2E tests ready (5 browsers)\n- Comprehensive documentation\n- Backup & recovery procedures documented\n\nTotal: 15 new files, ~3,500 LoC\nPhase 4 Status: ✅ PRODUCTION-READY\n\n🤖 Generated with [Claude Code](https://claude.com/claude-code)\n\nCo-Authored-By: Claude <noreply@anthropic.com>\nEOF\n)\")",
"Bash(git commit:*)",
"Bash(k6:*)",
"Bash(npx playwright:*)",
"Bash(npx newman:*)",
"Bash(chmod:*)",
"Bash(netstat -ano)",
"Bash(findstr \":5432\")",
"Bash(findstr \"LISTENING\")",
"Read(//Volumes/**)",
"Bash(find:*)",
"Bash(cd:*)",
"Bash(npm run migration:run:*)",
"Bash(mv:*)",
"Bash(curl:*)",
"Bash(npm run dev:*)",
"Bash(python3:*)",
"Bash(bash:*)",
"Bash(npm rebuild:*)",
"Bash(npm uninstall:*)",
"Bash(PGPASSWORD=xpeditis_password psql -h localhost -p 5432 -U xpeditis -d xpeditis_db -c \"SELECT id FROM organizations WHERE type = ''FREIGHT_FORWARDER'' LIMIT 1;\")",
"Bash(PGPASSWORD=xpeditis_dev_password psql -h localhost -p 5432 -U xpeditis -d xpeditis_dev -c \"SELECT id, name FROM organizations WHERE type = ''FREIGHT_FORWARDER'' LIMIT 1;\")",
"Bash(docker-compose:*)",
"Bash(npm run start:dev:*)",
"Bash(findstr:*)",
"Bash(taskkill:*)"
], ],
"deny": [], "deny": [],
"ask": [] "ask": []

View File

@ -33,7 +33,7 @@ export class AuthService {
password: string, password: string,
firstName: string, firstName: string,
lastName: string, lastName: string,
organizationId: string, organizationId?: string,
): Promise<{ accessToken: string; refreshToken: string; user: any }> { ): Promise<{ accessToken: string; refreshToken: string; user: any }> {
this.logger.log(`Registering new user: ${email}`); this.logger.log(`Registering new user: ${email}`);
@ -50,9 +50,12 @@ export class AuthService {
parallelism: 4, parallelism: 4,
}); });
// Validate or generate organization ID
const finalOrganizationId = this.validateOrGenerateOrganizationId(organizationId);
const user = User.create({ const user = User.create({
id: uuidv4(), id: uuidv4(),
organizationId, organizationId: finalOrganizationId,
email, email,
passwordHash, passwordHash,
firstName, firstName,
@ -195,4 +198,22 @@ export class AuthService {
return { accessToken, refreshToken }; return { accessToken, refreshToken };
} }
/**
* Validate or generate a valid organization ID
* If provided ID is invalid (not a UUID), generate a new one
*/
private validateOrGenerateOrganizationId(organizationId?: string): string {
// UUID v4 regex pattern
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
if (organizationId && uuidRegex.test(organizationId)) {
return organizationId;
}
// Generate new UUID if not provided or invalid
const newOrgId = uuidv4();
this.logger.warn(`Invalid or missing organization ID. Generated new ID: ${newOrgId}`);
return newOrgId;
}
} }

View File

@ -1,4 +1,4 @@
import { IsEmail, IsString, MinLength } from 'class-validator'; import { IsEmail, IsString, MinLength, IsOptional } from 'class-validator';
import { ApiProperty } from '@nestjs/swagger'; import { ApiProperty } from '@nestjs/swagger';
export class LoginDto { export class LoginDto {
@ -54,10 +54,12 @@ export class RegisterDto {
@ApiProperty({ @ApiProperty({
example: '550e8400-e29b-41d4-a716-446655440000', example: '550e8400-e29b-41d4-a716-446655440000',
description: 'Organization ID', description: 'Organization ID (optional, will create default organization if not provided)',
required: false,
}) })
@IsString() @IsString()
organizationId: string; @IsOptional()
organizationId?: string;
} }
export class AuthResponseDto { export class AuthResponseDto {

View File

@ -93,7 +93,7 @@ export class CMACGMRequestMapper {
currency: quotation.charges?.currency || 'USD', currency: quotation.charges?.currency || 'USD',
}, },
containerType: originalInput.containerType, containerType: originalInput.containerType,
mode: originalInput.mode, mode: (originalInput.mode as 'FCL' | 'LCL') || 'FCL',
etd: new Date(quotation.schedule?.departure_date), etd: new Date(quotation.schedule?.departure_date),
eta: new Date(quotation.schedule?.arrival_date), eta: new Date(quotation.schedule?.arrival_date),
transitDays, transitDays,

View File

@ -115,7 +115,7 @@ export class HapagLloydRequestMapper {
currency: quote.currency || 'EUR', currency: quote.currency || 'EUR',
}, },
containerType: originalInput.containerType, containerType: originalInput.containerType,
mode: originalInput.mode, mode: (originalInput.mode as 'FCL' | 'LCL') || 'FCL',
etd: new Date(quote.estimated_time_of_departure), etd: new Date(quote.estimated_time_of_departure),
eta: new Date(quote.estimated_time_of_arrival), eta: new Date(quote.estimated_time_of_arrival),
transitDays, transitDays,

View File

@ -19,7 +19,7 @@ export class MaerskRequestMapper {
destinationPortCode: input.destination, destinationPortCode: input.destination,
containerSize: size, containerSize: size,
containerType: type, containerType: type,
cargoMode: input.mode, cargoMode: (input.mode as 'FCL' | 'LCL') || 'FCL',
estimatedDepartureDate: input.departureDate.toISOString(), estimatedDepartureDate: input.departureDate.toISOString(),
numberOfContainers: input.quantity || 1, numberOfContainers: input.quantity || 1,
cargoWeight: input.weight, cargoWeight: input.weight,

View File

@ -73,7 +73,7 @@ export class MaerskConnector extends BaseCarrierConnector {
origin: input.origin, origin: input.origin,
destination: input.destination, destination: input.destination,
containerType: input.containerType, containerType: input.containerType,
departureDate: input.departureDate.toISOString(), departureDate: input.departureDate?.toISOString() || input.startDate.toISOString(),
quantity: input.quantity, quantity: input.quantity,
}, },
headers: { headers: {

View File

@ -118,7 +118,7 @@ export class MSCRequestMapper {
currency: quote.currency || 'USD', currency: quote.currency || 'USD',
}, },
containerType: originalInput.containerType, containerType: originalInput.containerType,
mode: originalInput.mode, mode: (originalInput.mode as 'FCL' | 'LCL') || 'FCL',
etd: new Date(quote.etd), etd: new Date(quote.etd),
eta: new Date(quote.eta), eta: new Date(quote.eta),
transitDays, transitDays,

View File

@ -102,7 +102,7 @@ export class ONERequestMapper {
currency: quote.currency || 'USD', currency: quote.currency || 'USD',
}, },
containerType: originalInput.containerType, containerType: originalInput.containerType,
mode: originalInput.mode, mode: (originalInput.mode as 'FCL' | 'LCL') || 'FCL',
etd: new Date(quote.departure_date), etd: new Date(quote.departure_date),
eta: new Date(quote.arrival_date), eta: new Date(quote.arrival_date),
transitDays, transitDays,

View File

@ -66,7 +66,7 @@ export class S3StorageAdapter implements StoragePort {
Body: options.body, Body: options.body,
ContentType: options.contentType, ContentType: options.contentType,
Metadata: options.metadata, Metadata: options.metadata,
ACL: options.acl || 'private', // ACL is deprecated in favor of bucket policies
}); });
await this.s3Client.send(command); await this.s3Client.send(command);