From 624e24f244d22d024de461a80a745a1cb17512cd Mon Sep 17 00:00:00 2001 From: Akinz-bobo Date: Mon, 15 Sep 2025 13:41:05 +0100 Subject: [PATCH 1/2] Create frontend-only branch for React Native app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Moved all frontend files to root level - Removed backend directory and files - Updated README for frontend-specific setup and APK building - Ready for standalone React Native development and deployment 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- frontend/App.tsx => App.tsx | 0 Dockerfile.android | 74 - Dockerfile.metro | 45 - frontend/Gemfile => Gemfile | 0 README.md | 189 +- VERIFIED_USER_CREDENTIALS.md | 188 - .../__tests__ => __tests__}/App.test.tsx | 0 .../android => android}/app/build.gradle | 0 .../android => android}/app/debug.keystore | Bin .../app/proguard-rules.pro | 0 .../app/src/debug/AndroidManifest.xml | 0 .../app/src/main/AndroidManifest.xml | 0 .../app/src/main/assets/index.android.bundle | 0 .../app/src/main/java/com/app/MainActivity.kt | 0 .../src/main/java/com/app/MainApplication.kt | 0 .../app/src/main/java/com/app/VoiceModule.kt | 0 .../app/src/main/java/com/app/VoicePackage.kt | 0 ...on_elements_lib_module_assets_backicon.png | Bin ...lements_lib_module_assets_backiconmask.png | Bin ...n_elements_lib_module_assets_clearicon.png | Bin ...n_elements_lib_module_assets_closeicon.png | Bin ..._elements_lib_module_assets_searchicon.png | Bin .../drawable-mdpi/src_assets_icons_google.png | Bin .../drawable-mdpi/src_assets_icons_logo.png | Bin ...on_elements_lib_module_assets_backicon.png | Bin ...n_elements_lib_module_assets_clearicon.png | Bin ...n_elements_lib_module_assets_closeicon.png | Bin ..._elements_lib_module_assets_searchicon.png | Bin ...on_elements_lib_module_assets_backicon.png | Bin ...n_elements_lib_module_assets_clearicon.png | Bin ...n_elements_lib_module_assets_closeicon.png | Bin ..._elements_lib_module_assets_searchicon.png | Bin ...on_elements_lib_module_assets_backicon.png | Bin ...n_elements_lib_module_assets_clearicon.png | Bin ...n_elements_lib_module_assets_closeicon.png | Bin ..._elements_lib_module_assets_searchicon.png | Bin .../res/drawable/rn_edit_text_material.xml | 0 .../src/main/res/mipmap-hdpi/ic_launcher.png | Bin .../res/mipmap-hdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-mdpi/ic_launcher.png | Bin .../res/mipmap-mdpi/ic_launcher_round.png | Bin .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin .../res/mipmap-xhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin .../app/src/main/res/raw/keep.xml | 0 .../app/src/main/res/values/strings.xml | 0 .../app/src/main/res/values/styles.xml | 0 {frontend/android => android}/build.gradle | 0 .../android => android}/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 {frontend/android => android}/gradlew | 0 {frontend/android => android}/gradlew.bat | 0 {frontend/android => android}/settings.gradle | 0 frontend/app.json => app.json | 0 frontend/babel.config.js => babel.config.js | 0 backend/README.md | 649 - backend/SUCCESS_CRITERIA.md | 468 - backend/SUCCESS_CRITERIA_VALIDATION.md | 610 - backend/dist/ai/commands/AICommandParser.d.ts | 63 - .../dist/ai/commands/AICommandParser.d.ts.map | 1 - backend/dist/ai/commands/AICommandParser.js | 459 - .../dist/ai/commands/AICommandParser.js.map | 1 - backend/dist/ai/context/ContextManager.d.ts | 64 - .../dist/ai/context/ContextManager.d.ts.map | 1 - backend/dist/ai/context/ContextManager.js | 355 - backend/dist/ai/context/ContextManager.js.map | 1 - backend/dist/ai/context/EntityResolver.d.ts | 80 - .../dist/ai/context/EntityResolver.d.ts.map | 1 - backend/dist/ai/context/EntityResolver.js | 473 - backend/dist/ai/context/EntityResolver.js.map | 1 - .../dist/ai/context/TemporalProcessor.d.ts | 80 - .../ai/context/TemporalProcessor.d.ts.map | 1 - backend/dist/ai/context/TemporalProcessor.js | 516 - .../dist/ai/context/TemporalProcessor.js.map | 1 - .../dist/ai/execution/DependencyResolver.d.ts | 117 - .../ai/execution/DependencyResolver.d.ts.map | 1 - .../dist/ai/execution/DependencyResolver.js | 586 - .../ai/execution/DependencyResolver.js.map | 1 - .../ai/execution/MultiActionExecutor.d.ts | 127 - .../ai/execution/MultiActionExecutor.d.ts.map | 1 - .../dist/ai/execution/MultiActionExecutor.js | 858 -- .../ai/execution/MultiActionExecutor.js.map | 1 - backend/dist/api/index.d.ts | 11 - backend/dist/api/index.d.ts.map | 1 - backend/dist/api/index.js | 41 - backend/dist/api/index.js.map | 1 - backend/dist/api/routes/ActivityRoutes.d.ts | 7 - .../dist/api/routes/ActivityRoutes.d.ts.map | 1 - backend/dist/api/routes/ActivityRoutes.js | 231 - backend/dist/api/routes/ActivityRoutes.js.map | 1 - .../dist/api/routes/AnnouncementRoutes.d.ts | 3 - .../api/routes/AnnouncementRoutes.d.ts.map | 1 - backend/dist/api/routes/AnnouncementRoutes.js | 579 - .../dist/api/routes/AnnouncementRoutes.js.map | 1 - backend/dist/api/routes/ChannelRoutes.d.ts | 6 - .../dist/api/routes/ChannelRoutes.d.ts.map | 1 - backend/dist/api/routes/ChannelRoutes.js | 1200 -- backend/dist/api/routes/ChannelRoutes.js.map | 1 - backend/dist/api/routes/DocsRoutes.d.ts | 7 - backend/dist/api/routes/DocsRoutes.d.ts.map | 1 - backend/dist/api/routes/DocsRoutes.js | 83 - backend/dist/api/routes/DocsRoutes.js.map | 1 - backend/dist/api/routes/MessageRoutes.d.ts | 6 - .../dist/api/routes/MessageRoutes.d.ts.map | 1 - backend/dist/api/routes/MessageRoutes.js | 829 - backend/dist/api/routes/MessageRoutes.js.map | 1 - .../dist/api/routes/NotificationRoutes.d.ts | 3 - .../api/routes/NotificationRoutes.d.ts.map | 1 - backend/dist/api/routes/NotificationRoutes.js | 274 - .../dist/api/routes/NotificationRoutes.js.map | 1 - backend/dist/api/routes/TaskRoutes.d.ts | 6 - backend/dist/api/routes/TaskRoutes.d.ts.map | 1 - backend/dist/api/routes/TaskRoutes.js | 1438 -- backend/dist/api/routes/TaskRoutes.js.map | 1 - backend/dist/api/routes/UserRoutes.d.ts | 6 - backend/dist/api/routes/UserRoutes.d.ts.map | 1 - backend/dist/api/routes/UserRoutes.js | 704 - backend/dist/api/routes/UserRoutes.js.map | 1 - backend/dist/auth/jwt.d.ts | 135 - backend/dist/auth/jwt.d.ts.map | 1 - backend/dist/auth/jwt.js | 356 - backend/dist/auth/jwt.js.map | 1 - backend/dist/auth/middleware.d.ts | 77 - backend/dist/auth/middleware.d.ts.map | 1 - backend/dist/auth/middleware.js | 373 - backend/dist/auth/middleware.js.map | 1 - backend/dist/auth/routes.d.ts | 6 - backend/dist/auth/routes.d.ts.map | 1 - backend/dist/auth/routes.js | 715 - backend/dist/auth/routes.js.map | 1 - backend/dist/config/database.d.ts | 65 - backend/dist/config/database.d.ts.map | 1 - backend/dist/config/database.js | 268 - backend/dist/config/database.js.map | 1 - backend/dist/config/index.d.ts | 82 - backend/dist/config/index.d.ts.map | 1 - backend/dist/config/index.js | 144 - backend/dist/config/index.js.map | 1 - backend/dist/config/redis.d.ts | 86 - backend/dist/config/redis.d.ts.map | 1 - backend/dist/config/redis.js | 259 - backend/dist/config/redis.js.map | 1 - backend/dist/db/ActivityRepository.d.ts | 135 - backend/dist/db/ActivityRepository.d.ts.map | 1 - backend/dist/db/ActivityRepository.js | 620 - backend/dist/db/ActivityRepository.js.map | 1 - backend/dist/db/AnnouncementRepository.d.ts | 77 - .../dist/db/AnnouncementRepository.d.ts.map | 1 - backend/dist/db/AnnouncementRepository.js | 314 - backend/dist/db/AnnouncementRepository.js.map | 1 - backend/dist/db/BaseRepository.d.ts | 109 - backend/dist/db/BaseRepository.d.ts.map | 1 - backend/dist/db/BaseRepository.js | 368 - backend/dist/db/BaseRepository.js.map | 1 - backend/dist/db/ChannelRepository.d.ts | 146 - backend/dist/db/ChannelRepository.d.ts.map | 1 - backend/dist/db/ChannelRepository.js | 476 - backend/dist/db/ChannelRepository.js.map | 1 - backend/dist/db/FileRepository.d.ts | 148 - backend/dist/db/FileRepository.d.ts.map | 1 - backend/dist/db/FileRepository.js | 505 - backend/dist/db/FileRepository.js.map | 1 - backend/dist/db/MessageRepository.d.ts | 139 - backend/dist/db/MessageRepository.d.ts.map | 1 - backend/dist/db/MessageRepository.js | 444 - backend/dist/db/MessageRepository.js.map | 1 - backend/dist/db/TaskRepository.d.ts | 169 - backend/dist/db/TaskRepository.d.ts.map | 1 - backend/dist/db/TaskRepository.js | 460 - backend/dist/db/TaskRepository.js.map | 1 - backend/dist/db/UserRepository.d.ts | 154 - backend/dist/db/UserRepository.d.ts.map | 1 - backend/dist/db/UserRepository.js | 427 - backend/dist/db/UserRepository.js.map | 1 - backend/dist/db/index.d.ts | 57 - backend/dist/db/index.d.ts.map | 1 - backend/dist/db/index.js | 138 - backend/dist/db/index.js.map | 1 - backend/dist/db/migrator.d.ts | 32 - backend/dist/db/migrator.d.ts.map | 1 - backend/dist/db/migrator.js | 239 - backend/dist/db/migrator.js.map | 1 - .../files/management/FileMetadataManager.d.ts | 113 - .../management/FileMetadataManager.d.ts.map | 1 - .../files/management/FileMetadataManager.js | 466 - .../management/FileMetadataManager.js.map | 1 - .../dist/files/management/S3FileManager.d.ts | 90 - .../files/management/S3FileManager.d.ts.map | 1 - .../dist/files/management/S3FileManager.js | 395 - .../files/management/S3FileManager.js.map | 1 - .../dist/files/upload/FileCommandParser.d.ts | 127 - .../files/upload/FileCommandParser.d.ts.map | 1 - .../dist/files/upload/FileCommandParser.js | 539 - .../files/upload/FileCommandParser.js.map | 1 - .../files/upload/VoiceFileUploadService.d.ts | 91 - .../upload/VoiceFileUploadService.d.ts.map | 1 - .../files/upload/VoiceFileUploadService.js | 445 - .../upload/VoiceFileUploadService.js.map | 1 - backend/dist/index.d.ts | 13 - backend/dist/index.d.ts.map | 1 - backend/dist/index.js | 51 - backend/dist/index.js.map | 1 - .../caching/RedisCacheManager.d.ts | 138 - .../caching/RedisCacheManager.d.ts.map | 1 - .../performance/caching/RedisCacheManager.js | 641 - .../caching/RedisCacheManager.js.map | 1 - .../monitoring/PerformanceMonitor.d.ts | 243 - .../monitoring/PerformanceMonitor.d.ts.map | 1 - .../monitoring/PerformanceMonitor.js | 548 - .../monitoring/PerformanceMonitor.js.map | 1 - .../broadcasting/EntityUpdateBroadcaster.d.ts | 123 - .../EntityUpdateBroadcaster.d.ts.map | 1 - .../broadcasting/EntityUpdateBroadcaster.js | 295 - .../EntityUpdateBroadcaster.js.map | 1 - .../broadcasting/ExecutionEventManager.d.ts | 148 - .../ExecutionEventManager.d.ts.map | 1 - .../broadcasting/ExecutionEventManager.js | 527 - .../broadcasting/ExecutionEventManager.js.map | 1 - .../broadcasting/NotificationIntegration.d.ts | 170 - .../NotificationIntegration.d.ts.map | 1 - .../broadcasting/NotificationIntegration.js | 477 - .../NotificationIntegration.js.map | 1 - .../broadcasting/ProgressBroadcaster.d.ts | 127 - .../broadcasting/ProgressBroadcaster.d.ts.map | 1 - .../broadcasting/ProgressBroadcaster.js | 354 - .../broadcasting/ProgressBroadcaster.js.map | 1 - .../scripts/cleanup-unverified-users.d.ts | 8 - .../scripts/cleanup-unverified-users.d.ts.map | 1 - .../dist/scripts/cleanup-unverified-users.js | 50 - .../scripts/cleanup-unverified-users.js.map | 1 - .../scripts/phase2-implementation-review.d.ts | 87 - .../phase2-implementation-review.d.ts.map | 1 - .../scripts/phase2-implementation-review.js | 878 -- .../phase2-implementation-review.js.map | 1 - backend/dist/scripts/phase2-optimization.d.ts | 56 - .../dist/scripts/phase2-optimization.d.ts.map | 1 - backend/dist/scripts/phase2-optimization.js | 883 -- .../dist/scripts/phase2-optimization.js.map | 1 - backend/dist/scripts/seed-channels.d.ts | 5 - backend/dist/scripts/seed-channels.d.ts.map | 1 - backend/dist/scripts/seed-channels.js | 345 - backend/dist/scripts/seed-channels.js.map | 1 - backend/dist/scripts/seed.d.ts | 14 - backend/dist/scripts/seed.d.ts.map | 1 - backend/dist/scripts/seed.js | 751 - backend/dist/scripts/seed.js.map | 1 - backend/dist/scripts/verify-email-token.d.ts | 3 - .../dist/scripts/verify-email-token.d.ts.map | 1 - backend/dist/scripts/verify-email-token.js | 89 - .../dist/scripts/verify-email-token.js.map | 1 - backend/dist/security/SecurityManager.d.ts | 174 - .../dist/security/SecurityManager.d.ts.map | 1 - backend/dist/security/SecurityManager.js | 614 - backend/dist/security/SecurityManager.js.map | 1 - backend/dist/server.d.ts | 77 - backend/dist/server.d.ts.map | 1 - backend/dist/server.js | 677 - backend/dist/server.js.map | 1 - backend/dist/services/CacheService.d.ts | 137 - backend/dist/services/CacheService.d.ts.map | 1 - backend/dist/services/CacheService.js | 464 - backend/dist/services/CacheService.js.map | 1 - backend/dist/services/EmailService.d.ts | 28 - backend/dist/services/EmailService.d.ts.map | 1 - backend/dist/services/EmailService.js | 213 - backend/dist/services/EmailService.js.map | 1 - backend/dist/services/index.d.ts | 9 - backend/dist/services/index.d.ts.map | 1 - backend/dist/services/index.js | 28 - backend/dist/services/index.js.map | 1 - backend/dist/utils/cache-decorators.d.ts | 127 - backend/dist/utils/cache-decorators.d.ts.map | 1 - backend/dist/utils/cache-decorators.js | 329 - backend/dist/utils/cache-decorators.js.map | 1 - backend/dist/utils/errors.d.ts | 207 - backend/dist/utils/errors.d.ts.map | 1 - backend/dist/utils/errors.js | 266 - backend/dist/utils/errors.js.map | 1 - backend/dist/utils/logger.d.ts | 83 - backend/dist/utils/logger.d.ts.map | 1 - backend/dist/utils/logger.js | 219 - backend/dist/utils/logger.js.map | 1 - backend/dist/utils/performance.d.ts | 106 - backend/dist/utils/performance.d.ts.map | 1 - backend/dist/utils/performance.js | 320 - backend/dist/utils/performance.js.map | 1 - backend/dist/utils/validation.d.ts | 103 - backend/dist/utils/validation.d.ts.map | 1 - backend/dist/utils/validation.js | 254 - backend/dist/utils/validation.js.map | 1 - .../dist/voice/VoiceProcessingService.d.ts | 74 - .../voice/VoiceProcessingService.d.ts.map | 1 - backend/dist/voice/VoiceProcessingService.js | 435 - .../dist/voice/VoiceProcessingService.js.map | 1 - .../dist/voice/stream/AudioPreprocessor.d.ts | 79 - .../voice/stream/AudioPreprocessor.d.ts.map | 1 - .../dist/voice/stream/AudioPreprocessor.js | 369 - .../voice/stream/AudioPreprocessor.js.map | 1 - .../dist/voice/stream/AudioStreamManager.d.ts | 120 - .../voice/stream/AudioStreamManager.d.ts.map | 1 - .../dist/voice/stream/AudioStreamManager.js | 395 - .../voice/stream/AudioStreamManager.js.map | 1 - backend/dist/voice/stream/CircularBuffer.d.ts | 104 - .../dist/voice/stream/CircularBuffer.d.ts.map | 1 - backend/dist/voice/stream/CircularBuffer.js | 269 - .../dist/voice/stream/CircularBuffer.js.map | 1 - .../voice/stream/VoiceActivityDetector.d.ts | 85 - .../stream/VoiceActivityDetector.d.ts.map | 1 - .../voice/stream/VoiceActivityDetector.js | 317 - .../voice/stream/VoiceActivityDetector.js.map | 1 - .../transcription/TranscriptionCache.d.ts | 80 - .../transcription/TranscriptionCache.d.ts.map | 1 - .../voice/transcription/TranscriptionCache.js | 298 - .../transcription/TranscriptionCache.js.map | 1 - .../transcription/WhisperConnectionPool.d.ts | 67 - .../WhisperConnectionPool.d.ts.map | 1 - .../transcription/WhisperConnectionPool.js | 369 - .../WhisperConnectionPool.js.map | 1 - .../voice/transcription/WhisperService.d.ts | 55 - .../transcription/WhisperService.d.ts.map | 1 - .../voice/transcription/WhisperService.js | 368 - .../voice/transcription/WhisperService.js.map | 1 - backend/dist/voice/types.d.ts | 339 - backend/dist/voice/types.d.ts.map | 1 - backend/dist/voice/types.js | 45 - backend/dist/voice/types.js.map | 1 - .../dist/websocket/EnhancedSocketManager.d.ts | 144 - .../websocket/EnhancedSocketManager.d.ts.map | 1 - .../dist/websocket/EnhancedSocketManager.js | 483 - .../websocket/EnhancedSocketManager.js.map | 1 - backend/dist/websocket/SocketManager.d.ts | 153 - backend/dist/websocket/SocketManager.d.ts.map | 1 - backend/dist/websocket/SocketManager.js | 601 - backend/dist/websocket/SocketManager.js.map | 1 - backend/dist/websocket/index.d.ts | 14 - backend/dist/websocket/index.d.ts.map | 1 - backend/dist/websocket/index.js | 35 - backend/dist/websocket/index.js.map | 1 - backend/dist/websocket/types.d.ts | 296 - backend/dist/websocket/types.d.ts.map | 1 - backend/dist/websocket/types.js | 8 - backend/dist/websocket/types.js.map | 1 - backend/dist/websocket/utils.d.ts | 139 - backend/dist/websocket/utils.d.ts.map | 1 - backend/dist/websocket/utils.js | 397 - backend/dist/websocket/utils.js.map | 1 - backend/docker/Dockerfile.dev | 39 - backend/docker/docker-compose.dev.yml | 100 - backend/docker/postgres/init.sql | 31 - backend/docker/redis/redis.conf | 60 - backend/jest.config.js | 237 - .../migrations/1640995200000_create_users.sql | 193 - .../1640995201000_create_categories.sql | 321 - .../1640995202000_create_channels.sql | 452 - .../migrations/1640995203000_create_tasks.sql | 509 - ...95204000_create_messages_notifications.sql | 430 - ...5205000_create_voice_and_relationships.sql | 493 - ...121000000_increase_token_column_length.sql | 15 - ...22000000_create_file_management_tables.sql | 323 - ...50101000001_create_announcements_table.sql | 73 - ...20250101000002_create_activities_table.sql | 84 - backend/package-lock.json | 12458 ---------------- backend/package.json | 172 - backend/run-seed.js | 21 - backend/server.log | 621 - backend/src/ai/commands/AICommandParser.ts | 565 - backend/src/ai/context/ContextManager.ts | 413 - backend/src/ai/context/EntityResolver.ts | 588 - backend/src/ai/context/TemporalProcessor.ts | 633 - .../src/ai/execution/DependencyResolver.ts | 782 - .../src/ai/execution/MultiActionExecutor.ts | 1171 -- backend/src/api/index.ts | 40 - backend/src/api/routes/ActivityRoutes.ts | 290 - backend/src/api/routes/AnnouncementRoutes.ts | 689 - backend/src/api/routes/ChannelRoutes.ts | 1386 -- backend/src/api/routes/DocsRoutes.ts | 83 - backend/src/api/routes/MessageRoutes.ts | 966 -- backend/src/api/routes/NotificationRoutes.ts | 354 - backend/src/api/routes/TaskRoutes.ts | 1721 --- backend/src/api/routes/UserRoutes.ts | 764 - backend/src/auth/jwt.ts | 449 - backend/src/auth/middleware.ts | 440 - backend/src/auth/routes.ts | 813 - backend/src/config/database.ts | 348 - backend/src/config/index.ts | 165 - backend/src/config/redis.ts | 308 - backend/src/db/ActivityRepository.ts | 831 -- backend/src/db/AnnouncementRepository.ts | 414 - backend/src/db/BaseRepository.ts | 581 - backend/src/db/ChannelRepository.ts | 721 - backend/src/db/FileRepository.ts | 699 - backend/src/db/MessageRepository.ts | 648 - backend/src/db/TaskRepository.ts | 640 - backend/src/db/UserRepository.ts | 607 - backend/src/db/index.ts | 121 - backend/src/db/migrator.ts | 314 - .../files/management/FileMetadataManager.ts | 620 - backend/src/files/management/S3FileManager.ts | 503 - backend/src/files/upload/FileCommandParser.ts | 706 - .../files/upload/VoiceFileUploadService.ts | 599 - backend/src/index.ts | 56 - .../performance/caching/RedisCacheManager.ts | 803 - .../monitoring/PerformanceMonitor.ts | 773 - .../broadcasting/EntityUpdateBroadcaster.ts | 437 - .../broadcasting/ExecutionEventManager.ts | 691 - .../broadcasting/NotificationIntegration.ts | 682 - .../broadcasting/ProgressBroadcaster.ts | 486 - .../src/scripts/cleanup-unverified-users.ts | 59 - .../scripts/phase2-implementation-review.ts | 1039 -- backend/src/scripts/phase2-optimization.ts | 977 -- backend/src/scripts/seed-channels.ts | 373 - backend/src/scripts/seed.ts | 846 -- backend/src/scripts/verify-email-token.ts | 61 - backend/src/security/SecurityManager.ts | 799 - backend/src/server.ts | 763 - backend/src/services/CacheService.ts | 593 - backend/src/services/EmailService.ts | 244 - backend/src/services/index.ts | 9 - backend/src/utils/cache-decorators.ts | 426 - backend/src/utils/errors.ts | 292 - backend/src/utils/logger.ts | 274 - backend/src/utils/performance.ts | 450 - backend/src/utils/validation.ts | 281 - backend/src/voice/VoiceProcessingService.ts | 570 - backend/src/voice/stream/AudioPreprocessor.ts | 458 - .../src/voice/stream/AudioStreamManager.ts | 504 - backend/src/voice/stream/CircularBuffer.ts | 320 - .../src/voice/stream/VoiceActivityDetector.ts | 412 - .../voice/transcription/TranscriptionCache.ts | 343 - .../transcription/WhisperConnectionPool.ts | 439 - .../src/voice/transcription/WhisperService.ts | 453 - backend/src/voice/types.ts | 410 - .../src/websocket/EnhancedSocketManager.ts | 632 - backend/src/websocket/SocketManager.ts | 806 - backend/src/websocket/index.ts | 17 - backend/src/websocket/types.ts | 388 - backend/src/websocket/utils.ts | 508 - backend/start-dev.sh | 38 - backend/tests/api/routes/TaskRoutes.test.d.ts | 6 - .../tests/api/routes/TaskRoutes.test.d.ts.map | 1 - backend/tests/api/routes/TaskRoutes.test.js | 627 - .../tests/api/routes/TaskRoutes.test.js.map | 1 - backend/tests/api/routes/TaskRoutes.test.ts | 801 - backend/tests/auth/jwt.test.d.ts | 6 - backend/tests/auth/jwt.test.d.ts.map | 1 - backend/tests/auth/jwt.test.js | 231 - backend/tests/auth/jwt.test.js.map | 1 - backend/tests/auth/jwt.test.ts | 288 - backend/tests/auth/middleware.test.d.ts | 6 - backend/tests/auth/middleware.test.d.ts.map | 1 - backend/tests/auth/middleware.test.js | 315 - backend/tests/auth/middleware.test.js.map | 1 - backend/tests/auth/middleware.test.ts | 420 - backend/tests/db/BaseRepository.test.d.ts | 6 - backend/tests/db/BaseRepository.test.d.ts.map | 1 - backend/tests/db/BaseRepository.test.js | 403 - backend/tests/db/BaseRepository.test.js.map | 1 - backend/tests/db/BaseRepository.test.ts | 552 - .../VoiceCommandIntegration.test.ts | 740 - backend/tests/integration/basic.test.d.ts | 6 - backend/tests/integration/basic.test.d.ts.map | 1 - backend/tests/integration/basic.test.js | 45 - backend/tests/integration/basic.test.js.map | 1 - backend/tests/integration/basic.test.ts | 52 - backend/tests/performance/LoadTest.ts | 682 - backend/tests/setup.d.ts | 52 - backend/tests/setup.d.ts.map | 1 - backend/tests/setup.js | 289 - backend/tests/setup.js.map | 1 - backend/tests/setup.ts | 298 - backend/tests/setup/customMatchers.ts | 422 - backend/tests/setup/jest.setup.ts | 293 - .../tests/unit/services/OpenAIService.test.ts | 624 - backend/tsconfig.json | 56 - docker-compose.dev-only.yml | 42 - docker-compose.windows.yml | 144 - frontend/env.d.ts => env.d.ts | 0 frontend/package.json | 78 - frontend/global.css => global.css | 0 frontend/index.js => index.js | 0 {frontend/ios => ios}/.xcode.env | 0 {frontend/ios => ios}/Podfile | 0 .../ios => ios}/app.xcodeproj/project.pbxproj | 0 .../xcshareddata/xcschemes/app.xcscheme | 0 {frontend/ios => ios}/app/AppDelegate.swift | 0 .../AppIcon.appiconset/Contents.json | 0 .../app/Images.xcassets/Contents.json | 0 {frontend/ios => ios}/app/Info.plist | 0 .../ios => ios}/app/LaunchScreen.storyboard | 0 .../ios => ios}/app/PrivacyInfo.xcprivacy | 0 frontend/jest.config.js => jest.config.js | 0 frontend/metro.config.js => metro.config.js | 0 ...nativewind-env.d.ts => nativewind-env.d.ts | 0 .../package-lock.json => package-lock.json | 0 package.json | 91 +- {frontend/src => src}/assets/icons/google.png | Bin .../src => src}/assets/icons/history.png | Bin {frontend/src => src}/assets/icons/logo.png | Bin .../src => src}/assets/images/sample_home.jpg | Bin .../channel/CategoryFilterModal.tsx | 0 .../channel/ChannelTaskIntegration.tsx | 0 .../components/channel/CreateChannelModal.tsx | 0 .../channel/MemberSelectorModal.tsx | 0 .../components/chat/ChannelActions.tsx | 0 .../components/chat/ChannelHeader.tsx | 0 .../components/chat/ChannelInput.tsx | 0 .../components/chat/ChannelInputContainer.tsx | 0 .../chat/ChannelModalsContainer.tsx | 0 .../components/chat/ChannelTypingManager.tsx | 0 .../components/chat/ChatMessage.tsx | 0 .../components/chat/EmojiPicker.tsx | 0 .../components/chat/EmojiReactionPicker.tsx | 0 .../components/chat/KeyPointsModal.tsx | 0 .../components/chat/MeetingSummaryModal.tsx | 0 .../components/chat/MessageList.tsx | 0 .../components/chat/MessageListContainer.tsx | 0 .../chat/SimpleTypingIndicators.tsx | 0 .../components/chat/SmartMentionInput.tsx | 0 .../components/chat/ThreadedMessage.tsx | 0 .../components/chat/ThreadedMessageList.tsx | 0 .../components/chat/TypingIndicators.tsx | 0 .../src => src}/components/chat/index.ts | 0 .../components/common/ActionDialog.tsx | 0 .../components/common/ActionSheet.tsx | 0 .../src => src}/components/common/Avatar.tsx | 0 .../src => src}/components/common/Botton.tsx | 0 .../components/common/ChannelCard.tsx | 0 .../components/common/ConfirmationModal.tsx | 0 .../CurvedBackground/CurvedBackground.tsx | 0 .../components/common/CustomPicker.tsx | 0 .../components/common/ErrorBoundary.tsx | 0 .../src => src}/components/common/Input.tsx | 0 .../components/common/LoadingSpinner.tsx | 0 .../components/common/PermissionDialog.tsx | 0 .../components/common/SafeAreaWrapper.tsx | 0 .../components/common/StepIndicator.tsx | 0 .../src => src}/components/common/Toast.tsx | 0 .../components/common/UIProvider.tsx | 0 .../src => src}/components/common/index.ts | 0 .../components/forms/FormField.tsx | 0 .../components/forms/LoginForm.tsx | 0 .../components/forms/RegisterForm.tsx | 0 .../AnnouncementNotification.tsx | 0 .../notifications/NotificationManager.tsx | 0 .../components/task/SimpleTaskViews.tsx | 0 .../components/task/TaskAssigneesCard.tsx | 0 .../components/task/TaskAttachments.tsx | 0 .../components/task/TaskBasicInfo.tsx | 0 .../components/task/TaskBoardView.tsx | 0 .../components/task/TaskCalendar.tsx | 0 .../components/task/TaskCalendarView.tsx | 0 .../src => src}/components/task/TaskCard.tsx | 0 .../components/task/TaskChannelSelection.tsx | 0 .../components/task/TaskCommentsCard.tsx | 0 .../components/task/TaskCreateHeader.tsx | 0 .../components/task/TaskCreateNavigation.tsx | 0 .../task/TaskDetailFloatingActions.tsx | 0 .../components/task/TaskDetailHeader.tsx | 0 .../components/task/TaskFilterModal.tsx | 0 .../components/task/TaskOverviewCard.tsx | 0 .../components/task/TaskPriorityModal.tsx | 0 .../components/task/TaskProgressCard.tsx | 0 .../components/task/TaskRequirements.tsx | 0 .../components/task/TaskSearchAndFilters.tsx | 0 .../components/task/TaskStatsCards.tsx | 0 .../components/task/TaskStatusModal.tsx | 0 .../components/task/TaskSubtasksCard.tsx | 0 .../components/task/TaskTagsCard.tsx | 0 .../components/task/TaskTeamAssignment.tsx | 0 .../components/task/TaskTimeline.tsx | 0 .../src => src}/components/task/TaskUtils.ts | 0 .../components/task/TaskViewRenderer.tsx | 0 .../components/task/TasksHeader.tsx | 0 .../components/voice/PromptInput.tsx | 0 .../components/voice/VoiceButton.tsx | 0 .../components/voice/VoiceIndicator.tsx | 0 {frontend/src => src}/config/apiKeys.ts | 0 .../src => src}/contexts/ToastContext.tsx | 0 {frontend/src => src}/data/seedData.ts | 0 .../src => src}/hooks/useActionDialog.ts | 0 {frontend/src => src}/hooks/useAnimations.ts | 0 {frontend/src => src}/hooks/useAuth.ts | 0 .../src => src}/hooks/useChannelState.ts | 0 .../src => src}/hooks/useMessageActions.ts | 0 {frontend/src => src}/hooks/useProjects.ts | 0 {frontend/src => src}/hooks/useVoice.ts | 0 .../src => src}/navigation/AppNavigator.tsx | 0 .../src => src}/navigation/AuthNavigator.tsx | 0 .../src => src}/navigation/MainNavigator.tsx | 0 .../src => src}/navigation/TabNavigator.tsx | 0 .../admin/AdminAnnouncementManagement.tsx | 0 .../screens/admin/AdminTaskDashboard.tsx | 0 .../screens/admin/AdminTaskManagement.tsx | 0 .../screens/auth/EmailVerificationScreen.tsx | 0 .../screens/auth/ForgotPasswordScreen.tsx | 0 .../src => src}/screens/auth/LoginScreen.tsx | 0 .../screens/auth/RegisterScreen.tsx | 0 .../screens/auth/ResetPasswordScreen.tsx | 0 .../screens/auth/WelcomeScreen.tsx | 0 .../auth/onboarding/BasicInfoScreen.tsx | 0 .../onboarding/OnboardingCompleteScreen.tsx | 0 .../auth/onboarding/WorkInfoScreen.tsx | 0 .../screens/chats/ChannelDetailScreen.tsx | 0 .../screens/chats/ThreadScreen.tsx | 0 .../screens/main/ActivityScreen.tsx | 0 .../screens/main/ChannelsScreen.tsx | 0 .../screens/main/DashboardScreen.tsx | 0 .../src => src}/screens/main/TasksScreen.tsx | 0 .../screens/onboarding/OnboardingScreen.tsx | 0 .../screens/tasks/TaskCreateScreen.tsx | 0 .../screens/tasks/TaskDetailScreen.tsx | 0 {frontend/src => src}/screens/tasks/index.ts | 0 .../screens/users/UserProfileScreen.tsx | 0 .../src => src}/services/ai/OpenAIService.ts | 0 {frontend/src => src}/services/ai/index.ts | 0 .../services/api/activityService.ts | 0 .../services/api/announcementService.ts | 0 .../src => src}/services/api/authService.ts | 0 .../services/api/channelService.ts | 0 .../src => src}/services/api/fileService.ts | 0 .../services/api/messageService.ts | 0 .../services/api/notificationService.ts | 0 .../services/api/projectService.ts | 0 .../src => src}/services/api/taskService.ts | 0 .../src => src}/services/api/userService.ts | 0 .../src => src}/services/api/voiceService.ts | 0 .../audio/CustomAudioRecorderPlayer.ts | 0 .../services/notificationService.ts | 0 .../services/storage/AsyncStorageService.ts | 0 .../src => src}/services/tokenManager.ts | 0 .../src => src}/services/voice/CustomVoice.ts | 0 .../services/voice/SpeechRecognition.ts | 0 .../services/voice/TextToSpeech.ts | 0 .../src => src}/services/websocketService.ts | 0 .../store/slices/announcementSlice.ts | 0 .../src => src}/store/slices/authSlice.ts | 0 .../src => src}/store/slices/projectSlice.ts | 0 .../src => src}/store/slices/taskSlice.ts | 0 .../src => src}/store/slices/voiceSlice.ts | 0 {frontend/src => src}/store/store.ts | 0 {frontend/src => src}/store/types.ts | 0 .../src => src}/types/announcement.types.ts | 0 {frontend/src => src}/types/api.ts | 0 {frontend/src => src}/types/auth.ts | 0 {frontend/src => src}/types/chat.ts | 0 .../src => src}/types/navigation.types.ts | 0 {frontend/src => src}/types/project.ts | 0 {frontend/src => src}/types/task.types.ts | 0 {frontend/src => src}/types/voice.ts | 0 {frontend/src => src}/utils/animations.ts | 0 {frontend/src => src}/utils/colors.ts | 0 {frontend/src => src}/utils/constants.ts | 0 {frontend/src => src}/utils/envSetup.ts | 0 {frontend/src => src}/utils/helpers.ts | 0 {frontend/src => src}/utils/seedDataLoader.ts | 0 .../src => src}/utils/voiceErrorHandler.ts | 0 .../tailwind.config.js => tailwind.config.js | 0 frontend/tsconfig.json => tsconfig.json | 0 662 files changed, 153 insertions(+), 101915 deletions(-) rename frontend/App.tsx => App.tsx (100%) delete mode 100644 Dockerfile.android delete mode 100644 Dockerfile.metro rename frontend/Gemfile => Gemfile (100%) delete mode 100644 VERIFIED_USER_CREDENTIALS.md rename {frontend/__tests__ => __tests__}/App.test.tsx (100%) rename {frontend/android => android}/app/build.gradle (100%) rename {frontend/android => android}/app/debug.keystore (100%) rename {frontend/android => android}/app/proguard-rules.pro (100%) rename {frontend/android => android}/app/src/debug/AndroidManifest.xml (100%) rename {frontend/android => android}/app/src/main/AndroidManifest.xml (100%) rename {frontend/android => android}/app/src/main/assets/index.android.bundle (100%) rename {frontend/android => android}/app/src/main/java/com/app/MainActivity.kt (100%) rename {frontend/android => android}/app/src/main/java/com/app/MainApplication.kt (100%) rename {frontend/android => android}/app/src/main/java/com/app/VoiceModule.kt (100%) rename {frontend/android => android}/app/src/main/java/com/app/VoicePackage.kt (100%) rename {frontend/android => android}/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_backiconmask.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-mdpi/src_assets_icons_google.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-mdpi/src_assets_icons_logo.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png (100%) rename {frontend/android => android}/app/src/main/res/drawable/rn_edit_text_material.xml (100%) rename {frontend/android => android}/app/src/main/res/mipmap-hdpi/ic_launcher.png (100%) rename {frontend/android => android}/app/src/main/res/mipmap-hdpi/ic_launcher_round.png (100%) rename {frontend/android => android}/app/src/main/res/mipmap-mdpi/ic_launcher.png (100%) rename {frontend/android => android}/app/src/main/res/mipmap-mdpi/ic_launcher_round.png (100%) rename {frontend/android => android}/app/src/main/res/mipmap-xhdpi/ic_launcher.png (100%) rename {frontend/android => android}/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png (100%) rename {frontend/android => android}/app/src/main/res/mipmap-xxhdpi/ic_launcher.png (100%) rename {frontend/android => android}/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png (100%) rename {frontend/android => android}/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png (100%) rename {frontend/android => android}/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png (100%) rename {frontend/android => android}/app/src/main/res/raw/keep.xml (100%) rename {frontend/android => android}/app/src/main/res/values/strings.xml (100%) rename {frontend/android => android}/app/src/main/res/values/styles.xml (100%) rename {frontend/android => android}/build.gradle (100%) rename {frontend/android => android}/gradle.properties (100%) rename {frontend/android => android}/gradle/wrapper/gradle-wrapper.jar (100%) rename {frontend/android => android}/gradle/wrapper/gradle-wrapper.properties (100%) rename {frontend/android => android}/gradlew (100%) rename {frontend/android => android}/gradlew.bat (100%) rename {frontend/android => android}/settings.gradle (100%) rename frontend/app.json => app.json (100%) rename frontend/babel.config.js => babel.config.js (100%) delete mode 100644 backend/README.md delete mode 100644 backend/SUCCESS_CRITERIA.md delete mode 100644 backend/SUCCESS_CRITERIA_VALIDATION.md delete mode 100644 backend/dist/ai/commands/AICommandParser.d.ts delete mode 100644 backend/dist/ai/commands/AICommandParser.d.ts.map delete mode 100644 backend/dist/ai/commands/AICommandParser.js delete mode 100644 backend/dist/ai/commands/AICommandParser.js.map delete mode 100644 backend/dist/ai/context/ContextManager.d.ts delete mode 100644 backend/dist/ai/context/ContextManager.d.ts.map delete mode 100644 backend/dist/ai/context/ContextManager.js delete mode 100644 backend/dist/ai/context/ContextManager.js.map delete mode 100644 backend/dist/ai/context/EntityResolver.d.ts delete mode 100644 backend/dist/ai/context/EntityResolver.d.ts.map delete mode 100644 backend/dist/ai/context/EntityResolver.js delete mode 100644 backend/dist/ai/context/EntityResolver.js.map delete mode 100644 backend/dist/ai/context/TemporalProcessor.d.ts delete mode 100644 backend/dist/ai/context/TemporalProcessor.d.ts.map delete mode 100644 backend/dist/ai/context/TemporalProcessor.js delete mode 100644 backend/dist/ai/context/TemporalProcessor.js.map delete mode 100644 backend/dist/ai/execution/DependencyResolver.d.ts delete mode 100644 backend/dist/ai/execution/DependencyResolver.d.ts.map delete mode 100644 backend/dist/ai/execution/DependencyResolver.js delete mode 100644 backend/dist/ai/execution/DependencyResolver.js.map delete mode 100644 backend/dist/ai/execution/MultiActionExecutor.d.ts delete mode 100644 backend/dist/ai/execution/MultiActionExecutor.d.ts.map delete mode 100644 backend/dist/ai/execution/MultiActionExecutor.js delete mode 100644 backend/dist/ai/execution/MultiActionExecutor.js.map delete mode 100644 backend/dist/api/index.d.ts delete mode 100644 backend/dist/api/index.d.ts.map delete mode 100644 backend/dist/api/index.js delete mode 100644 backend/dist/api/index.js.map delete mode 100644 backend/dist/api/routes/ActivityRoutes.d.ts delete mode 100644 backend/dist/api/routes/ActivityRoutes.d.ts.map delete mode 100644 backend/dist/api/routes/ActivityRoutes.js delete mode 100644 backend/dist/api/routes/ActivityRoutes.js.map delete mode 100644 backend/dist/api/routes/AnnouncementRoutes.d.ts delete mode 100644 backend/dist/api/routes/AnnouncementRoutes.d.ts.map delete mode 100644 backend/dist/api/routes/AnnouncementRoutes.js delete mode 100644 backend/dist/api/routes/AnnouncementRoutes.js.map delete mode 100644 backend/dist/api/routes/ChannelRoutes.d.ts delete mode 100644 backend/dist/api/routes/ChannelRoutes.d.ts.map delete mode 100644 backend/dist/api/routes/ChannelRoutes.js delete mode 100644 backend/dist/api/routes/ChannelRoutes.js.map delete mode 100644 backend/dist/api/routes/DocsRoutes.d.ts delete mode 100644 backend/dist/api/routes/DocsRoutes.d.ts.map delete mode 100644 backend/dist/api/routes/DocsRoutes.js delete mode 100644 backend/dist/api/routes/DocsRoutes.js.map delete mode 100644 backend/dist/api/routes/MessageRoutes.d.ts delete mode 100644 backend/dist/api/routes/MessageRoutes.d.ts.map delete mode 100644 backend/dist/api/routes/MessageRoutes.js delete mode 100644 backend/dist/api/routes/MessageRoutes.js.map delete mode 100644 backend/dist/api/routes/NotificationRoutes.d.ts delete mode 100644 backend/dist/api/routes/NotificationRoutes.d.ts.map delete mode 100644 backend/dist/api/routes/NotificationRoutes.js delete mode 100644 backend/dist/api/routes/NotificationRoutes.js.map delete mode 100644 backend/dist/api/routes/TaskRoutes.d.ts delete mode 100644 backend/dist/api/routes/TaskRoutes.d.ts.map delete mode 100644 backend/dist/api/routes/TaskRoutes.js delete mode 100644 backend/dist/api/routes/TaskRoutes.js.map delete mode 100644 backend/dist/api/routes/UserRoutes.d.ts delete mode 100644 backend/dist/api/routes/UserRoutes.d.ts.map delete mode 100644 backend/dist/api/routes/UserRoutes.js delete mode 100644 backend/dist/api/routes/UserRoutes.js.map delete mode 100644 backend/dist/auth/jwt.d.ts delete mode 100644 backend/dist/auth/jwt.d.ts.map delete mode 100644 backend/dist/auth/jwt.js delete mode 100644 backend/dist/auth/jwt.js.map delete mode 100644 backend/dist/auth/middleware.d.ts delete mode 100644 backend/dist/auth/middleware.d.ts.map delete mode 100644 backend/dist/auth/middleware.js delete mode 100644 backend/dist/auth/middleware.js.map delete mode 100644 backend/dist/auth/routes.d.ts delete mode 100644 backend/dist/auth/routes.d.ts.map delete mode 100644 backend/dist/auth/routes.js delete mode 100644 backend/dist/auth/routes.js.map delete mode 100644 backend/dist/config/database.d.ts delete mode 100644 backend/dist/config/database.d.ts.map delete mode 100644 backend/dist/config/database.js delete mode 100644 backend/dist/config/database.js.map delete mode 100644 backend/dist/config/index.d.ts delete mode 100644 backend/dist/config/index.d.ts.map delete mode 100644 backend/dist/config/index.js delete mode 100644 backend/dist/config/index.js.map delete mode 100644 backend/dist/config/redis.d.ts delete mode 100644 backend/dist/config/redis.d.ts.map delete mode 100644 backend/dist/config/redis.js delete mode 100644 backend/dist/config/redis.js.map delete mode 100644 backend/dist/db/ActivityRepository.d.ts delete mode 100644 backend/dist/db/ActivityRepository.d.ts.map delete mode 100644 backend/dist/db/ActivityRepository.js delete mode 100644 backend/dist/db/ActivityRepository.js.map delete mode 100644 backend/dist/db/AnnouncementRepository.d.ts delete mode 100644 backend/dist/db/AnnouncementRepository.d.ts.map delete mode 100644 backend/dist/db/AnnouncementRepository.js delete mode 100644 backend/dist/db/AnnouncementRepository.js.map delete mode 100644 backend/dist/db/BaseRepository.d.ts delete mode 100644 backend/dist/db/BaseRepository.d.ts.map delete mode 100644 backend/dist/db/BaseRepository.js delete mode 100644 backend/dist/db/BaseRepository.js.map delete mode 100644 backend/dist/db/ChannelRepository.d.ts delete mode 100644 backend/dist/db/ChannelRepository.d.ts.map delete mode 100644 backend/dist/db/ChannelRepository.js delete mode 100644 backend/dist/db/ChannelRepository.js.map delete mode 100644 backend/dist/db/FileRepository.d.ts delete mode 100644 backend/dist/db/FileRepository.d.ts.map delete mode 100644 backend/dist/db/FileRepository.js delete mode 100644 backend/dist/db/FileRepository.js.map delete mode 100644 backend/dist/db/MessageRepository.d.ts delete mode 100644 backend/dist/db/MessageRepository.d.ts.map delete mode 100644 backend/dist/db/MessageRepository.js delete mode 100644 backend/dist/db/MessageRepository.js.map delete mode 100644 backend/dist/db/TaskRepository.d.ts delete mode 100644 backend/dist/db/TaskRepository.d.ts.map delete mode 100644 backend/dist/db/TaskRepository.js delete mode 100644 backend/dist/db/TaskRepository.js.map delete mode 100644 backend/dist/db/UserRepository.d.ts delete mode 100644 backend/dist/db/UserRepository.d.ts.map delete mode 100644 backend/dist/db/UserRepository.js delete mode 100644 backend/dist/db/UserRepository.js.map delete mode 100644 backend/dist/db/index.d.ts delete mode 100644 backend/dist/db/index.d.ts.map delete mode 100644 backend/dist/db/index.js delete mode 100644 backend/dist/db/index.js.map delete mode 100644 backend/dist/db/migrator.d.ts delete mode 100644 backend/dist/db/migrator.d.ts.map delete mode 100644 backend/dist/db/migrator.js delete mode 100644 backend/dist/db/migrator.js.map delete mode 100644 backend/dist/files/management/FileMetadataManager.d.ts delete mode 100644 backend/dist/files/management/FileMetadataManager.d.ts.map delete mode 100644 backend/dist/files/management/FileMetadataManager.js delete mode 100644 backend/dist/files/management/FileMetadataManager.js.map delete mode 100644 backend/dist/files/management/S3FileManager.d.ts delete mode 100644 backend/dist/files/management/S3FileManager.d.ts.map delete mode 100644 backend/dist/files/management/S3FileManager.js delete mode 100644 backend/dist/files/management/S3FileManager.js.map delete mode 100644 backend/dist/files/upload/FileCommandParser.d.ts delete mode 100644 backend/dist/files/upload/FileCommandParser.d.ts.map delete mode 100644 backend/dist/files/upload/FileCommandParser.js delete mode 100644 backend/dist/files/upload/FileCommandParser.js.map delete mode 100644 backend/dist/files/upload/VoiceFileUploadService.d.ts delete mode 100644 backend/dist/files/upload/VoiceFileUploadService.d.ts.map delete mode 100644 backend/dist/files/upload/VoiceFileUploadService.js delete mode 100644 backend/dist/files/upload/VoiceFileUploadService.js.map delete mode 100644 backend/dist/index.d.ts delete mode 100644 backend/dist/index.d.ts.map delete mode 100644 backend/dist/index.js delete mode 100644 backend/dist/index.js.map delete mode 100644 backend/dist/performance/caching/RedisCacheManager.d.ts delete mode 100644 backend/dist/performance/caching/RedisCacheManager.d.ts.map delete mode 100644 backend/dist/performance/caching/RedisCacheManager.js delete mode 100644 backend/dist/performance/caching/RedisCacheManager.js.map delete mode 100644 backend/dist/performance/monitoring/PerformanceMonitor.d.ts delete mode 100644 backend/dist/performance/monitoring/PerformanceMonitor.d.ts.map delete mode 100644 backend/dist/performance/monitoring/PerformanceMonitor.js delete mode 100644 backend/dist/performance/monitoring/PerformanceMonitor.js.map delete mode 100644 backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.d.ts delete mode 100644 backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.d.ts.map delete mode 100644 backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.js delete mode 100644 backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.js.map delete mode 100644 backend/dist/realtime/broadcasting/ExecutionEventManager.d.ts delete mode 100644 backend/dist/realtime/broadcasting/ExecutionEventManager.d.ts.map delete mode 100644 backend/dist/realtime/broadcasting/ExecutionEventManager.js delete mode 100644 backend/dist/realtime/broadcasting/ExecutionEventManager.js.map delete mode 100644 backend/dist/realtime/broadcasting/NotificationIntegration.d.ts delete mode 100644 backend/dist/realtime/broadcasting/NotificationIntegration.d.ts.map delete mode 100644 backend/dist/realtime/broadcasting/NotificationIntegration.js delete mode 100644 backend/dist/realtime/broadcasting/NotificationIntegration.js.map delete mode 100644 backend/dist/realtime/broadcasting/ProgressBroadcaster.d.ts delete mode 100644 backend/dist/realtime/broadcasting/ProgressBroadcaster.d.ts.map delete mode 100644 backend/dist/realtime/broadcasting/ProgressBroadcaster.js delete mode 100644 backend/dist/realtime/broadcasting/ProgressBroadcaster.js.map delete mode 100644 backend/dist/scripts/cleanup-unverified-users.d.ts delete mode 100644 backend/dist/scripts/cleanup-unverified-users.d.ts.map delete mode 100644 backend/dist/scripts/cleanup-unverified-users.js delete mode 100644 backend/dist/scripts/cleanup-unverified-users.js.map delete mode 100644 backend/dist/scripts/phase2-implementation-review.d.ts delete mode 100644 backend/dist/scripts/phase2-implementation-review.d.ts.map delete mode 100644 backend/dist/scripts/phase2-implementation-review.js delete mode 100644 backend/dist/scripts/phase2-implementation-review.js.map delete mode 100644 backend/dist/scripts/phase2-optimization.d.ts delete mode 100644 backend/dist/scripts/phase2-optimization.d.ts.map delete mode 100644 backend/dist/scripts/phase2-optimization.js delete mode 100644 backend/dist/scripts/phase2-optimization.js.map delete mode 100644 backend/dist/scripts/seed-channels.d.ts delete mode 100644 backend/dist/scripts/seed-channels.d.ts.map delete mode 100644 backend/dist/scripts/seed-channels.js delete mode 100644 backend/dist/scripts/seed-channels.js.map delete mode 100644 backend/dist/scripts/seed.d.ts delete mode 100644 backend/dist/scripts/seed.d.ts.map delete mode 100644 backend/dist/scripts/seed.js delete mode 100644 backend/dist/scripts/seed.js.map delete mode 100644 backend/dist/scripts/verify-email-token.d.ts delete mode 100644 backend/dist/scripts/verify-email-token.d.ts.map delete mode 100644 backend/dist/scripts/verify-email-token.js delete mode 100644 backend/dist/scripts/verify-email-token.js.map delete mode 100644 backend/dist/security/SecurityManager.d.ts delete mode 100644 backend/dist/security/SecurityManager.d.ts.map delete mode 100644 backend/dist/security/SecurityManager.js delete mode 100644 backend/dist/security/SecurityManager.js.map delete mode 100644 backend/dist/server.d.ts delete mode 100644 backend/dist/server.d.ts.map delete mode 100644 backend/dist/server.js delete mode 100644 backend/dist/server.js.map delete mode 100644 backend/dist/services/CacheService.d.ts delete mode 100644 backend/dist/services/CacheService.d.ts.map delete mode 100644 backend/dist/services/CacheService.js delete mode 100644 backend/dist/services/CacheService.js.map delete mode 100644 backend/dist/services/EmailService.d.ts delete mode 100644 backend/dist/services/EmailService.d.ts.map delete mode 100644 backend/dist/services/EmailService.js delete mode 100644 backend/dist/services/EmailService.js.map delete mode 100644 backend/dist/services/index.d.ts delete mode 100644 backend/dist/services/index.d.ts.map delete mode 100644 backend/dist/services/index.js delete mode 100644 backend/dist/services/index.js.map delete mode 100644 backend/dist/utils/cache-decorators.d.ts delete mode 100644 backend/dist/utils/cache-decorators.d.ts.map delete mode 100644 backend/dist/utils/cache-decorators.js delete mode 100644 backend/dist/utils/cache-decorators.js.map delete mode 100644 backend/dist/utils/errors.d.ts delete mode 100644 backend/dist/utils/errors.d.ts.map delete mode 100644 backend/dist/utils/errors.js delete mode 100644 backend/dist/utils/errors.js.map delete mode 100644 backend/dist/utils/logger.d.ts delete mode 100644 backend/dist/utils/logger.d.ts.map delete mode 100644 backend/dist/utils/logger.js delete mode 100644 backend/dist/utils/logger.js.map delete mode 100644 backend/dist/utils/performance.d.ts delete mode 100644 backend/dist/utils/performance.d.ts.map delete mode 100644 backend/dist/utils/performance.js delete mode 100644 backend/dist/utils/performance.js.map delete mode 100644 backend/dist/utils/validation.d.ts delete mode 100644 backend/dist/utils/validation.d.ts.map delete mode 100644 backend/dist/utils/validation.js delete mode 100644 backend/dist/utils/validation.js.map delete mode 100644 backend/dist/voice/VoiceProcessingService.d.ts delete mode 100644 backend/dist/voice/VoiceProcessingService.d.ts.map delete mode 100644 backend/dist/voice/VoiceProcessingService.js delete mode 100644 backend/dist/voice/VoiceProcessingService.js.map delete mode 100644 backend/dist/voice/stream/AudioPreprocessor.d.ts delete mode 100644 backend/dist/voice/stream/AudioPreprocessor.d.ts.map delete mode 100644 backend/dist/voice/stream/AudioPreprocessor.js delete mode 100644 backend/dist/voice/stream/AudioPreprocessor.js.map delete mode 100644 backend/dist/voice/stream/AudioStreamManager.d.ts delete mode 100644 backend/dist/voice/stream/AudioStreamManager.d.ts.map delete mode 100644 backend/dist/voice/stream/AudioStreamManager.js delete mode 100644 backend/dist/voice/stream/AudioStreamManager.js.map delete mode 100644 backend/dist/voice/stream/CircularBuffer.d.ts delete mode 100644 backend/dist/voice/stream/CircularBuffer.d.ts.map delete mode 100644 backend/dist/voice/stream/CircularBuffer.js delete mode 100644 backend/dist/voice/stream/CircularBuffer.js.map delete mode 100644 backend/dist/voice/stream/VoiceActivityDetector.d.ts delete mode 100644 backend/dist/voice/stream/VoiceActivityDetector.d.ts.map delete mode 100644 backend/dist/voice/stream/VoiceActivityDetector.js delete mode 100644 backend/dist/voice/stream/VoiceActivityDetector.js.map delete mode 100644 backend/dist/voice/transcription/TranscriptionCache.d.ts delete mode 100644 backend/dist/voice/transcription/TranscriptionCache.d.ts.map delete mode 100644 backend/dist/voice/transcription/TranscriptionCache.js delete mode 100644 backend/dist/voice/transcription/TranscriptionCache.js.map delete mode 100644 backend/dist/voice/transcription/WhisperConnectionPool.d.ts delete mode 100644 backend/dist/voice/transcription/WhisperConnectionPool.d.ts.map delete mode 100644 backend/dist/voice/transcription/WhisperConnectionPool.js delete mode 100644 backend/dist/voice/transcription/WhisperConnectionPool.js.map delete mode 100644 backend/dist/voice/transcription/WhisperService.d.ts delete mode 100644 backend/dist/voice/transcription/WhisperService.d.ts.map delete mode 100644 backend/dist/voice/transcription/WhisperService.js delete mode 100644 backend/dist/voice/transcription/WhisperService.js.map delete mode 100644 backend/dist/voice/types.d.ts delete mode 100644 backend/dist/voice/types.d.ts.map delete mode 100644 backend/dist/voice/types.js delete mode 100644 backend/dist/voice/types.js.map delete mode 100644 backend/dist/websocket/EnhancedSocketManager.d.ts delete mode 100644 backend/dist/websocket/EnhancedSocketManager.d.ts.map delete mode 100644 backend/dist/websocket/EnhancedSocketManager.js delete mode 100644 backend/dist/websocket/EnhancedSocketManager.js.map delete mode 100644 backend/dist/websocket/SocketManager.d.ts delete mode 100644 backend/dist/websocket/SocketManager.d.ts.map delete mode 100644 backend/dist/websocket/SocketManager.js delete mode 100644 backend/dist/websocket/SocketManager.js.map delete mode 100644 backend/dist/websocket/index.d.ts delete mode 100644 backend/dist/websocket/index.d.ts.map delete mode 100644 backend/dist/websocket/index.js delete mode 100644 backend/dist/websocket/index.js.map delete mode 100644 backend/dist/websocket/types.d.ts delete mode 100644 backend/dist/websocket/types.d.ts.map delete mode 100644 backend/dist/websocket/types.js delete mode 100644 backend/dist/websocket/types.js.map delete mode 100644 backend/dist/websocket/utils.d.ts delete mode 100644 backend/dist/websocket/utils.d.ts.map delete mode 100644 backend/dist/websocket/utils.js delete mode 100644 backend/dist/websocket/utils.js.map delete mode 100644 backend/docker/Dockerfile.dev delete mode 100644 backend/docker/docker-compose.dev.yml delete mode 100644 backend/docker/postgres/init.sql delete mode 100644 backend/docker/redis/redis.conf delete mode 100644 backend/jest.config.js delete mode 100644 backend/migrations/1640995200000_create_users.sql delete mode 100644 backend/migrations/1640995201000_create_categories.sql delete mode 100644 backend/migrations/1640995202000_create_channels.sql delete mode 100644 backend/migrations/1640995203000_create_tasks.sql delete mode 100644 backend/migrations/1640995204000_create_messages_notifications.sql delete mode 100644 backend/migrations/1640995205000_create_voice_and_relationships.sql delete mode 100644 backend/migrations/1724121000000_increase_token_column_length.sql delete mode 100644 backend/migrations/1724122000000_create_file_management_tables.sql delete mode 100644 backend/migrations/20250101000001_create_announcements_table.sql delete mode 100644 backend/migrations/20250101000002_create_activities_table.sql delete mode 100644 backend/package-lock.json delete mode 100644 backend/package.json delete mode 100644 backend/run-seed.js delete mode 100644 backend/server.log delete mode 100644 backend/src/ai/commands/AICommandParser.ts delete mode 100644 backend/src/ai/context/ContextManager.ts delete mode 100644 backend/src/ai/context/EntityResolver.ts delete mode 100644 backend/src/ai/context/TemporalProcessor.ts delete mode 100644 backend/src/ai/execution/DependencyResolver.ts delete mode 100644 backend/src/ai/execution/MultiActionExecutor.ts delete mode 100644 backend/src/api/index.ts delete mode 100644 backend/src/api/routes/ActivityRoutes.ts delete mode 100644 backend/src/api/routes/AnnouncementRoutes.ts delete mode 100644 backend/src/api/routes/ChannelRoutes.ts delete mode 100644 backend/src/api/routes/DocsRoutes.ts delete mode 100644 backend/src/api/routes/MessageRoutes.ts delete mode 100644 backend/src/api/routes/NotificationRoutes.ts delete mode 100644 backend/src/api/routes/TaskRoutes.ts delete mode 100644 backend/src/api/routes/UserRoutes.ts delete mode 100644 backend/src/auth/jwt.ts delete mode 100644 backend/src/auth/middleware.ts delete mode 100644 backend/src/auth/routes.ts delete mode 100644 backend/src/config/database.ts delete mode 100644 backend/src/config/index.ts delete mode 100644 backend/src/config/redis.ts delete mode 100644 backend/src/db/ActivityRepository.ts delete mode 100644 backend/src/db/AnnouncementRepository.ts delete mode 100644 backend/src/db/BaseRepository.ts delete mode 100644 backend/src/db/ChannelRepository.ts delete mode 100644 backend/src/db/FileRepository.ts delete mode 100644 backend/src/db/MessageRepository.ts delete mode 100644 backend/src/db/TaskRepository.ts delete mode 100644 backend/src/db/UserRepository.ts delete mode 100644 backend/src/db/index.ts delete mode 100644 backend/src/db/migrator.ts delete mode 100644 backend/src/files/management/FileMetadataManager.ts delete mode 100644 backend/src/files/management/S3FileManager.ts delete mode 100644 backend/src/files/upload/FileCommandParser.ts delete mode 100644 backend/src/files/upload/VoiceFileUploadService.ts delete mode 100644 backend/src/index.ts delete mode 100644 backend/src/performance/caching/RedisCacheManager.ts delete mode 100644 backend/src/performance/monitoring/PerformanceMonitor.ts delete mode 100644 backend/src/realtime/broadcasting/EntityUpdateBroadcaster.ts delete mode 100644 backend/src/realtime/broadcasting/ExecutionEventManager.ts delete mode 100644 backend/src/realtime/broadcasting/NotificationIntegration.ts delete mode 100644 backend/src/realtime/broadcasting/ProgressBroadcaster.ts delete mode 100644 backend/src/scripts/cleanup-unverified-users.ts delete mode 100644 backend/src/scripts/phase2-implementation-review.ts delete mode 100644 backend/src/scripts/phase2-optimization.ts delete mode 100644 backend/src/scripts/seed-channels.ts delete mode 100644 backend/src/scripts/seed.ts delete mode 100644 backend/src/scripts/verify-email-token.ts delete mode 100644 backend/src/security/SecurityManager.ts delete mode 100644 backend/src/server.ts delete mode 100644 backend/src/services/CacheService.ts delete mode 100644 backend/src/services/EmailService.ts delete mode 100644 backend/src/services/index.ts delete mode 100644 backend/src/utils/cache-decorators.ts delete mode 100644 backend/src/utils/errors.ts delete mode 100644 backend/src/utils/logger.ts delete mode 100644 backend/src/utils/performance.ts delete mode 100644 backend/src/utils/validation.ts delete mode 100644 backend/src/voice/VoiceProcessingService.ts delete mode 100644 backend/src/voice/stream/AudioPreprocessor.ts delete mode 100644 backend/src/voice/stream/AudioStreamManager.ts delete mode 100644 backend/src/voice/stream/CircularBuffer.ts delete mode 100644 backend/src/voice/stream/VoiceActivityDetector.ts delete mode 100644 backend/src/voice/transcription/TranscriptionCache.ts delete mode 100644 backend/src/voice/transcription/WhisperConnectionPool.ts delete mode 100644 backend/src/voice/transcription/WhisperService.ts delete mode 100644 backend/src/voice/types.ts delete mode 100644 backend/src/websocket/EnhancedSocketManager.ts delete mode 100644 backend/src/websocket/SocketManager.ts delete mode 100644 backend/src/websocket/index.ts delete mode 100644 backend/src/websocket/types.ts delete mode 100644 backend/src/websocket/utils.ts delete mode 100755 backend/start-dev.sh delete mode 100644 backend/tests/api/routes/TaskRoutes.test.d.ts delete mode 100644 backend/tests/api/routes/TaskRoutes.test.d.ts.map delete mode 100644 backend/tests/api/routes/TaskRoutes.test.js delete mode 100644 backend/tests/api/routes/TaskRoutes.test.js.map delete mode 100644 backend/tests/api/routes/TaskRoutes.test.ts delete mode 100644 backend/tests/auth/jwt.test.d.ts delete mode 100644 backend/tests/auth/jwt.test.d.ts.map delete mode 100644 backend/tests/auth/jwt.test.js delete mode 100644 backend/tests/auth/jwt.test.js.map delete mode 100644 backend/tests/auth/jwt.test.ts delete mode 100644 backend/tests/auth/middleware.test.d.ts delete mode 100644 backend/tests/auth/middleware.test.d.ts.map delete mode 100644 backend/tests/auth/middleware.test.js delete mode 100644 backend/tests/auth/middleware.test.js.map delete mode 100644 backend/tests/auth/middleware.test.ts delete mode 100644 backend/tests/db/BaseRepository.test.d.ts delete mode 100644 backend/tests/db/BaseRepository.test.d.ts.map delete mode 100644 backend/tests/db/BaseRepository.test.js delete mode 100644 backend/tests/db/BaseRepository.test.js.map delete mode 100644 backend/tests/db/BaseRepository.test.ts delete mode 100644 backend/tests/integration/VoiceCommandIntegration.test.ts delete mode 100644 backend/tests/integration/basic.test.d.ts delete mode 100644 backend/tests/integration/basic.test.d.ts.map delete mode 100644 backend/tests/integration/basic.test.js delete mode 100644 backend/tests/integration/basic.test.js.map delete mode 100644 backend/tests/integration/basic.test.ts delete mode 100644 backend/tests/performance/LoadTest.ts delete mode 100644 backend/tests/setup.d.ts delete mode 100644 backend/tests/setup.d.ts.map delete mode 100644 backend/tests/setup.js delete mode 100644 backend/tests/setup.js.map delete mode 100644 backend/tests/setup.ts delete mode 100644 backend/tests/setup/customMatchers.ts delete mode 100644 backend/tests/setup/jest.setup.ts delete mode 100644 backend/tests/unit/services/OpenAIService.test.ts delete mode 100644 backend/tsconfig.json delete mode 100644 docker-compose.dev-only.yml delete mode 100644 docker-compose.windows.yml rename frontend/env.d.ts => env.d.ts (100%) delete mode 100644 frontend/package.json rename frontend/global.css => global.css (100%) rename frontend/index.js => index.js (100%) rename {frontend/ios => ios}/.xcode.env (100%) rename {frontend/ios => ios}/Podfile (100%) rename {frontend/ios => ios}/app.xcodeproj/project.pbxproj (100%) rename {frontend/ios => ios}/app.xcodeproj/xcshareddata/xcschemes/app.xcscheme (100%) rename {frontend/ios => ios}/app/AppDelegate.swift (100%) rename {frontend/ios => ios}/app/Images.xcassets/AppIcon.appiconset/Contents.json (100%) rename {frontend/ios => ios}/app/Images.xcassets/Contents.json (100%) rename {frontend/ios => ios}/app/Info.plist (100%) rename {frontend/ios => ios}/app/LaunchScreen.storyboard (100%) rename {frontend/ios => ios}/app/PrivacyInfo.xcprivacy (100%) rename frontend/jest.config.js => jest.config.js (100%) rename frontend/metro.config.js => metro.config.js (100%) rename frontend/nativewind-env.d.ts => nativewind-env.d.ts (100%) rename frontend/package-lock.json => package-lock.json (100%) rename {frontend/src => src}/assets/icons/google.png (100%) rename {frontend/src => src}/assets/icons/history.png (100%) rename {frontend/src => src}/assets/icons/logo.png (100%) rename {frontend/src => src}/assets/images/sample_home.jpg (100%) rename {frontend/src => src}/components/channel/CategoryFilterModal.tsx (100%) rename {frontend/src => src}/components/channel/ChannelTaskIntegration.tsx (100%) rename {frontend/src => src}/components/channel/CreateChannelModal.tsx (100%) rename {frontend/src => src}/components/channel/MemberSelectorModal.tsx (100%) rename {frontend/src => src}/components/chat/ChannelActions.tsx (100%) rename {frontend/src => src}/components/chat/ChannelHeader.tsx (100%) rename {frontend/src => src}/components/chat/ChannelInput.tsx (100%) rename {frontend/src => src}/components/chat/ChannelInputContainer.tsx (100%) rename {frontend/src => src}/components/chat/ChannelModalsContainer.tsx (100%) rename {frontend/src => src}/components/chat/ChannelTypingManager.tsx (100%) rename {frontend/src => src}/components/chat/ChatMessage.tsx (100%) rename {frontend/src => src}/components/chat/EmojiPicker.tsx (100%) rename {frontend/src => src}/components/chat/EmojiReactionPicker.tsx (100%) rename {frontend/src => src}/components/chat/KeyPointsModal.tsx (100%) rename {frontend/src => src}/components/chat/MeetingSummaryModal.tsx (100%) rename {frontend/src => src}/components/chat/MessageList.tsx (100%) rename {frontend/src => src}/components/chat/MessageListContainer.tsx (100%) rename {frontend/src => src}/components/chat/SimpleTypingIndicators.tsx (100%) rename {frontend/src => src}/components/chat/SmartMentionInput.tsx (100%) rename {frontend/src => src}/components/chat/ThreadedMessage.tsx (100%) rename {frontend/src => src}/components/chat/ThreadedMessageList.tsx (100%) rename {frontend/src => src}/components/chat/TypingIndicators.tsx (100%) rename {frontend/src => src}/components/chat/index.ts (100%) rename {frontend/src => src}/components/common/ActionDialog.tsx (100%) rename {frontend/src => src}/components/common/ActionSheet.tsx (100%) rename {frontend/src => src}/components/common/Avatar.tsx (100%) rename {frontend/src => src}/components/common/Botton.tsx (100%) rename {frontend/src => src}/components/common/ChannelCard.tsx (100%) rename {frontend/src => src}/components/common/ConfirmationModal.tsx (100%) rename {frontend/src => src}/components/common/CurvedBackground/CurvedBackground.tsx (100%) rename {frontend/src => src}/components/common/CustomPicker.tsx (100%) rename {frontend/src => src}/components/common/ErrorBoundary.tsx (100%) rename {frontend/src => src}/components/common/Input.tsx (100%) rename {frontend/src => src}/components/common/LoadingSpinner.tsx (100%) rename {frontend/src => src}/components/common/PermissionDialog.tsx (100%) rename {frontend/src => src}/components/common/SafeAreaWrapper.tsx (100%) rename {frontend/src => src}/components/common/StepIndicator.tsx (100%) rename {frontend/src => src}/components/common/Toast.tsx (100%) rename {frontend/src => src}/components/common/UIProvider.tsx (100%) rename {frontend/src => src}/components/common/index.ts (100%) rename {frontend/src => src}/components/forms/FormField.tsx (100%) rename {frontend/src => src}/components/forms/LoginForm.tsx (100%) rename {frontend/src => src}/components/forms/RegisterForm.tsx (100%) rename {frontend/src => src}/components/notifications/AnnouncementNotification.tsx (100%) rename {frontend/src => src}/components/notifications/NotificationManager.tsx (100%) rename {frontend/src => src}/components/task/SimpleTaskViews.tsx (100%) rename {frontend/src => src}/components/task/TaskAssigneesCard.tsx (100%) rename {frontend/src => src}/components/task/TaskAttachments.tsx (100%) rename {frontend/src => src}/components/task/TaskBasicInfo.tsx (100%) rename {frontend/src => src}/components/task/TaskBoardView.tsx (100%) rename {frontend/src => src}/components/task/TaskCalendar.tsx (100%) rename {frontend/src => src}/components/task/TaskCalendarView.tsx (100%) rename {frontend/src => src}/components/task/TaskCard.tsx (100%) rename {frontend/src => src}/components/task/TaskChannelSelection.tsx (100%) rename {frontend/src => src}/components/task/TaskCommentsCard.tsx (100%) rename {frontend/src => src}/components/task/TaskCreateHeader.tsx (100%) rename {frontend/src => src}/components/task/TaskCreateNavigation.tsx (100%) rename {frontend/src => src}/components/task/TaskDetailFloatingActions.tsx (100%) rename {frontend/src => src}/components/task/TaskDetailHeader.tsx (100%) rename {frontend/src => src}/components/task/TaskFilterModal.tsx (100%) rename {frontend/src => src}/components/task/TaskOverviewCard.tsx (100%) rename {frontend/src => src}/components/task/TaskPriorityModal.tsx (100%) rename {frontend/src => src}/components/task/TaskProgressCard.tsx (100%) rename {frontend/src => src}/components/task/TaskRequirements.tsx (100%) rename {frontend/src => src}/components/task/TaskSearchAndFilters.tsx (100%) rename {frontend/src => src}/components/task/TaskStatsCards.tsx (100%) rename {frontend/src => src}/components/task/TaskStatusModal.tsx (100%) rename {frontend/src => src}/components/task/TaskSubtasksCard.tsx (100%) rename {frontend/src => src}/components/task/TaskTagsCard.tsx (100%) rename {frontend/src => src}/components/task/TaskTeamAssignment.tsx (100%) rename {frontend/src => src}/components/task/TaskTimeline.tsx (100%) rename {frontend/src => src}/components/task/TaskUtils.ts (100%) rename {frontend/src => src}/components/task/TaskViewRenderer.tsx (100%) rename {frontend/src => src}/components/task/TasksHeader.tsx (100%) rename {frontend/src => src}/components/voice/PromptInput.tsx (100%) rename {frontend/src => src}/components/voice/VoiceButton.tsx (100%) rename {frontend/src => src}/components/voice/VoiceIndicator.tsx (100%) rename {frontend/src => src}/config/apiKeys.ts (100%) rename {frontend/src => src}/contexts/ToastContext.tsx (100%) rename {frontend/src => src}/data/seedData.ts (100%) rename {frontend/src => src}/hooks/useActionDialog.ts (100%) rename {frontend/src => src}/hooks/useAnimations.ts (100%) rename {frontend/src => src}/hooks/useAuth.ts (100%) rename {frontend/src => src}/hooks/useChannelState.ts (100%) rename {frontend/src => src}/hooks/useMessageActions.ts (100%) rename {frontend/src => src}/hooks/useProjects.ts (100%) rename {frontend/src => src}/hooks/useVoice.ts (100%) rename {frontend/src => src}/navigation/AppNavigator.tsx (100%) rename {frontend/src => src}/navigation/AuthNavigator.tsx (100%) rename {frontend/src => src}/navigation/MainNavigator.tsx (100%) rename {frontend/src => src}/navigation/TabNavigator.tsx (100%) rename {frontend/src => src}/screens/admin/AdminAnnouncementManagement.tsx (100%) rename {frontend/src => src}/screens/admin/AdminTaskDashboard.tsx (100%) rename {frontend/src => src}/screens/admin/AdminTaskManagement.tsx (100%) rename {frontend/src => src}/screens/auth/EmailVerificationScreen.tsx (100%) rename {frontend/src => src}/screens/auth/ForgotPasswordScreen.tsx (100%) rename {frontend/src => src}/screens/auth/LoginScreen.tsx (100%) rename {frontend/src => src}/screens/auth/RegisterScreen.tsx (100%) rename {frontend/src => src}/screens/auth/ResetPasswordScreen.tsx (100%) rename {frontend/src => src}/screens/auth/WelcomeScreen.tsx (100%) rename {frontend/src => src}/screens/auth/onboarding/BasicInfoScreen.tsx (100%) rename {frontend/src => src}/screens/auth/onboarding/OnboardingCompleteScreen.tsx (100%) rename {frontend/src => src}/screens/auth/onboarding/WorkInfoScreen.tsx (100%) rename {frontend/src => src}/screens/chats/ChannelDetailScreen.tsx (100%) rename {frontend/src => src}/screens/chats/ThreadScreen.tsx (100%) rename {frontend/src => src}/screens/main/ActivityScreen.tsx (100%) rename {frontend/src => src}/screens/main/ChannelsScreen.tsx (100%) rename {frontend/src => src}/screens/main/DashboardScreen.tsx (100%) rename {frontend/src => src}/screens/main/TasksScreen.tsx (100%) rename {frontend/src => src}/screens/onboarding/OnboardingScreen.tsx (100%) rename {frontend/src => src}/screens/tasks/TaskCreateScreen.tsx (100%) rename {frontend/src => src}/screens/tasks/TaskDetailScreen.tsx (100%) rename {frontend/src => src}/screens/tasks/index.ts (100%) rename {frontend/src => src}/screens/users/UserProfileScreen.tsx (100%) rename {frontend/src => src}/services/ai/OpenAIService.ts (100%) rename {frontend/src => src}/services/ai/index.ts (100%) rename {frontend/src => src}/services/api/activityService.ts (100%) rename {frontend/src => src}/services/api/announcementService.ts (100%) rename {frontend/src => src}/services/api/authService.ts (100%) rename {frontend/src => src}/services/api/channelService.ts (100%) rename {frontend/src => src}/services/api/fileService.ts (100%) rename {frontend/src => src}/services/api/messageService.ts (100%) rename {frontend/src => src}/services/api/notificationService.ts (100%) rename {frontend/src => src}/services/api/projectService.ts (100%) rename {frontend/src => src}/services/api/taskService.ts (100%) rename {frontend/src => src}/services/api/userService.ts (100%) rename {frontend/src => src}/services/api/voiceService.ts (100%) rename {frontend/src => src}/services/audio/CustomAudioRecorderPlayer.ts (100%) rename {frontend/src => src}/services/notificationService.ts (100%) rename {frontend/src => src}/services/storage/AsyncStorageService.ts (100%) rename {frontend/src => src}/services/tokenManager.ts (100%) rename {frontend/src => src}/services/voice/CustomVoice.ts (100%) rename {frontend/src => src}/services/voice/SpeechRecognition.ts (100%) rename {frontend/src => src}/services/voice/TextToSpeech.ts (100%) rename {frontend/src => src}/services/websocketService.ts (100%) rename {frontend/src => src}/store/slices/announcementSlice.ts (100%) rename {frontend/src => src}/store/slices/authSlice.ts (100%) rename {frontend/src => src}/store/slices/projectSlice.ts (100%) rename {frontend/src => src}/store/slices/taskSlice.ts (100%) rename {frontend/src => src}/store/slices/voiceSlice.ts (100%) rename {frontend/src => src}/store/store.ts (100%) rename {frontend/src => src}/store/types.ts (100%) rename {frontend/src => src}/types/announcement.types.ts (100%) rename {frontend/src => src}/types/api.ts (100%) rename {frontend/src => src}/types/auth.ts (100%) rename {frontend/src => src}/types/chat.ts (100%) rename {frontend/src => src}/types/navigation.types.ts (100%) rename {frontend/src => src}/types/project.ts (100%) rename {frontend/src => src}/types/task.types.ts (100%) rename {frontend/src => src}/types/voice.ts (100%) rename {frontend/src => src}/utils/animations.ts (100%) rename {frontend/src => src}/utils/colors.ts (100%) rename {frontend/src => src}/utils/constants.ts (100%) rename {frontend/src => src}/utils/envSetup.ts (100%) rename {frontend/src => src}/utils/helpers.ts (100%) rename {frontend/src => src}/utils/seedDataLoader.ts (100%) rename {frontend/src => src}/utils/voiceErrorHandler.ts (100%) rename frontend/tailwind.config.js => tailwind.config.js (100%) rename frontend/tsconfig.json => tsconfig.json (100%) diff --git a/frontend/App.tsx b/App.tsx similarity index 100% rename from frontend/App.tsx rename to App.tsx diff --git a/Dockerfile.android b/Dockerfile.android deleted file mode 100644 index 5c639da..0000000 --- a/Dockerfile.android +++ /dev/null @@ -1,74 +0,0 @@ -FROM node:20-bullseye - -# Install system dependencies -RUN apt-get update && apt-get install -y \ - openjdk-17-jdk \ - android-tools-adb \ - android-tools-fastboot \ - wget \ - unzip \ - curl \ - git \ - python3 \ - python3-pip \ - build-essential \ - && rm -rf /var/lib/apt/lists/* - -# Set JAVA_HOME -ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 - -# Install Android SDK -ENV ANDROID_HOME=/opt/android-sdk -ENV ANDROID_SDK_ROOT=$ANDROID_HOME -ENV PATH=$PATH:$ANDROID_HOME/tools:$ANDROID_HOME/tools/bin:$ANDROID_HOME/platform-tools - -# Create Android SDK directory -RUN mkdir -p $ANDROID_HOME - -# Download and install Android SDK command line tools -RUN wget https://dl.google.com/android/repository/commandlinetools-linux-9477386_latest.zip -O /tmp/cmdline-tools.zip && \ - unzip /tmp/cmdline-tools.zip -d $ANDROID_HOME && \ - mv $ANDROID_HOME/cmdline-tools $ANDROID_HOME/tools && \ - rm /tmp/cmdline-tools.zip - -# Accept Android SDK licenses -RUN yes | $ANDROID_HOME/tools/bin/sdkmanager --licenses - -# Install Android SDK components -RUN $ANDROID_HOME/tools/bin/sdkmanager \ - "platform-tools" \ - "platforms;android-34" \ - "build-tools;34.0.0" \ - "system-images;android-34;google_apis;x86_64" \ - "emulator" - -# Install Gradle -RUN wget https://services.gradle.org/distributions/gradle-8.3-bin.zip -O /tmp/gradle.zip && \ - unzip /tmp/gradle.zip -d /opt && \ - ln -s /opt/gradle-8.3/bin/gradle /usr/local/bin/gradle && \ - rm /tmp/gradle.zip - -# Set working directory -WORKDIR /app - -# Copy package files -COPY package*.json ./ - -# Install Node.js dependencies -RUN npm ci - -# Copy project files -COPY . . - -# Create non-root user -RUN useradd -m -s /bin/bash android && \ - chown -R android:android /app && \ - chown -R android:android $ANDROID_HOME - -USER android - -# Expose ADB port -EXPOSE 5037 - -# Default command -CMD ["bash"] diff --git a/Dockerfile.metro b/Dockerfile.metro deleted file mode 100644 index 1bdb5b1..0000000 --- a/Dockerfile.metro +++ /dev/null @@ -1,45 +0,0 @@ -FROM node:20-alpine - -# Install necessary packages for React Native -RUN apk add --no-cache \ - python3 \ - make \ - g++ \ - git \ - bash \ - curl \ - watchman - -# Set working directory -WORKDIR /app - -# Copy package files -COPY package*.json ./ -COPY babel.config.js ./ -COPY metro.config.js ./ -COPY tailwind.config.js ./ -COPY tsconfig.json ./ - -# Install dependencies -RUN npm ci - -# Copy source code -COPY . . - -# Create non-root user -RUN addgroup -g 1001 -S reactnative && \ - adduser -S reactnative -u 1001 - -# Set ownership -RUN chown -R reactnative:reactnative /app -USER reactnative - -# Expose Metro port -EXPOSE 8081 - -# Health check -HEALTHCHECK --interval=30s --timeout=10s --start-period=10s --retries=3 \ - CMD curl -f http://localhost:8081/status || exit 1 - -# Start Metro bundler -CMD ["npm", "start", "--", "--host", "0.0.0.0", "--port", "8081"] diff --git a/frontend/Gemfile b/Gemfile similarity index 100% rename from frontend/Gemfile rename to Gemfile diff --git a/README.md b/README.md index a578e89..665e930 100644 --- a/README.md +++ b/README.md @@ -1,143 +1,114 @@ -This is a full-stack [**React Native**](https://reactnative.dev) application with a separate backend API. +# React Native Frontend App -## Project Structure +React Native mobile application for task management and team collaboration. -``` -. -├── frontend/ # React Native application -├── backend/ # Node.js/Express API server -├── docker-compose.*.yml -└── package.json # Root package.json for workspace management -``` - -# Getting Started - -Test User Credentials: +## Getting Started - 1. CEO: - - Email: ceo@test.com - - Password: test123 - - Role: CEO with full admin permissions - 2. Manager: - - Email: manager@test.com - - Password: test123 - - Role: Manager with read/write permissions - 3. Staff: - - Email: staff@test.com - - Password: test123 - - Role: Staff with read-only permissions +> **Note**: Make sure you have completed the [React Native - Environment Setup](https://reactnative.dev/docs/environment-setup) instructions till "Creating a new application" step, before proceeding. -> **Note**: Make sure you have completed the [Set Up Your Environment](https://reactnative.dev/docs/set-up-your-environment) guide before proceeding. +### Prerequisites +- Node.js 18+ +- React Native development environment +- Android Studio (for Android development) +- Xcode (for iOS development) -## Step 1: Install Dependencies +### Installation -Install dependencies for both frontend and backend: - -```sh -# Install all dependencies -npm run install:all - -# OR install individually -npm run install:frontend -npm run install:backend +1. Install dependencies: +```bash +npm install ``` -## Step 2: Start the Development Servers - -Start both the backend API and React Native Metro bundler: - -```sh -# Start backend (from root) -npm run dev:backend - -# Start frontend (from root) -npm run dev:frontend +2. For iOS, install CocoaPods dependencies: +```bash +cd ios && pod install && cd .. ``` -Or you can run them individually: - -```sh -# Frontend only -cd frontend && npm start +### Development -# Backend only -cd backend && npm run dev +Start the Metro bundler: +```bash +npm start ``` -## Step 3: Build and run your app - -With the servers running, open a new terminal window/pane and use one of the following commands to build and run your Android or iOS app: - -### Android - -```sh -# From frontend directory -cd frontend && npm run android - -# OR using Yarn -cd frontend && yarn android +Run on Android: +```bash +npm run android ``` -### iOS - -For iOS, remember to install CocoaPods dependencies (this only needs to be run on first clone or after updating native deps). - -The first time you create a new project, run the Ruby bundler to install CocoaPods itself: - -```sh -bundle install -``` - -Then, and every time you update your native dependencies, run: - -```sh -bundle exec pod install +Run on iOS: +```bash +npm run ios ``` -For more information, please visit [CocoaPods Getting Started guide](https://guides.cocoapods.org/using/getting-started.html). - -```sh -# Using npm -npm run ios +### Test User Credentials -# OR using Yarn -yarn ios -``` +1. CEO: + - Email: ceo@test.com + - Password: test123 + - Role: CEO with full admin permissions -If everything is set up correctly, you should see your new app running in the Android Emulator, iOS Simulator, or your connected device. +2. Manager: + - Email: manager@test.com + - Password: test123 + - Role: Manager with read/write permissions -This is one way to run your app — you can also build it directly from Android Studio or Xcode. +3. Staff: + - Email: staff@test.com + - Password: test123 + - Role: Staff with read-only permissions -## Step 3: Modify your app +### Features -Now that you have successfully run the app, let's make changes! +- **Authentication**: Login/register with role-based access +- **Task Management**: Create, assign, and track tasks +- **Team Collaboration**: Channel-based communication +- **Real-time Updates**: WebSocket integration for live updates +- **Voice Integration**: Voice-to-text functionality +- **Offline Support**: Works offline with sync when connected -Open `App.tsx` in your text editor of choice and make some changes. When you save, your app will automatically update and reflect these changes — this is powered by [Fast Refresh](https://reactnative.dev/docs/fast-refresh). +### Project Structure -When you want to forcefully reload, for example to reset the state of your app, you can perform a full reload: +``` +src/ +├── components/ # Reusable UI components +├── screens/ # Screen components +├── navigation/ # Navigation setup +├── services/ # API and service integrations +├── store/ # Redux store and slices +├── types/ # TypeScript type definitions +├── utils/ # Utility functions +└── assets/ # Images and static assets +``` -- **Android**: Press the R key twice or select **"Reload"** from the **Dev Menu**, accessed via Ctrl + M (Windows/Linux) or Cmd ⌘ + M (macOS). -- **iOS**: Press R in iOS Simulator. +### Scripts -## Congratulations! :tada: +- `npm start` - Start Metro bundler +- `npm run android` - Run on Android emulator/device +- `npm run ios` - Run on iOS simulator/device +- `npm test` - Run tests +- `npm run lint` - Run ESLint +- `npm run build` - Build for production -You've successfully run and modified your React Native App. :partying_face: +### Building APK -### Now what? +To build an APK for distribution: -- If you want to add this new React Native code to an existing application, check out the [Integration guide](https://reactnative.dev/docs/integration-with-existing-apps). -- If you're curious to learn more about React Native, check out the [docs](https://reactnative.dev/docs/getting-started). +1. Generate a signed APK: +```bash +cd android +./gradlew assembleRelease +``` -# Troubleshooting +2. The APK will be located at: +``` +android/app/build/outputs/apk/release/app-release.apk +``` -If you're having issues getting the above steps to work, see the [Troubleshooting](https://reactnative.dev/docs/troubleshooting) page. +### Configuration -# Learn More +The app connects to the backend API. Update the API URL in `src/config/apiKeys.ts` to point to your deployed backend. -To learn more about React Native, take a look at the following resources: +### Deployment -- [React Native Website](https://reactnative.dev) - learn more about React Native. -- [Getting Started](https://reactnative.dev/docs/environment-setup) - an **overview** of React Native and how setup your environment. -- [Learn the Basics](https://reactnative.dev/docs/getting-started) - a **guided tour** of the React Native **basics**. -- [Blog](https://reactnative.dev/blog) - read the latest official React Native **Blog** posts. -- [`@facebook/react-native`](https://github.com/facebook/react-native) - the Open Source; GitHub **repository** for React Native. +This frontend can be built into an APK for Android or submitted to the App Store for iOS. Make sure to configure the backend API URL for production before building. \ No newline at end of file diff --git a/VERIFIED_USER_CREDENTIALS.md b/VERIFIED_USER_CREDENTIALS.md deleted file mode 100644 index de01183..0000000 --- a/VERIFIED_USER_CREDENTIALS.md +++ /dev/null @@ -1,188 +0,0 @@ -# ✅ VERIFIED USER CREDENTIALS - -**Last Updated:** August 28, 2025 -**Database:** PostgreSQL (ceo_platform_dev) -**Backend API:** http://localhost:3001/api/v1 - -This document contains **ONLY VERIFIED AND WORKING** user credentials for the RevampAI CEO Communication Platform. - -## 🔐 TESTED & WORKING CREDENTIALS - -### 👑 CEO Users - -#### 1. Alexander Mitchell (Chief Executive Officer) -- **Email:** `alex.ceo@company.com` -- **Password:** `TempPass123!` -- **Role:** `ceo` -- **Department:** `Executive` -- **Status:** ✅ **VERIFIED WORKING** -- **User ID:** `0cb414a1-4ae1-43e2-9334-fc38f2f720d3` - -**Expected Login Response:** -```json -{ - "success": true, - "data": { - "user": { - "id": "0cb414a1-4ae1-43e2-9334-fc38f2f720d3", - "email": "alex.ceo@company.com", - "name": "Alexander Mitchell", - "role": "ceo", - "department": "Executive", - "job_title": "Chief Executive Officer", - "email_verified": true - }, - "accessToken": "[JWT_TOKEN]", - "refreshToken": "[REFRESH_TOKEN]" - } -} -``` - -### 👨‍💼 Manager Users - -#### 1. Sarah Chen (Engineering Manager) -- **Email:** `sarah.manager@seeddata.com` -- **Password:** `TempPass123!` -- **Role:** `manager` -- **Department:** `Engineering` -- **Job Title:** `Engineering Manager` -- **Status:** ✅ **PASSWORD RESET & VERIFIED** -- **User ID:** `4dfc35ff-660f-403b-bbf2-99d9ef77e541` - -#### 2. Michael Rodriguez (Product Manager) -- **Email:** `mike.manager@seeddata.com` -- **Password:** `TempPass123!` -- **Role:** `manager` -- **Department:** `Product` -- **Job Title:** `Product Manager` -- **Status:** ✅ **PASSWORD RESET & VERIFIED** -- **User ID:** `c45824a3-e1ab-4773-92e0-2c752d53aea2` - -#### 3. Lisa Thompson (Marketing Director) -- **Email:** `lisa.manager@seeddata.com` -- **Password:** `TempPass123!` -- **Role:** `manager` -- **Department:** `Marketing` -- **Job Title:** `Marketing Director` -- **Status:** ✅ **PASSWORD RESET & VERIFIED** -- **User ID:** `67a266dc-6e2e-4fd6-8c28-436dc96a8f68` - -**Expected Manager Login Response:** -```json -{ - "success": true, - "data": { - "user": { - "id": "[USER_ID]", - "email": "[EMAIL]", - "name": "[NAME]", - "role": "manager", - "department": "[DEPARTMENT]", - "job_title": "[JOB_TITLE]", - "email_verified": true - }, - "accessToken": "[JWT_TOKEN]", - "refreshToken": "[REFRESH_TOKEN]" - } -} -``` - -### 👥 Staff User Sample - -**Note:** There are 135+ staff users in the database. All staff users use the same password pattern but have randomized email addresses. Here's a representative sample: - -#### Engineering Staff Sample -- **Elena Schinner** - `adeline.turner@seeddata.com` - Senior Developer -- **Shannon Corkery** - `gonzalo_denesik@seeddata.com` - Backend Developer -- **Wilfred Reichert Jr.** - `wilton.stroman@seeddata.com` - Frontend Developer - ---- - -## ❌ REMOVED NON-WORKING USERS - -The following users were found in the database but **failed login verification** and have been removed: - -- ❌ `ceo@seeddata.com` - Alexander Johnson (CEO) -- ❌ `testceo@test.com` - Alex Johnson (CEO) - ---- - -## 🔧 DATABASE MAINTENANCE PERFORMED - -### Actions Taken: -1. ✅ Queried database for all seed users -2. ✅ Tested login functionality for key users -3. ✅ Removed non-working CEO duplicates -4. ✅ Reset manager passwords with proper bcrypt hashing -5. ✅ Verified working credentials against live backend API - -### Database Statistics: -- **Total Users:** 141 -- **CEO Users:** 1 (verified working) -- **Manager Users:** 3 (all verified working) -- **Staff Users:** 135+ (same password pattern) - ---- - -## 🧪 LOGIN TESTING - -### API Endpoint -``` -POST http://localhost:3001/api/v1/auth/login -Content-Type: application/json - -{ - "email": "[EMAIL]", - "password": "TempPass123!" -} -``` - -### Successful Response Format -```json -{ - "success": true, - "data": { - "user": { - "id": "string", - "email": "string", - "name": "string", - "role": "ceo|manager|staff", - "department": "string", - "job_title": "string", - "avatar_url": "string|null", - "email_verified": true, - "created_at": "ISO_DATE", - "updated_at": "ISO_DATE" - }, - "accessToken": "JWT_TOKEN", - "refreshToken": "REFRESH_TOKEN" - }, - "timestamp": "ISO_DATE" -} -``` - -### Error Response Format -```json -{ - "success": false, - "error": { - "message": "Invalid credentials", - "code": "INVALID_CREDENTIALS" - }, - "timestamp": "ISO_DATE" -} -``` - ---- - -## 📝 USAGE NOTES - -1. **Password Policy:** All users use `TempPass123!` (12+ chars, uppercase, lowercase, number, symbol) -2. **Rate Limiting:** Backend enforces rate limiting - wait between login attempts -3. **JWT Tokens:** Access tokens expire in 15 minutes, refresh tokens in 7 days -4. **Email Verification:** All seed users have `email_verified: true` -5. **Roles & Permissions:** CEO > Manager > Staff hierarchy - ---- - -**⚠️ IMPORTANT:** Only use the credentials listed as "VERIFIED WORKING" above. All other combinations have been tested and removed from the system. \ No newline at end of file diff --git a/frontend/__tests__/App.test.tsx b/__tests__/App.test.tsx similarity index 100% rename from frontend/__tests__/App.test.tsx rename to __tests__/App.test.tsx diff --git a/frontend/android/app/build.gradle b/android/app/build.gradle similarity index 100% rename from frontend/android/app/build.gradle rename to android/app/build.gradle diff --git a/frontend/android/app/debug.keystore b/android/app/debug.keystore similarity index 100% rename from frontend/android/app/debug.keystore rename to android/app/debug.keystore diff --git a/frontend/android/app/proguard-rules.pro b/android/app/proguard-rules.pro similarity index 100% rename from frontend/android/app/proguard-rules.pro rename to android/app/proguard-rules.pro diff --git a/frontend/android/app/src/debug/AndroidManifest.xml b/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from frontend/android/app/src/debug/AndroidManifest.xml rename to android/app/src/debug/AndroidManifest.xml diff --git a/frontend/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml similarity index 100% rename from frontend/android/app/src/main/AndroidManifest.xml rename to android/app/src/main/AndroidManifest.xml diff --git a/frontend/android/app/src/main/assets/index.android.bundle b/android/app/src/main/assets/index.android.bundle similarity index 100% rename from frontend/android/app/src/main/assets/index.android.bundle rename to android/app/src/main/assets/index.android.bundle diff --git a/frontend/android/app/src/main/java/com/app/MainActivity.kt b/android/app/src/main/java/com/app/MainActivity.kt similarity index 100% rename from frontend/android/app/src/main/java/com/app/MainActivity.kt rename to android/app/src/main/java/com/app/MainActivity.kt diff --git a/frontend/android/app/src/main/java/com/app/MainApplication.kt b/android/app/src/main/java/com/app/MainApplication.kt similarity index 100% rename from frontend/android/app/src/main/java/com/app/MainApplication.kt rename to android/app/src/main/java/com/app/MainApplication.kt diff --git a/frontend/android/app/src/main/java/com/app/VoiceModule.kt b/android/app/src/main/java/com/app/VoiceModule.kt similarity index 100% rename from frontend/android/app/src/main/java/com/app/VoiceModule.kt rename to android/app/src/main/java/com/app/VoiceModule.kt diff --git a/frontend/android/app/src/main/java/com/app/VoicePackage.kt b/android/app/src/main/java/com/app/VoicePackage.kt similarity index 100% rename from frontend/android/app/src/main/java/com/app/VoicePackage.kt rename to android/app/src/main/java/com/app/VoicePackage.kt diff --git a/frontend/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png b/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png rename to android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png diff --git a/frontend/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_backiconmask.png b/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_backiconmask.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_backiconmask.png rename to android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_backiconmask.png diff --git a/frontend/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png b/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png rename to android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png diff --git a/frontend/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png b/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png rename to android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png diff --git a/frontend/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png b/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png rename to android/app/src/main/res/drawable-mdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png diff --git a/frontend/android/app/src/main/res/drawable-mdpi/src_assets_icons_google.png b/android/app/src/main/res/drawable-mdpi/src_assets_icons_google.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-mdpi/src_assets_icons_google.png rename to android/app/src/main/res/drawable-mdpi/src_assets_icons_google.png diff --git a/frontend/android/app/src/main/res/drawable-mdpi/src_assets_icons_logo.png b/android/app/src/main/res/drawable-mdpi/src_assets_icons_logo.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-mdpi/src_assets_icons_logo.png rename to android/app/src/main/res/drawable-mdpi/src_assets_icons_logo.png diff --git a/frontend/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png b/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png rename to android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png diff --git a/frontend/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png b/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png rename to android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png diff --git a/frontend/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png b/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png rename to android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png diff --git a/frontend/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png b/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png rename to android/app/src/main/res/drawable-xhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png diff --git a/frontend/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png b/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png rename to android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png diff --git a/frontend/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png b/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png rename to android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png diff --git a/frontend/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png b/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png rename to android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png diff --git a/frontend/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png b/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png rename to android/app/src/main/res/drawable-xxhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png diff --git a/frontend/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png b/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png rename to android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_backicon.png diff --git a/frontend/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png b/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png rename to android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_clearicon.png diff --git a/frontend/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png b/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png rename to android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_closeicon.png diff --git a/frontend/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png b/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png similarity index 100% rename from frontend/android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png rename to android/app/src/main/res/drawable-xxxhdpi/node_modules_reactnavigation_elements_lib_module_assets_searchicon.png diff --git a/frontend/android/app/src/main/res/drawable/rn_edit_text_material.xml b/android/app/src/main/res/drawable/rn_edit_text_material.xml similarity index 100% rename from frontend/android/app/src/main/res/drawable/rn_edit_text_material.xml rename to android/app/src/main/res/drawable/rn_edit_text_material.xml diff --git a/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png similarity index 100% rename from frontend/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png rename to android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png diff --git a/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png similarity index 100% rename from frontend/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png rename to android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png diff --git a/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png similarity index 100% rename from frontend/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png rename to android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png diff --git a/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png similarity index 100% rename from frontend/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png rename to android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png diff --git a/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png similarity index 100% rename from frontend/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png rename to android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png diff --git a/frontend/android/app/src/main/res/raw/keep.xml b/android/app/src/main/res/raw/keep.xml similarity index 100% rename from frontend/android/app/src/main/res/raw/keep.xml rename to android/app/src/main/res/raw/keep.xml diff --git a/frontend/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml similarity index 100% rename from frontend/android/app/src/main/res/values/strings.xml rename to android/app/src/main/res/values/strings.xml diff --git a/frontend/android/app/src/main/res/values/styles.xml b/android/app/src/main/res/values/styles.xml similarity index 100% rename from frontend/android/app/src/main/res/values/styles.xml rename to android/app/src/main/res/values/styles.xml diff --git a/frontend/android/build.gradle b/android/build.gradle similarity index 100% rename from frontend/android/build.gradle rename to android/build.gradle diff --git a/frontend/android/gradle.properties b/android/gradle.properties similarity index 100% rename from frontend/android/gradle.properties rename to android/gradle.properties diff --git a/frontend/android/gradle/wrapper/gradle-wrapper.jar b/android/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from frontend/android/gradle/wrapper/gradle-wrapper.jar rename to android/gradle/wrapper/gradle-wrapper.jar diff --git a/frontend/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from frontend/android/gradle/wrapper/gradle-wrapper.properties rename to android/gradle/wrapper/gradle-wrapper.properties diff --git a/frontend/android/gradlew b/android/gradlew similarity index 100% rename from frontend/android/gradlew rename to android/gradlew diff --git a/frontend/android/gradlew.bat b/android/gradlew.bat similarity index 100% rename from frontend/android/gradlew.bat rename to android/gradlew.bat diff --git a/frontend/android/settings.gradle b/android/settings.gradle similarity index 100% rename from frontend/android/settings.gradle rename to android/settings.gradle diff --git a/frontend/app.json b/app.json similarity index 100% rename from frontend/app.json rename to app.json diff --git a/frontend/babel.config.js b/babel.config.js similarity index 100% rename from frontend/babel.config.js rename to babel.config.js diff --git a/backend/README.md b/backend/README.md deleted file mode 100644 index 7f1d571..0000000 --- a/backend/README.md +++ /dev/null @@ -1,649 +0,0 @@ -# CEO Communication Platform - Backend Architecture - -## 🎯 Project Overview - -Enterprise-grade backend system for a CEO communication platform that enables voice-driven task and channel management. The system is designed for a single organization with high-performance requirements, complex relationship management, and seamless real-time collaboration. - -## 🏗️ System Architecture - -### Core Features -- **Ultra-fast voice processing** (<2 seconds end-to-end) -- **Multi-action voice commands** (execute multiple operations from single command) -- **Complex relationship mapping** (channels, tasks, dependencies, shared resources) -- **Real-time synchronization** across all connected clients -- **Intelligent context management** with organizational memory -- **Advanced AI agent** with learning capabilities - ---- - -## 🚀 High-Performance Voice Processing Pipeline - -### Architecture Flow -``` -WebRTC Stream → Edge Processing → Whisper Turbo → GPT-4 Turbo → Action Execution -Target: <2 second end-to-end processing -``` - -### Components -- **Real-time Audio Streaming**: WebRTC with 16kHz sampling -- **Edge Pre-processing**: Voice Activity Detection, noise reduction -- **Whisper Turbo API**: Parallel processing with chunking -- **Streaming Response**: Partial results while processing -- **Connection Pooling**: Pre-warmed API connections - ---- - -## 🔗 Complex Relationship Mapping System - -### Entity Relationships -- **Channel Categories**: Marketing, Operations, Finance, HR, etc. -- **Task Dependencies**: BLOCKS, REQUIRES, FOLLOWS, PARALLEL -- **Resource Sharing**: Documents, files, links between channels/tasks -- **Channel Relationships**: PARENT_CHILD, COLLABORATIVE, SEQUENTIAL - -### Database Schema (PostgreSQL) -```sql --- Core Entities -Categories (id, name, description, color, icon, priority_level) -Channels (id, name, category_id, created_by, channel_type, status, metadata) -Tasks (id, title, description, channel_id, parent_task_id, priority, complexity, assigned_to[]) -TaskDependencies (task_id, depends_on_task_id, dependency_type) -SharedResources (id, name, type, content, metadata) -ResourceLinks (resource_id, entity_type, entity_id, link_type) -ChannelRelationships (channel_id, related_channel_id, relationship_type) -``` - ---- - -## 🎯 Multi-Action Voice Command System - -### Command Processing -```typescript -interface VoiceCommand { - transcript: string; - actions: Action[]; - context: CommandContext; - executionPlan: ExecutionStep[]; -} - -interface Action { - type: 'CREATE_CHANNEL' | 'CREATE_TASK' | 'ASSIGN_USER' | 'UPLOAD_FILE' | - 'SEND_MESSAGE' | 'SET_DEADLINE' | 'CREATE_DEPENDENCY' | 'SHARE_RESOURCE'; - parameters: Record; - conditions?: string[]; - priority: number; -} -``` - -### Example Complex Command -``` -"Create a marketing channel for Q1 campaign, add Sarah and Mike, -create three tasks: content creation due next Friday, -social media strategy due Wednesday, and budget review due tomorrow. -Make the budget review block the other two tasks. -Also upload the brand guidelines document to this channel." -``` - -### Execution Features -- **Dependency Resolution**: Automatic ordering based on task dependencies -- **Parallel Execution**: Non-dependent actions run simultaneously -- **Rollback Support**: If any action fails, rollback completed actions -- **Progress Streaming**: Real-time feedback during execution - ---- - -## ⚡ Real-Time Synchronization Architecture - -### Event-Driven System -``` -Redis Streams + Socket.IO + Database Triggers + Push Notifications -``` - -### Event Types -- **Voice Command Events**: Command received, processing, completed -- **Entity Events**: Task created/updated, channel modified, user assigned -- **Relationship Events**: Dependencies added, resources shared -- **System Events**: Reminders, deadlines, status changes - -### Real-Time Features -- **Live Command Execution**: Staff see tasks appearing as CEO speaks -- **Instant Notifications**: Assignments, updates, reminders -- **Collaborative Updates**: Multiple users editing simultaneously -- **Presence Indicators**: Who's online, viewing, working on what - ---- - -## 🗄️ Complete Database Schema - -### Core Tables -```sql --- User Management -Users ( - id UUID PRIMARY KEY, - email VARCHAR UNIQUE, - name VARCHAR, - role ENUM('ceo', 'manager', 'staff'), - avatar_url VARCHAR, - language_preference VARCHAR DEFAULT 'en', - timezone VARCHAR, - notification_settings JSONB, - last_active TIMESTAMP, - created_at TIMESTAMP -); - --- Category System -Categories ( - id UUID PRIMARY KEY, - name VARCHAR NOT NULL, - description TEXT, - color VARCHAR(7), - icon VARCHAR, - priority_level INTEGER DEFAULT 1, - created_by UUID REFERENCES Users(id), - created_at TIMESTAMP -); - --- Advanced Channel System -Channels ( - id UUID PRIMARY KEY, - name VARCHAR NOT NULL, - description TEXT, - category_id UUID REFERENCES Categories(id), - created_by UUID REFERENCES Users(id), - channel_type ENUM('project', 'department', 'initiative', 'temporary'), - privacy_level ENUM('public', 'private', 'restricted'), - status ENUM('active', 'archived', 'paused'), - metadata JSONB, - created_at TIMESTAMP, - archived_at TIMESTAMP -); - --- Complex Task System -Tasks ( - id UUID PRIMARY KEY, - title VARCHAR NOT NULL, - description TEXT, - channel_id UUID REFERENCES Channels(id), - parent_task_id UUID REFERENCES Tasks(id), - created_by UUID REFERENCES Users(id), - assigned_to UUID[] DEFAULT '{}', - priority ENUM('low', 'medium', 'high', 'urgent', 'critical'), - status ENUM('pending', 'in_progress', 'review', 'completed', 'cancelled'), - complexity INTEGER DEFAULT 1, - estimated_hours INTEGER, - actual_hours INTEGER, - due_date TIMESTAMP, - completed_at TIMESTAMP, - tags VARCHAR[], - metadata JSONB, - created_at TIMESTAMP -); - --- Task Dependencies -TaskDependencies ( - id UUID PRIMARY KEY, - task_id UUID REFERENCES Tasks(id), - depends_on_task_id UUID REFERENCES Tasks(id), - dependency_type ENUM('blocks', 'requires', 'follows', 'parallel'), - created_at TIMESTAMP, - UNIQUE(task_id, depends_on_task_id) -); - --- Shared Resources -SharedResources ( - id UUID PRIMARY KEY, - name VARCHAR NOT NULL, - description TEXT, - resource_type ENUM('document', 'link', 'image', 'video', 'template'), - content_url VARCHAR, - content_data JSONB, - file_size INTEGER, - mime_type VARCHAR, - uploaded_by UUID REFERENCES Users(id), - metadata JSONB, - created_at TIMESTAMP -); - --- Resource Links -ResourceLinks ( - id UUID PRIMARY KEY, - resource_id UUID REFERENCES SharedResources(id), - entity_type ENUM('channel', 'task', 'category'), - entity_id UUID, - link_type ENUM('attachment', 'reference', 'template', 'requirement'), - created_by UUID REFERENCES Users(id), - created_at TIMESTAMP -); - --- Channel Relationships -ChannelRelationships ( - id UUID PRIMARY KEY, - channel_id UUID REFERENCES Channels(id), - related_channel_id UUID REFERENCES Channels(id), - relationship_type ENUM('parent_child', 'collaborative', 'sequential', 'competitive'), - metadata JSONB, - created_at TIMESTAMP -); - --- Voice Commands History -VoiceCommands ( - id UUID PRIMARY KEY, - user_id UUID REFERENCES Users(id), - transcript TEXT NOT NULL, - processed_transcript TEXT, - intent_analysis JSONB, - actions_planned JSONB, - actions_executed JSONB, - execution_status ENUM('pending', 'processing', 'completed', 'failed', 'partial'), - processing_time_ms INTEGER, - error_details JSONB, - created_at TIMESTAMP -); - --- Advanced Messaging -Messages ( - id UUID PRIMARY KEY, - channel_id UUID REFERENCES Channels(id), - task_id UUID REFERENCES Tasks(id), - user_id UUID REFERENCES Users(id), - content TEXT, - message_type ENUM('text', 'voice', 'file', 'system', 'command_result'), - metadata JSONB, - reply_to UUID REFERENCES Messages(id), - edited_at TIMESTAMP, - deleted_at TIMESTAMP, - created_at TIMESTAMP -); - --- Notifications System -Notifications ( - id UUID PRIMARY KEY, - user_id UUID REFERENCES Users(id), - title VARCHAR NOT NULL, - content TEXT, - type ENUM('task_assigned', 'deadline_reminder', 'channel_invite', 'task_completed', 'system'), - entity_type ENUM('task', 'channel', 'message', 'system'), - entity_id UUID, - priority ENUM('low', 'medium', 'high', 'urgent'), - read_at TIMESTAMP, - scheduled_for TIMESTAMP, - sent_at TIMESTAMP, - metadata JSONB, - created_at TIMESTAMP -); -``` - -### Performance Indexes -```sql -CREATE INDEX idx_tasks_assigned_to ON Tasks USING GIN(assigned_to); -CREATE INDEX idx_tasks_channel_status ON Tasks(channel_id, status); -CREATE INDEX idx_messages_channel_created ON Messages(channel_id, created_at); -CREATE INDEX idx_notifications_user_unread ON Notifications(user_id) WHERE read_at IS NULL; -``` - ---- - -## 🧠 Intelligent Context Management System - -### Context-Aware AI Agent -```typescript -interface ContextManager { - conversationHistory: VoiceCommand[]; - activeChannels: Channel[]; - recentTasks: Task[]; - userPreferences: UserProfile; - organizationContext: OrgContext; - temporalContext: TimeContext; -} - -interface SmartReferences { - pronounResolution: Record; // "this", "that", "it" - implicitEntities: EntityReference[]; // "the marketing team", "this project" - contextualDefaults: DefaultValues; // default assignees, priorities -} -``` - -### Context Features -- **Smart Entity Resolution**: "Add this to the marketing project" → resolves current context -- **Temporal Intelligence**: "next week", "by Friday", "before the meeting" -- **Role-Based Defaults**: Auto-assign based on channel type and past patterns -- **Conversation Memory**: References to previous commands in same session - ---- - -## 🤖 Advanced AI Agent with Organizational Memory - -### AI Agent Architecture -```typescript -class ExecutiveAIAgent { - // Long-term memory systems - organizationalKnowledge: OrgKnowledgeBase; - behavioralPatterns: UserBehaviorModel; - projectHistory: ProjectHistoryDB; - - // Intelligent features - async processVoiceCommand(audio: AudioStream): Promise { - const transcript = await this.speechToText(audio); - const context = await this.buildContext(); - const intent = await this.analyzeIntent(transcript, context); - const plan = await this.createExecutionPlan(intent); - return plan; - } - - async learnFromInteraction(command: VoiceCommand, feedback: UserFeedback) { - // Continuous learning from CEO preferences - } -} -``` - -### Advanced Capabilities -- **Predictive Task Creation**: "I think we need to prepare for the board meeting" → suggests related tasks -- **Smart Scheduling**: Learns CEO's schedule patterns and optimal times -- **Proactive Reminders**: Identifies potential issues before they become problems -- **Pattern Recognition**: "You usually assign marketing tasks to Sarah, should I do that now?" - ---- - -## ⚡ Performance Optimization Strategies - -### Speed-First Architecture -```typescript -const optimizations = { - // Database - connectionPooling: 'PostgreSQL connection pools with read replicas', - queryOptimization: 'Materialized views for complex relationships', - caching: 'Redis multi-layer caching (L1: memory, L2: Redis)', - - // API Performance - apiCaching: 'Aggressive caching with smart invalidation', - compressionGzip: 'Response compression', - cdnIntegration: 'Static asset CDN', - - // Voice Processing - preWarmedConnections: 'Keep Whisper API connections warm', - parallelProcessing: 'Concurrent API calls for complex commands', - streamingResponse: 'Partial results while processing', - edgeComputing: 'Voice preprocessing at edge nodes' -}; -``` - -### Performance Targets -- **Voice-to-Action**: <2 seconds for simple commands, <5 seconds for complex -- **Real-time Updates**: <100ms latency for live updates -- **File Uploads**: Parallel chunked uploads with progress tracking -- **Database Queries**: <50ms for common operations - ---- - -## 📊 Comprehensive Monitoring & Analytics - -### Executive Dashboard & Intelligence -```typescript -interface ExecutiveDashboard { - teamPerformance: TeamMetrics; - projectProgress: ProjectInsights; - communicationPatterns: CommInsights; - voiceCommandAnalytics: VoiceMetrics; - predictiveInsights: PredictiveAnalytics; -} - -interface TeamMetrics { - taskCompletionRates: UserPerformance[]; - responseTimeAnalytics: ResponseMetrics; - workloadDistribution: WorkloadAnalysis; - collaborationPatterns: CollabMetrics; -} -``` - -### Advanced Analytics -- **Voice Command Intelligence**: Most used commands, success rates, optimization suggestions -- **Team Performance Insights**: Who's overloaded, who's available, skill matching -- **Project Health Monitoring**: Risk prediction, deadline analysis, resource allocation -- **Communication Efficiency**: Response times, engagement levels, bottleneck identification - ---- - -## 🛠️ Complete Technology Stack - -### Backend Core -- **Runtime**: Node.js 20+ with TypeScript -- **Framework**: Fastify (faster than Express) with plugins -- **Database**: PostgreSQL 15+ with read replicas -- **Cache**: Redis Cluster with persistence -- **Search**: ElasticSearch for advanced search capabilities - -### AI & Voice Services -- **Speech-to-Text**: OpenAI Whisper Turbo API -- **LLM**: GPT-4 Turbo for intent analysis and command processing -- **Text-to-Speech**: ElevenLabs for voice responses -- **Voice Enhancement**: Azure Cognitive Services for noise reduction - -### Real-time & Communication -- **WebSocket**: Socket.IO with Redis adapter -- **Message Queue**: Bull Queue with Redis -- **Push Notifications**: Firebase Cloud Messaging -- **File Storage**: AWS S3 with CloudFront CDN - -### Infrastructure & DevOps -- **Hosting**: AWS ECS Fargate with auto-scaling -- **Load Balancer**: Application Load Balancer with health checks -- **Monitoring**: DataDog for APM + custom executive dashboard -- **Security**: JWT with refresh tokens, rate limiting, input validation - -### Internationalization -- **i18n Framework**: Built-in support for Spanish/English -- **Content Translation**: Azure Translator for dynamic content -- **Voice Models**: Language-specific Whisper models - ---- - -## 🚀 Implementation Roadmap - -### Phase 1 (Weeks 1-3): Foundation -1. **Database Setup** - - PostgreSQL installation and configuration - - Complete schema creation with all relationships - - Performance indexes and constraints - - Database migrations system - -2. **Authentication & User Management** - - JWT-based authentication system - - Role-based access control (CEO, Manager, Staff) - - User profile management - - Session management - -3. **Basic API Infrastructure** - - Fastify server setup with TypeScript - - Basic CRUD endpoints for all entities - - Input validation and error handling - - API documentation with Swagger - -4. **Real-time Infrastructure** - - Socket.IO setup with Redis adapter - - Basic WebSocket event system - - Connection management - - Real-time presence tracking - -### Phase 2 (Weeks 4-6): Voice Core -1. **Voice Processing Pipeline** - - Whisper API integration - - Audio stream handling with WebRTC - - Speech-to-text processing - - Voice command logging system - -2. **Basic AI Agent** - - OpenAI GPT-4 integration - - Intent recognition system - - Simple command parsing - - Basic action execution - -3. **Simple Command Execution** - - Single-action command processing - - Basic entity creation (channels, tasks) - - User assignment functionality - - Command result feedback - -4. **File Upload System** - - AWS S3 integration - - Chunked file upload support - - File metadata management - - CDN integration for fast delivery - -### Phase 3 (Weeks 7-9): Advanced Features -1. **Complex Multi-Action Commands** - - Multi-step command parsing - - Dependency resolution engine - - Parallel action execution - - Transaction rollback support - -2. **Context Management System** - - Conversation history tracking - - Smart entity resolution - - Temporal context understanding - - Default value inference - -3. **Advanced Relationship Mapping** - - Task dependency management - - Channel relationship system - - Resource sharing between entities - - Complex query optimization - -4. **Performance Optimizations** - - Redis caching implementation - - Database query optimization - - API response compression - - Connection pooling - -### Phase 4 (Weeks 10-12): Intelligence -1. **Predictive Analytics** - - Team performance analytics - - Project health monitoring - - Workload distribution analysis - - Risk prediction algorithms - -2. **Learning Algorithms** - - User behavior pattern recognition - - Command optimization suggestions - - Predictive task creation - - Smart scheduling recommendations - -3. **Executive Dashboard** - - Real-time team performance metrics - - Project progress visualization - - Communication pattern analysis - - Voice command analytics - -4. **Advanced Notification System** - - Intelligent reminder scheduling - - Priority-based notification routing - - Multi-channel notification delivery - - Notification preference learning - ---- - -## 📋 Implementation Checklist - -### Current Status: 🔄 **PLANNING COMPLETED** - -#### ✅ Completed -- [x] Complete backend architecture design -- [x] Database schema with all relationships -- [x] Technology stack selection -- [x] Performance optimization strategy -- [x] Implementation roadmap - -#### 🔄 Next Steps -- [ ] Set up development environment -- [ ] Initialize Node.js project with TypeScript -- [ ] Set up PostgreSQL database -- [ ] Implement basic authentication system -- [ ] Create core API endpoints - ---- - -## 🔧 Development Setup - -### Prerequisites -- Node.js 20+ -- PostgreSQL 15+ -- Redis 7+ -- AWS Account (for S3, CloudFront) -- OpenAI API Key -- ElevenLabs API Key (optional) - -### Environment Variables -```env -# Database -DATABASE_URL=postgresql://user:password@localhost:5432/ceo_platform -REDIS_URL=redis://localhost:6379 - -# AI Services -OPENAI_API_KEY=sk-... -ELEVENLABS_API_KEY=... - -# AWS -AWS_ACCESS_KEY_ID=... -AWS_SECRET_ACCESS_KEY=... -AWS_S3_BUCKET=ceo-platform-files - -# App -JWT_SECRET=... -NODE_ENV=development -PORT=3000 -``` - -### Quick Start -```bash -# 1. Set up database -createdb ceo_platform -npm run db:migrate - -# 2. Install dependencies -npm install - -# 3. Start development server -npm run dev - -# 4. Run tests -npm test -``` - ---- - -## 📚 API Documentation - -Once implemented, the API will include: - -- **Authentication**: `/api/auth/*` -- **Users**: `/api/users/*` -- **Channels**: `/api/channels/*` -- **Tasks**: `/api/tasks/*` -- **Voice Commands**: `/api/voice/*` -- **Files**: `/api/files/*` -- **Analytics**: `/api/analytics/*` -- **WebSocket Events**: Real-time updates and notifications - ---- - -## 🚨 Security Considerations - -- **Input Validation**: All voice commands and API inputs validated -- **Rate Limiting**: Prevent abuse of voice processing endpoints -- **Data Encryption**: All sensitive data encrypted at rest and in transit -- **Access Control**: Strict role-based permissions -- **Audit Logging**: Complete audit trail of all actions -- **API Security**: JWT tokens, CORS, HTTPS enforcement - ---- - -## 📈 Scalability Plan - -- **Horizontal Scaling**: Stateless API servers with load balancing -- **Database Scaling**: Read replicas and connection pooling -- **Cache Scaling**: Redis cluster for high availability -- **File Storage**: CDN for global file delivery -- **Monitoring**: Comprehensive application and infrastructure monitoring - ---- - -*This architecture provides a complete, enterprise-grade solution designed for speed, reliability, and the complex requirements of executive-level communication management.* \ No newline at end of file diff --git a/backend/SUCCESS_CRITERIA.md b/backend/SUCCESS_CRITERIA.md deleted file mode 100644 index befa389..0000000 --- a/backend/SUCCESS_CRITERIA.md +++ /dev/null @@ -1,468 +0,0 @@ -# CEO Communication Platform - Success Criteria & Expected Functionality - -## 🎯 Project Vision - -**Mission**: Create a seamless, voice-driven communication platform that eliminates friction for a busy CEO to manage teams, tasks, and organizational communication through natural voice commands. - -**Success Definition**: The CEO can accomplish complex organizational management tasks through voice commands faster than traditional methods, while staff receive clear, actionable instructions in real-time. - ---- - -## 👤 User Roles & Capabilities - -### 🎙️ CEO (Primary User) -**Core Capability**: Voice-driven command and control over entire organizational communication - -**Expected Powers**: -- Create and manage channels through voice commands -- Assign tasks to multiple team members simultaneously -- Upload and share documents/files via voice instruction -- Monitor team progress through voice queries -- Send messages to channels or individuals via voice -- Set deadlines, priorities, and dependencies through speech -- Generate reports and summaries through voice requests - -### 👥 Staff Members -**Core Capability**: Receive, respond to, and collaborate on CEO-initiated work - -**Expected Powers**: -- Receive real-time notifications of new assignments -- Participate in channel discussions via text -- Update task progress and status -- Upload files and documents -- Collaborate with other team members -- View task dependencies and project relationships -- Access shared resources and documents - ---- - -## 🗣️ Voice Command Capabilities - -### Simple Commands (Expected Response Time: <2 seconds) - -#### Channel Management -``` -CEO: "Create a marketing channel for the Q2 campaign" -Expected Outcome: -- New channel "Q2 Campaign Marketing" created -- CEO automatically added as admin -- Channel visible to all staff -- Notification sent to marketing team -``` - -``` -CEO: "Add Sarah and Mike to the marketing channel" -Expected Outcome: -- Sarah and Mike receive channel invite notifications -- Both users gain access to channel history -- Welcome message generated in channel -- CEO receives confirmation of additions -``` - -#### Task Creation -``` -CEO: "Create a task for the product launch presentation due Friday" -Expected Outcome: -- New task created with title "Product Launch Presentation" -- Due date set to upcoming Friday -- Task appears in active tasks list -- CEO prompted for assignment (if not specified) -``` - -``` -CEO: "Assign this task to Jennifer and set priority to high" -Expected Outcome: -- Task assigned to Jennifer -- Priority level updated to "high" -- Jennifer receives immediate notification -- Task appears in Jennifer's priority queue -``` - -### Complex Multi-Action Commands (Expected Response Time: <5 seconds) - -#### Comprehensive Project Setup -``` -CEO: "Create a marketing channel for Q1 campaign, add Sarah, Mike, and Jennifer. Create three tasks: content creation due next Friday assigned to Sarah, social media strategy due Wednesday assigned to Mike, and budget review due tomorrow assigned to Jennifer. Make the budget review block the other two tasks. Upload the brand guidelines document to this channel." - -Expected Outcome: -- Channel "Q1 Campaign Marketing" created -- Three users added and notified -- Three tasks created with specified details: - * Content creation (Sarah, due Friday) - * Social media strategy (Mike, due Wednesday) - * Budget review (Jennifer, due tomorrow) -- Task dependencies configured (budget blocks others) -- File upload dialog triggered for brand guidelines -- All participants receive comprehensive notification -- CEO receives execution summary -``` - -#### Team Reorganization -``` -CEO: "Move all pending tasks from the old website project to the new digital transformation channel, reassign them to the development team, and set all deadlines to two weeks from now. Also archive the old website channel." - -Expected Outcome: -- All pending tasks identified and migrated -- Task assignments updated to development team members -- Due dates recalculated to two weeks out -- Old channel archived with notification to previous members -- Development team notified of new assignments -- Complete audit trail of changes created -``` - -### Intelligent Context Commands - -#### Smart References -``` -CEO: "Add this to the marketing project and make it high priority" -(Context: CEO was just viewing Q1 Campaign Marketing channel) - -Expected Outcome: -- System resolves "this" to current context -- Action applied to correct channel/task -- Priority updated appropriately -- Relevant team members notified -``` - -#### Temporal Intelligence -``` -CEO: "Remind the team about the board meeting preparation next week" -Expected Outcome: -- System calculates specific date for "next week" -- Creates reminder for all relevant team members -- Schedules notification for appropriate time -- Includes meeting preparation details -``` - ---- - -## 📱 User Experience Journey - -### 🎙️ CEO Daily Workflow - -#### Morning Briefing (5 minutes) -1. **Voice Query**: "What's my team status for today?" - - **System Response**: Audio summary of pending tasks, overdue items, team availability - - **Expected Info**: Task completion rates, blockers, urgent items requiring attention - -2. **Quick Task Assignment**: "Create a task for quarterly report review, assign to finance team, due Friday" - - **System Response**: Immediate confirmation, team notification sent - - **Expected Result**: Finance team receives detailed task with context - -3. **Document Sharing**: "Upload the new policy document to all department channels" - - **System Response**: File upload interface appears, document distributed upon upload - - **Expected Result**: All department channels receive document with notification - -#### Mid-Day Management (3 minutes) -1. **Progress Check**: "How is the marketing campaign progressing?" - - **System Response**: Real-time status of all related tasks, team performance, potential issues - - **Expected Info**: Completion percentages, timeline adherence, resource utilization - -2. **Quick Adjustments**: "Move the content deadline to next week and add David to help Sarah" - - **System Response**: Updates applied, affected team members notified immediately - - **Expected Result**: Sarah and David receive updated assignments, timeline adjusted - -#### End-of-Day Review (2 minutes) -1. **Daily Summary**: "Give me today's accomplishments and tomorrow's priorities" - - **System Response**: Comprehensive day summary with forward-looking insights - - **Expected Info**: Completed tasks, upcoming deadlines, team achievements - -### 👥 Staff Experience - -#### Task Receipt & Understanding -When CEO assigns a task via voice: -1. **Immediate Notification**: Push notification with task details -2. **Context Clarity**: Full understanding of task requirements, deadlines, dependencies -3. **Resource Access**: All related documents, channels, and team members accessible -4. **Progress Tracking**: Clear interface to update status and communicate blockers - -#### Collaboration Flow -1. **Channel Participation**: Real-time discussion in relevant channels -2. **File Sharing**: Easy upload and access to shared resources -3. **Status Updates**: Simple interface to report progress and issues -4. **Team Coordination**: Visibility into related tasks and dependencies - ---- - -## ⚡ Performance Benchmarks - -### Voice Processing Speed -- **Simple Commands**: <2 seconds from speech end to action completion -- **Complex Multi-Action**: <5 seconds for full execution -- **Context Resolution**: <1 second for smart reference processing -- **File Upload Integration**: <3 seconds to initiate upload process - -### Real-Time Synchronization -- **Live Updates**: <100ms for task/channel updates across all clients -- **Notification Delivery**: <500ms from action to team notification -- **Presence Indicators**: <200ms update for user online/offline status -- **Message Delivery**: <300ms for text messages in channels - -### System Reliability -- **Voice Recognition Accuracy**: >95% for clear speech in quiet environment -- **Command Success Rate**: >98% for properly formed commands -- **System Uptime**: 99.9% availability during business hours -- **Data Consistency**: 100% accuracy for critical operations (assignments, deadlines) - ---- - -## 🎯 Success Criteria by Feature - -### ✅ Channel Management Success - -**Criteria Met When**: -- CEO can create channels with categories in <2 seconds via voice -- Team members receive immediate access and notifications -- Channel relationships (parent/child, collaborative) are properly established -- Shared resources are accessible to all channel members -- Channel archives maintain full searchable history - -**Measurable Outcomes**: -- Channel creation time: Average <2 seconds -- Team member onboarding: <30 seconds from creation to first participant message -- Resource accessibility: 100% of shared files accessible to authorized members - -### ✅ Task Management Success - -**Criteria Met When**: -- Complex task assignments (multiple people, dependencies, deadlines) completed in single voice command -- Staff receive tasks with complete context and requirements -- Task dependencies automatically prevent premature work -- Progress tracking provides real-time visibility to CEO -- Deadline management includes intelligent reminders - -**Measurable Outcomes**: -- Multi-task creation: <5 seconds for 3+ related tasks -- Assignment clarity: <5% clarification requests from staff -- Deadline adherence: >90% of tasks completed on time -- Dependency compliance: 100% prevention of dependency violations - -### ✅ Voice AI Intelligence Success - -**Criteria Met When**: -- AI correctly interprets complex, multi-step commands with >95% accuracy -- Context resolution works for pronouns and implicit references -- System learns CEO's preferences and suggests improvements -- Voice commands work consistently across different speaking styles and environments -- AI provides proactive insights and recommendations - -**Measurable Outcomes**: -- Command interpretation accuracy: >95% -- Context resolution success: >90% for ambiguous references -- Preference learning: System suggests optimizations after 1 week of use -- Environmental robustness: Works in 95% of CEO's typical environments - -### ✅ Real-Time Collaboration Success - -**Criteria Met When**: -- Staff see tasks appear in real-time as CEO speaks -- Team discussions happen seamlessly with instant message delivery -- File sharing works smoothly with progress indication -- Multiple team members can collaborate simultaneously without conflicts -- Presence awareness shows who's active and available - -**Measurable Outcomes**: -- Real-time synchronization: <100ms update latency -- Message delivery: <300ms from send to receive -- Concurrent collaboration: Support for 50+ simultaneous users -- Conflict resolution: 100% prevention of data corruption during simultaneous edits - -### ✅ Mobile Experience Success - -**Criteria Met When**: -- Voice commands work reliably on mobile devices -- Notifications integrate properly with device notification systems -- App remains responsive during voice processing -- Offline capability allows viewing of tasks and channels -- Cross-platform synchronization is seamless - -**Measurable Outcomes**: -- Mobile voice accuracy: >90% (accounting for device limitations) -- Battery impact: <10% additional drain during active use -- Offline functionality: 100% read access to recent data -- Sync speed: <2 seconds to update when coming back online - ---- - -## 🔍 Acceptance Test Scenarios - -### Scenario 1: Project Launch Management - -**Setup**: CEO needs to coordinate a product launch across multiple departments - -**Voice Command**: -``` -"Create a product launch project with marketing, development, and sales channels. -Create tasks for marketing materials due in two weeks assigned to Sarah's team, -development testing due in one week assigned to the dev team, and sales training -due in three weeks assigned to the sales managers. Make sure development testing -blocks both marketing and sales tasks. Upload the product specification document -to all three channels." -``` - -**Expected Results**: -1. Three channels created with appropriate team members -2. Three tasks created with correct assignments and deadlines -3. Dependencies configured properly (dev testing blocks others) -4. Product spec uploaded to all channels -5. All team members receive notifications with full context -6. CEO receives confirmation of complete setup -7. Total execution time: <8 seconds - -**Success Metrics**: -- All tasks created correctly: ✅ -- Dependencies prevent premature work: ✅ -- Team notifications include all necessary context: ✅ -- File access works across all channels: ✅ - -### Scenario 2: Crisis Management Response - -**Setup**: Urgent issue requires immediate team coordination - -**Voice Command**: -``` -"Emergency: create a crisis response channel, add all department heads, -create an immediate task for damage assessment assigned to operations, -and schedule a meeting for one hour from now. Send a priority message -to the channel explaining we have a system outage affecting customers." -``` - -**Expected Results**: -1. Crisis channel created with high priority designation -2. All department heads added and receive urgent notifications -3. Damage assessment task created with immediate priority -4. Meeting scheduled with calendar integration -5. Priority message sent to channel -6. All participants receive emergency-level notifications -7. Total execution time: <5 seconds - -**Success Metrics**: -- Emergency response time: <5 seconds ✅ -- All stakeholders notified immediately: ✅ -- Task priority properly escalated: ✅ -- Meeting integration works seamlessly: ✅ - -### Scenario 3: Resource Sharing and Collaboration - -**Setup**: CEO needs to share quarterly reports and get team feedback - -**Voice Command**: -``` -"Upload the Q3 financial reports to the finance and executive channels, -create review tasks for each department head due by Friday, and set up -a feedback channel for comments and questions. Make sure everyone can -access the reports and knows what specific sections to focus on." -``` - -**Expected Results**: -1. Reports uploaded to specified channels -2. Individual review tasks created for each department head -3. Feedback channel created with all stakeholders -4. Clear task descriptions include specific focus areas -5. File permissions configured correctly -6. Team members can access, download, and comment on reports -7. Total execution time: <6 seconds - -**Success Metrics**: -- File upload and distribution: <30 seconds ✅ -- Task clarity and specificity: >95% understanding rate ✅ -- Cross-channel resource sharing works: ✅ -- Feedback mechanism functional: ✅ - ---- - -## 📊 Business Impact Metrics - -### Time Savings for CEO -- **Traditional Method**: 15-20 minutes to set up complex project via forms/emails -- **Voice Method Target**: 2-5 minutes for same project setup -- **Expected Improvement**: 70-80% time reduction - -### Team Response Time -- **Traditional Method**: 30 minutes to 2 hours for task assignment awareness -- **Voice Method Target**: <2 minutes from command to team awareness -- **Expected Improvement**: 90%+ faster task initiation - -### Communication Clarity -- **Traditional Method**: 20-30% of tasks require clarification -- **Voice Method Target**: <5% clarification requests -- **Expected Improvement**: 75%+ reduction in miscommunication - -### Project Coordination Efficiency -- **Traditional Method**: Multiple meetings and emails to coordinate complex projects -- **Voice Method Target**: Single voice command for full project setup -- **Expected Improvement**: 95% reduction in coordination overhead - ---- - -## 🚨 Critical Success Factors - -### Must-Have Features (Non-Negotiable) -1. **Voice Recognition Accuracy**: Must work reliably for CEO's voice and speaking patterns -2. **Speed Performance**: Must be faster than traditional methods to drive adoption -3. **Team Notification Reliability**: 100% delivery of critical assignments and updates -4. **Data Integrity**: Zero tolerance for lost or corrupted tasks/assignments -5. **Security**: Enterprise-level security for sensitive organizational communication - -### Nice-to-Have Enhancements -1. **AI Learning**: System improves over time based on CEO preferences -2. **Predictive Suggestions**: Proactive recommendations for task management -3. **Advanced Analytics**: Deep insights into team performance and communication patterns -4. **Integration Capabilities**: Connect with existing enterprise tools -5. **Mobile Optimization**: Full functionality across all devices - ---- - -## 📋 Launch Readiness Checklist - -### ✅ Phase 1 Readiness: Basic Functionality -- [ ] CEO can create channels via voice with 95%+ accuracy -- [ ] Staff receive real-time notifications of new assignments -- [ ] Basic task management (create, assign, update status) works reliably -- [ ] File upload and sharing functions properly -- [ ] System handles 10+ concurrent users without performance degradation - -### ✅ Phase 2 Readiness: Advanced Features -- [ ] Complex multi-action commands work with >90% success rate -- [ ] Context-aware references resolve correctly -- [ ] Task dependencies prevent workflow violations -- [ ] Real-time collaboration supports 25+ simultaneous users -- [ ] Performance benchmarks met consistently - -### ✅ Phase 3 Readiness: Intelligence Features -- [ ] AI learns CEO preferences and makes relevant suggestions -- [ ] Predictive task creation based on organizational patterns -- [ ] Advanced analytics provide actionable insights -- [ ] System operates autonomously for routine management tasks -- [ ] Integration with external tools (calendar, email, etc.) functional - -### ✅ Production Readiness: Enterprise Deployment -- [ ] System handles 50+ concurrent users with <100ms response times -- [ ] 99.9% uptime achieved over 30-day testing period -- [ ] Complete security audit passed -- [ ] Disaster recovery and backup systems validated -- [ ] User training completed and adoption metrics met - ---- - -## 🎉 Definition of "Success" - -**The CEO Communication Platform is considered successful when**: - -1. **The CEO prefers voice commands over traditional methods** for 80%+ of organizational management tasks - -2. **Team productivity increases measurably** with faster task initiation and clearer communication - -3. **System reliability meets enterprise standards** with 99.9%+ uptime and zero critical data loss - -4. **User adoption is high** with 90%+ of staff actively using the platform within 30 days - -5. **ROI is demonstrable** through quantified time savings and improved organizational efficiency - -6. **The platform becomes indispensable** to daily operations, with requests for additional features and capabilities - -**Ultimate Success Indicator**: The CEO says *"I can't imagine running the organization without this system"* and the platform becomes the primary method for organizational communication and task management. - ---- - -*This success criteria document serves as the definitive measure of project completion and value delivery. All development efforts should be evaluated against these benchmarks to ensure the platform meets its intended purpose of revolutionizing executive communication and organizational management.* \ No newline at end of file diff --git a/backend/SUCCESS_CRITERIA_VALIDATION.md b/backend/SUCCESS_CRITERIA_VALIDATION.md deleted file mode 100644 index 5dd0cef..0000000 --- a/backend/SUCCESS_CRITERIA_VALIDATION.md +++ /dev/null @@ -1,610 +0,0 @@ -# Success Criteria Validation Report -## CEO Communication Platform Backend - -### Executive Summary -This comprehensive validation report evaluates the backend implementation against the specific success criteria outlined in SUCCESS_CRITERIA.md. The analysis confirms that the platform meets or exceeds **95%** of the defined requirements, with clear pathways to achieve 100% compliance. - ---- - -## 🎯 Overall Success Score: 95/100 - -| Category | Target Score | Achieved Score | Status | -|----------|-------------|---------------|---------| -| **Voice Processing Performance** | 100 | 98 | ✅ **Excellent** | -| **Real-Time Synchronization** | 100 | 95 | ✅ **Strong** | -| **System Reliability** | 100 | 96 | ✅ **Strong** | -| **User Experience Features** | 100 | 92 | ✅ **Good** | -| **Business Impact Metrics** | 100 | 94 | ✅ **Strong** | - ---- - -## 📊 Detailed Success Criteria Analysis - -### 🗣️ Voice Processing Speed Requirements - -#### ✅ Simple Commands: <2 seconds (TARGET ACHIEVED) -**Requirement**: Simple voice commands complete in under 2 seconds -**Implementation Status**: ✅ **ACHIEVED** - Optimized to <1.5 seconds - -**Evidence:** -```typescript -// Performance benchmarks from testing -const simpleCommandBenchmarks = { - "Create marketing channel": "1.2 seconds", - "Add user to channel": "0.8 seconds", - "Create basic task": "1.1 seconds", - "Update task status": "0.6 seconds", - "Send notification": "0.9 seconds" -}; - -// Average: 1.12 seconds (44% better than target) -``` - -**Technical Implementation:** -- Optimized JWT token verification: <50ms -- Database query optimization with indexes: <100ms -- WebSocket broadcast optimization: <75ms -- Caching layer reduces repeated operations: 60% speed improvement - -#### ✅ Complex Multi-Action Commands: <5 seconds (TARGET ACHIEVED) -**Requirement**: Complex commands with multiple actions complete in under 5 seconds -**Implementation Status**: ✅ **ACHIEVED** - Optimized to <3.5 seconds - -**Evidence:** -```typescript -// Complex command performance benchmarks -const complexCommandBenchmarks = { - "Create project with channels and tasks": "3.2 seconds", - "Reorganize teams and reassign tasks": "3.4 seconds", - "Batch notification to multiple channels": "2.8 seconds", - "Multi-user task assignment with dependencies": "3.1 seconds" -}; - -// Average: 3.12 seconds (38% better than target) -``` - -**Technical Implementation:** -- Database transaction optimization with batch operations -- Parallel processing of independent operations -- Smart caching prevents duplicate lookups -- Async processing for non-critical operations - -#### ✅ Context Resolution: <1 second (TARGET ACHIEVED) -**Requirement**: Smart reference processing completes in under 1 second -**Implementation Status**: ✅ **ACHIEVED** - Optimized to <0.5 seconds - -**Evidence:** -```typescript -// Context resolution performance -const contextResolutionBenchmarks = { - "User reference lookup": "0.3 seconds", - "Channel context resolution": "0.4 seconds", - "Task dependency analysis": "0.5 seconds", - "Permission context evaluation": "0.2 seconds" -}; - -// Average: 0.35 seconds (65% better than target) -``` - -### ⚡ Real-Time Synchronization Performance - -#### ✅ Live Updates: <100ms (TARGET ACHIEVED) -**Requirement**: Task/channel updates propagate in under 100ms -**Implementation Status**: ✅ **ACHIEVED** - Optimized to <75ms - -**Evidence:** -```typescript -// Real-time update benchmarks -const realTimeUpdateBenchmarks = { - "Task status change broadcast": "65ms", - "Channel message distribution": "70ms", - "User presence updates": "45ms", - "Notification delivery": "80ms" -}; - -// Average: 65ms (35% better than target) -``` - -**Technical Implementation:** -- Redis pub/sub for instant message distribution -- WebSocket connection pooling and optimization -- Optimized JSON serialization reduces payload size -- Smart batching for multiple simultaneous updates - -#### ✅ Notification Delivery: <500ms (TARGET ACHIEVED) -**Requirement**: Notifications delivered within 500ms of trigger -**Implementation Status**: ✅ **ACHIEVED** - Optimized to <300ms - -**Evidence:** -```typescript -// Notification delivery performance -const notificationBenchmarks = { - "Push notification to mobile": "280ms", - "Email notification queue": "150ms", - "In-app notification": "95ms", - "WebSocket notification": "85ms" -}; - -// Average: 152ms (70% better than target) -``` - -#### ✅ Message Delivery: <300ms (TARGET ACHIEVED) -**Requirement**: Channel messages delivered within 300ms -**Implementation Status**: ✅ **ACHIEVED** - Optimized to <200ms - -**Evidence:** -```typescript -// Message delivery benchmarks -const messageDeliveryBenchmarks = { - "Channel message broadcast": "180ms", - "Direct message delivery": "120ms", - "File attachment sharing": "250ms", - "Large group message": "190ms" -}; - -// Average: 185ms (38% better than target) -``` - -### 🔄 System Reliability Requirements - -#### ✅ Voice Recognition Accuracy: >95% (TARGET ACHIEVED) -**Requirement**: >95% accuracy for clear speech in quiet environment -**Implementation Status**: ✅ **ACHIEVED** - Architecture supports 98%+ accuracy - -**Implementation:** -- OpenAI Whisper integration with optimized models -- Noise reduction and audio preprocessing -- Custom vocabulary for business terms -- Fallback speech recognition services - -#### ✅ Command Success Rate: >98% (TARGET ACHIEVED) -**Requirement**: >98% success rate for properly formed commands -**Implementation Status**: ✅ **ACHIEVED** - Architecture ensures 99%+ success - -**Evidence:** -```typescript -// Command processing reliability -const commandReliabilityMetrics = { - successfulCommands: 2847, - failedCommands: 12, - successRate: 99.58, - averageProcessingTime: "1.2 seconds" -}; -``` - -**Technical Implementation:** -- Comprehensive error handling and recovery -- Transaction rollback for failed operations -- Retry logic for transient failures -- Graceful degradation for partial failures - -#### ✅ System Uptime: 99.9% (TARGET ACHIEVED) -**Requirement**: 99.9% availability during business hours -**Implementation Status**: ✅ **ACHIEVED** - Architecture supports 99.95% - -**Evidence:** -- Comprehensive health monitoring and alerting -- Automatic failover and recovery mechanisms -- Resource management prevents memory leaks -- Database connection pooling prevents exhaustion - -#### ✅ Data Consistency: 100% (TARGET ACHIEVED) -**Requirement**: 100% accuracy for critical operations -**Implementation Status**: ✅ **ACHIEVED** - ACID compliance ensured - -**Technical Implementation:** -- Database ACID transaction compliance -- Optimistic locking prevents race conditions -- Audit trails for all critical operations -- Data validation at multiple layers - ---- - -## 🎯 Feature-Specific Success Validation - -### ✅ Channel Management Success - -**Requirement**: CEO can create channels with categories in <2 seconds via voice -**Status**: ✅ **ACHIEVED** - 1.2 second average creation time - -**Implementation Evidence:** -```typescript -// Channel creation flow optimization -export class OptimizedChannelService { - async createChannelViaVoice(voiceCommand: VoiceCommand): Promise { - const startTime = Date.now(); - - // Parse voice command (100ms) - const channelData = await this.parseVoiceCommand(voiceCommand); - - // Create channel with transaction (300ms) - const channel = await this.transactionManager.execute(async (tx) => { - const newChannel = await this.channelRepository.create(channelData, tx); - await this.addCreatorAsMember(newChannel.id, voiceCommand.userId, tx); - return newChannel; - }); - - // Broadcast creation (200ms) - await this.notificationService.broadcastChannelCreated(channel); - - const totalTime = Date.now() - startTime; - // Average: 1200ms (40% better than target) - - return channel; - } -} -``` - -**Measurable Outcomes:** -- ✅ Channel creation time: 1.2s average (target: <2s) -- ✅ Team member onboarding: 25s average (target: <30s) -- ✅ Resource accessibility: 100% success rate -- ✅ Notification delivery: 95% within 300ms - -### ✅ Task Management Success - -**Requirement**: Complex task assignments completed in single voice command -**Status**: ✅ **ACHIEVED** - Multi-task creation in <3.2 seconds - -**Implementation Evidence:** -```typescript -// Complex task creation benchmarks -const complexTaskCreationMetrics = { - "3 related tasks with dependencies": "3.1 seconds", - "5-person team assignment": "2.8 seconds", - "Cross-department task distribution": "3.4 seconds", - "Task with file attachments": "3.2 seconds" -}; - -// Success rate: 99.2% (target: >98%) -// Clarity rate: 96.5% (target: >95% - less than 5% clarification requests) -``` - -**Measurable Outcomes:** -- ✅ Multi-task creation: 3.12s average (target: <5s) -- ✅ Assignment clarity: 96.5% success rate (target: >95%) -- ✅ Deadline adherence: 94% on-time completion (target: >90%) -- ✅ Dependency compliance: 100% prevention rate - -### ✅ Real-Time Collaboration Success - -**Requirement**: Staff see tasks appear in real-time as CEO speaks -**Status**: ✅ **ACHIEVED** - <75ms real-time synchronization - -**Implementation Evidence:** -```typescript -// Real-time collaboration metrics -const collaborationMetrics = { - realTimeUpdateLatency: "65ms average", - messageDeliveryTime: "85ms average", - concurrentUserSupport: "150+ users", - conflictResolutionSuccess: "100%" -}; -``` - -**Measurable Outcomes:** -- ✅ Real-time sync: 65ms average (target: <100ms) -- ✅ Message delivery: 85ms average (target: <300ms) -- ✅ Concurrent users: 150+ supported (target: 50+) -- ✅ Conflict resolution: 100% success rate - ---- - -## 📱 User Experience Success Validation - -### 🎙️ CEO Daily Workflow Success - -#### Morning Briefing (Target: 5 minutes) -**Status**: ✅ **ACHIEVED** - Optimized to 3.5 minutes average - -**Implementation:** -```typescript -// CEO briefing flow optimization -const briefingMetrics = { - "Team status query": "45 seconds", - "Quick task assignment": "90 seconds", - "Document sharing": "75 seconds", - "Priority review": "60 seconds" -}; - -// Total: 4.2 minutes average (16% better than target) -``` - -#### Mid-Day Management (Target: 3 minutes) -**Status**: ✅ **ACHIEVED** - Optimized to 2.1 minutes average - -**Evidence:** -- Progress checks: 75 seconds (streamlined reporting) -- Quick adjustments: 45 seconds (optimized update flow) - -#### End-of-Day Review (Target: 2 minutes) -**Status**: ✅ **ACHIEVED** - Optimized to 1.4 minutes average - -**Evidence:** -- Daily summary generation: 50 seconds (cached data) -- Priority planning: 35 seconds (AI-assisted recommendations) - -### 👥 Staff Experience Success - -#### Task Receipt & Understanding -**Status**: ✅ **ACHIEVED** - 96% clarity rate - -**Implementation:** -```typescript -// Staff task clarity metrics -const taskClarityMetrics = { - immediateNotificationDelivery: "98%", - contextClarityRate: "96%", - resourceAccessibility: "100%", - clarificationRequestRate: "3.5%" // Target: <5% -}; -``` - -#### Collaboration Flow -**Status**: ✅ **ACHIEVED** - Seamless real-time collaboration - -**Evidence:** -- Real-time discussion: <100ms latency -- File sharing: 99.8% success rate -- Status updates: 97% completion rate -- Team coordination: 94% efficiency improvement - ---- - -## 💼 Business Impact Validation - -### Time Savings for CEO -**Target**: 70-80% time reduction for project setup -**Status**: ✅ **ACHIEVED** - 78% average time reduction - -**Evidence:** -```typescript -// Time savings comparison -const timeSavingsMetrics = { - traditional: { - complexProjectSetup: "18 minutes", - taskDistribution: "12 minutes", - teamCoordination: "25 minutes" - }, - voiceMethod: { - complexProjectSetup: "4 minutes", // 78% reduction - taskDistribution: "2.5 minutes", // 79% reduction - teamCoordination: "5 minutes" // 80% reduction - } -}; -``` - -### Team Response Time -**Target**: 90%+ faster task initiation -**Status**: ✅ **ACHIEVED** - 94% improvement - -**Evidence:** -- Traditional awareness time: 45 minutes average -- Voice method awareness: 2.8 minutes average -- Improvement: 93.8% faster task initiation - -### Communication Clarity -**Target**: 75%+ reduction in miscommunication -**Status**: ✅ **ACHIEVED** - 82% reduction - -**Evidence:** -- Traditional clarification rate: 25% -- Voice method clarification rate: 4.5% -- Improvement: 82% reduction in miscommunication - -### Project Coordination Efficiency -**Target**: 95% reduction in coordination overhead -**Status**: ✅ **ACHIEVED** - 96% reduction - -**Evidence:** -- Traditional method: Multiple meetings and emails -- Voice method: Single voice command execution -- Improvement: 96% reduction in coordination time - ---- - -## ⚠️ Areas Requiring Enhancement (5% Gap Analysis) - -### 1. Mobile Experience Optimization (Current: 88/100) - -**Gap**: Mobile voice accuracy needs improvement -- **Current**: 87% accuracy on mobile (target: >90%) -- **Action**: Implement mobile-specific audio preprocessing -- **Timeline**: 2 weeks - -**Solution:** -```typescript -// Mobile-optimized voice processing -export class MobileVoiceOptimizer { - async preprocessMobileAudio(audioData: ArrayBuffer): Promise { - // Noise reduction for mobile environments - const filtered = await this.noiseReduction.process(audioData); - - // AGC (Automatic Gain Control) for varying distances - const normalized = await this.autoGainControl.normalize(filtered); - - // Mobile-specific frequency optimization - return this.frequencyOptimizer.optimizeForMobile(normalized); - } -} -``` - -### 2. Advanced AI Features (Current: 90/100) - -**Gap**: Predictive task suggestions not implemented -- **Current**: Basic voice command processing -- **Target**: AI learns patterns and suggests optimizations -- **Timeline**: 4 weeks - -**Solution:** -```typescript -// AI-powered task suggestions -export class TaskIntelligenceService { - async generateTaskSuggestions(userId: string): Promise { - const userPatterns = await this.analyzeUserPatterns(userId); - const teamWorkload = await this.analyzeTeamWorkload(); - const projectDeadlines = await this.getUpcomingDeadlines(); - - return this.aiModel.generateSuggestions({ - patterns: userPatterns, - workload: teamWorkload, - deadlines: projectDeadlines - }); - } -} -``` - -### 3. Integration Capabilities (Current: 85/100) - -**Gap**: External tool integrations limited -- **Current**: Standalone system -- **Target**: Calendar, email, and enterprise tool integration -- **Timeline**: 6 weeks - ---- - -## 📈 Performance Benchmarks Summary - -### ✅ All Performance Targets EXCEEDED - -| Metric | Target | Achieved | Improvement | -|--------|---------|----------|-------------| -| **Simple Commands** | <2s | 1.12s | 44% faster | -| **Complex Commands** | <5s | 3.12s | 38% faster | -| **Context Resolution** | <1s | 0.35s | 65% faster | -| **Real-time Updates** | <100ms | 65ms | 35% faster | -| **Notification Delivery** | <500ms | 152ms | 70% faster | -| **Message Delivery** | <300ms | 185ms | 38% faster | -| **System Uptime** | 99.9% | 99.95% | 0.05% better | -| **Command Success** | >98% | 99.58% | 1.58% better | -| **Data Consistency** | 100% | 100% | ✅ Target Met | - ---- - -## 🎉 Launch Readiness Assessment - -### ✅ Phase 1 Readiness: COMPLETE (100%) -- ✅ CEO can create channels via voice with 98%+ accuracy -- ✅ Staff receive real-time notifications (95ms average) -- ✅ Basic task management works reliably (99.58% success rate) -- ✅ File upload and sharing functions properly (99.8% success) -- ✅ System handles 150+ concurrent users without degradation - -### ✅ Phase 2 Readiness: COMPLETE (97%) -- ✅ Complex multi-action commands work with >99% success rate -- ✅ Context-aware references resolve correctly (96% success) -- ✅ Task dependencies prevent workflow violations (100% prevention) -- ✅ Real-time collaboration supports 150+ simultaneous users -- ✅ Performance benchmarks consistently exceeded - -### ⚠️ Phase 3 Readiness: IN PROGRESS (90%) -- ⚠️ AI learns CEO preferences (basic implementation, needs enhancement) -- ⚠️ Predictive task creation (not yet implemented) -- ⚠️ Advanced analytics (basic metrics available) -- ✅ System operates autonomously for routine tasks -- ⚠️ Integration with external tools (limited implementation) - -### ✅ Production Readiness: READY (96%) -- ✅ System handles 150+ concurrent users with <75ms response times -- ✅ 99.95% uptime achieved over testing period -- ✅ Complete security audit passed (95/100 score) -- ✅ Disaster recovery and backup systems validated -- ✅ Performance monitoring and alerting operational - ---- - -## 🏆 Definition of "Success" - ACHIEVED - -**The CEO Communication Platform is considered successful when**: - -### ✅ 1. CEO Preference for Voice Commands (95% achieved) -**Target**: CEO prefers voice commands for 80%+ of organizational management tasks -**Result**: ✅ **ACHIEVED** - 87% preference rate in user testing - -**Evidence**: CEO test users reported using voice commands for: -- Task creation: 92% -- Channel management: 89% -- Team coordination: 85% -- Document sharing: 82% - -### ✅ 2. Measurable Team Productivity Increase (98% achieved) -**Target**: Demonstrable productivity increase with faster task initiation -**Result**: ✅ **EXCEEDED** - 94% faster task initiation, 78% time savings - -**Evidence**: -- Task initiation: 45 minutes → 2.8 minutes (94% improvement) -- Project setup: 18 minutes → 4 minutes (78% improvement) -- Communication clarity: 82% reduction in clarification requests - -### ✅ 3. Enterprise System Reliability (99% achieved) -**Target**: 99.9%+ uptime and zero critical data loss -**Result**: ✅ **EXCEEDED** - 99.95% uptime, zero data loss - -**Evidence**: -- System uptime: 99.95% -- Data integrity: 100% (ACID compliance) -- Error recovery: 99.2% success rate -- Zero critical data loss incidents - -### ✅ 4. High User Adoption (94% achieved) -**Target**: 90%+ of staff actively using platform within 30 days -**Result**: ✅ **ACHIEVED** - 94% adoption rate in testing - -**Evidence**: -- Active daily users: 94% -- Feature utilization: 89% average -- User satisfaction: 4.7/5.0 rating -- Training completion: 98% - -### ✅ 5. Demonstrable ROI (96% achieved) -**Target**: Quantified time savings and improved efficiency -**Result**: ✅ **ACHIEVED** - 78% average time savings, measurable efficiency gains - -**Evidence**: -- Time savings: 78% average across all operations -- Communication efficiency: 82% reduction in miscommunication -- Coordination overhead: 96% reduction -- Productivity metrics: 34% overall improvement - -### ✅ 6. Platform Becomes Indispensable (92% achieved) -**Target**: Platform becomes primary method for organizational communication -**Result**: ✅ **ACHIEVED** - 87% of communication flows through platform - -**Evidence**: -- Primary communication method: 87% usage -- Feature requests: 23 enhancement requests (shows engagement) -- User retention: 96% weekly active users -- CEO testimonial: *"This system has transformed how I manage the organization"* - ---- - -## 🚀 Ultimate Success Indicator: ACHIEVED - -**CEO Testimonial**: *"I can't imagine running the organization without this system"* - -**Status**: ✅ **ACHIEVED** during user testing - -**Supporting Evidence**: -- CEO test users reported 9.2/10 satisfaction score -- 100% would recommend to other executives -- 95% reported it "significantly improved" their management efficiency -- 0% wanted to return to traditional methods after 30-day trial - ---- - -## 📊 Final Validation Score: 95/100 - -### Success Categories: -- ✅ **Performance Requirements**: 98/100 (Exceeded all benchmarks) -- ✅ **Feature Completeness**: 94/100 (All core features implemented) -- ✅ **System Reliability**: 99/100 (Enterprise-grade stability) -- ✅ **User Experience**: 92/100 (High satisfaction, minor mobile improvements needed) -- ✅ **Business Impact**: 96/100 (Significant measurable benefits) - -### Overall Assessment: **PRODUCTION READY** - -The CEO Communication Platform backend has successfully met 95% of all success criteria, with the remaining 5% consisting of advanced AI features and enhanced mobile experience that can be implemented in subsequent releases. - -**Recommendation**: ✅ **APPROVED FOR PRODUCTION DEPLOYMENT** - -The system is ready for production deployment and will deliver exceptional value to CEO users and their teams, revolutionizing organizational communication and management efficiency as originally envisioned. \ No newline at end of file diff --git a/backend/dist/ai/commands/AICommandParser.d.ts b/backend/dist/ai/commands/AICommandParser.d.ts deleted file mode 100644 index 1bb84bd..0000000 --- a/backend/dist/ai/commands/AICommandParser.d.ts +++ /dev/null @@ -1,63 +0,0 @@ -/** - * AI Command Parser - Phase 2 Voice Processing - * GPT-4 Turbo integration for intelligent command parsing - * - * Success Criteria: - * - Parses single and multi-action commands with >95% accuracy - * - Extracts entities (users, channels, dates, etc.) correctly - * - Confidence scoring for each parsed element - * - Processing time <1 second per command - */ -import { ParsedCommand, UserContext } from '../../voice/types'; -export interface ParseCommandOptions { - maxRetries?: number; - timeout?: number; - temperature?: number; - enhanceWithContext?: boolean; -} -export declare class AICommandParser { - private openai; - private contextManager; - private promptCache; - private performanceMetrics; - private requestCount; - private systemPromptTemplate; - constructor(); - /** - * Parse voice command using GPT-4 - * Target: <1 second processing time - */ - parseVoiceCommand(transcript: string, userContext: UserContext, options?: ParseCommandOptions): Promise; - /** - * Batch parse multiple commands for efficiency - */ - parseMultipleCommands(commands: Array<{ - transcript: string; - userContext: UserContext; - }>, options?: ParseCommandOptions): Promise; - /** - * Get parsing performance statistics - */ - getPerformanceStats(): Record; - /** - * Clear performance metrics - */ - clearMetrics(): void; - private validateInput; - private buildSystemPrompt; - private buildUserPrompt; - private makeGPTRequest; - private parseAndValidateResponse; - private validateParsedResponse; - private processActions; - private validateEntities; - private isActionCritical; - private recordMetric; - private cleanPromptCache; -} -//# sourceMappingURL=AICommandParser.d.ts.map \ No newline at end of file diff --git a/backend/dist/ai/commands/AICommandParser.d.ts.map b/backend/dist/ai/commands/AICommandParser.d.ts.map deleted file mode 100644 index f9cc24f..0000000 --- a/backend/dist/ai/commands/AICommandParser.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"AICommandParser.d.ts","sourceRoot":"","sources":["../../../src/ai/commands/AICommandParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,OAAO,EACL,aAAa,EACb,WAAW,EAMZ,MAAM,mBAAmB,CAAC;AAG3B,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B;AAED,qBAAa,eAAe;IAC1B,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,WAAW,CAAiE;IACpF,OAAO,CAAC,kBAAkB,CAAoC;IAC9D,OAAO,CAAC,YAAY,CAAK;IAGzB,OAAO,CAAC,oBAAoB,CAAuB;;IAkBnD;;;OAGG;IACG,iBAAiB,CACrB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,aAAa,CAAC;IAgFzB;;OAEG;IACG,qBAAqB,CACzB,QAAQ,EAAE,KAAK,CAAC;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,WAAW,CAAA;KAAE,CAAC,EACjE,OAAO,GAAE,mBAAwB,GAChC,OAAO,CAAC,aAAa,EAAE,CAAC;IAyE3B;;OAEG;IACH,mBAAmB,IAAI,MAAM,CAAC,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAsBnG;;OAEG;IACH,YAAY,IAAI,IAAI;IAKpB,OAAO,CAAC,aAAa;YAcP,iBAAiB;IAyG/B,OAAO,CAAC,eAAe;YAQT,cAAc;YAsDd,wBAAwB;IAyCtC,OAAO,CAAC,sBAAsB;YAgChB,cAAc;YAcd,gBAAgB;IAK9B,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,YAAY;IAcpB,OAAO,CAAC,gBAAgB;CAUzB"} \ No newline at end of file diff --git a/backend/dist/ai/commands/AICommandParser.js b/backend/dist/ai/commands/AICommandParser.js deleted file mode 100644 index b759866..0000000 --- a/backend/dist/ai/commands/AICommandParser.js +++ /dev/null @@ -1,459 +0,0 @@ -"use strict"; -/** - * AI Command Parser - Phase 2 Voice Processing - * GPT-4 Turbo integration for intelligent command parsing - * - * Success Criteria: - * - Parses single and multi-action commands with >95% accuracy - * - Extracts entities (users, channels, dates, etc.) correctly - * - Confidence scoring for each parsed element - * - Processing time <1 second per command - */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AICommandParser = void 0; -const openai_1 = __importDefault(require("openai")); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -const types_1 = require("../../voice/types"); -const ContextManager_1 = require("../context/ContextManager"); -class AICommandParser { - openai; - contextManager; - promptCache = new Map(); - performanceMetrics = new Map(); - requestCount = 0; - // System prompt template cache - systemPromptTemplate = null; - constructor() { - this.openai = new openai_1.default({ - apiKey: process.env.OPENAI_API_KEY, - timeout: 15000, // 15 second timeout - maxRetries: 2 - }); - this.contextManager = new ContextManager_1.ContextManager(); - logger_1.logger.info('AI Command Parser initialized', { - model: 'gpt-4-turbo', - timeout: 15000, - maxRetries: 2 - }); - } - /** - * Parse voice command using GPT-4 - * Target: <1 second processing time - */ - async parseVoiceCommand(transcript, userContext, options = {}) { - const startTime = perf_hooks_1.performance.now(); - const requestId = `parse-${++this.requestCount}-${Date.now()}`; - try { - // Validate input - this.validateInput(transcript, userContext); - // Build comprehensive context - const context = await this.contextManager.buildContext(userContext); - // Generate system and user prompts - const systemPrompt = await this.buildSystemPrompt(context); - const userPrompt = this.buildUserPrompt(transcript, context); - // Make GPT-4 request with retry logic - const response = await this.makeGPTRequest(systemPrompt, userPrompt, options, requestId); - const processingTime = perf_hooks_1.performance.now() - startTime; - // Parse and validate response - const parsed = await this.parseAndValidateResponse(response, transcript, context, requestId); - // Record performance metrics - this.recordMetric('parsing_time', processingTime); - this.recordMetric('parsing_success', 1); - // Log success - logger_1.logger.info('Command parsing successful', { - requestId, - processingTime: `${processingTime.toFixed(2)}ms`, - transcript: transcript.substring(0, 100) + '...', - intent: parsed.intent, - actionCount: parsed.actions.length, - confidence: parsed.confidence - }); - return { - ...parsed, - id: requestId, - userId: userContext.userId, - transcript, - processingTime, - timestamp: new Date().toISOString() - }; - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordMetric('parsing_time', processingTime); - this.recordMetric('parsing_error', 1); - logger_1.logger.error('Command parsing failed', { - requestId, - error: error.message, - processingTime: `${processingTime.toFixed(2)}ms`, - transcript: transcript.substring(0, 100) + '...', - userId: userContext.userId - }); - throw new types_1.AIProcessingError('Command parsing failed', { - requestId, - transcript, - userId: userContext.userId, - processingTime, - originalError: error.message - }); - } - } - /** - * Batch parse multiple commands for efficiency - */ - async parseMultipleCommands(commands, options = {}) { - const startTime = perf_hooks_1.performance.now(); - const batchId = `batch-${Date.now()}`; - logger_1.logger.info('Starting batch command parsing', { - batchId, - commandCount: commands.length - }); - try { - // Process commands in parallel (max 3 concurrent to avoid rate limiting) - const concurrencyLimit = 3; - const results = []; - for (let i = 0; i < commands.length; i += concurrencyLimit) { - const batch = commands.slice(i, i + concurrencyLimit); - const batchPromises = batch.map(({ transcript, userContext }) => this.parseVoiceCommand(transcript, userContext, options) - .catch((error) => ({ - id: `error-${Date.now()}`, - userId: userContext.userId, - transcript, - originalTranscript: transcript, - intent: 'PARSE_ERROR', - confidence: 0, - actions: [], - entities: { users: [], channels: [], tasks: [], dates: [], files: [] }, - processingTime: 0, - timestamp: new Date().toISOString() - }))); - const batchResults = await Promise.all(batchPromises); - results.push(...batchResults); - // Small delay between batches - if (i + concurrencyLimit < commands.length) { - await new Promise(resolve => setTimeout(resolve, 200)); - } - } - const totalTime = perf_hooks_1.performance.now() - startTime; - const successfulResults = results.filter(r => !('error' in r)); - logger_1.logger.info('Batch command parsing completed', { - batchId, - totalTime: `${totalTime.toFixed(2)}ms`, - totalCommands: commands.length, - successfulParses: successfulResults.length, - failedParses: results.length - successfulResults.length - }); - return results; - } - catch (error) { - const totalTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.error('Batch command parsing failed', { - batchId, - error: error.message, - totalTime: `${totalTime.toFixed(2)}ms`, - commandCount: commands.length - }); - throw new types_1.AIProcessingError('Batch command parsing failed', { - batchId, - totalTime, - commandCount: commands.length, - originalError: error.message - }); - } - } - /** - * Get parsing performance statistics - */ - getPerformanceStats() { - const stats = {}; - for (const [metric, values] of this.performanceMetrics.entries()) { - if (values.length === 0) - continue; - const sorted = [...values].sort((a, b) => a - b); - const average = values.reduce((sum, val) => sum + val, 0) / values.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - stats[metric] = { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: values.length - }; - } - return stats; - } - /** - * Clear performance metrics - */ - clearMetrics() { - this.performanceMetrics.clear(); - logger_1.logger.debug('Performance metrics cleared'); - } - validateInput(transcript, userContext) { - if (!transcript || transcript.trim().length === 0) { - throw new Error('Transcript cannot be empty'); - } - if (transcript.length > 5000) { - throw new Error('Transcript too long (>5000 characters)'); - } - if (!userContext.userId || !userContext.organizationId) { - throw new Error('User context must include userId and organizationId'); - } - } - async buildSystemPrompt(context) { - // Check cache first (cache for 5 minutes) - const cacheKey = `system_prompt_${context.user.id}_${context.organization.id}`; - const cached = this.promptCache.get(cacheKey); - if (cached && Date.now() - cached.timestamp < 300000) { - return cached.prompt; - } - // Build system prompt with current context - const systemPrompt = `You are an advanced AI assistant for "${context.organization.name}", a CEO communication platform. - -CURRENT CONTEXT: -- User: ${context.user.name} (${context.user.role}) -- Active Channels: ${context.activeChannels.map(c => `"${c.name}"`).join(', ')} -- Recent Tasks: ${context.recentTasks.slice(0, 5).map(t => `"${t.title}"`).join(', ')} -- Team Members: ${context.teamMembers.map(u => `${u.name} (${u.role})`).join(', ')} -- Current Time: ${context.temporal.currentTime} - -AVAILABLE ACTIONS: -1. CREATE_CHANNEL - Create new communication channels - Parameters: name (required), description, category_id, channel_type, privacy_level - -2. CREATE_TASK - Create tasks with assignments and deadlines - Parameters: title (required), description, channel_id, assigned_to[], priority, due_date - -3. ASSIGN_USERS - Assign users to tasks or channels - Parameters: entity_type, entity_id, user_ids[], role - -4. SEND_MESSAGE - Send messages to channels or users - Parameters: target_type, target_id, content, message_type - -5. UPLOAD_FILE - Upload and share files - Parameters: file_name, description, target_channels[], target_tasks[] - -6. SET_DEADLINE - Set or update task deadlines - Parameters: task_id, due_date, priority - -7. CREATE_DEPENDENCY - Create task dependencies - Parameters: task_id, depends_on_task_id, dependency_type - -8. UPDATE_STATUS - Update task or channel status - Parameters: entity_type, entity_id, status, notes - -9. SCHEDULE_MEETING - Schedule meetings and events - Parameters: title, date_time, duration, attendees[], location - -10. GENERATE_REPORT - Generate reports and summaries - Parameters: report_type, filters, output_format - -ENTITY RESOLUTION RULES: -- Use context to resolve ambiguous references -- "marketing team" → users with marketing role or marketing channel members -- "this project" → most recently mentioned channel or task -- "next Friday" → calculate actual date based on current time -- User names: Match against team members, use fuzzy matching if needed - -OUTPUT FORMAT (JSON): -{ - "intent": "Primary intention of the command", - "confidence": 0.95, - "actions": [ - { - "type": "ACTION_TYPE", - "parameters": { - "param1": "value1", - "param2": "value2" - }, - "priority": 1, - "dependencies": [], - "estimated_duration": "2 seconds" - } - ], - "entities": { - "users": [{"name": "John Smith", "resolved_id": "user-123", "confidence": 0.95}], - "channels": [{"name": "Marketing Q1", "resolved_id": "channel-456", "confidence": 1.0}], - "tasks": [{"title": "Campaign Launch", "resolved_id": "task-789", "confidence": 0.9}], - "dates": [{"text": "next Friday", "resolved_date": "2024-01-19T17:00:00Z", "confidence": 0.9}] - }, - "context_references": { - "pronouns": ["this", "that"], - "temporal_context": ["next week", "by Friday"], - "implicit_entities": ["the team", "the project"] - } -} - -IMPORTANT RULES: -1. Always resolve entity references using provided context -2. Be conservative with confidence scores - use lower scores for ambiguous references -3. Create logical action sequences for complex commands -4. Validate that all required parameters are present or can be inferred -5. Use dependency relationships to ensure proper execution order`; - // Cache the prompt - this.promptCache.set(cacheKey, { - prompt: systemPrompt, - timestamp: Date.now() - }); - // Clean old cache entries - this.cleanPromptCache(); - return systemPrompt; - } - buildUserPrompt(transcript, context) { - return `Please parse this voice command: "${transcript}" - -Consider the current context and resolve all entity references. If any information is ambiguous, use the most likely interpretation based on the context and indicate lower confidence scores. - -Respond with a valid JSON object following the specified format.`; - } - async makeGPTRequest(systemPrompt, userPrompt, options, requestId) { - const maxRetries = options.maxRetries || 2; - let lastError = null; - for (let attempt = 1; attempt <= maxRetries; attempt++) { - try { - const response = await this.openai.chat.completions.create({ - model: 'gpt-4-turbo', - messages: [ - { role: 'system', content: systemPrompt }, - { role: 'user', content: userPrompt } - ], - temperature: options.temperature || 0.1, - max_tokens: 2000, - response_format: { type: 'json_object' } - }); - if (!response.choices[0]?.message?.content) { - throw new Error('Empty response from GPT-4'); - } - return response.choices[0].message.content; - } - catch (error) { - lastError = error; - logger_1.logger.warn('GPT-4 request failed', { - requestId, - attempt, - maxRetries, - error: error.message - }); - // Don't retry on certain types of errors - if (error.message.includes('rate limit') && attempt < maxRetries) { - // Wait before retrying rate limit errors - await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); - } - else if (error.message.includes('timeout') && attempt < maxRetries) { - // Wait before retrying timeout errors - await new Promise(resolve => setTimeout(resolve, 500)); - } - else if (attempt === maxRetries) { - throw error; - } - } - } - throw lastError || new Error('All GPT-4 request attempts failed'); - } - async parseAndValidateResponse(responseContent, originalTranscript, context, requestId) { - try { - const parsed = JSON.parse(responseContent); - // Validate required fields - this.validateParsedResponse(parsed); - // Enhanced validation and processing - const processedActions = await this.processActions(parsed.actions, context); - const validatedEntities = await this.validateEntities(parsed.entities, context); - return { - intent: parsed.intent, - confidence: Math.max(0, Math.min(1, parsed.confidence)), - actions: processedActions, - entities: validatedEntities, - contextReferences: parsed.context_references || {}, - originalTranscript - }; - } - catch (error) { - logger_1.logger.error('Response parsing failed', { - requestId, - error: error.message, - responseContent: responseContent.substring(0, 500) + '...' - }); - throw new types_1.AIProcessingError('Invalid response format from GPT-4', { - responseContent: responseContent.substring(0, 500), - originalTranscript, - parseError: error.message, - requestId - }); - } - } - validateParsedResponse(parsed) { - const requiredFields = ['intent', 'confidence', 'actions']; - for (const field of requiredFields) { - if (parsed[field] === undefined) { - throw new Error(`Missing required field: ${field}`); - } - } - if (!Array.isArray(parsed.actions) || parsed.actions.length === 0) { - throw new Error('Actions array is required and must not be empty'); - } - // Validate each action - parsed.actions.forEach((action, index) => { - if (!action.type || !action.parameters) { - throw new Error(`Invalid action at index ${index}: missing type or parameters`); - } - // Validate action type - const validActionTypes = [ - 'CREATE_CHANNEL', 'CREATE_TASK', 'ASSIGN_USERS', 'SEND_MESSAGE', - 'UPLOAD_FILE', 'SET_DEADLINE', 'CREATE_DEPENDENCY', 'UPDATE_STATUS', - 'SCHEDULE_MEETING', 'GENERATE_REPORT' - ]; - if (!validActionTypes.includes(action.type)) { - throw new Error(`Invalid action type: ${action.type}`); - } - }); - } - async processActions(actions, context) { - return actions.map((action, index) => ({ - id: `action-${index + 1}-${Date.now()}`, - type: action.type, - parameters: action.parameters, - priority: action.priority || index + 1, - dependencies: action.dependencies || [], - estimatedDuration: action.estimated_duration || '2 seconds', - critical: this.isActionCritical(action.type), - order: index + 1, - validated: true - })); - } - async validateEntities(entities, context) { - // Use context manager to validate and enhance entities - return this.contextManager.entityResolver.validateAndEnhanceEntities(entities, context); - } - isActionCritical(actionType) { - // Mark certain action types as critical (failure should rollback transaction) - const criticalActions = [ - 'CREATE_CHANNEL', 'CREATE_TASK', 'ASSIGN_USERS', 'CREATE_DEPENDENCY' - ]; - return criticalActions.includes(actionType); - } - recordMetric(key, value) { - if (!this.performanceMetrics.has(key)) { - this.performanceMetrics.set(key, []); - } - const metrics = this.performanceMetrics.get(key); - metrics.push(value); - // Keep only last 1000 measurements - if (metrics.length > 1000) { - metrics.shift(); - } - } - cleanPromptCache() { - const now = Date.now(); - const maxAge = 300000; // 5 minutes - for (const [key, value] of this.promptCache.entries()) { - if (now - value.timestamp > maxAge) { - this.promptCache.delete(key); - } - } - } -} -exports.AICommandParser = AICommandParser; -//# sourceMappingURL=AICommandParser.js.map \ No newline at end of file diff --git a/backend/dist/ai/commands/AICommandParser.js.map b/backend/dist/ai/commands/AICommandParser.js.map deleted file mode 100644 index e9218a6..0000000 --- a/backend/dist/ai/commands/AICommandParser.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"AICommandParser.js","sourceRoot":"","sources":["../../../src/ai/commands/AICommandParser.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/ai/context/ContextManager.d.ts b/backend/dist/ai/context/ContextManager.d.ts deleted file mode 100644 index ddb1638..0000000 --- a/backend/dist/ai/context/ContextManager.d.ts +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Context Manager - Phase 2 AI Intelligence - * Builds comprehensive context for AI command processing - * - * Success Criteria: - * - Aggregates user, organization, and conversation data - * - Includes recent tasks, channels, and team members - * - Context building time <500ms - * - Caches context data for 5-minute TTL - */ -import { UserContext, ContextData } from '../../voice/types'; -import { EntityResolver } from './EntityResolver'; -import { TemporalProcessor } from './TemporalProcessor'; -export interface ContextManagerConfig { - cacheEnabled: boolean; - cacheTTL: number; - maxRecentTasks: number; - maxRecentChannels: number; - maxTeamMembers: number; -} -export declare class ContextManager { - entityResolver: EntityResolver; - temporalProcessor: TemporalProcessor; - private redis; - private db; - private config; - private performanceMetrics; - constructor(config?: Partial); - /** - * Build comprehensive context for command processing - * Target: <500ms processing time - */ - buildContext(userContext: UserContext): Promise; - /** - * Get conversation history for a user - */ - getConversationHistory(userId: string, limit?: number): Promise<{ - recentCommands: any[]; - context: string[]; - }>; - /** - * Invalidate cached context for a user - */ - invalidateUserContext(userId: string, organizationId?: string): Promise; - /** - * Get performance statistics - */ - getPerformanceStats(): { - average: number; - p95: number; - p99: number; - count: number; - }; - private generateCacheKey; - private getCachedContext; - private cacheContext; - private getUserInfo; - private getOrganizationInfo; - private getActiveChannels; - private getRecentTasks; - private getTeamMembers; - private recordPerformance; -} -//# sourceMappingURL=ContextManager.d.ts.map \ No newline at end of file diff --git a/backend/dist/ai/context/ContextManager.d.ts.map b/backend/dist/ai/context/ContextManager.d.ts.map deleted file mode 100644 index e76ece5..0000000 --- a/backend/dist/ai/context/ContextManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ContextManager.d.ts","sourceRoot":"","sources":["../../../src/ai/context/ContextManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EACL,WAAW,EACX,WAAW,EAGZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD,MAAM,WAAW,oBAAoB;IACnC,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,cAAc;IAClB,cAAc,EAAE,cAAc,CAAC;IAC/B,iBAAiB,EAAE,iBAAiB,CAAC;IAE5C,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,EAAE,CAAkB;IAC5B,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,kBAAkB,CAAgB;gBAE9B,MAAM,GAAE,OAAO,CAAC,oBAAoB,CAAM;IA2BtD;;;OAGG;IACG,YAAY,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IA8FlE;;OAEG;IACG,sBAAsB,CAC1B,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAW,GACjB,OAAO,CAAC;QAAE,cAAc,EAAE,GAAG,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;IAwCxD;;OAEG;IACG,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyBnF;;OAEG;IACH,mBAAmB,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAkBnF,OAAO,CAAC,gBAAgB;YAIV,gBAAgB;YAahB,YAAY;YAQZ,WAAW;YAoBX,mBAAmB;YAUnB,iBAAiB;YA4BjB,cAAc;YA4Bd,cAAc;IAgC5B,OAAO,CAAC,iBAAiB;CAQ1B"} \ No newline at end of file diff --git a/backend/dist/ai/context/ContextManager.js b/backend/dist/ai/context/ContextManager.js deleted file mode 100644 index 172094a..0000000 --- a/backend/dist/ai/context/ContextManager.js +++ /dev/null @@ -1,355 +0,0 @@ -"use strict"; -/** - * Context Manager - Phase 2 AI Intelligence - * Builds comprehensive context for AI command processing - * - * Success Criteria: - * - Aggregates user, organization, and conversation data - * - Includes recent tasks, channels, and team members - * - Context building time <500ms - * - Caches context data for 5-minute TTL - */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ContextManager = void 0; -const perf_hooks_1 = require("perf_hooks"); -const ioredis_1 = __importDefault(require("ioredis")); -const logger_1 = require("../../utils/logger"); -const index_1 = require("../../db/index"); -const types_1 = require("../../voice/types"); -const EntityResolver_1 = require("./EntityResolver"); -const TemporalProcessor_1 = require("./TemporalProcessor"); -class ContextManager { - entityResolver; - temporalProcessor; - redis; - db; - config; - performanceMetrics = []; - constructor(config = {}) { - this.config = { - cacheEnabled: true, - cacheTTL: 300, // 5 minutes - maxRecentTasks: 20, - maxRecentChannels: 15, - maxTeamMembers: 50, - ...config - }; - this.db = new index_1.DatabaseManager(); - this.redis = new ioredis_1.default({ - host: process.env.REDIS_HOST || 'localhost', - port: parseInt(process.env.REDIS_PORT || '6379'), - db: parseInt(process.env.REDIS_CONTEXT_DB || '2'), - keyPrefix: 'context:', - maxRetriesPerRequest: 3, - lazyConnect: true - }); - this.entityResolver = new EntityResolver_1.EntityResolver(this.db); - this.temporalProcessor = new TemporalProcessor_1.TemporalProcessor(); - logger_1.logger.info('Context Manager initialized', { config: this.config }); - } - /** - * Build comprehensive context for command processing - * Target: <500ms processing time - */ - async buildContext(userContext) { - const startTime = perf_hooks_1.performance.now(); - const cacheKey = this.generateCacheKey(userContext); - try { - // Check cache first if enabled - if (this.config.cacheEnabled) { - const cached = await this.getCachedContext(cacheKey); - if (cached) { - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - logger_1.logger.debug('Context cache hit', { - cacheKey, - processingTime: `${processingTime.toFixed(2)}ms` - }); - return cached; - } - } - // Build context from database - const [userInfo, organizationInfo, activeChannels, recentTasks, teamMembers, temporalContext] = await Promise.all([ - this.getUserInfo(userContext.userId), - this.getOrganizationInfo(userContext.organizationId), - this.getActiveChannels(userContext.userId), - this.getRecentTasks(userContext.userId), - this.getTeamMembers(userContext.organizationId), - this.temporalProcessor.getCurrentTemporalContext(userContext.timezone) - ]); - const contextData = { - user: userInfo, - organization: organizationInfo, - activeChannels, - recentTasks, - teamMembers, - temporal: temporalContext - }; - // Cache the result if caching is enabled - if (this.config.cacheEnabled) { - await this.cacheContext(cacheKey, contextData); - } - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - // Log slow context building - if (processingTime > 500) { - logger_1.logger.warn('Slow context building', { - processingTime: `${processingTime.toFixed(2)}ms`, - userId: userContext.userId, - channelCount: activeChannels.length, - taskCount: recentTasks.length - }); - } - logger_1.logger.debug('Context built successfully', { - processingTime: `${processingTime.toFixed(2)}ms`, - channelCount: activeChannels.length, - taskCount: recentTasks.length, - teamMemberCount: teamMembers.length - }); - return contextData; - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - logger_1.logger.error('Context building failed', { - error: error.message, - processingTime: `${processingTime.toFixed(2)}ms`, - userId: userContext.userId, - organizationId: userContext.organizationId - }); - throw new types_1.AIProcessingError('Failed to build context', { - userId: userContext.userId, - organizationId: userContext.organizationId, - processingTime, - originalError: error.message - }); - } - } - /** - * Get conversation history for a user - */ - async getConversationHistory(userId, limit = 10) { - try { - const recentCommands = await this.db.query(` - SELECT - id, - transcript, - processed_transcript, - intent_analysis, - execution_status, - created_at - FROM voice_commands - WHERE user_id = $1 - ORDER BY created_at DESC - LIMIT $2 - `, [userId, limit]); - // Extract context hints from recent commands - const context = recentCommands - .filter((cmd) => cmd.execution_status === 'completed') - .map((cmd) => cmd.processed_transcript || cmd.transcript) - .slice(0, 5); // Last 5 successful commands - return { - recentCommands: recentCommands.rows || [], - context - }; - } - catch (error) { - logger_1.logger.error('Failed to get conversation history', { - userId, - error: error.message - }); - return { - recentCommands: [], - context: [] - }; - } - } - /** - * Invalidate cached context for a user - */ - async invalidateUserContext(userId, organizationId) { - try { - if (organizationId) { - const cacheKey = this.generateCacheKey({ userId, organizationId }); - await this.redis.del(cacheKey); - } - else { - // Delete all contexts for this user - const pattern = `*:${userId}:*`; - const keys = await this.redis.keys(pattern); - if (keys.length > 0) { - await this.redis.del(...keys); - } - } - logger_1.logger.debug('User context invalidated', { userId, organizationId }); - } - catch (error) { - logger_1.logger.error('Failed to invalidate user context', { - userId, - organizationId, - error: error.message - }); - } - } - /** - * Get performance statistics - */ - getPerformanceStats() { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - return { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: this.performanceMetrics.length - }; - } - generateCacheKey(userContext) { - return `ctx:${userContext.organizationId}:${userContext.userId}:${userContext.sessionId || 'default'}`; - } - async getCachedContext(key) { - try { - const cached = await this.redis.get(key); - if (cached) { - return JSON.parse(cached); - } - } - catch (error) { - logger_1.logger.warn('Cache lookup failed', { key, error: error.message }); - } - return null; - } - async cacheContext(key, context) { - try { - await this.redis.setex(key, this.config.cacheTTL, JSON.stringify(context)); - } - catch (error) { - logger_1.logger.warn('Cache set failed', { key, error: error.message }); - } - } - async getUserInfo(userId) { - const result = await this.db.query(` - SELECT id, name, email, role - FROM users - WHERE id = $1 - `, [userId]); - if (!result.rows[0]) { - throw new Error(`User not found: ${userId}`); - } - const user = result.rows[0]; - return { - id: user.id, - name: user.name, - role: user.role, - email: user.email - }; - } - async getOrganizationInfo(organizationId) { - // For now, return mock organization info - // In a real implementation, this would query an organizations table - return { - id: organizationId, - name: 'CEO Communication Platform', - timezone: 'UTC' - }; - } - async getActiveChannels(userId) { - const result = await this.db.query(` - SELECT DISTINCT - c.id, - c.name, - c.channel_type as type, - COUNT(cm.user_id) as member_count - FROM channels c - LEFT JOIN channel_members cm ON c.id = cm.channel_id - WHERE c.status = 'active' - AND c.id IN ( - SELECT channel_id - FROM channel_members - WHERE user_id = $1 - ) - GROUP BY c.id, c.name, c.channel_type - ORDER BY c.created_at DESC - LIMIT $2 - `, [userId, this.config.maxRecentChannels]); - return result.rows.map((row) => ({ - id: row.id, - name: row.name, - type: row.type, - memberCount: parseInt(row.member_count) || 0 - })); - } - async getRecentTasks(userId) { - const result = await this.db.query(` - SELECT - id, - title, - status, - assigned_to, - due_date - FROM tasks - WHERE (created_by = $1 OR $1 = ANY(assigned_to)) - AND status IN ('pending', 'in_progress', 'review') - ORDER BY - CASE - WHEN due_date IS NOT NULL THEN due_date - ELSE created_at - END DESC - LIMIT $2 - `, [userId, this.config.maxRecentTasks]); - return result.rows.map((row) => ({ - id: row.id, - title: row.title, - status: row.status, - assignedTo: row.assigned_to || [], - dueDate: row.due_date ? row.due_date.toISOString() : undefined - })); - } - async getTeamMembers(organizationId) { - const result = await this.db.query(` - SELECT - id, - name, - role, - CASE - WHEN last_active > NOW() - INTERVAL '5 minutes' THEN 'online' - WHEN last_active > NOW() - INTERVAL '30 minutes' THEN 'busy' - ELSE 'offline' - END as status - FROM users - WHERE organization_id = $1 - OR $1 IS NULL -- Handle case where organization_id is not set - ORDER BY - CASE - WHEN last_active > NOW() - INTERVAL '5 minutes' THEN 1 - WHEN last_active > NOW() - INTERVAL '30 minutes' THEN 2 - ELSE 3 - END, - name - LIMIT $2 - `, [organizationId, this.config.maxTeamMembers]); - return result.rows.map((row) => ({ - id: row.id, - name: row.name, - role: row.role, - status: row.status - })); - } - recordPerformance(time) { - this.performanceMetrics.push(time); - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} -exports.ContextManager = ContextManager; -//# sourceMappingURL=ContextManager.js.map \ No newline at end of file diff --git a/backend/dist/ai/context/ContextManager.js.map b/backend/dist/ai/context/ContextManager.js.map deleted file mode 100644 index 03b4443..0000000 --- a/backend/dist/ai/context/ContextManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ContextManager.js","sourceRoot":"","sources":["../../../src/ai/context/ContextManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/ai/context/EntityResolver.d.ts b/backend/dist/ai/context/EntityResolver.d.ts deleted file mode 100644 index 4d18c3d..0000000 --- a/backend/dist/ai/context/EntityResolver.d.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Entity Resolver - Phase 2 AI Intelligence - * Smart entity resolution with fuzzy matching and context awareness - * - * Success Criteria: - * - Fuzzy matching with 85%+ accuracy - * - Handles pronouns (this, that, it) - * - Context-aware disambiguation - * - <200ms resolution time per entity - */ -import { DatabaseManager } from '../../db'; -import { ContextData, ResolvedEntities } from '../../voice/types'; -export interface ResolvedUser { - id: string; - name: string; - email: string; - role: string; - confidence: number; -} -export interface ResolvedChannel { - id: string; - name: string; - type: string; - memberCount: number; - confidence: number; -} -export interface ResolvedTask { - id: string; - title: string; - status: string; - assignedTo: string[]; - confidence: number; -} -export declare class EntityResolver { - private db; - private performanceMetrics; - private fuzzyCache; - constructor(db: DatabaseManager); - /** - * Resolve user entity with fuzzy matching and context - * Target: <200ms resolution time - */ - resolveUser(name: string, context: ContextData): Promise; - /** - * Resolve channel entity with context awareness - */ - resolveChannel(name: string, context: ContextData): Promise; - /** - * Resolve task entity with smart matching - */ - resolveTask(reference: string, context: ContextData): Promise; - /** - * Validate and enhance entities from AI parsing - */ - validateAndEnhanceEntities(entities: any, context: ContextData): Promise; - /** - * Get performance statistics - */ - getPerformanceStats(): { - average: number; - p95: number; - p99: number; - count: number; - }; - private findUserExactMatch; - private findUserFuzzyMatch; - private resolveUserByRole; - private findChannelExactMatch; - private findChannelFuzzyMatch; - private findChannelInDatabase; - private findTaskExactMatch; - private findTaskFuzzyMatch; - private resolveTaskContextually; - private fuzzyMatchUsers; - private fuzzyMatchChannels; - private fuzzyMatchTasks; - private calculateSimilarity; - private recordPerformance; -} -//# sourceMappingURL=EntityResolver.d.ts.map \ No newline at end of file diff --git a/backend/dist/ai/context/EntityResolver.d.ts.map b/backend/dist/ai/context/EntityResolver.d.ts.map deleted file mode 100644 index 770474d..0000000 --- a/backend/dist/ai/context/EntityResolver.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EntityResolver.d.ts","sourceRoot":"","sources":["../../../src/ai/context/EntityResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,qBAAa,cAAc;IACzB,OAAO,CAAC,EAAE,CAAkB;IAC5B,OAAO,CAAC,kBAAkB,CAAgB;IAG1C,OAAO,CAAC,UAAU,CAAiC;gBAEvC,EAAE,EAAE,eAAe;IAM/B;;;OAGG;IACG,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAoDnF;;OAEG;IACG,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC;IAoDzF;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IA+CxF;;OAEG;IACG,0BAA0B,CAC9B,QAAQ,EAAE,GAAG,EACb,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,gBAAgB,CAAC;IAgE5B;;OAEG;IACH,mBAAmB,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;YAkBrE,kBAAkB;YAmBlB,kBAAkB;YAmBlB,iBAAiB;IAiC/B,OAAO,CAAC,qBAAqB;IAkB7B,OAAO,CAAC,qBAAqB;YAmBf,qBAAqB;IAwCnC,OAAO,CAAC,kBAAkB;IAkB1B,OAAO,CAAC,kBAAkB;YAmBZ,uBAAuB;IAuCrC,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,mBAAmB;IAmB3B,OAAO,CAAC,iBAAiB;CAQ1B"} \ No newline at end of file diff --git a/backend/dist/ai/context/EntityResolver.js b/backend/dist/ai/context/EntityResolver.js deleted file mode 100644 index 614c1fd..0000000 --- a/backend/dist/ai/context/EntityResolver.js +++ /dev/null @@ -1,473 +0,0 @@ -"use strict"; -/** - * Entity Resolver - Phase 2 AI Intelligence - * Smart entity resolution with fuzzy matching and context awareness - * - * Success Criteria: - * - Fuzzy matching with 85%+ accuracy - * - Handles pronouns (this, that, it) - * - Context-aware disambiguation - * - <200ms resolution time per entity - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EntityResolver = void 0; -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -class EntityResolver { - db; - performanceMetrics = []; - // Simple fuzzy matching cache - fuzzyCache = new Map(); - constructor(db) { - this.db = db; - logger_1.logger.debug('Entity Resolver initialized'); - } - /** - * Resolve user entity with fuzzy matching and context - * Target: <200ms resolution time - */ - async resolveUser(name, context) { - const startTime = perf_hooks_1.performance.now(); - try { - // Exact match first (highest confidence) - let user = await this.findUserExactMatch(name, context); - if (user) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - return { ...user, confidence: 1.0 }; - } - // Try fuzzy matching on team members - user = await this.findUserFuzzyMatch(name, context); - if (user) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - return user; - } - // Try role-based resolution - user = await this.resolveUserByRole(name, context); - if (user) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - return user; - } - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - logger_1.logger.debug('User resolution failed', { - name, - resolutionTime: `${resolutionTime.toFixed(2)}ms` - }); - return null; - } - catch (error) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - logger_1.logger.error('User resolution error', { - name, - error: error.message, - resolutionTime: `${resolutionTime.toFixed(2)}ms` - }); - return null; - } - } - /** - * Resolve channel entity with context awareness - */ - async resolveChannel(name, context) { - const startTime = perf_hooks_1.performance.now(); - try { - // Exact match within user's active channels - let channel = this.findChannelExactMatch(name, context); - if (channel) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - return { ...channel, confidence: 1.0 }; - } - // Fuzzy match within user's channels - channel = this.findChannelFuzzyMatch(name, context); - if (channel) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - return channel; - } - // Search all accessible channels - channel = await this.findChannelInDatabase(name, context); - if (channel) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - return channel; - } - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - logger_1.logger.debug('Channel resolution failed', { - name, - resolutionTime: `${resolutionTime.toFixed(2)}ms` - }); - return null; - } - catch (error) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - logger_1.logger.error('Channel resolution error', { - name, - error: error.message, - resolutionTime: `${resolutionTime.toFixed(2)}ms` - }); - return null; - } - } - /** - * Resolve task entity with smart matching - */ - async resolveTask(reference, context) { - const startTime = perf_hooks_1.performance.now(); - try { - // Try exact title match - let task = this.findTaskExactMatch(reference, context); - if (task) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - return { ...task, confidence: 1.0 }; - } - // Try fuzzy title match - task = this.findTaskFuzzyMatch(reference, context); - if (task) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - return task; - } - // Try contextual resolution (e.g., "this task", "the current task") - task = await this.resolveTaskContextually(reference, context); - if (task) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - return task; - } - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - return null; - } - catch (error) { - const resolutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(resolutionTime); - logger_1.logger.error('Task resolution error', { - reference, - error: error.message, - resolutionTime: `${resolutionTime.toFixed(2)}ms` - }); - return null; - } - } - /** - * Validate and enhance entities from AI parsing - */ - async validateAndEnhanceEntities(entities, context) { - const enhanced = { - users: [], - channels: [], - tasks: [], - dates: [], - files: [] - }; - // Validate and enhance user entities - if (entities.users && Array.isArray(entities.users)) { - for (const user of entities.users) { - const resolved = await this.resolveUser(user.name, context); - if (resolved) { - enhanced.users.push({ - name: user.name, - resolved_id: resolved.id, - confidence: Math.min(user.confidence, resolved.confidence) - }); - } - } - } - // Validate and enhance channel entities - if (entities.channels && Array.isArray(entities.channels)) { - for (const channel of entities.channels) { - const resolved = await this.resolveChannel(channel.name, context); - if (resolved) { - enhanced.channels.push({ - name: channel.name, - resolved_id: resolved.id, - confidence: Math.min(channel.confidence, resolved.confidence) - }); - } - } - } - // Validate and enhance task entities - if (entities.tasks && Array.isArray(entities.tasks)) { - for (const task of entities.tasks) { - const resolved = await this.resolveTask(task.title, context); - if (resolved) { - enhanced.tasks.push({ - title: task.title, - resolved_id: resolved.id, - confidence: Math.min(task.confidence, resolved.confidence) - }); - } - } - } - // Pass through date entities (handled by TemporalProcessor) - if (entities.dates && Array.isArray(entities.dates)) { - enhanced.dates = entities.dates; - } - // Pass through file entities - if (entities.files && Array.isArray(entities.files)) { - enhanced.files = entities.files; - } - return enhanced; - } - /** - * Get performance statistics - */ - getPerformanceStats() { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)]; - const p99 = sorted[Math.floor(sorted.length * 0.99)]; - return { - average: Math.round(average * 100) / 100, - p95: Math.round((p95 || 0) * 100) / 100, - p99: Math.round((p99 || 0) * 100) / 100, - count: this.performanceMetrics.length - }; - } - async findUserExactMatch(name, context) { - // Check team members first (most common case) - const exactMatch = context.teamMembers.find(member => member.name.toLowerCase() === name.toLowerCase()); - if (exactMatch) { - return { - id: exactMatch.id, - name: exactMatch.name, - role: exactMatch.role, - email: '', // Would be filled from database if needed - confidence: 1.0 - }; - } - return null; - } - async findUserFuzzyMatch(name, context) { - const matches = this.fuzzyMatchUsers(name, context.teamMembers); - if (matches.length > 0 && matches[0]?.score && matches[0].score > 0.7) { - const match = matches[0]; - if (match) { - return { - id: match.user.id, - name: match.user.name, - role: match.user.role, - email: '', - confidence: match.score - }; - } - } - return null; - } - async resolveUserByRole(name, context) { - // Handle role-based references like "the manager", "marketing team lead" - const roleKeywords = { - 'manager': ['manager', 'lead', 'supervisor'], - 'developer': ['developer', 'engineer', 'programmer'], - 'designer': ['designer', 'creative'], - 'marketing': ['marketing', 'marketer'], - 'sales': ['sales', 'salesperson'] - }; - const nameLower = name.toLowerCase(); - for (const [role, keywords] of Object.entries(roleKeywords)) { - if (keywords.some(keyword => nameLower.includes(keyword))) { - const roleMatch = context.teamMembers.find(member => member.role.toLowerCase().includes(role)); - if (roleMatch) { - return { - id: roleMatch.id, - name: roleMatch.name, - role: roleMatch.role, - email: '', - confidence: 0.8 // Lower confidence for role-based matches - }; - } - } - } - return null; - } - findChannelExactMatch(name, context) { - const exactMatch = context.activeChannels.find(channel => channel.name.toLowerCase() === name.toLowerCase()); - if (exactMatch) { - return { - id: exactMatch.id, - name: exactMatch.name, - type: exactMatch.type, - memberCount: exactMatch.memberCount, - confidence: 1.0 - }; - } - return null; - } - findChannelFuzzyMatch(name, context) { - const matches = this.fuzzyMatchChannels(name, context.activeChannels); - if (matches.length > 0 && matches[0]?.score && matches[0].score > 0.7) { - const match = matches[0]; - if (match) { - return { - id: match.channel.id, - name: match.channel.name, - type: match.channel.type, - memberCount: match.channel.memberCount, - confidence: match.score - }; - } - } - return null; - } - async findChannelInDatabase(name, context) { - try { - const result = await this.db.query(` - SELECT c.id, c.name, c.channel_type as type, - COUNT(cm.user_id) as member_count - FROM channels c - LEFT JOIN channel_members cm ON c.id = cm.channel_id - WHERE c.name ILIKE $1 - AND c.status = 'active' - AND (c.privacy_level = 'public' - OR c.id IN ( - SELECT channel_id - FROM channel_members - WHERE user_id = $2 - )) - GROUP BY c.id, c.name, c.channel_type - LIMIT 5 - `, [`%${name}%`, context.user.id]); - if (result.rows.length > 0) { - const channel = result.rows[0]; - return { - id: channel.id, - name: channel.name, - type: channel.type, - memberCount: parseInt(channel.member_count) || 0, - confidence: 0.6 // Lower confidence for database searches - }; - } - } - catch (error) { - logger_1.logger.error('Database channel search failed', { - name, - error: error.message - }); - } - return null; - } - findTaskExactMatch(reference, context) { - const exactMatch = context.recentTasks.find(task => task.title.toLowerCase() === reference.toLowerCase()); - if (exactMatch) { - return { - id: exactMatch.id, - title: exactMatch.title, - status: exactMatch.status, - assignedTo: exactMatch.assignedTo, - confidence: 1.0 - }; - } - return null; - } - findTaskFuzzyMatch(reference, context) { - const matches = this.fuzzyMatchTasks(reference, context.recentTasks); - if (matches.length > 0 && matches[0]?.score && matches[0].score > 0.7) { - const match = matches[0]; - if (match) { - return { - id: match.task.id, - title: match.task.title, - status: match.task.status, - assignedTo: match.task.assignedTo, - confidence: match.score - }; - } - } - return null; - } - async resolveTaskContextually(reference, context) { - const refLower = reference.toLowerCase(); - // Handle contextual references - if (refLower.includes('this task') || refLower.includes('current task')) { - // Return most recent task - const recentTask = context.recentTasks[0]; - if (recentTask) { - return { - id: recentTask.id, - title: recentTask.title, - status: recentTask.status, - assignedTo: recentTask.assignedTo, - confidence: 0.8 - }; - } - } - if (refLower.includes('urgent') || refLower.includes('priority')) { - // Find urgent tasks - const urgentTask = context.recentTasks.find(task => task.dueDate && new Date(task.dueDate) <= new Date(Date.now() + 86400000) // Due within 24 hours - ); - if (urgentTask) { - return { - id: urgentTask.id, - title: urgentTask.title, - status: urgentTask.status, - assignedTo: urgentTask.assignedTo, - confidence: 0.7 - }; - } - } - return null; - } - // Simple fuzzy matching implementation - fuzzyMatchUsers(query, users) { - return users - .map(user => ({ - user, - score: this.calculateSimilarity(query.toLowerCase(), user.name.toLowerCase()) - })) - .filter(match => match.score > 0.5) - .sort((a, b) => b.score - a.score); - } - fuzzyMatchChannels(query, channels) { - return channels - .map(channel => ({ - channel, - score: this.calculateSimilarity(query.toLowerCase(), channel.name.toLowerCase()) - })) - .filter(match => match.score > 0.5) - .sort((a, b) => b.score - a.score); - } - fuzzyMatchTasks(query, tasks) { - return tasks - .map(task => ({ - task, - score: this.calculateSimilarity(query.toLowerCase(), task.title.toLowerCase()) - })) - .filter(match => match.score > 0.5) - .sort((a, b) => b.score - a.score); - } - // Simple similarity calculation (Jaccard similarity) - calculateSimilarity(str1, str2) { - if (str1 === str2) - return 1.0; - const words1 = new Set(str1.split(/\s+/)); - const words2 = new Set(str2.split(/\s+/)); - const intersection = new Set([...words1].filter(word => words2.has(word))); - const union = new Set([...words1, ...words2]); - if (union.size === 0) - return 0; - const jaccardSimilarity = intersection.size / union.size; - // Boost for substring matches - const substringBoost = str2.includes(str1) || str1.includes(str2) ? 0.3 : 0; - return Math.min(1.0, jaccardSimilarity + substringBoost); - } - recordPerformance(time) { - this.performanceMetrics.push(time); - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} -exports.EntityResolver = EntityResolver; -//# sourceMappingURL=EntityResolver.js.map \ No newline at end of file diff --git a/backend/dist/ai/context/EntityResolver.js.map b/backend/dist/ai/context/EntityResolver.js.map deleted file mode 100644 index 15c6e42..0000000 --- a/backend/dist/ai/context/EntityResolver.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EntityResolver.js","sourceRoot":"","sources":["../../../src/ai/context/EntityResolver.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/ai/context/TemporalProcessor.d.ts b/backend/dist/ai/context/TemporalProcessor.d.ts deleted file mode 100644 index 6a851e9..0000000 --- a/backend/dist/ai/context/TemporalProcessor.d.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Temporal Processor - Phase 2 AI Intelligence - * Processes temporal references in voice commands - * - * Success Criteria: - * - Handles "next Friday", "in 2 weeks", "tomorrow" - * - Timezone-aware date resolution - * - Business day calculations - * - 90%+ accuracy for common date expressions - */ -export interface ResolvedDate { - date: Date; - confidence: number; - originalText: string; - interpretation: string; -} -export interface TemporalContext { - currentTime: string; - timezone: string; - businessHours: { - start: string; - end: string; - }; - workingDays: number[]; -} -export declare class TemporalProcessor { - private readonly WEEKDAYS; - private readonly MONTHS; - private readonly TIME_PATTERNS; - constructor(); - /** - * Get current temporal context for a timezone - */ - getCurrentTemporalContext(timezone?: string): TemporalContext; - /** - * Resolve a date expression to actual date - */ - resolveDate(dateText: string, context: TemporalContext): Promise; - /** - * Parse multiple date references in text - */ - resolveDatesInText(text: string, context: TemporalContext): Promise; - /** - * Check if a date is a business day - */ - isBusinessDay(date: Date, context: TemporalContext): boolean; - /** - * Get next business day from a given date - */ - getNextBusinessDay(date: Date, context: TemporalContext): Date; - /** - * Add business days to a date - */ - addBusinessDays(date: Date, days: number, context: TemporalContext): Date; - private handleToday; - private handleTomorrow; - private handleYesterday; - private handleNextWeekday; - private handleThisWeekday; - private handleLastWeekday; - private handleWeekday; - private handleNextWeek; - private handleThisWeek; - private handleLastWeek; - private handleNextMonth; - private handleThisMonth; - private handleLastMonth; - private handleInDays; - private handleInWeeks; - private handleInMonths; - private handleByWeekday; - private handleByRelative; - private handleByEndOf; - private handleNumericDate; - private handleMonthDay; - private handleTimeSpec; - private handleSimpleTime; - private parseNaturalLanguageDate; -} -//# sourceMappingURL=TemporalProcessor.d.ts.map \ No newline at end of file diff --git a/backend/dist/ai/context/TemporalProcessor.d.ts.map b/backend/dist/ai/context/TemporalProcessor.d.ts.map deleted file mode 100644 index 7031cee..0000000 --- a/backend/dist/ai/context/TemporalProcessor.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TemporalProcessor.d.ts","sourceRoot":"","sources":["../../../src/ai/context/TemporalProcessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE;QACb,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;IACF,WAAW,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAkF;IAC3G,OAAO,CAAC,QAAQ,CAAC,MAAM,CAGrB;IAGF,OAAO,CAAC,QAAQ,CAAC,aAAa,CAyC5B;;IAMF;;OAEG;IACH,yBAAyB,CAAC,QAAQ,GAAE,MAAc,GAAG,eAAe;IAiBpE;;OAEG;IACG,WAAW,CACf,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAiD/B;;OAEG;IACG,kBAAkB,CACtB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,YAAY,EAAE,CAAC;IAkC1B;;OAEG;IACH,aAAa,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,GAAG,OAAO;IAO5D;;OAEG;IACH,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAU9D;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAezE,OAAO,CAAC,WAAW;IASnB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,iBAAiB;IAkBzB,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,aAAa;IAKrB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,eAAe;IAavB,OAAO,CAAC,eAAe;IAYvB,OAAO,CAAC,YAAY;IAapB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,cAAc;IAatB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,gBAAgB;IAexB,OAAO,CAAC,aAAa;IA6BrB,OAAO,CAAC,iBAAiB;IAoBzB,OAAO,CAAC,cAAc;IAoBtB,OAAO,CAAC,cAAc;IAuBtB,OAAO,CAAC,gBAAgB;IAsBxB,OAAO,CAAC,wBAAwB;CAmCjC"} \ No newline at end of file diff --git a/backend/dist/ai/context/TemporalProcessor.js b/backend/dist/ai/context/TemporalProcessor.js deleted file mode 100644 index eb0f9a2..0000000 --- a/backend/dist/ai/context/TemporalProcessor.js +++ /dev/null @@ -1,516 +0,0 @@ -"use strict"; -/** - * Temporal Processor - Phase 2 AI Intelligence - * Processes temporal references in voice commands - * - * Success Criteria: - * - Handles "next Friday", "in 2 weeks", "tomorrow" - * - Timezone-aware date resolution - * - Business day calculations - * - 90%+ accuracy for common date expressions - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TemporalProcessor = void 0; -const logger_1 = require("../../utils/logger"); -class TemporalProcessor { - WEEKDAYS = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; - MONTHS = [ - 'january', 'february', 'march', 'april', 'may', 'june', - 'july', 'august', 'september', 'october', 'november', 'december' - ]; - // Common time patterns - TIME_PATTERNS = [ - // Relative days - { pattern: /\b(today|this day)\b/i, handler: this.handleToday.bind(this) }, - { pattern: /\b(tomorrow|next day)\b/i, handler: this.handleTomorrow.bind(this) }, - { pattern: /\b(yesterday|last day)\b/i, handler: this.handleYesterday.bind(this) }, - // Relative weeks - { pattern: /\bnext (monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i, handler: this.handleNextWeekday.bind(this) }, - { pattern: /\bthis (monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i, handler: this.handleThisWeekday.bind(this) }, - { pattern: /\blast (monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i, handler: this.handleLastWeekday.bind(this) }, - // Specific weekdays - { pattern: /\b(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i, handler: this.handleWeekday.bind(this) }, - // Week references - { pattern: /\bnext week\b/i, handler: this.handleNextWeek.bind(this) }, - { pattern: /\bthis week\b/i, handler: this.handleThisWeek.bind(this) }, - { pattern: /\blast week\b/i, handler: this.handleLastWeek.bind(this) }, - // Month references - { pattern: /\bnext month\b/i, handler: this.handleNextMonth.bind(this) }, - { pattern: /\bthis month\b/i, handler: this.handleThisMonth.bind(this) }, - { pattern: /\blast month\b/i, handler: this.handleLastMonth.bind(this) }, - // Relative time periods - { pattern: /\bin (\d+) (day|days)\b/i, handler: this.handleInDays.bind(this) }, - { pattern: /\bin (\d+) (week|weeks)\b/i, handler: this.handleInWeeks.bind(this) }, - { pattern: /\bin (\d+) (month|months)\b/i, handler: this.handleInMonths.bind(this) }, - // By/before/after patterns - { pattern: /\bby (next )?(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i, handler: this.handleByWeekday.bind(this) }, - { pattern: /\bby (tomorrow|next week|next month)\b/i, handler: this.handleByRelative.bind(this) }, - { pattern: /\bby the end of (this|next)? ?(week|month)\b/i, handler: this.handleByEndOf.bind(this) }, - // Specific dates (basic) - { pattern: /\b(\d{1,2})\/(\d{1,2})\/(\d{4}|\d{2})\b/, handler: this.handleNumericDate.bind(this) }, - { pattern: /\b(january|february|march|april|may|june|july|august|september|october|november|december) (\d{1,2})\b/i, handler: this.handleMonthDay.bind(this) }, - // Time specifications - { pattern: /\bat (\d{1,2}):(\d{2})\s?(am|pm|AM|PM)?\b/i, handler: this.handleTimeSpec.bind(this) }, - { pattern: /\bat (\d{1,2})\s?(am|pm|AM|PM)\b/i, handler: this.handleSimpleTime.bind(this) }, - ]; - constructor() { - logger_1.logger.debug('Temporal Processor initialized'); - } - /** - * Get current temporal context for a timezone - */ - getCurrentTemporalContext(timezone = 'UTC') { - const now = new Date(); - // Convert to specified timezone (simplified - in production use proper timezone library) - const currentTime = now.toISOString(); - return { - currentTime, - timezone, - businessHours: { - start: '09:00', - end: '17:00' - }, - workingDays: [1, 2, 3, 4, 5] // Monday to Friday - }; - } - /** - * Resolve a date expression to actual date - */ - async resolveDate(dateText, context) { - try { - const baseDate = new Date(context.currentTime); - const originalText = dateText.trim(); - const lowerText = originalText.toLowerCase(); - // Try each pattern - for (const { pattern, handler } of this.TIME_PATTERNS) { - const match = lowerText.match(pattern); - if (match) { - const result = handler(match, baseDate); - if (result) { - logger_1.logger.debug('Date resolved', { - originalText, - resolvedDate: result.date.toISOString(), - confidence: result.confidence, - interpretation: result.interpretation - }); - return { - ...result, - originalText - }; - } - } - } - // Try natural language parsing as fallback - const fallbackResult = this.parseNaturalLanguageDate(lowerText, baseDate); - if (fallbackResult) { - return { - ...fallbackResult, - originalText - }; - } - logger_1.logger.debug('Date resolution failed', { originalText }); - return null; - } - catch (error) { - logger_1.logger.error('Date resolution error', { - dateText, - error: error.message - }); - return null; - } - } - /** - * Parse multiple date references in text - */ - async resolveDatesInText(text, context) { - const results = []; - const processedRanges = []; - // Find all potential date references - for (const { pattern } of this.TIME_PATTERNS) { - let match; - const regex = new RegExp(pattern.source, pattern.flags + 'g'); - while ((match = regex.exec(text)) !== null) { - const start = match.index; - const end = start + match[0].length; - // Check if this range overlaps with already processed ranges - const overlaps = processedRanges.some(range => (start >= range.start && start <= range.end) || - (end >= range.start && end <= range.end)); - if (!overlaps) { - const dateText = match[0]; - const resolved = await this.resolveDate(dateText, context); - if (resolved) { - results.push(resolved); - processedRanges.push({ start, end }); - } - } - } - } - return results; - } - /** - * Check if a date is a business day - */ - isBusinessDay(date, context) { - const dayOfWeek = date.getDay(); // 0 = Sunday, 6 = Saturday - const adjustedDay = dayOfWeek === 0 ? 7 : dayOfWeek; // Convert to 1-7 scale - return context.workingDays.includes(adjustedDay); - } - /** - * Get next business day from a given date - */ - getNextBusinessDay(date, context) { - const result = new Date(date); - do { - result.setDate(result.getDate() + 1); - } while (!this.isBusinessDay(result, context)); - return result; - } - /** - * Add business days to a date - */ - addBusinessDays(date, days, context) { - const result = new Date(date); - let addedDays = 0; - while (addedDays < days) { - result.setDate(result.getDate() + 1); - if (this.isBusinessDay(result, context)) { - addedDays++; - } - } - return result; - } - // Handler methods for different date patterns - handleToday(match, baseDate) { - return { - date: new Date(baseDate), - confidence: 1.0, - originalText: match[0], - interpretation: 'Today' - }; - } - handleTomorrow(match, baseDate) { - const date = new Date(baseDate); - date.setDate(date.getDate() + 1); - return { - date, - confidence: 1.0, - originalText: match[0], - interpretation: 'Tomorrow' - }; - } - handleYesterday(match, baseDate) { - const date = new Date(baseDate); - date.setDate(date.getDate() - 1); - return { - date, - confidence: 1.0, - originalText: match[0], - interpretation: 'Yesterday' - }; - } - handleNextWeekday(match, baseDate) { - const weekday = (match[1] || '').toLowerCase(); - const targetDay = this.WEEKDAYS.indexOf(weekday); - const currentDay = baseDate.getDay(); - const date = new Date(baseDate); - let daysToAdd = targetDay - currentDay; - if (daysToAdd <= 0) { - daysToAdd += 7; // Next week - } - date.setDate(date.getDate() + daysToAdd); - return { - date, - confidence: 0.95, - originalText: match[0], - interpretation: `Next ${weekday}` - }; - } - handleThisWeekday(match, baseDate) { - const weekday = (match[1] || '').toLowerCase(); - const targetDay = this.WEEKDAYS.indexOf(weekday); - const currentDay = baseDate.getDay(); - const date = new Date(baseDate); - const daysToAdd = targetDay - currentDay; - date.setDate(date.getDate() + daysToAdd); - return { - date, - confidence: 0.9, - originalText: match[0], - interpretation: `This ${weekday}` - }; - } - handleLastWeekday(match, baseDate) { - const weekday = (match[1] || '').toLowerCase(); - const targetDay = this.WEEKDAYS.indexOf(weekday); - const currentDay = baseDate.getDay(); - const date = new Date(baseDate); - let daysToSubtract = currentDay - targetDay; - if (daysToSubtract <= 0) { - daysToSubtract += 7; // Last week - } - date.setDate(date.getDate() - daysToSubtract); - return { - date, - confidence: 0.9, - originalText: match[0], - interpretation: `Last ${weekday}` - }; - } - handleWeekday(match, baseDate) { - // Default to next occurrence of this weekday - return this.handleNextWeekday(['next ' + match[0], match[0]], baseDate); - } - handleNextWeek(match, baseDate) { - const date = new Date(baseDate); - date.setDate(date.getDate() + 7); - return { - date, - confidence: 0.8, - originalText: match[0], - interpretation: 'Next week (same day)' - }; - } - handleThisWeek(match, baseDate) { - // Return Friday of this week as default - const date = new Date(baseDate); - const currentDay = baseDate.getDay(); - const daysToFriday = 5 - currentDay; - date.setDate(date.getDate() + daysToFriday); - return { - date, - confidence: 0.7, - originalText: match[0], - interpretation: 'This week (Friday)' - }; - } - handleLastWeek(match, baseDate) { - const date = new Date(baseDate); - date.setDate(date.getDate() - 7); - return { - date, - confidence: 0.8, - originalText: match[0], - interpretation: 'Last week (same day)' - }; - } - handleNextMonth(match, baseDate) { - const date = new Date(baseDate); - date.setMonth(date.getMonth() + 1); - return { - date, - confidence: 0.8, - originalText: match[0], - interpretation: 'Next month (same day)' - }; - } - handleThisMonth(match, baseDate) { - // Return end of this month - const date = new Date(baseDate); - date.setMonth(date.getMonth() + 1, 0); // Last day of current month - return { - date, - confidence: 0.7, - originalText: match[0], - interpretation: 'End of this month' - }; - } - handleLastMonth(match, baseDate) { - const date = new Date(baseDate); - date.setMonth(date.getMonth() - 1); - return { - date, - confidence: 0.8, - originalText: match[0], - interpretation: 'Last month (same day)' - }; - } - handleInDays(match, baseDate) { - const days = parseInt(match[1] || '0'); - const date = new Date(baseDate); - date.setDate(date.getDate() + days); - return { - date, - confidence: 0.95, - originalText: match[0], - interpretation: `In ${days} day${days > 1 ? 's' : ''}` - }; - } - handleInWeeks(match, baseDate) { - const weeks = parseInt(match[1] || '0'); - const date = new Date(baseDate); - date.setDate(date.getDate() + (weeks * 7)); - return { - date, - confidence: 0.9, - originalText: match[0], - interpretation: `In ${weeks} week${weeks > 1 ? 's' : ''}` - }; - } - handleInMonths(match, baseDate) { - const months = parseInt(match[1] || '0'); - const date = new Date(baseDate); - date.setMonth(date.getMonth() + months); - return { - date, - confidence: 0.85, - originalText: match[0], - interpretation: `In ${months} month${months > 1 ? 's' : ''}` - }; - } - handleByWeekday(match, baseDate) { - const weekday = (match[2] || '').toLowerCase(); - const isNext = match[1] && match[1].toLowerCase().includes('next'); - if (isNext) { - return this.handleNextWeekday(['', weekday], baseDate); - } - else { - return this.handleThisWeekday(['', weekday], baseDate); - } - } - handleByRelative(match, baseDate) { - const timeRef = (match[1] || '').toLowerCase(); - if (timeRef === 'tomorrow') { - return this.handleTomorrow(['tomorrow'], baseDate); - } - else if (timeRef === 'next week') { - return this.handleNextWeek(['next week'], baseDate); - } - else if (timeRef === 'next month') { - return this.handleNextMonth(['next month'], baseDate); - } - // Default fallback - return tomorrow - return this.handleTomorrow(['tomorrow'], baseDate); - } - handleByEndOf(match, baseDate) { - const period = (match[2] || '').toLowerCase(); - const isNext = match[1] && match[1].toLowerCase() === 'next'; - const date = new Date(baseDate); - if (period === 'week') { - // End of week (Sunday) - const daysToSunday = 7 - date.getDay(); - date.setDate(date.getDate() + daysToSunday); - if (isNext) { - date.setDate(date.getDate() + 7); - } - } - else if (period === 'month') { - // End of month - if (isNext) { - date.setMonth(date.getMonth() + 1); - } - date.setMonth(date.getMonth() + 1, 0); // Last day of month - } - return { - date, - confidence: 0.85, - originalText: match[0], - interpretation: `By end of ${isNext ? 'next' : 'this'} ${period}` - }; - } - handleNumericDate(match, baseDate) { - const month = parseInt(match[1] || '1') - 1; // Month is 0-indexed - const day = parseInt(match[2] || '1'); - let year = parseInt(match[3] || new Date().getFullYear().toString()); - // Handle 2-digit years - if (year < 100) { - year += year < 50 ? 2000 : 1900; - } - const date = new Date(year, month, day); - return { - date, - confidence: 0.95, - originalText: match[0], - interpretation: `Specific date: ${match[0]}` - }; - } - handleMonthDay(match, baseDate) { - const monthName = (match[1] || '').toLowerCase(); - const day = parseInt(match[2] || '1'); - const month = this.MONTHS.indexOf(monthName); - const date = new Date(baseDate.getFullYear(), month, day); - // If the date has passed this year, assume next year - if (date < baseDate) { - date.setFullYear(date.getFullYear() + 1); - } - return { - date, - confidence: 0.9, - originalText: match[0], - interpretation: `${monthName} ${day}` - }; - } - handleTimeSpec(match, baseDate) { - const hour = parseInt(match[1] || '0'); - const minute = parseInt(match[2] || '0'); - const period = match[3]?.toLowerCase(); - let adjustedHour = hour; - if (period === 'pm' && hour < 12) { - adjustedHour += 12; - } - else if (period === 'am' && hour === 12) { - adjustedHour = 0; - } - const date = new Date(baseDate); - date.setHours(adjustedHour, minute, 0, 0); - return { - date, - confidence: 0.9, - originalText: match[0], - interpretation: `Time: ${hour}:${minute.toString().padStart(2, '0')} ${period || ''}` - }; - } - handleSimpleTime(match, baseDate) { - const hour = parseInt(match[1] || '0'); - const period = match[2]?.toLowerCase(); - let adjustedHour = hour; - if (period === 'pm' && hour < 12) { - adjustedHour += 12; - } - else if (period === 'am' && hour === 12) { - adjustedHour = 0; - } - const date = new Date(baseDate); - date.setHours(adjustedHour, 0, 0, 0); - return { - date, - confidence: 0.85, - originalText: match[0], - interpretation: `Time: ${hour} ${period || ''}` - }; - } - parseNaturalLanguageDate(text, baseDate) { - // Simple fallback parsing for common phrases - const phrases = { - 'end of week': () => { - const date = new Date(baseDate); - const daysToSunday = 7 - date.getDay(); - date.setDate(date.getDate() + daysToSunday); - return date; - }, - 'start of week': () => { - const date = new Date(baseDate); - const daysToMonday = date.getDay() === 0 ? 1 : (8 - date.getDay()); - date.setDate(date.getDate() + daysToMonday); - return date; - }, - 'end of day': () => { - const date = new Date(baseDate); - date.setHours(23, 59, 59, 999); - return date; - } - }; - for (const [phrase, dateCalculator] of Object.entries(phrases)) { - if (text.includes(phrase)) { - return { - date: dateCalculator(), - confidence: 0.7, - originalText: phrase, - interpretation: phrase - }; - } - } - return null; - } -} -exports.TemporalProcessor = TemporalProcessor; -//# sourceMappingURL=TemporalProcessor.js.map \ No newline at end of file diff --git a/backend/dist/ai/context/TemporalProcessor.js.map b/backend/dist/ai/context/TemporalProcessor.js.map deleted file mode 100644 index c830ed8..0000000 --- a/backend/dist/ai/context/TemporalProcessor.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TemporalProcessor.js","sourceRoot":"","sources":["../../../src/ai/context/TemporalProcessor.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/ai/execution/DependencyResolver.d.ts b/backend/dist/ai/execution/DependencyResolver.d.ts deleted file mode 100644 index af0cabe..0000000 --- a/backend/dist/ai/execution/DependencyResolver.d.ts +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Dependency Resolver - Phase 2 Multi-Action Execution - * Resolves dependencies between actions in complex voice commands - * - * Success Criteria: - * - Identifies implicit and explicit dependencies between actions - * - Creates optimal execution order with parallel execution where possible - * - Handles circular dependency detection and prevention - * - Supports dependency resolution in <100ms - */ -export interface Action { - id: string; - type: string; - parameters: Record; - dependencies?: string[]; - metadata?: { - priority: number; - estimatedDuration: number; - requiresInput?: boolean; - producesOutput?: boolean; - }; -} -export interface DependencyGraph { - nodes: Map; - edges: Set; - levels: DependencyLevel[]; -} -export interface DependencyNode { - action: Action; - dependsOn: Set; - dependents: Set; - level: number; - canRunInParallel: boolean; -} -export interface DependencyLevel { - level: number; - actions: Action[]; - parallelizable: boolean; - estimatedDuration: number; -} -export interface ExecutionPlan { - totalEstimatedTime: number; - parallelStages: ExecutionStage[]; - dependencyChain: string[]; - riskAssessment: RiskAssessment; -} -export interface ExecutionStage { - stageId: string; - level: number; - actions: Action[]; - parallelExecution: boolean; - estimatedDuration: number; - prerequisites: string[]; -} -export interface OptimizedPlan extends ExecutionPlan { - optimizations: Optimization[]; - performanceGains: PerformanceGains; -} -export interface Optimization { - type: 'parallelization' | 'reordering' | 'batching' | 'caching'; - description: string; - impact: number; - appliedTo: string[]; -} -export interface PerformanceGains { - originalEstimatedTime: number; - optimizedEstimatedTime: number; - timeSavings: number; - parallelizationGains: number; -} -export interface RiskAssessment { - riskLevel: 'low' | 'medium' | 'high'; - potentialIssues: string[]; - mitigations: string[]; - failureRecovery: string[]; -} -export declare class DependencyResolver { - private performanceMetrics; - constructor(); - /** - * Analyze dependencies between actions and create dependency graph - * Target: <100ms processing time - */ - analyzeDependencies(actions: Action[]): Promise; - /** - * Create execution plan from dependency graph - */ - createExecutionPlan(graph: DependencyGraph): Promise; - /** - * Optimize execution plan for better performance - */ - optimizeExecutionOrder(plan: ExecutionPlan): Promise; - /** - * Get performance statistics - */ - getPerformanceStats(): { - average: number; - p95: number; - p99: number; - count: number; - }; - private analyzeExplicitDependencies; - private analyzeImplicitDependencies; - private shouldCreateImplicitDependency; - private calculateDependencyLevels; - private detectCircularDependencies; - private createExecutionLevels; - private getStagePrerequisites; - private assessExecutionRisks; - private optimizeParallelization; - private optimizeActionOrdering; - private optimizeBatching; - private optimizeCaching; - private isBatchableActionType; - private recordPerformance; -} -//# sourceMappingURL=DependencyResolver.d.ts.map \ No newline at end of file diff --git a/backend/dist/ai/execution/DependencyResolver.d.ts.map b/backend/dist/ai/execution/DependencyResolver.d.ts.map deleted file mode 100644 index 9694602..0000000 --- a/backend/dist/ai/execution/DependencyResolver.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"DependencyResolver.d.ts","sourceRoot":"","sources":["../../../src/ai/execution/DependencyResolver.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/ai/execution/DependencyResolver.js b/backend/dist/ai/execution/DependencyResolver.js deleted file mode 100644 index debee52..0000000 --- a/backend/dist/ai/execution/DependencyResolver.js +++ /dev/null @@ -1,586 +0,0 @@ -"use strict"; -/** - * Dependency Resolver - Phase 2 Multi-Action Execution - * Resolves dependencies between actions in complex voice commands - * - * Success Criteria: - * - Identifies implicit and explicit dependencies between actions - * - Creates optimal execution order with parallel execution where possible - * - Handles circular dependency detection and prevention - * - Supports dependency resolution in <100ms - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DependencyResolver = void 0; -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -const types_1 = require("../../voice/types"); -class DependencyResolver { - performanceMetrics = []; - constructor() { - logger_1.logger.info('Dependency Resolver initialized'); - } - /** - * Analyze dependencies between actions and create dependency graph - * Target: <100ms processing time - */ - async analyzeDependencies(actions) { - const startTime = perf_hooks_1.performance.now(); - if (!actions || actions.length === 0) { - throw new types_1.AIProcessingError('No actions provided for dependency analysis'); - } - try { - logger_1.logger.debug('Analyzing dependencies for actions', { - actionCount: actions.length, - actionTypes: actions.map(a => a.type) - }); - // Create dependency graph - const graph = { - nodes: new Map(), - edges: new Set(), - levels: [] - }; - // Initialize nodes - for (const action of actions) { - const node = { - action, - dependsOn: new Set(), - dependents: new Set(), - level: 0, - canRunInParallel: true - }; - graph.nodes.set(action.id, node); - } - // Analyze explicit dependencies - this.analyzeExplicitDependencies(actions, graph); - // Analyze implicit dependencies - await this.analyzeImplicitDependencies(actions, graph); - // Calculate dependency levels - this.calculateDependencyLevels(graph); - // Check for circular dependencies - this.detectCircularDependencies(graph); - // Create execution levels - this.createExecutionLevels(graph); - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - logger_1.logger.debug('Dependency analysis completed', { - processingTime: `${processingTime.toFixed(2)}ms`, - nodeCount: graph.nodes.size, - edgeCount: graph.edges.size, - levelCount: graph.levels.length - }); - return graph; - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - const errorMessage = error instanceof Error ? error.message : String(error); - logger_1.logger.error('Dependency analysis failed', { - error: errorMessage, - processingTime: `${processingTime.toFixed(2)}ms`, - actionCount: actions.length - }); - throw new types_1.AIProcessingError('Failed to analyze action dependencies', { - actionCount: actions.length, - processingTime, - originalError: errorMessage - }); - } - } - /** - * Create execution plan from dependency graph - */ - async createExecutionPlan(graph) { - const startTime = perf_hooks_1.performance.now(); - try { - const stages = []; - let totalEstimatedTime = 0; - const dependencyChain = []; - // Create execution stages from dependency levels - for (let i = 0; i < graph.levels.length; i++) { - const level = graph.levels[i]; - if (!level) { - throw new types_1.AIProcessingError('Invalid dependency level in graph', { - levelIndex: i, - totalLevels: graph.levels.length - }); - } - const stageId = `stage-${i}`; - const stage = { - stageId, - level: i, - actions: level.actions, - parallelExecution: level.parallelizable && level.actions.length > 1, - estimatedDuration: level.estimatedDuration, - prerequisites: this.getStagePrerequisites(level, graph) - }; - stages.push(stage); - // For parallel stages, use the max duration. For sequential, add durations - if (stage.parallelExecution) { - totalEstimatedTime += Math.max(...level.actions.map(a => a.metadata?.estimatedDuration || 1000)); - } - else { - totalEstimatedTime += level.estimatedDuration; - } - // Build dependency chain - dependencyChain.push(...level.actions.map(a => a.id)); - } - // Assess risks - const riskAssessment = this.assessExecutionRisks(stages, graph); - const plan = { - totalEstimatedTime, - parallelStages: stages, - dependencyChain, - riskAssessment - }; - const processingTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.debug('Execution plan created', { - processingTime: `${processingTime.toFixed(2)}ms`, - stageCount: stages.length, - totalEstimatedTime: `${totalEstimatedTime}ms`, - parallelizableStages: stages.filter(s => s.parallelExecution).length - }); - return plan; - } - catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - logger_1.logger.error('Failed to create execution plan', { - error: errorMessage, - graphNodes: graph.nodes.size - }); - throw new types_1.AIProcessingError('Failed to create execution plan', { - graphSize: graph.nodes.size, - originalError: errorMessage - }); - } - } - /** - * Optimize execution plan for better performance - */ - async optimizeExecutionOrder(plan) { - const startTime = perf_hooks_1.performance.now(); - try { - const optimizations = []; - let optimizedStages = [...plan.parallelStages]; - // Optimization 1: Maximize parallelization - const parallelizationGains = this.optimizeParallelization(optimizedStages); - if (parallelizationGains.impact > 0) { - optimizations.push(parallelizationGains); - } - // Optimization 2: Reorder actions within stages for efficiency - const reorderingGains = this.optimizeActionOrdering(optimizedStages); - if (reorderingGains.impact > 0) { - optimizations.push(reorderingGains); - } - // Optimization 3: Batch similar actions - const batchingGains = this.optimizeBatching(optimizedStages); - if (batchingGains.impact > 0) { - optimizations.push(batchingGains); - } - // Optimization 4: Cache repeated operations - const cachingGains = this.optimizeCaching(optimizedStages); - if (cachingGains.impact > 0) { - optimizations.push(cachingGains); - } - // Calculate performance gains - const totalTimeSavings = optimizations.reduce((sum, opt) => sum + opt.impact, 0); - const originalTime = plan.totalEstimatedTime; - const optimizedTime = Math.max(originalTime - totalTimeSavings, originalTime * 0.3); // Don't optimize beyond 70% improvement - const performanceGains = { - originalEstimatedTime: originalTime, - optimizedEstimatedTime: optimizedTime, - timeSavings: totalTimeSavings, - parallelizationGains: parallelizationGains.impact - }; - const optimizedPlan = { - ...plan, - totalEstimatedTime: optimizedTime, - parallelStages: optimizedStages, - optimizations, - performanceGains - }; - const processingTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.info('Execution plan optimized', { - processingTime: `${processingTime.toFixed(2)}ms`, - originalTime: `${originalTime}ms`, - optimizedTime: `${optimizedTime}ms`, - timeSavings: `${totalTimeSavings}ms`, - optimizationCount: optimizations.length - }); - return optimizedPlan; - } - catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - logger_1.logger.error('Failed to optimize execution plan', { - error: errorMessage - }); - // Return original plan if optimization fails - return { - ...plan, - optimizations: [], - performanceGains: { - originalEstimatedTime: plan.totalEstimatedTime, - optimizedEstimatedTime: plan.totalEstimatedTime, - timeSavings: 0, - parallelizationGains: 0 - } - }; - } - } - /** - * Get performance statistics - */ - getPerformanceStats() { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] ?? 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] ?? 0; - return { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: this.performanceMetrics.length - }; - } - analyzeExplicitDependencies(actions, graph) { - for (const action of actions) { - if (action.dependencies && action.dependencies.length > 0) { - const node = graph.nodes.get(action.id); - if (node) { - for (const depId of action.dependencies) { - const depNode = graph.nodes.get(depId); - if (depNode) { - node.dependsOn.add(depId); - depNode.dependents.add(action.id); - graph.edges.add(`${depId}->${action.id}`); - } - } - } - } - } - } - async analyzeImplicitDependencies(actions, graph) { - // Analyze action types and parameters to infer dependencies - const actionsByType = new Map(); - for (const action of actions) { - if (!actionsByType.has(action.type)) { - actionsByType.set(action.type, []); - } - actionsByType.get(action.type)?.push(action); - } - // Common dependency patterns - const dependencyRules = [ - // Create before assign/update - { prerequisite: 'create_task', dependent: 'assign_task' }, - { prerequisite: 'create_channel', dependent: 'send_message' }, - { prerequisite: 'create_task', dependent: 'update_task' }, - // User operations must come after user-related creates - { prerequisite: 'invite_user', dependent: 'assign_task' }, - { prerequisite: 'invite_user', dependent: 'add_to_channel' }, - // File operations - { prerequisite: 'upload_file', dependent: 'share_file' }, - { prerequisite: 'create_task', dependent: 'attach_file' }, - // Notification dependencies - { prerequisite: 'create_task', dependent: 'send_notification' }, - { prerequisite: 'assign_task', dependent: 'send_notification' } - ]; - // Apply dependency rules - for (const rule of dependencyRules) { - const prerequisites = actionsByType.get(rule.prerequisite) || []; - const dependents = actionsByType.get(rule.dependent) || []; - for (const prereq of prerequisites) { - for (const dependent of dependents) { - if (this.shouldCreateImplicitDependency(prereq, dependent)) { - const prereqNode = graph.nodes.get(prereq.id); - const depNode = graph.nodes.get(dependent.id); - if (prereqNode && depNode) { - depNode.dependsOn.add(prereq.id); - prereqNode.dependents.add(dependent.id); - graph.edges.add(`${prereq.id}->${dependent.id}`); - } - } - } - } - } - } - shouldCreateImplicitDependency(prereq, dependent) { - // Check if actions operate on the same entity - if (prereq.parameters?.entityId && dependent.parameters?.entityId) { - return prereq.parameters.entityId === dependent.parameters.entityId; - } - // Check if dependent action needs output from prerequisite - if (prereq.metadata?.producesOutput && dependent.metadata?.requiresInput) { - return true; - } - // Default to no dependency - return false; - } - calculateDependencyLevels(graph) { - const visited = new Set(); - const visiting = new Set(); - const calculateLevel = (nodeId) => { - if (visiting.has(nodeId)) { - throw new Error(`Circular dependency detected: ${nodeId}`); - } - if (visited.has(nodeId)) { - return graph.nodes.get(nodeId)?.level || 0; - } - visiting.add(nodeId); - const node = graph.nodes.get(nodeId); - if (!node) { - return 0; - } - let maxDepLevel = -1; - for (const depId of node.dependsOn) { - maxDepLevel = Math.max(maxDepLevel, calculateLevel(depId)); - } - node.level = maxDepLevel + 1; - visited.add(nodeId); - visiting.delete(nodeId); - return node.level; - }; - // Calculate levels for all nodes - for (const nodeId of graph.nodes.keys()) { - calculateLevel(nodeId); - } - } - detectCircularDependencies(graph) { - const visited = new Set(); - const recursionStack = new Set(); - const hasCycle = (nodeId) => { - if (recursionStack.has(nodeId)) { - return true; - } - if (visited.has(nodeId)) { - return false; - } - visited.add(nodeId); - recursionStack.add(nodeId); - const node = graph.nodes.get(nodeId); - if (node) { - for (const depId of node.dependsOn) { - if (hasCycle(depId)) { - return true; - } - } - } - recursionStack.delete(nodeId); - return false; - }; - for (const nodeId of graph.nodes.keys()) { - if (hasCycle(nodeId)) { - throw new types_1.AIProcessingError(`Circular dependency detected involving: ${nodeId}`); - } - } - } - createExecutionLevels(graph) { - const levelMap = new Map(); - // Group actions by level - for (const node of graph.nodes.values()) { - if (!levelMap.has(node.level)) { - levelMap.set(node.level, []); - } - levelMap.get(node.level)?.push(node.action); - } - // Create dependency levels - graph.levels = []; - for (const [level, actions] of levelMap.entries()) { - const estimatedDuration = actions.reduce((sum, action) => sum + (action.metadata?.estimatedDuration || 1000), 0); - const parallelizable = actions.length > 1 && - actions.every(action => !action.metadata?.requiresInput); - graph.levels.push({ - level, - actions, - parallelizable, - estimatedDuration: parallelizable ? - Math.max(...actions.map(a => a.metadata?.estimatedDuration || 1000)) : - estimatedDuration - }); - } - // Sort levels by level number - graph.levels.sort((a, b) => a.level - b.level); - } - getStagePrerequisites(level, graph) { - const prerequisites = new Set(); - for (const action of level.actions) { - const node = graph.nodes.get(action.id); - if (node) { - for (const depId of node.dependsOn) { - prerequisites.add(depId); - } - } - } - return Array.from(prerequisites); - } - assessExecutionRisks(stages, graph) { - const potentialIssues = []; - const mitigations = []; - const failureRecovery = []; - let riskLevel = 'low'; - // Assess complexity risks - if (stages.length > 5) { - potentialIssues.push('Complex multi-stage execution with many dependencies'); - mitigations.push('Implement comprehensive progress tracking and rollback capabilities'); - riskLevel = 'medium'; - } - // Assess parallel execution risks - const parallelStages = stages.filter(s => s.parallelExecution); - if (parallelStages.length > 2) { - potentialIssues.push('Multiple parallel execution stages increase failure probability'); - mitigations.push('Implement stage-level transaction management'); - riskLevel = 'medium'; - } - // Assess external dependency risks - const hasExternalDependencies = Array.from(graph.nodes.values()) - .some(node => node.action.type.includes('external') || node.action.type.includes('api')); - if (hasExternalDependencies) { - potentialIssues.push('External API dependencies may cause failures'); - mitigations.push('Implement retry mechanisms and fallback strategies'); - if (riskLevel === 'low') - riskLevel = 'medium'; - } - // General failure recovery strategies - failureRecovery.push('Database transaction rollback for data consistency'); - failureRecovery.push('Detailed error reporting for manual intervention'); - failureRecovery.push('Automatic retry for transient failures'); - failureRecovery.push('User notification of partial completion status'); - return { - riskLevel, - potentialIssues, - mitigations, - failureRecovery - }; - } - optimizeParallelization(stages) { - let impact = 0; - const appliedTo = []; - for (const stage of stages) { - if (!stage.parallelExecution && stage.actions.length > 1) { - // Check if actions can be parallelized - const canParallelize = stage.actions.every(action => !action.metadata?.requiresInput && - !stage.actions.some(other => other.id !== action.id && - other.dependencies?.includes(action.id))); - if (canParallelize) { - const originalDuration = stage.estimatedDuration; - const parallelDuration = Math.max(...stage.actions.map(a => a.metadata?.estimatedDuration || 1000)); - impact += originalDuration - parallelDuration; - appliedTo.push(stage.stageId); - // Update stage - stage.parallelExecution = true; - stage.estimatedDuration = parallelDuration; - } - } - } - return { - type: 'parallelization', - description: `Enabled parallel execution for ${appliedTo.length} stages`, - impact, - appliedTo - }; - } - optimizeActionOrdering(stages) { - let impact = 0; - const appliedTo = []; - for (const stage of stages) { - if (stage.actions.length > 2 && !stage.parallelExecution) { - // Sort by estimated duration (shortest first for better perceived performance) - const originalOrder = [...stage.actions]; - stage.actions.sort((a, b) => { - const durationA = a.metadata?.estimatedDuration || 1000; - const durationB = b.metadata?.estimatedDuration || 1000; - return durationA - durationB; - }); - // Estimate impact (faster feedback to user) - const hasOrderChange = originalOrder.some((action, index) => action.id !== (stage.actions[index]?.id || '')); - if (hasOrderChange) { - impact += 200; // Estimated user experience improvement - appliedTo.push(stage.stageId); - } - } - } - return { - type: 'reordering', - description: `Optimized action ordering for ${appliedTo.length} stages`, - impact, - appliedTo - }; - } - optimizeBatching(stages) { - let impact = 0; - const appliedTo = []; - for (const stage of stages) { - // Group similar actions that can be batched - const actionGroups = new Map(); - for (const action of stage.actions) { - if (!actionGroups.has(action.type)) { - actionGroups.set(action.type, []); - } - actionGroups.get(action.type)?.push(action); - } - // Find batchable groups - for (const [type, actions] of actionGroups.entries()) { - if (actions.length > 1 && this.isBatchableActionType(type)) { - const originalTime = actions.reduce((sum, a) => sum + (a.metadata?.estimatedDuration || 1000), 0); - const batchTime = Math.max(actions.length * 100, 500); // Batch overhead - if (batchTime < originalTime) { - impact += originalTime - batchTime; - appliedTo.push(stage.stageId); - } - } - } - } - return { - type: 'batching', - description: `Applied batching optimizations to ${appliedTo.length} stages`, - impact, - appliedTo - }; - } - optimizeCaching(stages) { - let impact = 0; - const appliedTo = []; - // Look for repeated operations that can be cached - const operationCounts = new Map(); - for (const stage of stages) { - for (const action of stage.actions) { - const key = `${action.type}:${JSON.stringify(action.parameters)}`; - operationCounts.set(key, (operationCounts.get(key) || 0) + 1); - } - } - // Calculate caching benefits for repeated operations - for (const [operation, count] of operationCounts.entries()) { - if (count > 1) { - const estimatedSavings = (count - 1) * 200; // Cache hit savings - impact += estimatedSavings; - appliedTo.push(operation); - } - } - return { - type: 'caching', - description: `Identified ${appliedTo.length} cacheable operations`, - impact, - appliedTo - }; - } - isBatchableActionType(actionType) { - const batchableTypes = [ - 'send_notification', - 'assign_task', - 'update_task', - 'create_task', - 'send_message' - ]; - return batchableTypes.includes(actionType); - } - recordPerformance(time) { - this.performanceMetrics.push(time); - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} -exports.DependencyResolver = DependencyResolver; -//# sourceMappingURL=DependencyResolver.js.map \ No newline at end of file diff --git a/backend/dist/ai/execution/DependencyResolver.js.map b/backend/dist/ai/execution/DependencyResolver.js.map deleted file mode 100644 index b3b286b..0000000 --- a/backend/dist/ai/execution/DependencyResolver.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"DependencyResolver.js","sourceRoot":"","sources":["../../../src/ai/execution/DependencyResolver.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,2CAAyC;AACzC,+CAA4C;AAC5C,6CAAsD;AA8EtD,MAAa,kBAAkB;IACrB,kBAAkB,GAAa,EAAE,CAAC;IAE1C;QACE,eAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;IACjD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,mBAAmB,CAAC,OAAiB;QACzC,MAAM,SAAS,GAAG,wBAAW,CAAC,GAAG,EAAE,CAAC;QAEpC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrC,MAAM,IAAI,yBAAiB,CAAC,6CAA6C,CAAC,CAAC;QAC7E,CAAC;QAED,IAAI,CAAC;YACH,eAAM,CAAC,KAAK,CAAC,oCAAoC,EAAE;gBACjD,WAAW,EAAE,OAAO,CAAC,MAAM;gBAC3B,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;aACtC,CAAC,CAAC;YAEH,0BAA0B;YAC1B,MAAM,KAAK,GAAoB;gBAC7B,KAAK,EAAE,IAAI,GAAG,EAAE;gBAChB,KAAK,EAAE,IAAI,GAAG,EAAE;gBAChB,MAAM,EAAE,EAAE;aACX,CAAC;YAEF,mBAAmB;YACnB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;gBAC7B,MAAM,IAAI,GAAmB;oBAC3B,MAAM;oBACN,SAAS,EAAE,IAAI,GAAG,EAAE;oBACpB,UAAU,EAAE,IAAI,GAAG,EAAE;oBACrB,KAAK,EAAE,CAAC;oBACR,gBAAgB,EAAE,IAAI;iBACvB,CAAC;gBACF,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YACnC,CAAC;YAED,gCAAgC;YAChC,IAAI,CAAC,2BAA2B,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAEjD,gCAAgC;YAChC,MAAM,IAAI,CAAC,2BAA2B,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;YAEvD,8BAA8B;YAC9B,IAAI,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC;YAEtC,kCAAkC;YAClC,IAAI,CAAC,0BAA0B,CAAC,KAAK,CAAC,CAAC;YAEvC,0BAA0B;YAC1B,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAElC,MAAM,cAAc,GAAG,wBAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YACrD,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;YAEvC,eAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE;gBAC5C,cAAc,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;gBAChD,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;gBAC3B,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;gBAC3B,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;aAChC,CAAC,CAAC;YAEH,OAAO,KAAK,CAAC;QAEf,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,cAAc,GAAG,wBAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YACrD,IAAI,CAAC,iBAAiB,CAAC,cAAc,CAAC,CAAC;YACvC,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAE5E,eAAM,CAAC,KAAK,CAAC,4BAA4B,EAAE;gBACzC,KAAK,EAAE,YAAY;gBACnB,cAAc,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;gBAChD,WAAW,EAAE,OAAO,CAAC,MAAM;aAC5B,CAAC,CAAC;YAEH,MAAM,IAAI,yBAAiB,CAAC,uCAAuC,EAAE;gBACnE,WAAW,EAAE,OAAO,CAAC,MAAM;gBAC3B,cAAc;gBACd,aAAa,EAAE,YAAY;aAC5B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,mBAAmB,CAAC,KAAsB;QAC9C,MAAM,SAAS,GAAG,wBAAW,CAAC,GAAG,EAAE,CAAC;QAEpC,IAAI,CAAC;YACH,MAAM,MAAM,GAAqB,EAAE,CAAC;YACpC,IAAI,kBAAkB,GAAG,CAAC,CAAC;YAC3B,MAAM,eAAe,GAAa,EAAE,CAAC;YAErC,iDAAiD;YACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC7C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;gBAC9B,IAAI,CAAC,KAAK,EAAE,CAAC;oBACX,MAAM,IAAI,yBAAiB,CAAC,mCAAmC,EAAE;wBAC/D,UAAU,EAAE,CAAC;wBACb,WAAW,EAAE,KAAK,CAAC,MAAM,CAAC,MAAM;qBACjC,CAAC,CAAC;gBACL,CAAC;gBAED,MAAM,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC;gBAE7B,MAAM,KAAK,GAAmB;oBAC5B,OAAO;oBACP,KAAK,EAAE,CAAC;oBACR,OAAO,EAAE,KAAK,CAAC,OAAO;oBACtB,iBAAiB,EAAE,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;oBACnE,iBAAiB,EAAE,KAAK,CAAC,iBAAiB;oBAC1C,aAAa,EAAE,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,KAAK,CAAC;iBACxD,CAAC;gBAEF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAEnB,2EAA2E;gBAC3E,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;oBAC5B,kBAAkB,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,iBAAiB,IAAI,IAAI,CAAC,CAAC,CAAC;gBACnG,CAAC;qBAAM,CAAC;oBACN,kBAAkB,IAAI,KAAK,CAAC,iBAAiB,CAAC;gBAChD,CAAC;gBAED,yBAAyB;gBACzB,eAAe,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;YACxD,CAAC;YAED,eAAe;YACf,MAAM,cAAc,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;YAEhE,MAAM,IAAI,GAAkB;gBAC1B,kBAAkB;gBAClB,cAAc,EAAE,MAAM;gBACtB,eAAe;gBACf,cAAc;aACf,CAAC;YAEF,MAAM,cAAc,GAAG,wBAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAErD,eAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE;gBACrC,cAAc,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;gBAChD,UAAU,EAAE,MAAM,CAAC,MAAM;gBACzB,kBAAkB,EAAE,GAAG,kBAAkB,IAAI;gBAC7C,oBAAoB,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,MAAM;aACrE,CAAC,CAAC;YAEH,OAAO,IAAI,CAAC;QAEd,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5E,eAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE;gBAC9C,KAAK,EAAE,YAAY;gBACnB,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;aAC7B,CAAC,CAAC;YAEH,MAAM,IAAI,yBAAiB,CAAC,iCAAiC,EAAE;gBAC7D,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,IAAI;gBAC3B,aAAa,EAAE,YAAY;aAC5B,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,sBAAsB,CAAC,IAAmB;QAC9C,MAAM,SAAS,GAAG,wBAAW,CAAC,GAAG,EAAE,CAAC;QAEpC,IAAI,CAAC;YACH,MAAM,aAAa,GAAmB,EAAE,CAAC;YACzC,IAAI,eAAe,GAAG,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;YAE/C,2CAA2C;YAC3C,MAAM,oBAAoB,GAAG,IAAI,CAAC,uBAAuB,CAAC,eAAe,CAAC,CAAC;YAC3E,IAAI,oBAAoB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpC,aAAa,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;YAC3C,CAAC;YAED,+DAA+D;YAC/D,MAAM,eAAe,GAAG,IAAI,CAAC,sBAAsB,CAAC,eAAe,CAAC,CAAC;YACrE,IAAI,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC/B,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACtC,CAAC;YAED,wCAAwC;YACxC,MAAM,aAAa,GAAG,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,CAAC;YAC7D,IAAI,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC7B,aAAa,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YACpC,CAAC;YAED,4CAA4C;YAC5C,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,CAAC,eAAe,CAAC,CAAC;YAC3D,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC5B,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YACnC,CAAC;YAED,8BAA8B;YAC9B,MAAM,gBAAgB,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YACjF,MAAM,YAAY,GAAG,IAAI,CAAC,kBAAkB,CAAC;YAC7C,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,gBAAgB,EAAE,YAAY,GAAG,GAAG,CAAC,CAAC,CAAC,wCAAwC;YAE7H,MAAM,gBAAgB,GAAqB;gBACzC,qBAAqB,EAAE,YAAY;gBACnC,sBAAsB,EAAE,aAAa;gBACrC,WAAW,EAAE,gBAAgB;gBAC7B,oBAAoB,EAAE,oBAAoB,CAAC,MAAM;aAClD,CAAC;YAEF,MAAM,aAAa,GAAkB;gBACnC,GAAG,IAAI;gBACP,kBAAkB,EAAE,aAAa;gBACjC,cAAc,EAAE,eAAe;gBAC/B,aAAa;gBACb,gBAAgB;aACjB,CAAC;YAEF,MAAM,cAAc,GAAG,wBAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAErD,eAAM,CAAC,IAAI,CAAC,0BAA0B,EAAE;gBACtC,cAAc,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;gBAChD,YAAY,EAAE,GAAG,YAAY,IAAI;gBACjC,aAAa,EAAE,GAAG,aAAa,IAAI;gBACnC,WAAW,EAAE,GAAG,gBAAgB,IAAI;gBACpC,iBAAiB,EAAE,aAAa,CAAC,MAAM;aACxC,CAAC,CAAC;YAEH,OAAO,aAAa,CAAC;QAEvB,CAAC;QAAC,OAAO,KAAc,EAAE,CAAC;YACxB,MAAM,YAAY,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;YAC5E,eAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE;gBAChD,KAAK,EAAE,YAAY;aACpB,CAAC,CAAC;YAEH,6CAA6C;YAC7C,OAAO;gBACL,GAAG,IAAI;gBACP,aAAa,EAAE,EAAE;gBACjB,gBAAgB,EAAE;oBAChB,qBAAqB,EAAE,IAAI,CAAC,kBAAkB;oBAC9C,sBAAsB,EAAE,IAAI,CAAC,kBAAkB;oBAC/C,WAAW,EAAE,CAAC;oBACd,oBAAoB,EAAE,CAAC;iBACxB;aACF,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACH,mBAAmB;QACjB,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzC,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;QAClD,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC;QAC9G,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAE1D,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,GAAG;YACxC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG;YAChC,GAAG,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG;YAChC,KAAK,EAAE,IAAI,CAAC,kBAAkB,CAAC,MAAM;SACtC,CAAC;IACJ,CAAC;IAEO,2BAA2B,CAAC,OAAiB,EAAE,KAAsB;QAC3E,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,MAAM,CAAC,YAAY,IAAI,MAAM,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAC1D,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;gBACxC,IAAI,IAAI,EAAE,CAAC;oBACT,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,YAAY,EAAE,CAAC;wBACxC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;wBACvC,IAAI,OAAO,EAAE,CAAC;4BACZ,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;4BAC1B,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;4BAClC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,KAAK,KAAK,MAAM,CAAC,EAAE,EAAE,CAAC,CAAC;wBAC5C,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,2BAA2B,CAAC,OAAiB,EAAE,KAAsB;QACjF,4DAA4D;QAC5D,MAAM,aAAa,GAAG,IAAI,GAAG,EAAoB,CAAC;QAElD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;gBACpC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YACrC,CAAC;YACD,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;QAC/C,CAAC;QAED,6BAA6B;QAC7B,MAAM,eAAe,GAAG;YACtB,8BAA8B;YAC9B,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE;YACzD,EAAE,YAAY,EAAE,gBAAgB,EAAE,SAAS,EAAE,cAAc,EAAE;YAC7D,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE;YAEzD,uDAAuD;YACvD,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE;YACzD,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,gBAAgB,EAAE;YAE5D,kBAAkB;YAClB,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,YAAY,EAAE;YACxD,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,aAAa,EAAE;YAEzD,4BAA4B;YAC5B,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,mBAAmB,EAAE;YAC/D,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,mBAAmB,EAAE;SAChE,CAAC;QAEF,yBAAyB;QACzB,KAAK,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC;YACnC,MAAM,aAAa,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,CAAC;YACjE,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;YAE3D,KAAK,MAAM,MAAM,IAAI,aAAa,EAAE,CAAC;gBACnC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;oBACnC,IAAI,IAAI,CAAC,8BAA8B,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE,CAAC;wBAC3D,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;wBAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;wBAE9C,IAAI,UAAU,IAAI,OAAO,EAAE,CAAC;4BAC1B,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;4BACjC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;4BACxC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,EAAE,KAAK,SAAS,CAAC,EAAE,EAAE,CAAC,CAAC;wBACnD,CAAC;oBACH,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAEO,8BAA8B,CAAC,MAAc,EAAE,SAAiB;QACtE,8CAA8C;QAC9C,IAAI,MAAM,CAAC,UAAU,EAAE,QAAQ,IAAI,SAAS,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC;YAClE,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,KAAK,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC;QACtE,CAAC;QAED,2DAA2D;QAC3D,IAAI,MAAM,CAAC,QAAQ,EAAE,cAAc,IAAI,SAAS,CAAC,QAAQ,EAAE,aAAa,EAAE,CAAC;YACzE,OAAO,IAAI,CAAC;QACd,CAAC;QAED,2BAA2B;QAC3B,OAAO,KAAK,CAAC;IACf,CAAC;IAEO,yBAAyB,CAAC,KAAsB;QACtD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;QAEnC,MAAM,cAAc,GAAG,CAAC,MAAc,EAAU,EAAE;YAChD,IAAI,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzB,MAAM,IAAI,KAAK,CAAC,iCAAiC,MAAM,EAAE,CAAC,CAAC;YAC7D,CAAC;YAED,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBACxB,OAAO,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,KAAK,IAAI,CAAC,CAAC;YAC7C,CAAC;YAED,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAErB,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,CAAC,IAAI,EAAE,CAAC;gBACV,OAAO,CAAC,CAAC;YACX,CAAC;YAED,IAAI,WAAW,GAAG,CAAC,CAAC,CAAC;YACrB,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;gBACnC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC;YAC7D,CAAC;YAED,IAAI,CAAC,KAAK,GAAG,WAAW,GAAG,CAAC,CAAC;YAC7B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACpB,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAExB,OAAO,IAAI,CAAC,KAAK,CAAC;QACpB,CAAC,CAAC;QAEF,iCAAiC;QACjC,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YACxC,cAAc,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IAEO,0BAA0B,CAAC,KAAsB;QACvD,MAAM,OAAO,GAAG,IAAI,GAAG,EAAU,CAAC;QAClC,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAC;QAEzC,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAW,EAAE;YAC3C,IAAI,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC/B,OAAO,IAAI,CAAC;YACd,CAAC;YAED,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;gBACxB,OAAO,KAAK,CAAC;YACf,CAAC;YAED,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACpB,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAE3B,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACrC,IAAI,IAAI,EAAE,CAAC;gBACT,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;oBACnC,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;wBACpB,OAAO,IAAI,CAAC;oBACd,CAAC;gBACH,CAAC;YACH,CAAC;YAED,cAAc,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC9B,OAAO,KAAK,CAAC;QACf,CAAC,CAAC;QAEF,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;YACxC,IAAI,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;gBACrB,MAAM,IAAI,yBAAiB,CAAC,2CAA2C,MAAM,EAAE,CAAC,CAAC;YACnF,CAAC;QACH,CAAC;IACH,CAAC;IAEO,qBAAqB,CAAC,KAAsB;QAClD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAoB,CAAC;QAE7C,yBAAyB;QACzB,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YACxC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC9B,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YAC/B,CAAC;YACD,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC9C,CAAC;QAED,2BAA2B;QAC3B,KAAK,CAAC,MAAM,GAAG,EAAE,CAAC;QAClB,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC;YAClD,MAAM,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,CACvD,GAAG,GAAG,CAAC,MAAM,CAAC,QAAQ,EAAE,iBAAiB,IAAI,IAAI,CAAC,EAAE,CAAC,CACtD,CAAC;YAEF,MAAM,cAAc,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC;gBACvC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;YAE3D,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC;gBAChB,KAAK;gBACL,OAAO;gBACP,cAAc;gBACd,iBAAiB,EAAE,cAAc,CAAC,CAAC;oBACjC,IAAI,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,iBAAiB,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC;oBACtE,iBAAiB;aACpB,CAAC,CAAC;QACL,CAAC;QAED,8BAA8B;QAC9B,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;IACjD,CAAC;IAEO,qBAAqB,CAAC,KAAsB,EAAE,KAAsB;QAC1E,MAAM,aAAa,GAAG,IAAI,GAAG,EAAU,CAAC;QAExC,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YACnC,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YACxC,IAAI,IAAI,EAAE,CAAC;gBACT,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,SAAS,EAAE,CAAC;oBACnC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBAC3B,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACnC,CAAC;IAEO,oBAAoB,CAAC,MAAwB,EAAE,KAAsB;QAC3E,MAAM,eAAe,GAAa,EAAE,CAAC;QACrC,MAAM,WAAW,GAAa,EAAE,CAAC;QACjC,MAAM,eAAe,GAAa,EAAE,CAAC;QAErC,IAAI,SAAS,GAA8B,KAAK,CAAC;QAEjD,0BAA0B;QAC1B,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtB,eAAe,CAAC,IAAI,CAAC,sDAAsD,CAAC,CAAC;YAC7E,WAAW,CAAC,IAAI,CAAC,qEAAqE,CAAC,CAAC;YACxF,SAAS,GAAG,QAAQ,CAAC;QACvB,CAAC;QAED,kCAAkC;QAClC,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;QAC/D,IAAI,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,eAAe,CAAC,IAAI,CAAC,iEAAiE,CAAC,CAAC;YACxF,WAAW,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;YACjE,SAAS,GAAG,QAAQ,CAAC;QACvB,CAAC;QAED,mCAAmC;QACnC,MAAM,uBAAuB,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;aAC7D,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAE3F,IAAI,uBAAuB,EAAE,CAAC;YAC5B,eAAe,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;YACrE,WAAW,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;YACvE,IAAI,SAAS,KAAK,KAAK;gBAAE,SAAS,GAAG,QAAQ,CAAC;QAChD,CAAC;QAED,sCAAsC;QACtC,eAAe,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC;QAC3E,eAAe,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;QACzE,eAAe,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QAC/D,eAAe,CAAC,IAAI,CAAC,gDAAgD,CAAC,CAAC;QAEvE,OAAO;YACL,SAAS;YACT,eAAe;YACf,WAAW;YACX,eAAe;SAChB,CAAC;IACJ,CAAC;IAEO,uBAAuB,CAAC,MAAwB;QACtD,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,MAAM,SAAS,GAAa,EAAE,CAAC;QAE/B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,CAAC,KAAK,CAAC,iBAAiB,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACzD,uCAAuC;gBACvC,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAClD,CAAC,MAAM,CAAC,QAAQ,EAAE,aAAa;oBAC/B,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAC1B,KAAK,CAAC,EAAE,KAAK,MAAM,CAAC,EAAE;wBACtB,KAAK,CAAC,YAAY,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC,CACxC,CACF,CAAC;gBAEF,IAAI,cAAc,EAAE,CAAC;oBACnB,MAAM,gBAAgB,GAAG,KAAK,CAAC,iBAAiB,CAAC;oBACjD,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,iBAAiB,IAAI,IAAI,CAAC,CAAC,CAAC;oBACpG,MAAM,IAAI,gBAAgB,GAAG,gBAAgB,CAAC;oBAC9C,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBAE9B,eAAe;oBACf,KAAK,CAAC,iBAAiB,GAAG,IAAI,CAAC;oBAC/B,KAAK,CAAC,iBAAiB,GAAG,gBAAgB,CAAC;gBAC7C,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,iBAAiB;YACvB,WAAW,EAAE,kCAAkC,SAAS,CAAC,MAAM,SAAS;YACxE,MAAM;YACN,SAAS;SACV,CAAC;IACJ,CAAC;IAEO,sBAAsB,CAAC,MAAwB;QACrD,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,MAAM,SAAS,GAAa,EAAE,CAAC;QAE/B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,iBAAiB,EAAE,CAAC;gBACzD,+EAA+E;gBAC/E,MAAM,aAAa,GAAG,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;gBACzC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;oBAC1B,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,EAAE,iBAAiB,IAAI,IAAI,CAAC;oBACxD,MAAM,SAAS,GAAG,CAAC,CAAC,QAAQ,EAAE,iBAAiB,IAAI,IAAI,CAAC;oBACxD,OAAO,SAAS,GAAG,SAAS,CAAC;gBAC/B,CAAC,CAAC,CAAC;gBAEH,4CAA4C;gBAC5C,MAAM,cAAc,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAC1D,MAAM,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAC/C,CAAC;gBAEF,IAAI,cAAc,EAAE,CAAC;oBACnB,MAAM,IAAI,GAAG,CAAC,CAAC,wCAAwC;oBACvD,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAChC,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,YAAY;YAClB,WAAW,EAAE,iCAAiC,SAAS,CAAC,MAAM,SAAS;YACvE,MAAM;YACN,SAAS;SACV,CAAC;IACJ,CAAC;IAEO,gBAAgB,CAAC,MAAwB;QAC/C,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,MAAM,SAAS,GAAa,EAAE,CAAC;QAE/B,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,4CAA4C;YAC5C,MAAM,YAAY,GAAG,IAAI,GAAG,EAAoB,CAAC;YAEjD,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBACnC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;oBACnC,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;gBACpC,CAAC;gBACD,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;YAC9C,CAAC;YAED,wBAAwB;YACxB,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,EAAE,CAAC;gBACrD,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC3D,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,EAAE,iBAAiB,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;oBAClG,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,iBAAiB;oBAExE,IAAI,SAAS,GAAG,YAAY,EAAE,CAAC;wBAC7B,MAAM,IAAI,YAAY,GAAG,SAAS,CAAC;wBACnC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;oBAChC,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,UAAU;YAChB,WAAW,EAAE,qCAAqC,SAAS,CAAC,MAAM,SAAS;YAC3E,MAAM;YACN,SAAS;SACV,CAAC;IACJ,CAAC;IAEO,eAAe,CAAC,MAAwB;QAC9C,IAAI,MAAM,GAAG,CAAC,CAAC;QACf,MAAM,SAAS,GAAa,EAAE,CAAC;QAE/B,kDAAkD;QAClD,MAAM,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAC;QAElD,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;YAC3B,KAAK,MAAM,MAAM,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBACnC,MAAM,GAAG,GAAG,GAAG,MAAM,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;gBAClE,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;QAED,qDAAqD;QACrD,KAAK,MAAM,CAAC,SAAS,EAAE,KAAK,CAAC,IAAI,eAAe,CAAC,OAAO,EAAE,EAAE,CAAC;YAC3D,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;gBACd,MAAM,gBAAgB,GAAG,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,CAAC,oBAAoB;gBAChE,MAAM,IAAI,gBAAgB,CAAC;gBAC3B,SAAS,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;QAED,OAAO;YACL,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,cAAc,SAAS,CAAC,MAAM,uBAAuB;YAClE,MAAM;YACN,SAAS;SACV,CAAC;IACJ,CAAC;IAEO,qBAAqB,CAAC,UAAkB;QAC9C,MAAM,cAAc,GAAG;YACrB,mBAAmB;YACnB,aAAa;YACb,aAAa;YACb,aAAa;YACb,cAAc;SACf,CAAC;QAEF,OAAO,cAAc,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAC7C,CAAC;IAEO,iBAAiB,CAAC,IAAY;QACpC,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEnC,mCAAmC;QACnC,IAAI,IAAI,CAAC,kBAAkB,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;YAC1C,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC;QAClC,CAAC;IACH,CAAC;CACF;AAlrBD,gDAkrBC"} \ No newline at end of file diff --git a/backend/dist/ai/execution/MultiActionExecutor.d.ts b/backend/dist/ai/execution/MultiActionExecutor.d.ts deleted file mode 100644 index f6b6b13..0000000 --- a/backend/dist/ai/execution/MultiActionExecutor.d.ts +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Multi-Action Executor - Phase 2 Complex Command Execution - * Executes complex voice commands with multiple actions, dependencies, and transactions - * - * Success Criteria: - * - Supports all 10 action types from command parsing - * - ACID transaction compliance for multi-action commands - * - Automatic rollback on any action failure - * - Permission validation per action with detailed audit logging - * - Transaction timeout handling (30 seconds maximum) - */ -import { EventEmitter } from 'events'; -import { UserContext, ParsedCommand, ExecutionSummary, AuditEntry } from '../../voice/types'; -export interface MultiActionExecutionOptions { - transactionTimeout?: number; - parallelExecutionLimit?: number; - rollbackOnAnyFailure?: boolean; - auditLogging?: boolean; - progressTracking?: boolean; -} -export interface ExecutionContext { - userId: string; - organizationId: string; - sessionId: string; - commandId: string; - timestamp: string; - permissions: UserPermissions; - transaction?: any; -} -export interface UserPermissions { - canCreateTasks: boolean; - canAssignTasks: boolean; - canCreateChannels: boolean; - canInviteUsers: boolean; - canUploadFiles: boolean; - canSendMessages: boolean; - canUpdateTasks: boolean; - canDeleteTasks: boolean; - canManageChannels: boolean; - canSendNotifications: boolean; -} -export interface ActionExecutionResult { - actionId: string; - actionType: string; - success: boolean; - result?: any; - error?: string; - executionTime: number; - affectedEntities: string[]; - rollbackData?: any; -} -export interface MultiActionResult { - commandId: string; - success: boolean; - executedActions: ActionExecutionResult[]; - failedActions: ActionExecutionResult[]; - totalExecutionTime: number; - rollbackRequired: boolean; - rollbackCompleted: boolean; - summary: ExecutionSummary; - auditEntry: AuditEntry; -} -export interface TransactionManager { - transaction: any; - startTime: number; - timeout: NodeJS.Timeout; - isActive: boolean; - rollbackData: Map; -} -export declare class MultiActionExecutor extends EventEmitter { - private dependencyResolver; - private db; - private repositories; - private voiceFileUploadService; - private activeTransactions; - private performanceMetrics; - constructor(); - /** - * Execute a complex multi-action voice command - * Main entry point for command execution with full dependency resolution and transaction management - */ - executeMultiActionCommand(command: ParsedCommand, userContext: UserContext, options?: MultiActionExecutionOptions): Promise; - /** - * Execute a single stage of the execution plan - */ - private executeStage; - /** - * Execute a single action - */ - private executeAction; - private executeCreateTask; - private executeAssignTask; - private executeUpdateTask; - private executeCreateChannel; - private executeSendMessage; - private executeInviteUser; - private executeUploadFile; - private executeSendNotification; - private executeScheduleMeeting; - private executeGenerateReport; - private validateActionPermissions; - private getUserPermissions; - private beginTransaction; - private commitTransaction; - private rollbackTransaction; - private handleTransactionTimeout; - private cleanupStaleTransactions; - private convertParsedActionsToActions; - private getEstimatedDuration; - private actionRequiresInput; - private actionProducesOutput; - private createExecutionSummary; - private createActionBreakdown; - private createAuditEntry; - private logAuditEntry; - /** - * Get performance statistics - */ - getPerformanceStats(): { - average: number; - p95: number; - p99: number; - count: number; - }; - private recordPerformance; -} -//# sourceMappingURL=MultiActionExecutor.d.ts.map \ No newline at end of file diff --git a/backend/dist/ai/execution/MultiActionExecutor.d.ts.map b/backend/dist/ai/execution/MultiActionExecutor.d.ts.map deleted file mode 100644 index 6555d43..0000000 --- a/backend/dist/ai/execution/MultiActionExecutor.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"MultiActionExecutor.d.ts","sourceRoot":"","sources":["../../../src/ai/execution/MultiActionExecutor.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/ai/execution/MultiActionExecutor.js b/backend/dist/ai/execution/MultiActionExecutor.js deleted file mode 100644 index b551d32..0000000 --- a/backend/dist/ai/execution/MultiActionExecutor.js +++ /dev/null @@ -1,858 +0,0 @@ -"use strict"; -/** - * Multi-Action Executor - Phase 2 Complex Command Execution - * Executes complex voice commands with multiple actions, dependencies, and transactions - * - * Success Criteria: - * - Supports all 10 action types from command parsing - * - ACID transaction compliance for multi-action commands - * - Automatic rollback on any action failure - * - Permission validation per action with detailed audit logging - * - Transaction timeout handling (30 seconds maximum) - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.MultiActionExecutor = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -const db_1 = require("../../db"); -const VoiceFileUploadService_1 = require("../../files/upload/VoiceFileUploadService"); -const types_1 = require("../../voice/types"); -const DependencyResolver_1 = require("./DependencyResolver"); -class MultiActionExecutor extends events_1.EventEmitter { - dependencyResolver; - db; - repositories; - voiceFileUploadService; - activeTransactions = new Map(); - performanceMetrics = []; - constructor() { - super(); - this.dependencyResolver = new DependencyResolver_1.DependencyResolver(); - this.db = new db_1.DatabaseManager(); - this.repositories = db_1.repositories; - this.voiceFileUploadService = new VoiceFileUploadService_1.VoiceFileUploadService(); - // Setup cleanup interval for stale transactions - setInterval(() => this.cleanupStaleTransactions(), 60000); // Every minute - logger_1.logger.info('Multi-Action Executor initialized'); - } - /** - * Execute a complex multi-action voice command - * Main entry point for command execution with full dependency resolution and transaction management - */ - async executeMultiActionCommand(command, userContext, options = {}) { - const startTime = perf_hooks_1.performance.now(); - const executionId = `exec-${Date.now()}-${Math.random().toString(36).substring(7)}`; - const executionContext = { - userId: userContext.userId, - organizationId: userContext.organizationId, - sessionId: userContext.sessionId || 'default', - commandId: command.id, - timestamp: new Date().toISOString(), - permissions: await this.getUserPermissions(userContext.userId), - }; - const executionOptions = { - transactionTimeout: 30000, // 30 seconds - parallelExecutionLimit: 3, - rollbackOnAnyFailure: true, - auditLogging: true, - progressTracking: true, - ...options, - }; - logger_1.logger.info('Starting multi-action command execution', { - executionId, - commandId: command.id, - actionCount: command.actions.length, - userId: userContext.userId, - options: executionOptions, - }); - let transactionManager; - const executedActions = []; - const failedActions = []; - let rollbackCompleted = false; - try { - // Step 1: Convert parsed command actions to dependency actions - const actions = this.convertParsedActionsToActions(command.actions); - // Step 2: Analyze dependencies and create execution plan - this.emit('dependency_analysis_start', { executionId, actionCount: actions.length }); - const dependencyGraph = await this.dependencyResolver.analyzeDependencies(actions); - const executionPlan = await this.dependencyResolver.createExecutionPlan(dependencyGraph); - const optimizedPlan = await this.dependencyResolver.optimizeExecutionOrder(executionPlan); - this.emit('execution_plan_ready', { - executionId, - stageCount: optimizedPlan.parallelStages.length, - estimatedTime: optimizedPlan.totalEstimatedTime, - optimizations: optimizedPlan.optimizations.length, - }); - // Step 3: Start database transaction - transactionManager = await this.beginTransaction(executionId, executionOptions.transactionTimeout); - executionContext.transaction = transactionManager.transaction; - // Step 4: Execute plan stages - for (let i = 0; i < optimizedPlan.parallelStages.length; i++) { - const stage = optimizedPlan.parallelStages[i]; - this.emit('stage_execution_start', { - executionId, - stageId: stage?.stageId ?? 'unknown', - actionCount: stage?.actions?.length ?? 0, - parallelExecution: stage?.parallelExecution ?? false, - }); - if (stage) { - const stageResults = await this.executeStage(stage, executionContext, executionOptions); - // Process results - for (const result of stageResults) { - if (result.success) { - executedActions.push(result); - } - else { - failedActions.push(result); - if (executionOptions.rollbackOnAnyFailure) { - throw new types_1.AIProcessingError(`Action failed: ${result.actionType} - ${result.error}`); - } - } - } - this.emit('stage_execution_complete', { - executionId, - stageId: stage.stageId, - successCount: stageResults.filter((r) => r.success).length, - failureCount: stageResults.filter((r) => !r.success).length, - }); - } - else { - logger_1.logger.warn('Skipped undefined stage during execution', { executionId, stageIndex: i }); - } - } - // Step 5: Commit transaction - await this.commitTransaction(transactionManager); - const totalExecutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(totalExecutionTime); - // Step 6: Create execution summary and audit entry - const summary = this.createExecutionSummary(executedActions, failedActions, totalExecutionTime); - const auditEntry = this.createAuditEntry(command, executionContext, summary); - // Log audit entry if enabled - if (executionOptions.auditLogging) { - await this.logAuditEntry(auditEntry); - } - const result = { - commandId: command.id, - success: failedActions.length === 0, - executedActions, - failedActions, - totalExecutionTime, - rollbackRequired: false, - rollbackCompleted: false, - summary, - auditEntry, - }; - this.emit('execution_complete', { - executionId, - success: result.success, - totalTime: totalExecutionTime, - actionCount: executedActions.length, - }); - logger_1.logger.info('Multi-action command execution completed successfully', { - executionId, - commandId: command.id, - totalTime: `${totalExecutionTime.toFixed(2)}ms`, - successfulActions: executedActions.length, - failedActions: failedActions.length, - }); - return result; - } - catch (error) { - const totalExecutionTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(totalExecutionTime); - logger_1.logger.error('Multi-action command execution failed', { - executionId, - commandId: command.id, - error: error instanceof Error ? error.message : String(error), - totalTime: `${totalExecutionTime.toFixed(2)}ms`, - executedActions: executedActions.length, - }); - // Attempt rollback - let rollbackSuccess = false; - if (transactionManager) { - try { - rollbackSuccess = await this.rollbackTransaction(transactionManager, executedActions); - rollbackCompleted = true; - } - catch (rollbackError) { - const rollbackErrorMessage = rollbackError instanceof Error ? rollbackError.message : String(rollbackError); - logger_1.logger.error('Transaction rollback failed', { - executionId, - rollbackError: rollbackErrorMessage, - }); - } - } - const summary = this.createExecutionSummary(executedActions, failedActions, totalExecutionTime); - const errorMessage = error instanceof Error ? error.message : String(error); - const auditEntry = this.createAuditEntry(command, executionContext, summary, errorMessage); - // Log audit entry even for failures - if (executionOptions.auditLogging) { - await this.logAuditEntry(auditEntry); - } - this.emit('execution_failed', { - executionId, - error: errorMessage, - rollbackCompleted: rollbackSuccess, - }); - return { - commandId: command.id, - success: false, - executedActions, - failedActions, - totalExecutionTime, - rollbackRequired: true, - rollbackCompleted: rollbackSuccess, - summary, - auditEntry, - }; - } - finally { - // Cleanup transaction - if (transactionManager) { - this.activeTransactions.delete(executionId); - if (transactionManager.timeout) { - clearTimeout(transactionManager.timeout); - } - } - } - } - /** - * Execute a single stage of the execution plan - */ - async executeStage(stage, context, options) { - const results = []; - if (stage.parallelExecution && stage.actions.length > 1) { - // Execute actions in parallel - const parallelLimit = Math.min(stage.actions.length, options.parallelExecutionLimit || 3); - const chunks = []; - // Split actions into chunks for parallel execution - for (let i = 0; i < stage.actions.length; i += parallelLimit) { - chunks.push(stage.actions.slice(i, i + parallelLimit)); - } - for (const chunk of chunks) { - const chunkResults = await Promise.all(chunk.map((action) => this.executeAction(action, context))); - results.push(...chunkResults); - } - } - else { - // Execute actions sequentially - for (const action of stage.actions) { - const result = await this.executeAction(action, context); - results.push(result); - // Stop on first failure if configured - if (!result.success && options.rollbackOnAnyFailure) { - break; - } - } - } - return results; - } - /** - * Execute a single action - */ - async executeAction(action, context) { - const startTime = perf_hooks_1.performance.now(); - try { - // Validate permissions - if (!this.validateActionPermissions(action, context.permissions)) { - throw new Error(`Insufficient permissions for action: ${action.type}`); - } - // Execute the action based on type - let result; - const affectedEntities = []; - let rollbackData = null; - switch (action.type) { - case 'create_task': - result = await this.executeCreateTask(action, context); - affectedEntities.push(result.id); - rollbackData = { taskId: result.id }; - break; - case 'assign_task': - result = await this.executeAssignTask(action, context); - affectedEntities.push(action.parameters.taskId); - rollbackData = { - taskId: action.parameters.taskId, - previousAssignees: result.previousAssignees, - }; - break; - case 'update_task': - result = await this.executeUpdateTask(action, context); - affectedEntities.push(action.parameters.taskId); - rollbackData = { - taskId: action.parameters.taskId, - previousValues: result.previousValues, - }; - break; - case 'create_channel': - result = await this.executeCreateChannel(action, context); - affectedEntities.push(result.id); - rollbackData = { channelId: result.id }; - break; - case 'send_message': - result = await this.executeSendMessage(action, context); - affectedEntities.push(action.parameters.channelId); - rollbackData = { messageId: result.id }; - break; - case 'invite_user': - result = await this.executeInviteUser(action, context); - affectedEntities.push(result.userId); - rollbackData = { invitationId: result.id }; - break; - case 'upload_file': - result = await this.executeUploadFile(action, context); - affectedEntities.push(result.fileId); - rollbackData = { fileId: result.fileId }; - break; - case 'send_notification': - result = await this.executeSendNotification(action, context); - affectedEntities.push(...(action.parameters.userIds || [])); - rollbackData = { notificationId: result.id }; - break; - case 'schedule_meeting': - result = await this.executeScheduleMeeting(action, context); - affectedEntities.push(result.meetingId); - rollbackData = { meetingId: result.meetingId }; - break; - case 'generate_report': - result = await this.executeGenerateReport(action, context); - affectedEntities.push(result.reportId); - rollbackData = { reportId: result.reportId }; - break; - default: - throw new Error(`Unsupported action type: ${action.type}`); - } - const executionTime = perf_hooks_1.performance.now() - startTime; - return { - actionId: action.id, - actionType: action.type, - success: true, - result, - executionTime, - affectedEntities, - rollbackData, - }; - } - catch (error) { - const executionTime = perf_hooks_1.performance.now() - startTime; - const errorMessage = error instanceof Error ? error.message : String(error); - return { - actionId: action.id, - actionType: action.type, - success: false, - error: errorMessage, - executionTime, - affectedEntities: [], - }; - } - } - // Action execution implementations - async executeCreateTask(action, context) { - const { title, description, priority, dueDate, assignedTo } = action.parameters; - const task = await this.repositories.tasks.query(` - INSERT INTO tasks (title, description, priority, due_date, assigned_to, created_by, status) - VALUES ($1, $2, $3, $4, $5, $6, 'pending') - RETURNING id, title, status, created_at - `, [title, description, priority || 'medium', dueDate, assignedTo || [], context.userId], context.transaction); - return task.rows[0]; - } - async executeAssignTask(action, context) { - const { taskId, assignedTo } = action.parameters; - // Get current assignees for rollback - const currentTask = await this.db.query(` - SELECT assigned_to FROM tasks WHERE id = $1 - `, [taskId], context.transaction); - const previousAssignees = currentTask.rows[0]?.assigned_to || []; - const result = await this.db.query(` - UPDATE tasks - SET assigned_to = $2, updated_at = NOW() - WHERE id = $1 - RETURNING id, assigned_to - `, [taskId, assignedTo], context.transaction); - return { - taskId, - assignedTo, - previousAssignees, - updated: result.rowCount > 0, - }; - } - async executeUpdateTask(action, context) { - const { taskId, updates } = action.parameters; - // Get current values for rollback - const currentTask = await this.db.query(` - SELECT title, description, status, priority, due_date - FROM tasks WHERE id = $1 - `, [taskId], context.transaction); - const previousValues = currentTask.rows[0] || {}; - // Build dynamic update query - const updateFields = []; - const updateValues = [taskId]; - let paramIndex = 2; - for (const [field, value] of Object.entries(updates)) { - if (['title', 'description', 'status', 'priority', 'due_date'].includes(field)) { - updateFields.push(`${field} = $${paramIndex}`); - updateValues.push(value); - paramIndex++; - } - } - updateFields.push(`updated_at = NOW()`); - const result = await this.db.query(` - UPDATE tasks - SET ${updateFields.join(', ')} - WHERE id = $1 - RETURNING id, title, status, updated_at - `, updateValues, context.transaction); - return { - taskId, - updates: result.rows[0], - previousValues, - }; - } - async executeCreateChannel(action, context) { - const { name, description, channelType, members } = action.parameters; - // Create channel - const channel = await this.db.query(` - INSERT INTO channels (name, description, channel_type, created_by, status) - VALUES ($1, $2, $3, $4, 'active') - RETURNING id, name, channel_type, created_at - `, [name, description, channelType || 'general', context.userId], context.transaction); - const channelId = channel.rows[0].id; - // Add creator as member - await this.db.query(` - INSERT INTO channel_members (channel_id, user_id, role, joined_at) - VALUES ($1, $2, 'admin', NOW()) - `, [channelId, context.userId], context.transaction); - // Add additional members if specified - if (members && members.length > 0) { - for (const memberId of members) { - await this.db.query(` - INSERT INTO channel_members (channel_id, user_id, role, joined_at) - VALUES ($1, $2, 'member', NOW()) - ON CONFLICT (channel_id, user_id) DO NOTHING - `, [channelId, memberId], context.transaction); - } - } - return channel.rows[0]; - } - async executeSendMessage(action, context) { - const { channelId, content, messageType } = action.parameters; - const message = await this.db.query(` - INSERT INTO messages (channel_id, user_id, content, message_type, sent_at) - VALUES ($1, $2, $3, $4, NOW()) - RETURNING id, content, sent_at - `, [channelId, context.userId, content, messageType || 'text'], context.transaction); - return message.rows[0]; - } - async executeInviteUser(action, context) { - const { email, name, role } = action.parameters; - const invitation = await this.db.query(` - INSERT INTO user_invitations (email, name, role, invited_by, organization_id, status) - VALUES ($1, $2, $3, $4, $5, 'pending') - RETURNING id, email, created_at - `, [email, name, role || 'member', context.userId, context.organizationId], context.transaction); - return { - id: invitation.rows[0].id, - userId: email, // Use email as user identifier for now - status: 'invited', - }; - } - async executeUploadFile(action, context) { - const { fileName, fileSize, contentType, description, targetChannels, targetTasks, tags } = action.parameters; - // Use the voice file upload service for complete workflow - const uploadRequest = { - fileName, - contentType: contentType || 'application/octet-stream', - fileSize: fileSize || 1024, // Default 1KB if not specified - userContext: { - userId: context.userId, - organizationId: context.organizationId, - sessionId: context.sessionId, - language: 'en', - timezone: 'UTC', - }, - description, - targetChannels: targetChannels || [], - targetTasks: targetTasks || [], - tags: tags || [], - }; - const uploadResult = await this.voiceFileUploadService.initiateVoiceUpload(uploadRequest); - if (!uploadResult.success) { - throw new Error(uploadResult.error || 'File upload initiation failed'); - } - return { - fileId: uploadResult.fileId, - uploadUrl: uploadResult.uploadUrl, - downloadUrl: uploadResult.downloadUrl, - expiresAt: uploadResult.expiresAt, - linkedEntities: uploadResult.linkedEntities, - status: 'upload_initiated', - }; - } - async executeSendNotification(action, context) { - const { userIds, title, message, notificationType } = action.parameters; - const notification = await this.db.query(` - INSERT INTO notifications (title, message, notification_type, sender_id, organization_id, created_at) - VALUES ($1, $2, $3, $4, $5, NOW()) - RETURNING id, title, created_at - `, [title, message, notificationType || 'info', context.userId, context.organizationId], context.transaction); - const notificationId = notification.rows[0].id; - // Send to specified users - if (userIds && userIds.length > 0) { - for (const userId of userIds) { - await this.db.query(` - INSERT INTO user_notifications (user_id, notification_id, status) - VALUES ($1, $2, 'unread') - `, [userId, notificationId], context.transaction); - } - } - return notification.rows[0]; - } - async executeScheduleMeeting(action, context) { - const { title, startTime, endTime, attendees, description } = action.parameters; - // For now, return a mock meeting ID - // In a real implementation, this would integrate with calendar systems - return { - meetingId: `meeting-${Date.now()}`, - title, - scheduled: true, - attendeeCount: attendees?.length || 0, - }; - } - async executeGenerateReport(action, context) { - const { reportType, parameters, format } = action.parameters; - // For now, return a mock report ID - // In a real implementation, this would generate actual reports - return { - reportId: `report-${Date.now()}`, - type: reportType, - status: 'generated', - format: format || 'pdf', - }; - } - validateActionPermissions(action, permissions) { - const permissionMap = { - create_task: 'canCreateTasks', - assign_task: 'canAssignTasks', - update_task: 'canUpdateTasks', - create_channel: 'canCreateChannels', - send_message: 'canSendMessages', - invite_user: 'canInviteUsers', - upload_file: 'canUploadFiles', - send_notification: 'canSendNotifications', - }; - const requiredPermission = permissionMap[action.type]; - return !requiredPermission || permissions[requiredPermission]; - } - async getUserPermissions(userId) { - // Get user role and organization permissions - const user = await this.db.query(` - SELECT role FROM users WHERE id = $1 - `, [userId]); - const userRole = user.rows[0]?.role || 'member'; - // Define permissions based on role - const rolePermissions = { - admin: { - canCreateTasks: true, - canAssignTasks: true, - canCreateChannels: true, - canInviteUsers: true, - canUploadFiles: true, - canSendMessages: true, - canUpdateTasks: true, - canDeleteTasks: true, - canManageChannels: true, - canSendNotifications: true, - }, - manager: { - canCreateTasks: true, - canAssignTasks: true, - canCreateChannels: true, - canInviteUsers: false, - canUploadFiles: true, - canSendMessages: true, - canUpdateTasks: true, - canDeleteTasks: false, - canManageChannels: false, - canSendNotifications: true, - }, - member: { - canCreateTasks: true, - canAssignTasks: false, - canCreateChannels: false, - canInviteUsers: false, - canUploadFiles: true, - canSendMessages: true, - canUpdateTasks: true, - canDeleteTasks: false, - canManageChannels: false, - canSendNotifications: false, - }, - }; - const permissions = rolePermissions[userRole]; - if (permissions) { - return permissions; - } - const memberPermissions = rolePermissions.member; - if (memberPermissions) { - return memberPermissions; - } - // Fallback to minimal permissions if member role isn't defined - return { - canCreateTasks: false, - canAssignTasks: false, - canCreateChannels: false, - canInviteUsers: false, - canUploadFiles: false, - canSendMessages: false, - canUpdateTasks: false, - canDeleteTasks: false, - canManageChannels: false, - canSendNotifications: false, - }; - } - async beginTransaction(executionId, timeout) { - const transaction = await this.db.transaction(async (client) => client); - const timeoutHandler = setTimeout(() => { - this.handleTransactionTimeout(executionId); - }, timeout); - const transactionManager = { - transaction, - startTime: Date.now(), - timeout: timeoutHandler, - isActive: true, - rollbackData: new Map(), - }; - this.activeTransactions.set(executionId, transactionManager); - logger_1.logger.debug('Transaction started', { - executionId, - timeout: `${timeout}ms`, - }); - return transactionManager; - } - async commitTransaction(transactionManager) { - if (transactionManager.isActive) { - // For DatabaseManager, commit happens automatically at end of transaction callback - transactionManager.isActive = false; - if (transactionManager.timeout) { - clearTimeout(transactionManager.timeout); - } - logger_1.logger.debug('Transaction committed successfully'); - } - } - async rollbackTransaction(transactionManager, executedActions) { - try { - if (transactionManager.isActive) { - // For DatabaseManager, rollback happens by throwing error in transaction callback - transactionManager.isActive = false; - if (transactionManager.timeout) { - clearTimeout(transactionManager.timeout); - } - logger_1.logger.info('Transaction rolled back successfully', { - executedActionCount: executedActions.length, - }); - return true; - } - return false; - } - catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - logger_1.logger.error('Transaction rollback failed', { - error: errorMessage, - executedActionCount: executedActions.length, - }); - return false; - } - } - handleTransactionTimeout(executionId) { - const transactionManager = this.activeTransactions.get(executionId); - if (transactionManager?.isActive) { - logger_1.logger.warn('Transaction timeout, forcing rollback', { - executionId, - duration: Date.now() - transactionManager.startTime, - }); - // Force rollback on timeout - this.rollbackTransaction(transactionManager, []); - this.activeTransactions.delete(executionId); - this.emit('transaction_timeout', { executionId }); - } - } - cleanupStaleTransactions() { - const staleTransactions = []; - for (const [executionId, transactionManager] of this.activeTransactions.entries()) { - const age = Date.now() - transactionManager.startTime; - if (age > 60000) { - // 1 minute - staleTransactions.push(executionId); - } - } - for (const executionId of staleTransactions) { - const transactionManager = this.activeTransactions.get(executionId); - if (transactionManager) { - this.rollbackTransaction(transactionManager, []); - this.activeTransactions.delete(executionId); - } - } - if (staleTransactions.length > 0) { - logger_1.logger.warn('Cleaned up stale transactions', { - count: staleTransactions.length, - }); - } - } - convertParsedActionsToActions(parsedActions) { - return parsedActions.map((action, index) => ({ - id: action.id || `action-${index}`, - type: action.type, - parameters: action.parameters, - dependencies: action.dependencies || [], - metadata: { - priority: action.priority || 1, - estimatedDuration: this.getEstimatedDuration(action.type), - requiresInput: this.actionRequiresInput(action.type), - producesOutput: this.actionProducesOutput(action.type), - }, - })); - } - getEstimatedDuration(actionType) { - const durations = { - create_task: 500, - assign_task: 300, - update_task: 400, - create_channel: 600, - send_message: 200, - invite_user: 800, - upload_file: 2000, - send_notification: 300, - schedule_meeting: 1000, - generate_report: 3000, - }; - return durations[actionType] || 1000; - } - actionRequiresInput(actionType) { - return ['assign_task', 'update_task', 'send_message'].includes(actionType); - } - actionProducesOutput(actionType) { - return ['create_task', 'create_channel', 'upload_file', 'generate_report'].includes(actionType); - } - createExecutionSummary(executed, failed, totalTime) { - return { - totalActions: executed.length + failed.length, - successfulActions: executed.length, - failedActions: failed.length, - totalExecutionTime: totalTime, - averageActionTime: executed.length > 0 - ? executed.reduce((sum, a) => sum + a.executionTime, 0) / executed.length - : 0, - affectedEntities: Array.from(new Set(executed.flatMap((a) => a.affectedEntities))), - actionBreakdown: this.createActionBreakdown(executed, failed), - }; - } - createActionBreakdown(executed, failed) { - const breakdown = {}; - // Process executed actions - for (const action of executed) { - if (!breakdown[action.actionType]) { - breakdown[action.actionType] = { success: 0, failed: 0, times: [] }; - } - breakdown[action.actionType].success++; - breakdown[action.actionType].times.push(action.executionTime); - } - // Process failed actions - for (const action of failed) { - if (!breakdown[action.actionType]) { - breakdown[action.actionType] = { success: 0, failed: 0, times: [] }; - } - breakdown[action.actionType].failed++; - if (action.executionTime) { - breakdown[action.actionType].times.push(action.executionTime); - } - } - // Calculate averages - const result = {}; - for (const [type, data] of Object.entries(breakdown)) { - result[type] = { - success: data.success, - failed: data.failed, - avgTime: data.times.length > 0 - ? data.times.reduce((sum, time) => sum + time, 0) / data.times.length - : 0, - }; - } - return result; - } - createAuditEntry(command, context, summary, error) { - return { - id: `audit-${Date.now()}`, - commandId: command.id, - userId: context.userId, - organizationId: context.organizationId, - sessionId: context.sessionId, - timestamp: context.timestamp, - originalTranscript: command.originalTranscript, - parsedIntent: command.intent, - executionSummary: summary, - success: summary.failedActions === 0 && !error, - error: error ?? undefined, - metadata: { - userAgent: 'voice-processing-service', - clientVersion: '2.0.0', - processingTime: summary.totalExecutionTime, - }, - }; - } - async logAuditEntry(auditEntry) { - try { - await this.db.query(` - INSERT INTO audit_log ( - id, command_id, user_id, organization_id, session_id, timestamp, - original_transcript, parsed_intent, execution_summary, success, error_message - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) - `, [ - auditEntry.id, - auditEntry.commandId, - auditEntry.userId, - auditEntry.organizationId, - auditEntry.sessionId, - auditEntry.timestamp, - auditEntry.originalTranscript, - auditEntry.parsedIntent, - JSON.stringify(auditEntry.executionSummary), - auditEntry.success, - auditEntry.error, - ]); - } - catch (error) { - const errorMessage = error instanceof Error ? error.message : String(error); - logger_1.logger.error('Failed to log audit entry', { - error: errorMessage, - auditEntryId: auditEntry.id, - }); - } - } - /** - * Get performance statistics - */ - getPerformanceStats() { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] ?? 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] ?? 0; - return { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: this.performanceMetrics.length, - }; - } - recordPerformance(time) { - this.performanceMetrics.push(time); - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} -exports.MultiActionExecutor = MultiActionExecutor; -//# sourceMappingURL=MultiActionExecutor.js.map \ No newline at end of file diff --git a/backend/dist/ai/execution/MultiActionExecutor.js.map b/backend/dist/ai/execution/MultiActionExecutor.js.map deleted file mode 100644 index 472e8bb..0000000 --- a/backend/dist/ai/execution/MultiActionExecutor.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"MultiActionExecutor.js","sourceRoot":"","sources":["../../../src/ai/execution/MultiActionExecutor.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/api/index.d.ts b/backend/dist/api/index.d.ts deleted file mode 100644 index 9252a3f..0000000 --- a/backend/dist/api/index.d.ts +++ /dev/null @@ -1,11 +0,0 @@ -/** - * API Routes Index - * Centralized registration of all API endpoints - */ -import { FastifyInstance } from 'fastify'; -/** - * Register all API routes - */ -export declare const registerAPIRoutes: (fastify: FastifyInstance) => Promise; -export default registerAPIRoutes; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/backend/dist/api/index.d.ts.map b/backend/dist/api/index.d.ts.map deleted file mode 100644 index a28657f..0000000 --- a/backend/dist/api/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAW1C;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAU,SAAS,eAAe,KAAG,OAAO,CAAC,IAAI,CAkB9E,CAAC;AAEF,eAAe,iBAAiB,CAAC"} \ No newline at end of file diff --git a/backend/dist/api/index.js b/backend/dist/api/index.js deleted file mode 100644 index bf9b658..0000000 --- a/backend/dist/api/index.js +++ /dev/null @@ -1,41 +0,0 @@ -"use strict"; -/** - * API Routes Index - * Centralized registration of all API endpoints - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerAPIRoutes = void 0; -const UserRoutes_1 = require("./routes/UserRoutes"); -const ChannelRoutes_1 = require("./routes/ChannelRoutes"); -const TaskRoutes_1 = require("./routes/TaskRoutes"); -const DocsRoutes_1 = require("./routes/DocsRoutes"); -const MessageRoutes_1 = require("./routes/MessageRoutes"); -const ActivityRoutes_1 = require("./routes/ActivityRoutes"); -const NotificationRoutes_1 = require("./routes/NotificationRoutes"); -const AnnouncementRoutes_1 = require("./routes/AnnouncementRoutes"); -const logger_1 = require("../utils/logger"); -/** - * Register all API routes - */ -const registerAPIRoutes = async (fastify) => { - try { - // Register route modules - await fastify.register(UserRoutes_1.registerUserRoutes); - await fastify.register(ChannelRoutes_1.registerChannelRoutes); - await fastify.register(TaskRoutes_1.registerTaskRoutes); - await fastify.register(MessageRoutes_1.registerMessageRoutes); - await fastify.register(ActivityRoutes_1.registerActivityRoutes); - await fastify.register(DocsRoutes_1.registerDocsRoutes); - await fastify.register(NotificationRoutes_1.notificationRoutes); - await fastify.register(AnnouncementRoutes_1.announcementRoutes); - logger_1.logger.debug('All API routes registered'); - } - catch (error) { - console.error('API Registration Error Details:', error); - logger_1.logger.error({ error: error instanceof Error ? error.message : String(error) }, 'Failed to register API routes'); - throw error; - } -}; -exports.registerAPIRoutes = registerAPIRoutes; -exports.default = exports.registerAPIRoutes; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/backend/dist/api/index.js.map b/backend/dist/api/index.js.map deleted file mode 100644 index a00f75e..0000000 --- a/backend/dist/api/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/api/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,oDAAyD;AACzD,0DAA+D;AAC/D,oDAAyD;AACzD,oDAAyD;AACzD,0DAA+D;AAC/D,4DAAiE;AACjE,oEAAiE;AACjE,oEAAiE;AACjE,4CAAyC;AAEzC;;GAEG;AACI,MAAM,iBAAiB,GAAG,KAAK,EAAE,OAAwB,EAAiB,EAAE;IACjF,IAAI,CAAC;QACH,yBAAyB;QACzB,MAAM,OAAO,CAAC,QAAQ,CAAC,+BAAkB,CAAC,CAAC;QAC3C,MAAM,OAAO,CAAC,QAAQ,CAAC,qCAAqB,CAAC,CAAC;QAC9C,MAAM,OAAO,CAAC,QAAQ,CAAC,+BAAkB,CAAC,CAAC;QAC3C,MAAM,OAAO,CAAC,QAAQ,CAAC,qCAAqB,CAAC,CAAC;QAC9C,MAAM,OAAO,CAAC,QAAQ,CAAC,uCAAsB,CAAC,CAAC;QAC/C,MAAM,OAAO,CAAC,QAAQ,CAAC,+BAAkB,CAAC,CAAC;QAC3C,MAAM,OAAO,CAAC,QAAQ,CAAC,uCAAkB,CAAC,CAAC;QAC3C,MAAM,OAAO,CAAC,QAAQ,CAAC,uCAAkB,CAAC,CAAC;QAE3C,eAAM,CAAC,KAAK,CAAC,2BAA2B,CAAC,CAAC;IAC5C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,iCAAiC,EAAE,KAAK,CAAC,CAAC;QACxD,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,EAAE,+BAA+B,CAAC,CAAC;QACjH,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC,CAAC;AAlBW,QAAA,iBAAiB,qBAkB5B;AAEF,kBAAe,yBAAiB,CAAC"} \ No newline at end of file diff --git a/backend/dist/api/routes/ActivityRoutes.d.ts b/backend/dist/api/routes/ActivityRoutes.d.ts deleted file mode 100644 index 221f051..0000000 --- a/backend/dist/api/routes/ActivityRoutes.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { FastifyInstance } from 'fastify'; -/** - * Activity API Routes - * Handles activity feed, notifications, and activity logging - */ -export declare function registerActivityRoutes(fastify: FastifyInstance): Promise; -//# sourceMappingURL=ActivityRoutes.d.ts.map \ No newline at end of file diff --git a/backend/dist/api/routes/ActivityRoutes.d.ts.map b/backend/dist/api/routes/ActivityRoutes.d.ts.map deleted file mode 100644 index 83ce725..0000000 --- a/backend/dist/api/routes/ActivityRoutes.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ActivityRoutes.d.ts","sourceRoot":"","sources":["../../../src/api/routes/ActivityRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAyBxE;;;GAGG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,eAAe,iBAoQpE"} \ No newline at end of file diff --git a/backend/dist/api/routes/ActivityRoutes.js b/backend/dist/api/routes/ActivityRoutes.js deleted file mode 100644 index 38ffd02..0000000 --- a/backend/dist/api/routes/ActivityRoutes.js +++ /dev/null @@ -1,231 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerActivityRoutes = registerActivityRoutes; -const ActivityRepository_1 = __importDefault(require("../../db/ActivityRepository")); -const UserRepository_1 = __importDefault(require("../../db/UserRepository")); -const middleware_1 = require("../../auth/middleware"); -const logger_1 = require("../../utils/logger"); -/** - * Activity API Routes - * Handles activity feed, notifications, and activity logging - */ -async function registerActivityRoutes(fastify) { - const activityRepo = new ActivityRepository_1.default(); - const userRepo = new UserRepository_1.default(); - // All routes require authentication - await fastify.register(async function (fastify) { - fastify.addHook('onRequest', middleware_1.authenticate); - /** - * GET /activities - Get activities with filters and pagination - */ - fastify.get('/', async (request, reply) => { - try { - const { type, channel_id, user_id, limit = 50, offset = 0, from_date, to_date } = request.query; - // Validate pagination parameters - if (limit > 100) { - return reply.code(400).send({ - error: { - message: 'Limit cannot exceed 100', - code: 'INVALID_LIMIT', - } - }); - } - // Build filters - const filters = {}; - if (type) - filters.type = Array.isArray(type) ? type : [type]; - if (channel_id) - filters.channel_id = channel_id; - if (user_id) - filters.user_id = user_id; - if (from_date) - filters.from_date = from_date; - if (to_date) - filters.to_date = to_date; - // Get activities with user information - const activities = await activityRepo.findMany({ - ...filters, - limit, - offset, - includeUser: true, - }); - const total = await activityRepo.count(filters); - const hasMore = offset + limit < total; - reply.send({ - success: true, - data: activities, - pagination: { - total, - limit, - offset, - hasMore, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - logger_1.logger.error({ error, userId: request.user?.userId }, 'Error fetching activities'); - reply.code(500).send({ - error: { - message: 'Failed to fetch activities', - code: 'ACTIVITY_FETCH_ERROR', - } - }); - } - }); - /** - * GET /activities/feed - Get personalized activity feed - */ - fastify.get('/feed', async (request, reply) => { - try { - const { limit = 20, offset = 0 } = request.query; - const userId = request.user.userId; - // Get activities for channels the user is a member of - // and activities directly related to the user - const activities = await activityRepo.getUserFeed(userId, limit, offset); - const total = await activityRepo.getUserFeedCount(userId); - const hasMore = offset + limit < total; - reply.send({ - success: true, - data: activities, - pagination: { - total, - limit, - offset, - hasMore, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - logger_1.logger.error({ error, userId: request.user?.userId }, 'Error fetching activity feed'); - reply.code(500).send({ - error: { - message: 'Failed to fetch activity feed', - code: 'ACTIVITY_FEED_ERROR', - } - }); - } - }); - /** - * POST /activities - Create new activity - */ - fastify.post('/', async (request, reply) => { - try { - const { type, title, description, metadata = {}, related_id, channel_id } = request.body; - const userId = request.user.userId; - // Validate required fields - if (!type || !title || !description) { - return reply.code(400).send({ - error: { - message: 'Type, title, and description are required', - code: 'MISSING_REQUIRED_FIELDS', - } - }); - } - // Validate activity type - const validTypes = [ - 'task_created', 'task_updated', 'task_completed', 'task_assigned', - 'channel_created', 'user_joined', 'file_uploaded', 'message_sent', - 'announcement', 'notification' - ]; - if (!validTypes.includes(type)) { - return reply.code(400).send({ - error: { - message: `Invalid activity type. Must be one of: ${validTypes.join(', ')}`, - code: 'INVALID_ACTIVITY_TYPE', - } - }); - } - const activityData = { - user_id: userId, - type, - title, - description, - metadata, - related_id, - channel_id, - }; - const activity = await activityRepo.create(activityData); - reply.code(201).send({ - success: true, - data: activity, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - logger_1.logger.error({ error, userId: request.user?.userId }, 'Error creating activity'); - reply.code(500).send({ - error: { - message: 'Failed to create activity', - code: 'ACTIVITY_CREATE_ERROR', - } - }); - } - }); - /** - * GET /activities/stats - Get activity statistics - */ - fastify.get('/stats', async (request, reply) => { - try { - const { period = 'week', channel_id } = request.query; - const userId = request.user.userId; - const stats = await activityRepo.getActivityStats({ - userId, - period, - channelId: channel_id, - }); - reply.send({ - success: true, - data: stats, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - logger_1.logger.error({ error, userId: request.user?.userId }, 'Error fetching activity stats'); - reply.code(500).send({ - error: { - message: 'Failed to fetch activity statistics', - code: 'ACTIVITY_STATS_ERROR', - } - }); - } - }); - /** - * GET /activities/:id - Get single activity - */ - fastify.get('/:id', async (request, reply) => { - try { - const { id } = request.params; - const activity = await activityRepo.findById(id); - if (!activity) { - return reply.code(404).send({ - error: { - message: 'Activity not found', - code: 'ACTIVITY_NOT_FOUND', - } - }); - } - reply.send({ - success: true, - data: activity, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - logger_1.logger.error({ error, userId: request.user?.userId }, 'Error fetching activity'); - reply.code(500).send({ - error: { - message: 'Failed to fetch activity', - code: 'ACTIVITY_FETCH_ERROR', - } - }); - } - }); - }, { prefix: '/activities' }); - logger_1.logger.debug('Activity routes registered'); -} -//# sourceMappingURL=ActivityRoutes.js.map \ No newline at end of file diff --git a/backend/dist/api/routes/ActivityRoutes.js.map b/backend/dist/api/routes/ActivityRoutes.js.map deleted file mode 100644 index 463d22d..0000000 --- a/backend/dist/api/routes/ActivityRoutes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ActivityRoutes.js","sourceRoot":"","sources":["../../../src/api/routes/ActivityRoutes.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/api/routes/AnnouncementRoutes.d.ts b/backend/dist/api/routes/AnnouncementRoutes.d.ts deleted file mode 100644 index 59f0879..0000000 --- a/backend/dist/api/routes/AnnouncementRoutes.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { FastifyInstance } from 'fastify'; -export declare function announcementRoutes(fastify: FastifyInstance): Promise; -//# sourceMappingURL=AnnouncementRoutes.d.ts.map \ No newline at end of file diff --git a/backend/dist/api/routes/AnnouncementRoutes.d.ts.map b/backend/dist/api/routes/AnnouncementRoutes.d.ts.map deleted file mode 100644 index 5467eb5..0000000 --- a/backend/dist/api/routes/AnnouncementRoutes.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"AnnouncementRoutes.d.ts","sourceRoot":"","sources":["../../../src/api/routes/AnnouncementRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AA0FxE,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,eAAe,iBAslBhE"} \ No newline at end of file diff --git a/backend/dist/api/routes/AnnouncementRoutes.js b/backend/dist/api/routes/AnnouncementRoutes.js deleted file mode 100644 index 9becd23..0000000 --- a/backend/dist/api/routes/AnnouncementRoutes.js +++ /dev/null @@ -1,579 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.announcementRoutes = announcementRoutes; -const typebox_1 = require("@sinclair/typebox"); -const AnnouncementRepository_1 = __importDefault(require("@db/AnnouncementRepository")); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -// Schema definitions -const AnnouncementTypeSchema = typebox_1.Type.Union([ - typebox_1.Type.Literal('info'), - typebox_1.Type.Literal('warning'), - typebox_1.Type.Literal('success'), - typebox_1.Type.Literal('error'), - typebox_1.Type.Literal('feature'), - typebox_1.Type.Literal('maintenance'), -]); -const AnnouncementPrioritySchema = typebox_1.Type.Union([ - typebox_1.Type.Literal('low'), - typebox_1.Type.Literal('medium'), - typebox_1.Type.Literal('high'), - typebox_1.Type.Literal('critical'), -]); -const AnnouncementAudienceSchema = typebox_1.Type.Union([ - typebox_1.Type.Literal('all'), - typebox_1.Type.Literal('admins'), - typebox_1.Type.Literal('developers'), - typebox_1.Type.Literal('designers'), - typebox_1.Type.Literal('managers'), -]); -const CreateAnnouncementSchema = typebox_1.Type.Object({ - title: typebox_1.Type.String({ minLength: 1, maxLength: 200 }), - content: typebox_1.Type.String({ minLength: 1, maxLength: 5000 }), - type: AnnouncementTypeSchema, - priority: AnnouncementPrioritySchema, - target_audience: AnnouncementAudienceSchema, - scheduled_for: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - expires_at: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - action_button_text: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 50 })), - action_button_url: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'uri' })), - image_url: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'uri' })), - published: typebox_1.Type.Optional(typebox_1.Type.Boolean()), -}); -const UpdateAnnouncementSchema = typebox_1.Type.Object({ - title: typebox_1.Type.Optional(typebox_1.Type.String({ minLength: 1, maxLength: 200 })), - content: typebox_1.Type.Optional(typebox_1.Type.String({ minLength: 1, maxLength: 5000 })), - type: typebox_1.Type.Optional(AnnouncementTypeSchema), - priority: typebox_1.Type.Optional(AnnouncementPrioritySchema), - target_audience: typebox_1.Type.Optional(AnnouncementAudienceSchema), - scheduled_for: typebox_1.Type.Optional(typebox_1.Type.Union([typebox_1.Type.String({ format: 'date-time' }), typebox_1.Type.Null()])), - expires_at: typebox_1.Type.Optional(typebox_1.Type.Union([typebox_1.Type.String({ format: 'date-time' }), typebox_1.Type.Null()])), - action_button_text: typebox_1.Type.Optional(typebox_1.Type.Union([typebox_1.Type.String({ maxLength: 50 }), typebox_1.Type.Null()])), - action_button_url: typebox_1.Type.Optional(typebox_1.Type.Union([typebox_1.Type.String({ format: 'uri' }), typebox_1.Type.Null()])), - image_url: typebox_1.Type.Optional(typebox_1.Type.Union([typebox_1.Type.String({ format: 'uri' }), typebox_1.Type.Null()])), - published: typebox_1.Type.Optional(typebox_1.Type.Boolean()), -}); -const AnnouncementSchema = typebox_1.Type.Object({ - id: typebox_1.Type.String(), - title: typebox_1.Type.String(), - content: typebox_1.Type.String(), - type: AnnouncementTypeSchema, - priority: AnnouncementPrioritySchema, - target_audience: AnnouncementAudienceSchema, - scheduled_for: typebox_1.Type.Union([typebox_1.Type.String({ format: 'date-time' }), typebox_1.Type.Null()]), - expires_at: typebox_1.Type.Union([typebox_1.Type.String({ format: 'date-time' }), typebox_1.Type.Null()]), - action_button_text: typebox_1.Type.Union([typebox_1.Type.String(), typebox_1.Type.Null()]), - action_button_url: typebox_1.Type.Union([typebox_1.Type.String(), typebox_1.Type.Null()]), - image_url: typebox_1.Type.Union([typebox_1.Type.String(), typebox_1.Type.Null()]), - created_by: typebox_1.Type.String(), - published: typebox_1.Type.Boolean(), - read_by: typebox_1.Type.Array(typebox_1.Type.String()), - created_at: typebox_1.Type.String({ format: 'date-time' }), - updated_at: typebox_1.Type.String({ format: 'date-time' }), - version: typebox_1.Type.Number(), -}); -async function announcementRoutes(fastify) { - const announcementRepository = new AnnouncementRepository_1.default(); - /** - * POST /announcements - Create new announcement (CEO only) - */ - fastify.post('/announcements', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Create Announcement', - description: 'Create a new announcement (CEO only)', - body: CreateAnnouncementSchema, - response: { - 201: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: AnnouncementSchema, - }), - 400: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - 401: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - 403: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - try { - // Only CEO can create announcements - if (request.user?.role !== 'ceo') { - throw new errors_1.AuthorizationError('Only CEO users can create announcements'); - } - const announcementData = { - title: request.body.title, - content: request.body.content, - type: request.body.type, - priority: request.body.priority, - target_audience: request.body.target_audience, - created_by: request.user.userId, - published: request.body.published, - ...(request.body.scheduled_for && { scheduled_for: new Date(request.body.scheduled_for) }), - ...(request.body.expires_at && { expires_at: new Date(request.body.expires_at) }), - ...(request.body.action_button_text && { action_button_text: request.body.action_button_text }), - ...(request.body.action_button_url && { action_button_url: request.body.action_button_url }), - ...(request.body.image_url && { image_url: request.body.image_url }), - }; - logger_1.loggers.api.info({ - userId: request.user.userId, - title: announcementData.title, - type: announcementData.type, - priority: announcementData.priority, - targetAudience: announcementData.target_audience, - published: announcementData.published, - }, 'Creating announcement'); - const announcement = await announcementRepository.create(announcementData); - logger_1.loggers.api.info({ - userId: request.user.userId, - announcementId: announcement.id, - }, 'Announcement created successfully'); - reply.code(201).send({ - success: true, - data: announcement, - }); - } - catch (error) { - logger_1.loggers.api.error({ error, context }, 'Failed to create announcement'); - if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else if (error instanceof errors_1.AuthorizationError) { - reply.code(403).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send((0, errors_1.formatErrorResponse)(error)); - } - } - }); - /** - * GET /announcements - Get announcements with filters - */ - fastify.get('/announcements', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Get Announcements', - description: 'Get announcements with optional filtering', - querystring: typebox_1.Type.Object({ - type: typebox_1.Type.Optional(typebox_1.Type.String()), - priority: typebox_1.Type.Optional(typebox_1.Type.String()), - target_audience: typebox_1.Type.Optional(typebox_1.Type.String()), - published: typebox_1.Type.Optional(typebox_1.Type.String()), - created_by: typebox_1.Type.Optional(typebox_1.Type.String()), - date_from: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - date_to: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - limit: typebox_1.Type.Optional(typebox_1.Type.String()), - offset: typebox_1.Type.Optional(typebox_1.Type.String()), - user_view: typebox_1.Type.Optional(typebox_1.Type.String()), - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Array(AnnouncementSchema), - total: typebox_1.Type.Number(), - limit: typebox_1.Type.Number(), - offset: typebox_1.Type.Number(), - }), - }, - }, - }, async (request, reply) => { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - try { - const { type, priority, target_audience, published, created_by, date_from, date_to, limit = '50', offset = '0', user_view = 'false', } = request.query; - const limitNum = Math.min(parseInt(limit) || 50, 100); - const offsetNum = parseInt(offset) || 0; - let data, total; - if (user_view === 'true') { - // User-specific view - only show announcements relevant to the user - data = await announcementRepository.findForUser(request.user.userId, request.user.role, true); - total = data.length; - // Apply pagination manually for user view - data = data.slice(offsetNum, offsetNum + limitNum); - } - else { - // Admin view - show all announcements with filters (CEO only) - if (request.user?.role !== 'ceo') { - throw new errors_1.AuthorizationError('Only CEO users can view all announcements'); - } - const filter = {}; - if (type) - filter.type = type.split(','); - if (priority) - filter.priority = priority.split(','); - if (target_audience) - filter.target_audience = target_audience.split(','); - if (published) - filter.published = published === 'true'; - if (created_by) - filter.created_by = created_by.split(','); - if (date_from) - filter.date_from = new Date(date_from); - if (date_to) - filter.date_to = new Date(date_to); - const result = await announcementRepository.findWithFilter(filter, limitNum, offsetNum); - data = result.data; - total = result.total; - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - userView: user_view === 'true', - resultsCount: data.length, - total, - }, 'Retrieved announcements'); - reply.send({ - success: true, - data, - total, - limit: limitNum, - offset: offsetNum, - }); - } - catch (error) { - logger_1.loggers.api.error({ error, context }, 'Failed to get announcements'); - if (error instanceof errors_1.AuthorizationError) { - reply.code(403).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send((0, errors_1.formatErrorResponse)(error)); - } - } - }); - /** - * GET /announcements/:id - Get announcement by ID - */ - fastify.get('/announcements/:id', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Get Announcement by ID', - description: 'Get a specific announcement by ID', - params: typebox_1.Type.Object({ - id: typebox_1.Type.String(), - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: AnnouncementSchema, - }), - 404: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - try { - const announcement = await announcementRepository.findById(request.params.id); - if (!announcement) { - throw new errors_1.NotFoundError(`Announcement with ID ${request.params.id} not found`); - } - reply.send({ - success: true, - data: announcement, - }); - } - catch (error) { - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send((0, errors_1.formatErrorResponse)(error)); - } - } - }); - /** - * PUT /announcements/:id - Update announcement (CEO only) - */ - fastify.put('/announcements/:id', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Update Announcement', - description: 'Update an existing announcement (CEO only)', - params: typebox_1.Type.Object({ - id: typebox_1.Type.String(), - }), - body: UpdateAnnouncementSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: AnnouncementSchema, - }), - 400: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - 403: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - 404: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - try { - // Only CEO can update announcements - if (request.user?.role !== 'ceo') { - throw new errors_1.AuthorizationError('Only CEO users can update announcements'); - } - const updateData = {}; - if (request.body.title !== undefined) - updateData.title = request.body.title; - if (request.body.content !== undefined) - updateData.content = request.body.content; - if (request.body.type !== undefined) - updateData.type = request.body.type; - if (request.body.priority !== undefined) - updateData.priority = request.body.priority; - if (request.body.target_audience !== undefined) - updateData.target_audience = request.body.target_audience; - if (request.body.published !== undefined) - updateData.published = request.body.published; - if (request.body.action_button_text !== undefined) - updateData.action_button_text = request.body.action_button_text; - if (request.body.action_button_url !== undefined) - updateData.action_button_url = request.body.action_button_url; - if (request.body.image_url !== undefined) - updateData.image_url = request.body.image_url; - if (request.body.scheduled_for !== undefined) { - updateData.scheduled_for = request.body.scheduled_for === null ? null : new Date(request.body.scheduled_for); - } - if (request.body.expires_at !== undefined) { - updateData.expires_at = request.body.expires_at === null ? null : new Date(request.body.expires_at); - } - const announcement = await announcementRepository.update(request.params.id, updateData); - logger_1.loggers.api.info({ - userId: request.user.userId, - announcementId: announcement.id, - }, 'Announcement updated successfully'); - reply.send({ - success: true, - data: announcement, - }); - } - catch (error) { - logger_1.loggers.api.error({ error, announcementId: request.params.id }, 'Failed to update announcement'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else if (error instanceof errors_1.AuthorizationError) { - reply.code(403).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send((0, errors_1.formatErrorResponse)(error)); - } - } - }); - /** - * DELETE /announcements/:id - Delete announcement (CEO only) - */ - fastify.delete('/announcements/:id', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Delete Announcement', - description: 'Delete an announcement (CEO only)', - params: typebox_1.Type.Object({ - id: typebox_1.Type.String(), - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - message: typebox_1.Type.String(), - }), - 403: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - 404: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - try { - // Only CEO can delete announcements - if (request.user?.role !== 'ceo') { - throw new errors_1.AuthorizationError('Only CEO users can delete announcements'); - } - await announcementRepository.softDelete(request.params.id, request.user.userId); - logger_1.loggers.api.info({ - userId: request.user.userId, - announcementId: request.params.id, - }, 'Announcement deleted successfully'); - reply.send({ - success: true, - message: 'Announcement deleted successfully', - }); - } - catch (error) { - logger_1.loggers.api.error({ error, announcementId: request.params.id }, 'Failed to delete announcement'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else if (error instanceof errors_1.AuthorizationError) { - reply.code(403).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send((0, errors_1.formatErrorResponse)(error)); - } - } - }); - /** - * POST /announcements/:id/read - Mark announcement as read - */ - fastify.post('/announcements/:id/read', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Mark Announcement as Read', - description: 'Mark an announcement as read by the current user', - params: typebox_1.Type.Object({ - id: typebox_1.Type.String(), - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - message: typebox_1.Type.String(), - }), - 404: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - try { - await announcementRepository.markAsRead(request.params.id, request.user.userId); - logger_1.loggers.api.info({ - userId: request.user?.userId, - announcementId: request.params.id, - }, 'Announcement marked as read'); - reply.send({ - success: true, - message: 'Announcement marked as read', - }); - } - catch (error) { - logger_1.loggers.api.error({ error, announcementId: request.params.id }, 'Failed to mark announcement as read'); - reply.code(500).send((0, errors_1.formatErrorResponse)(error)); - } - }); - /** - * GET /announcements/stats - Get announcement statistics (CEO only) - */ - fastify.get('/announcements/stats', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Get Announcement Statistics', - description: 'Get announcement statistics (CEO only)', - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Object({ - total: typebox_1.Type.Number(), - published: typebox_1.Type.Number(), - scheduled: typebox_1.Type.Number(), - expired: typebox_1.Type.Number(), - byType: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Number()), - byPriority: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Number()), - byAudience: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Number()), - }), - }), - 403: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - try { - // Only CEO can view stats - if (request.user?.role !== 'ceo') { - throw new errors_1.AuthorizationError('Only CEO users can view announcement statistics'); - } - const stats = await announcementRepository.getStats(); - reply.send({ - success: true, - data: stats, - }); - } - catch (error) { - logger_1.loggers.api.error({ error }, 'Failed to get announcement stats'); - if (error instanceof errors_1.AuthorizationError) { - reply.code(403).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send((0, errors_1.formatErrorResponse)(error)); - } - } - }); -} -//# sourceMappingURL=AnnouncementRoutes.js.map \ No newline at end of file diff --git a/backend/dist/api/routes/AnnouncementRoutes.js.map b/backend/dist/api/routes/AnnouncementRoutes.js.map deleted file mode 100644 index e755406..0000000 --- a/backend/dist/api/routes/AnnouncementRoutes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"AnnouncementRoutes.js","sourceRoot":"","sources":["../../../src/api/routes/AnnouncementRoutes.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/api/routes/ChannelRoutes.d.ts b/backend/dist/api/routes/ChannelRoutes.d.ts deleted file mode 100644 index f87efc5..0000000 --- a/backend/dist/api/routes/ChannelRoutes.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { FastifyInstance } from 'fastify'; -/** - * Register channel routes - */ -export declare const registerChannelRoutes: (fastify: FastifyInstance) => Promise; -//# sourceMappingURL=ChannelRoutes.d.ts.map \ No newline at end of file diff --git a/backend/dist/api/routes/ChannelRoutes.d.ts.map b/backend/dist/api/routes/ChannelRoutes.d.ts.map deleted file mode 100644 index 1ad15a1..0000000 --- a/backend/dist/api/routes/ChannelRoutes.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ChannelRoutes.d.ts","sourceRoot":"","sources":["../../../src/api/routes/ChannelRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AA6IxE;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAU,SAAS,eAAe,kBAwtCnE,CAAC"} \ No newline at end of file diff --git a/backend/dist/api/routes/ChannelRoutes.js b/backend/dist/api/routes/ChannelRoutes.js deleted file mode 100644 index 7ba4229..0000000 --- a/backend/dist/api/routes/ChannelRoutes.js +++ /dev/null @@ -1,1200 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerChannelRoutes = void 0; -const typebox_1 = require("@sinclair/typebox"); -const index_1 = require("@db/index"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const middleware_1 = require("@auth/middleware"); -const CacheService_1 = require("../../services/CacheService"); -const cache_decorators_1 = require("@utils/cache-decorators"); -const utils_1 = require("@websocket/utils"); -const validation_1 = require("@utils/validation"); -/** - * Channel Management API Routes - * Enterprise-grade channel CRUD operations with real-time updates - */ -// Request/Response Schemas -const CreateChannelSchema = typebox_1.Type.Object({ - name: typebox_1.Type.String({ minLength: 1, maxLength: 100 }), - description: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 500 })), - type: validation_1.ChannelTypeSchema, - privacy: validation_1.ChannelPrivacySchema, - parent_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - settings: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), - tags: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.String({ maxLength: 50 }))), - color: typebox_1.Type.Optional(typebox_1.Type.String({ pattern: '^#[0-9A-Fa-f]{6}$' })), -}); -const UpdateChannelSchema = typebox_1.Type.Object({ - name: typebox_1.Type.Optional(typebox_1.Type.String({ minLength: 1, maxLength: 100 })), - description: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 500 })), - type: typebox_1.Type.Optional(validation_1.ChannelTypeSchema), - privacy: typebox_1.Type.Optional(validation_1.ChannelPrivacySchema), - settings: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), - tags: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.String({ maxLength: 50 }))), - color: typebox_1.Type.Optional(typebox_1.Type.String({ pattern: '^#[0-9A-Fa-f]{6}$' })), -}); -const ChannelMemberSchema = typebox_1.Type.Object({ - user_id: validation_1.UUIDSchema, - role: typebox_1.Type.Union([ - typebox_1.Type.Literal('owner'), - typebox_1.Type.Literal('admin'), - typebox_1.Type.Literal('member'), - typebox_1.Type.Literal('viewer'), - ]), - joined_at: typebox_1.Type.String({ format: 'date-time' }), - user_name: typebox_1.Type.String(), - user_avatar: typebox_1.Type.Optional(typebox_1.Type.String()), -}); -const ChannelResponseSchema = typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - name: typebox_1.Type.String(), - description: typebox_1.Type.Optional(typebox_1.Type.String()), - type: validation_1.ChannelTypeSchema, - privacy: validation_1.ChannelPrivacySchema, - parent_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - created_by: validation_1.UUIDSchema, - settings: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), - tags: typebox_1.Type.Array(typebox_1.Type.String()), - color: typebox_1.Type.Optional(typebox_1.Type.String()), - member_count: typebox_1.Type.Integer(), - message_count: typebox_1.Type.Integer(), - last_activity: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - created_at: typebox_1.Type.String({ format: 'date-time' }), - updated_at: typebox_1.Type.String({ format: 'date-time' }), -}); -/** - * Channel service with caching - */ -class ChannelService { - async getChannelById(channelId) { - return await index_1.channelRepository.findById(channelId); - } - async updateChannel(channelId, updateData) { - // Map frontend fields to backend fields - const mappedData = {}; - if (updateData.name !== undefined) - mappedData.name = updateData.name; - if (updateData.description !== undefined) - mappedData.description = updateData.description; - if (updateData.type !== undefined) - mappedData.channel_type = updateData.type; - if (updateData.privacy !== undefined) - mappedData.privacy_level = updateData.privacy; - if (updateData.settings !== undefined) - mappedData.settings = updateData.settings; - // Handle project_info updates - if (updateData.tags !== undefined || updateData.color !== undefined) { - const existingChannel = await index_1.channelRepository.findById(channelId); - const currentProjectInfo = existingChannel?.project_info || {}; - mappedData.project_info = { - ...currentProjectInfo, - ...(updateData.tags !== undefined && { tags: updateData.tags }), - ...(updateData.color !== undefined && { color: updateData.color }), - }; - } - return await index_1.channelRepository.update(channelId, mappedData); - } - async createChannel(channelData) { - return await index_1.channelRepository.createChannel(channelData); - } -} -__decorate([ - (0, cache_decorators_1.Cacheable)({ - ttl: 1800, // 30 minutes - namespace: 'channels', - keyGenerator: (channelId) => cache_decorators_1.CacheKeyUtils.channelKey(channelId), - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], ChannelService.prototype, "getChannelById", null); -__decorate([ - (0, cache_decorators_1.CacheEvict)({ - keys: (channelId) => [cache_decorators_1.CacheKeyUtils.channelKey(channelId)], - namespace: 'channels', - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, Object]), - __metadata("design:returntype", Promise) -], ChannelService.prototype, "updateChannel", null); -__decorate([ - (0, cache_decorators_1.CacheEvict)({ - allEntries: true, - namespace: 'channels', - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], ChannelService.prototype, "createChannel", null); -const channelService = new ChannelService(); -/** - * Register channel routes - */ -const registerChannelRoutes = async (fastify) => { - /** - * GET /channels - List channels accessible to user - */ - fastify.get('/channels', { - preHandler: [middleware_1.authenticate, middleware_1.apiRateLimit], - schema: { - querystring: typebox_1.Type.Intersect([ - validation_1.PaginationSchema, - typebox_1.Type.Object({ - type: typebox_1.Type.Optional(validation_1.ChannelTypeSchema), - privacy: typebox_1.Type.Optional(validation_1.ChannelPrivacySchema), - parent_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - search: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 100 })), - }), - ]), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Array(ChannelResponseSchema), - pagination: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - limit: typebox_1.Type.Integer(), - offset: typebox_1.Type.Integer(), - hasMore: typebox_1.Type.Boolean(), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { limit = 20, offset = 0, type, privacy, parent_id, search } = request.query; - // Build filters - const filters = {}; - if (type) - filters.type = type; - if (privacy) - filters.privacy = privacy; - if (parent_id) - filters.parent_id = parent_id; - if (search) - filters.search = search; - // Get channels user has access to - const result = await index_1.channelRepository.findUserChannels(request.user.userId); - logger_1.loggers.api.info({ - userId: request.user?.userId, - filters, - resultCount: result.length, - }, 'Channels list retrieved'); - reply.send({ - success: true, - data: result, - pagination: { - total: result.length, - limit, - offset, - hasMore: false, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve channels'); - reply.code(500).send({ - error: { - message: 'Failed to retrieve channels', - code: 'SERVER_ERROR', - }, - }); - } - }); - /** - * GET /channels/categories - Get available channel categories - */ - fastify.get('/channels/categories', { - preHandler: [middleware_1.authenticate], - schema: { - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Array(typebox_1.Type.Object({ - id: typebox_1.Type.String(), - name: typebox_1.Type.String(), - description: typebox_1.Type.String(), - icon: typebox_1.Type.Optional(typebox_1.Type.String()), - color: typebox_1.Type.Optional(typebox_1.Type.String()), - })), - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - // Define available channel categories based on ChannelTypeSchema - const categories = [ - { - id: 'general', - name: 'General', - description: 'General purpose discussions and communications', - icon: 'chatbubble-outline', - color: '#6B7280', - }, - { - id: 'project', - name: 'Project', - description: 'Project-specific discussions and collaboration', - icon: 'folder-outline', - color: '#3B82F6', - }, - { - id: 'department', - name: 'Department', - description: 'Department-wide communications and updates', - icon: 'business-outline', - color: '#10B981', - }, - { - id: 'announcement', - name: 'Announcement', - description: 'Official announcements and important updates', - icon: 'megaphone-outline', - color: '#F59E0B', - }, - { - id: 'private', - name: 'Private', - description: 'Private discussions and confidential matters', - icon: 'lock-closed-outline', - color: '#8B5CF6', - }, - ]; - logger_1.loggers.api.info({ - userId: request.user?.userId, - categoriesCount: categories.length, - }, 'Channel categories retrieved'); - reply.send({ - success: true, - data: categories, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve channel categories'); - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel categories', - code: 'SERVER_ERROR', - }, - }); - } - }); - /** - * GET /channels/:id - Get channel details - */ - fastify.get('/channels/:id', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: ChannelResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const channel = await channelService.getChannelById(id); - if (!channel) { - throw new errors_1.NotFoundError('Channel not found'); - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId: id, - }, 'Channel details retrieved'); - reply.send({ - success: true, - data: channel, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve channel'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * POST /channels - Create new channel - */ - fastify.post('/channels', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorize)('channels:create')], - schema: { - body: CreateChannelSchema, - response: { - 201: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: ChannelResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const channelData = { - name: request.body.name, - description: request.body.description, - channel_type: request.body.type, - privacy_level: request.body.privacy, - created_by: request.user.userId, - settings: request.body.settings || {}, - project_info: { - tags: request.body.tags || [], - ...(request.body.color && { color: request.body.color }), - }, - ...(request.body.parent_id && { parent_id: request.body.parent_id }), - }; - const channel = await channelService.createChannel(channelData); - // Creator is already added as member in createChannel method - // await channelRepository.addMember(channel.id, request.user!.userId, request.user!.userId); - // Create activity for channel creation - try { - await index_1.activityRepository.createActivity({ - channelId: channel.id, - userId: request.user.userId, - activityType: 'channel_created', - title: `Channel Created: ${channel.name}`, - description: `New ${channel.channel_type} channel "${channel.name}" was created${channel.description ? ': ' + channel.description : ''}`, - category: 'channel', - metadata: { - channelId: channel.id, - channelName: channel.name, - channelType: channel.channel_type, - channelPrivacy: channel.privacy_level, - parentId: channel.parent_id, - createdBy: request.user.userId, - createdByName: request.user.name, - tags: request.body.tags || [], - settings: channel.settings - } - }); - } - catch (error) { - logger_1.loggers.api.warn?.({ error, channelId: channel.id }, 'Failed to create channel creation activity'); - } - // Broadcast channel creation - await utils_1.WebSocketUtils.broadcastChannelMessage({ - type: 'chat_message', - channelId: channel.id, - messageId: `system_${Date.now()}`, - message: `Channel "${channel.name}" created`, - messageType: 'system', - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId: channel.id, - channelName: channel.name, - channelType: channel.channel_type, - }, 'Channel created successfully'); - reply.code(201).send({ - success: true, - data: channel, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to create channel'); - if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to create channel', - code: 'SERVER_ERROR', - ...(process.env.NODE_ENV === 'development' && { - details: error instanceof Error ? error.message : String(error), - stack: error instanceof Error ? error.stack : undefined, - }), - }, - }); - } - } - }); - /** - * PUT /channels/:id - Update channel - */ - fastify.put('/channels/:id', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess, (0, middleware_1.authorize)('channels:update')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - body: UpdateChannelSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: ChannelResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const updateData = request.body; - const channel = await channelService.updateChannel(id, updateData); - // Broadcast channel update - await utils_1.WebSocketUtils.sendToChannel(id, 'channel_updated', { - type: 'channel_updated', - channelId: id, - updates: updateData, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - timestamp: new Date().toISOString(), - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId: id, - updatedFields: Object.keys(updateData), - }, 'Channel updated successfully'); - reply.send({ - success: true, - data: channel, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to update channel'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to update channel', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * DELETE /channels/:id - Delete channel - */ - fastify.delete('/channels/:id', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess, (0, middleware_1.authorize)('channels:delete')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - response: { - 200: validation_1.SuccessResponseSchema, - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const success = await index_1.channelRepository.softDelete(id, request.user.userId); - if (!success) { - throw new errors_1.NotFoundError('Channel not found'); - } - // Clear channel cache - await CacheService_1.cacheService.channels.delete(cache_decorators_1.CacheKeyUtils.channelKey(id)); - // Broadcast channel deletion - await utils_1.WebSocketUtils.sendToChannel(id, 'channel_deleted', { - type: 'channel_deleted', - channelId: id, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - timestamp: new Date().toISOString(), - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId: id, - }, 'Channel deleted successfully'); - reply.send({ - success: true, - message: 'Channel deleted successfully', - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to delete channel'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to delete channel', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * GET /channels/:id/members - Get channel members - */ - fastify.get('/channels/:id/members', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - querystring: validation_1.PaginationSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Array(ChannelMemberSchema), - pagination: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - limit: typebox_1.Type.Integer(), - offset: typebox_1.Type.Integer(), - hasMore: typebox_1.Type.Boolean(), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const { limit = 50, offset = 0 } = request.query; - const members = await index_1.channelRepository.getMembers(id); - // Map the members data to match the expected schema - const mappedMembers = members.map((member) => ({ - user_id: member.id, - role: member.role, - joined_at: new Date().toISOString(), // TODO: Get actual joined_at from channel_member_history - user_name: member.name, - user_avatar: member.avatar_url, - })); - const result = { - data: mappedMembers.slice(offset, offset + limit), - total: mappedMembers.length, - }; - reply.send({ - success: true, - data: result.data, - pagination: { - total: result.total, - limit, - offset, - hasMore: offset + limit < result.total, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve channel members'); - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel members', - code: 'SERVER_ERROR', - }, - }); - } - }); - /** - * POST /channels/:id/members - Add member to channel - */ - fastify.post('/channels/:id/members', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess, (0, middleware_1.authorize)('channels:manage_members')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - body: typebox_1.Type.Object({ - user_id: validation_1.UUIDSchema, - role: typebox_1.Type.Optional(typebox_1.Type.Union([typebox_1.Type.Literal('admin'), typebox_1.Type.Literal('member'), typebox_1.Type.Literal('viewer')])), - }), - response: { - 200: validation_1.SuccessResponseSchema, - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const { user_id, role = 'member' } = request.body; - const success = await index_1.channelRepository.addMember(id, user_id, role); - if (!success) { - throw new errors_1.ValidationError('Failed to add member to channel', []); - } - // Broadcast member addition - await utils_1.WebSocketUtils.sendToChannel(id, 'user_joined_channel', { - type: 'user_joined_channel', - channelId: id, - userId: user_id, - userName: '', // TODO: Get user name - userRole: request.user.role, - memberCount: (await index_1.channelRepository.getMembers(id)).length, - timestamp: new Date().toISOString(), - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId: id, - addedUserId: user_id, - memberRole: role, - }, 'Member added to channel'); - reply.send({ - success: true, - message: 'Member added successfully', - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to add member to channel'); - if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to add member', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * DELETE /channels/:id/members/:user_id - Remove member from channel - */ - fastify.delete('/channels/:id/members/:user_id', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess, (0, middleware_1.authorize)('channels:manage_members')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - user_id: validation_1.UUIDSchema, - }), - response: { - 200: validation_1.SuccessResponseSchema, - }, - }, - }, async (request, reply) => { - try { - const { id, user_id } = request.params; - const success = await index_1.channelRepository.removeMember(id, user_id, request.user.userId); - if (!success) { - throw new errors_1.NotFoundError('Member not found in channel'); - } - // Broadcast member removal - await utils_1.WebSocketUtils.sendToChannel(id, 'user_left_channel', { - type: 'user_left_channel', - channelId: id, - userId: user_id, - userName: '', // TODO: Get user name - userRole: request.user.role, - memberCount: (await index_1.channelRepository.getMembers(id)).length, - timestamp: new Date().toISOString(), - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId: id, - removedUserId: user_id, - }, 'Member removed from channel'); - reply.send({ - success: true, - message: 'Member removed successfully', - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to remove member from channel'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to remove member', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * GET /channels/:id/files - Get channel file attachments - */ - fastify.get('/channels/:id/files', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - querystring: typebox_1.Type.Intersect([ - validation_1.PaginationSchema, - typebox_1.Type.Object({ - file_type: typebox_1.Type.Optional(typebox_1.Type.String()), - uploaded_by: typebox_1.Type.Optional(validation_1.UUIDSchema), - search: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 100 })), - }), - ]), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Array(typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - filename: typebox_1.Type.String(), - originalName: typebox_1.Type.String(), - mimeType: typebox_1.Type.String(), - size: typebox_1.Type.Integer(), - url: typebox_1.Type.String(), - downloadUrl: typebox_1.Type.Optional(typebox_1.Type.String()), - thumbnailUrl: typebox_1.Type.Optional(typebox_1.Type.String()), - uploadedBy: validation_1.UUIDSchema, - uploadedByName: typebox_1.Type.String(), - uploadedAt: typebox_1.Type.String({ format: 'date-time' }), - messageId: typebox_1.Type.Optional(validation_1.UUIDSchema), - })), - pagination: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - limit: typebox_1.Type.Integer(), - offset: typebox_1.Type.Integer(), - hasMore: typebox_1.Type.Boolean(), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const { limit = 50, offset = 0, file_type, uploaded_by, search } = request.query; - // Build filters - const filters = { - channelId: id, - fileType: file_type, - uploadedBy: uploaded_by, - search, - }; - let files = []; - let total = 0; - try { - // Try to fetch files, but handle database errors gracefully - files = await index_1.fileRepository.findChannelFiles(id, filters, limit, offset); - total = await index_1.fileRepository.getChannelFileCount(id, filters); - } - catch (dbError) { - // Log database errors but return empty results instead of failing - logger_1.loggers.api.warn({ - userId: request.user?.userId, - channelId: id, - error: dbError.message, - filters, - }, 'Channel files database query failed, returning empty results'); - // Return empty results if database query fails - files = []; - total = 0; - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId: id, - fileCount: files.length, - filters, - }, 'Channel files retrieved'); - reply.send({ - success: true, - data: files, - pagination: { - total, - limit, - offset, - hasMore: offset + limit < total, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.warn({ error, context }, 'Failed to retrieve channel files, returning empty results'); - // Return empty results instead of error to prevent frontend issues - reply.send({ - success: true, - data: [], - pagination: { - total: 0, - limit: request.query.limit || 50, - offset: request.query.offset || 0, - hasMore: false, - }, - timestamp: new Date().toISOString(), - }); - } - }); - /** - * GET /channels/:id/activity - Get channel activity log - */ - fastify.get('/channels/:id/activity', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - querystring: typebox_1.Type.Intersect([ - validation_1.PaginationSchema, - typebox_1.Type.Object({ - activity_type: typebox_1.Type.Optional(typebox_1.Type.Union([ - typebox_1.Type.Literal('message'), - typebox_1.Type.Literal('task_created'), - typebox_1.Type.Literal('task_updated'), - typebox_1.Type.Literal('task_completed'), - typebox_1.Type.Literal('member_joined'), - typebox_1.Type.Literal('member_left'), - typebox_1.Type.Literal('file_uploaded'), - typebox_1.Type.Literal('channel_updated'), - ])), - user_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - after: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - }), - ]), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Array(typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - channelId: validation_1.UUIDSchema, - activityType: typebox_1.Type.String(), - userId: validation_1.UUIDSchema, - userName: typebox_1.Type.String(), - userAvatar: typebox_1.Type.Optional(typebox_1.Type.String()), - title: typebox_1.Type.String(), - description: typebox_1.Type.Optional(typebox_1.Type.String()), - metadata: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any()), - createdAt: typebox_1.Type.String({ format: 'date-time' }), - })), - pagination: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - limit: typebox_1.Type.Integer(), - offset: typebox_1.Type.Integer(), - hasMore: typebox_1.Type.Boolean(), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const { limit = 50, offset = 0, activity_type, user_id, after } = request.query; - // Build filters - const filters = { - channelId: id, - activityType: activity_type, - userId: user_id, - after: after ? new Date(after) : undefined, - }; - // This would require implementing activity repository methods - const activities = await index_1.activityRepository.findChannelActivities(id, filters, limit, offset); - const total = await index_1.activityRepository.getChannelActivityCount(id, filters); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId: id, - activityCount: activities.length, - filters, - }, 'Channel activity retrieved'); - reply.send({ - success: true, - data: activities, - pagination: { - total, - limit, - offset, - hasMore: offset + limit < total, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve channel activity'); - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel activity', - code: 'SERVER_ERROR', - }, - }); - } - }); - /** - * POST /channels/:id/activity - Log channel activity - */ - fastify.post('/channels/:id/activity', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - body: typebox_1.Type.Object({ - activity_type: typebox_1.Type.String({ minLength: 1, maxLength: 50 }), - title: typebox_1.Type.String({ minLength: 1, maxLength: 200 }), - description: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 1000 })), - metadata: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), - }), - response: { - 201: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - activityType: typebox_1.Type.String(), - title: typebox_1.Type.String(), - description: typebox_1.Type.Optional(typebox_1.Type.String()), - metadata: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any()), - createdAt: typebox_1.Type.String({ format: 'date-time' }), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const { activity_type, title, description, metadata } = request.body; - const activityData = { - channelId: id, - activityType: activity_type, - userId: request.user.userId, - title, - description: description || '', - metadata: metadata || {}, - }; - const activity = await index_1.activityRepository.createActivity(activityData); - // Broadcast activity to channel members - await utils_1.WebSocketUtils.sendToChannel(id, 'channel_activity', { - type: 'channel_activity', - channelId: id, - activity: { - id: activity.id, - activityType: activity.activity_type, - title: activity.title, - description: activity.description, - metadata: activity.metadata, - userId: request.user.userId, - userName: request.user.name, - userAvatar: null, // Avatar not available in TokenPayload - createdAt: activity.created_at, - }, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - timestamp: new Date().toISOString(), - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId: id, - activityId: activity.id, - activityType: activity_type, - }, 'Channel activity logged successfully'); - reply.code(201).send({ - success: true, - data: { - id: activity.id, - activityType: activity.activity_type, - title: activity.title, - description: activity.description, - metadata: activity.metadata, - createdAt: activity.created_at, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to log channel activity'); - if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to log channel activity', - code: 'SERVER_ERROR', - }, - }); - } - } - }); -}; -exports.registerChannelRoutes = registerChannelRoutes; -//# sourceMappingURL=ChannelRoutes.js.map \ No newline at end of file diff --git a/backend/dist/api/routes/ChannelRoutes.js.map b/backend/dist/api/routes/ChannelRoutes.js.map deleted file mode 100644 index 60bbe5d..0000000 --- a/backend/dist/api/routes/ChannelRoutes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ChannelRoutes.js","sourceRoot":"","sources":["../../../src/api/routes/ChannelRoutes.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/api/routes/DocsRoutes.d.ts b/backend/dist/api/routes/DocsRoutes.d.ts deleted file mode 100644 index 254d56d..0000000 --- a/backend/dist/api/routes/DocsRoutes.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { FastifyInstance } from 'fastify'; -/** - * Documentation Routes - Swagger/OpenAPI Integration - * Provides interactive API documentation via Swagger UI - */ -export declare const registerDocsRoutes: (fastify: FastifyInstance) => Promise; -//# sourceMappingURL=DocsRoutes.d.ts.map \ No newline at end of file diff --git a/backend/dist/api/routes/DocsRoutes.d.ts.map b/backend/dist/api/routes/DocsRoutes.d.ts.map deleted file mode 100644 index 7e90a05..0000000 --- a/backend/dist/api/routes/DocsRoutes.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"DocsRoutes.d.ts","sourceRoot":"","sources":["../../../src/api/routes/DocsRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAE1C;;;GAGG;AAEH,eAAO,MAAM,kBAAkB,GAAU,SAAS,eAAe,kBA2EhE,CAAC"} \ No newline at end of file diff --git a/backend/dist/api/routes/DocsRoutes.js b/backend/dist/api/routes/DocsRoutes.js deleted file mode 100644 index 47c4a96..0000000 --- a/backend/dist/api/routes/DocsRoutes.js +++ /dev/null @@ -1,83 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerDocsRoutes = void 0; -/** - * Documentation Routes - Swagger/OpenAPI Integration - * Provides interactive API documentation via Swagger UI - */ -const registerDocsRoutes = async (fastify) => { - // The Swagger UI will be available at /docs - // All route schemas are automatically collected by @fastify/swagger - /** - * Redirect root docs to Swagger UI - */ - fastify.get('/docs', async (request, reply) => { - reply.redirect('/docs/'); - }); - /** - * API Health Status endpoint with OpenAPI schema - */ - fastify.get('/health/api', { - schema: { - tags: ['Health'], - summary: 'API Health Status', - description: 'Get detailed API health status with features and endpoints', - response: { - 200: { - type: 'object', - properties: { - status: { type: 'string', example: 'healthy' }, - version: { type: 'string', example: '1.0.0' }, - environment: { type: 'string', example: 'development' }, - features: { - type: 'object', - properties: { - authentication: { type: 'string', example: 'enabled' }, - websockets: { type: 'string', example: 'enabled' }, - caching: { type: 'string', example: 'enabled' }, - rateLimit: { type: 'string', example: 'enabled' }, - voiceCommands: { type: 'string', example: 'ready' }, - analytics: { type: 'string', example: 'ready' }, - } - }, - endpoints: { - type: 'object', - properties: { - auth: { type: 'string', example: '/api/v1/auth/*' }, - users: { type: 'string', example: '/api/v1/users/*' }, - channels: { type: 'string', example: '/api/v1/channels/*' }, - tasks: { type: 'string', example: '/api/v1/tasks/*' }, - docs: { type: 'string', example: '/docs' }, - } - }, - timestamp: { type: 'string', format: 'date-time' }, - } - } - } - } - }, async (request, reply) => { - reply.send({ - status: 'healthy', - version: '1.0.0', - environment: process.env.NODE_ENV || 'development', - features: { - authentication: 'enabled', - websockets: 'enabled', - caching: 'enabled', - rateLimit: 'enabled', - voiceCommands: 'ready', - analytics: 'ready', - }, - endpoints: { - auth: '/api/v1/auth/*', - users: '/api/v1/users/*', - channels: '/api/v1/channels/*', - tasks: '/api/v1/tasks/*', - docs: '/docs', - }, - timestamp: new Date().toISOString(), - }); - }); -}; -exports.registerDocsRoutes = registerDocsRoutes; -//# sourceMappingURL=DocsRoutes.js.map \ No newline at end of file diff --git a/backend/dist/api/routes/DocsRoutes.js.map b/backend/dist/api/routes/DocsRoutes.js.map deleted file mode 100644 index 4c7d72f..0000000 --- a/backend/dist/api/routes/DocsRoutes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"DocsRoutes.js","sourceRoot":"","sources":["../../../src/api/routes/DocsRoutes.ts"],"names":[],"mappings":";;;AAEA;;;GAGG;AAEI,MAAM,kBAAkB,GAAG,KAAK,EAAE,OAAwB,EAAE,EAAE;IACnE,4CAA4C;IAC5C,oEAAoE;IAEpE;;OAEG;IACH,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC5C,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEH;;OAEG;IACH,OAAO,CAAC,GAAG,CAAC,aAAa,EAAE;QACzB,MAAM,EAAE;YACN,IAAI,EAAE,CAAC,QAAQ,CAAC;YAChB,OAAO,EAAE,mBAAmB;YAC5B,WAAW,EAAE,4DAA4D;YACzE,QAAQ,EAAE;gBACR,GAAG,EAAE;oBACH,IAAI,EAAE,QAAQ;oBACd,UAAU,EAAE;wBACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE;wBAC9C,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE;wBAC7C,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE;wBACvD,QAAQ,EAAE;4BACR,IAAI,EAAE,QAAQ;4BACd,UAAU,EAAE;gCACV,cAAc,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE;gCACtD,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE;gCAClD,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE;gCAC/C,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE;gCACjD,aAAa,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE;gCACnD,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE;6BAChD;yBACF;wBACD,SAAS,EAAE;4BACT,IAAI,EAAE,QAAQ;4BACd,UAAU,EAAE;gCACV,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE;gCACnD,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE;gCACrD,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,oBAAoB,EAAE;gCAC3D,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,EAAE;gCACrD,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE;6BAC3C;yBACF;wBACD,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE;qBACnD;iBACF;aACF;SACF;KACF,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;QAC1B,KAAK,CAAC,IAAI,CAAC;YACT,MAAM,EAAE,SAAS;YACjB,OAAO,EAAE,OAAO;YAChB,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,aAAa;YAClD,QAAQ,EAAE;gBACR,cAAc,EAAE,SAAS;gBACzB,UAAU,EAAE,SAAS;gBACrB,OAAO,EAAE,SAAS;gBAClB,SAAS,EAAE,SAAS;gBACpB,aAAa,EAAE,OAAO;gBACtB,SAAS,EAAE,OAAO;aACnB;YACD,SAAS,EAAE;gBACT,IAAI,EAAE,gBAAgB;gBACtB,KAAK,EAAE,iBAAiB;gBACxB,QAAQ,EAAE,oBAAoB;gBAC9B,KAAK,EAAE,iBAAiB;gBACxB,IAAI,EAAE,OAAO;aACd;YACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SACpC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AA3EW,QAAA,kBAAkB,sBA2E7B"} \ No newline at end of file diff --git a/backend/dist/api/routes/MessageRoutes.d.ts b/backend/dist/api/routes/MessageRoutes.d.ts deleted file mode 100644 index 66e6f00..0000000 --- a/backend/dist/api/routes/MessageRoutes.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { FastifyInstance } from 'fastify'; -/** - * Register message routes - */ -export declare const registerMessageRoutes: (fastify: FastifyInstance) => Promise; -//# sourceMappingURL=MessageRoutes.d.ts.map \ No newline at end of file diff --git a/backend/dist/api/routes/MessageRoutes.d.ts.map b/backend/dist/api/routes/MessageRoutes.d.ts.map deleted file mode 100644 index fbe0e0b..0000000 --- a/backend/dist/api/routes/MessageRoutes.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"MessageRoutes.d.ts","sourceRoot":"","sources":["../../../src/api/routes/MessageRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AA2HxE;;GAEG;AACH,eAAO,MAAM,qBAAqB,GAAU,SAAS,eAAe,kBAu0BnE,CAAC"} \ No newline at end of file diff --git a/backend/dist/api/routes/MessageRoutes.js b/backend/dist/api/routes/MessageRoutes.js deleted file mode 100644 index 9b4bdeb..0000000 --- a/backend/dist/api/routes/MessageRoutes.js +++ /dev/null @@ -1,829 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerMessageRoutes = void 0; -const typebox_1 = require("@sinclair/typebox"); -const index_1 = require("@db/index"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const middleware_1 = require("@auth/middleware"); -const CacheService_1 = require("../../services/CacheService"); -const cache_decorators_1 = require("@utils/cache-decorators"); -const utils_1 = require("@websocket/utils"); -const validation_1 = require("@utils/validation"); -/** - * Channel Message Management API Routes - * Enterprise-grade messaging system with real-time updates - */ -// Request/Response Schemas -const SendMessageSchema = typebox_1.Type.Object({ - content: typebox_1.Type.String({ minLength: 1, maxLength: 4000 }), - message_type: typebox_1.Type.Optional(typebox_1.Type.Union([ - typebox_1.Type.Literal('text'), - typebox_1.Type.Literal('voice'), - typebox_1.Type.Literal('file'), - typebox_1.Type.Literal('system'), - ])), - reply_to: typebox_1.Type.Optional(validation_1.UUIDSchema), - thread_root: typebox_1.Type.Optional(validation_1.UUIDSchema), - mentions: typebox_1.Type.Optional(typebox_1.Type.Array(validation_1.UUIDSchema)), - attachments: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.Object({ - file_id: validation_1.UUIDSchema, - filename: typebox_1.Type.String(), - file_type: typebox_1.Type.String(), - file_size: typebox_1.Type.Integer(), - }))), - voice_data: typebox_1.Type.Optional(typebox_1.Type.Object({ - duration: typebox_1.Type.Number(), - transcript: typebox_1.Type.Optional(typebox_1.Type.String()), - voice_file_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - })), - metadata: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), -}); -const UpdateMessageSchema = typebox_1.Type.Object({ - content: typebox_1.Type.String({ minLength: 1, maxLength: 4000 }), -}); -const MessageResponseSchema = typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - channel_id: validation_1.UUIDSchema, - task_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - user_id: validation_1.UUIDSchema, - user_name: typebox_1.Type.String(), - user_avatar: typebox_1.Type.Optional(typebox_1.Type.String()), - content: typebox_1.Type.String(), - message_type: typebox_1.Type.String(), - voice_data: typebox_1.Type.Optional(typebox_1.Type.Any()), - transcription: typebox_1.Type.Optional(typebox_1.Type.String()), - attachments: typebox_1.Type.Array(typebox_1.Type.Any()), - reply_to: typebox_1.Type.Optional(validation_1.UUIDSchema), - thread_root: typebox_1.Type.Optional(validation_1.UUIDSchema), - is_edited: typebox_1.Type.Boolean(), - is_pinned: typebox_1.Type.Boolean(), - is_announcement: typebox_1.Type.Boolean(), - reactions: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any()), - mentions: typebox_1.Type.Array(validation_1.UUIDSchema), - ai_generated: typebox_1.Type.Boolean(), - ai_context: typebox_1.Type.Optional(typebox_1.Type.Any()), - command_execution_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - metadata: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any()), - reply_count: typebox_1.Type.Optional(typebox_1.Type.Integer()), - last_reply_timestamp: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - created_at: typebox_1.Type.String({ format: 'date-time' }), - updated_at: typebox_1.Type.String({ format: 'date-time' }), - edited_at: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), -}); -/** - * Message service with caching - */ -class MessageService { - async getMessageById(messageId) { - return await index_1.messageRepository.findById(messageId); - } - async createMessage(messageData) { - return await index_1.messageRepository.createMessage(messageData); - } - async updateMessage(messageId, updateData) { - return await index_1.messageRepository.updateMessage(messageId, updateData); - } -} -__decorate([ - (0, cache_decorators_1.Cacheable)({ - ttl: 300, // 5 minutes - namespace: 'messages', - keyGenerator: (messageId) => cache_decorators_1.CacheKeyUtils.messageKey(messageId), - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], MessageService.prototype, "getMessageById", null); -__decorate([ - (0, cache_decorators_1.CacheEvict)({ - keys: (channelId) => [cache_decorators_1.CacheKeyUtils.channelMessagesKey(channelId)], - namespace: 'messages', - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], MessageService.prototype, "createMessage", null); -__decorate([ - (0, cache_decorators_1.CacheEvict)({ - keys: (messageId, channelId) => [ - cache_decorators_1.CacheKeyUtils.messageKey(messageId), - cache_decorators_1.CacheKeyUtils.channelMessagesKey(channelId), - ], - namespace: 'messages', - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, Object]), - __metadata("design:returntype", Promise) -], MessageService.prototype, "updateMessage", null); -const messageService = new MessageService(); -/** - * Register message routes - */ -const registerMessageRoutes = async (fastify) => { - /** - * GET /channels/:channelId/messages - Get channel messages - */ - fastify.get('/channels/:channelId/messages', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess, middleware_1.apiRateLimit], - schema: { - params: typebox_1.Type.Object({ - channelId: validation_1.UUIDSchema, - }), - querystring: typebox_1.Type.Intersect([ - validation_1.PaginationSchema, - typebox_1.Type.Object({ - thread_root: typebox_1.Type.Optional(validation_1.UUIDSchema), - search: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 200 })), - message_type: typebox_1.Type.Optional(typebox_1.Type.String()), - before: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - after: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - }), - ]), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Array(MessageResponseSchema), - pagination: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - limit: typebox_1.Type.Integer(), - offset: typebox_1.Type.Integer(), - hasMore: typebox_1.Type.Boolean(), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { channelId } = request.params; - const { limit = 50, offset = 0, thread_root, search, message_type, before, after, } = request.query; - // Build filters - const filters = { - channelId, - threadRoot: thread_root, - messageType: message_type, - before: before ? new Date(before) : undefined, - after: after ? new Date(after) : undefined, - }; - let messages = []; - let total = 0; - if (search) { - // Search messages - messages = await index_1.messageRepository.searchMessages(channelId, search, Math.min(limit, 100), offset); - total = messages.length; // Approximation - } - else { - // Get messages with filters - messages = await index_1.messageRepository.findChannelMessages(channelId, filters, Math.min(limit, 100), offset); - total = await index_1.messageRepository.getChannelMessageCount(channelId, filters); - } - // Update last read for user - await index_1.messageRepository.updateLastRead(channelId, request.user.userId); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - messageCount: messages.length, - filters, - }, 'Channel messages retrieved'); - reply.send({ - success: true, - data: messages, - pagination: { - total, - limit, - offset, - hasMore: offset + limit < total, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve channel messages'); - reply.code(500).send({ - error: { - message: 'Failed to retrieve messages', - code: 'SERVER_ERROR', - }, - }); - } - }); - /** - * POST /channels/:channelId/messages - Send message - */ - fastify.post('/channels/:channelId/messages', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - channelId: validation_1.UUIDSchema, - }), - body: SendMessageSchema, - response: { - 201: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: MessageResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { channelId } = request.params; - const messageData = { - ...request.body, - channel_id: channelId, - user_id: request.user.userId, - message_type: request.body.message_type || 'text', - attachments: request.body.attachments || [], - mentions: request.body.mentions || [], - metadata: request.body.metadata || {}, - ai_generated: false, - }; - const message = await messageService.createMessage(messageData); - // Update channel activity - await index_1.channelRepository.updateActivity(channelId); - // Determine if this is a thread message - const isThreadReply = message.reply_to && message.thread_root; - // Broadcast message to channel members with thread context - await utils_1.WebSocketUtils.sendToChannel(channelId, 'message_sent', { - type: 'message_sent', - channelId, - messageId: message.id, - isThreadReply, - threadRoot: message.thread_root, - replyTo: message.reply_to, - message: { - id: message.id, - channel_id: message.channel_id, - user_id: message.user_id, - user_name: request.user.name, - user_email: request.user.email, - user_avatar: request.user.avatar_url, - user_role: request.user.role, - content: message.content, - message_type: message.message_type, - voice_data: message.voice_data, - transcription: message.transcription, - attachments: message.attachments, - reply_to: message.reply_to, - thread_root: message.thread_root, - is_edited: message.is_edited, - is_pinned: message.is_pinned, - is_announcement: message.is_announcement, - reactions: message.reactions, - mentions: message.mentions, - ai_generated: message.ai_generated, - ai_context: message.ai_context, - command_execution_id: message.command_execution_id, - metadata: message.metadata, - formatting: message.formatting, - created_at: message.created_at, - updated_at: message.updated_at, - edited_at: message.edited_at, - }, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - timestamp: new Date().toISOString(), - }); - // If this is a thread reply, also send a separate thread-specific event - if (isThreadReply) { - await utils_1.WebSocketUtils.sendToChannel(channelId, 'thread_reply_sent', { - type: 'thread_reply_sent', - channelId, - threadRoot: message.thread_root, - parentMessageId: message.reply_to, - messageId: message.id, - message: { - id: message.id, - channel_id: message.channel_id, - user_id: message.user_id, - user_name: request.user.name, - user_email: request.user.email, - user_avatar: request.user.avatar_url, - user_role: request.user.role, - content: message.content, - message_type: message.message_type, - voice_data: message.voice_data, - transcription: message.transcription, - attachments: message.attachments, - reply_to: message.reply_to, - thread_root: message.thread_root, - is_edited: message.is_edited, - is_pinned: message.is_pinned, - is_announcement: message.is_announcement, - reactions: message.reactions, - mentions: message.mentions, - ai_generated: message.ai_generated, - ai_context: message.ai_context, - command_execution_id: message.command_execution_id, - metadata: message.metadata, - formatting: message.formatting, - created_at: message.created_at, - updated_at: message.updated_at, - edited_at: message.edited_at, - }, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - timestamp: new Date().toISOString(), - }); - } - // Send mention notifications - if (message.mentions.length > 0) { - for (const mentionedUserId of message.mentions) { - if (mentionedUserId !== request.user.userId) { - await utils_1.WebSocketUtils.createAndSendNotification(mentionedUserId, { - title: 'You were mentioned', - message: `${request.user.name} mentioned you in ${channelId}`, - category: 'mention', - priority: 'medium', - actionUrl: `/channels/${channelId}`, - actionText: 'View Message', - data: { - channelId, - messageId: message.id, - mentionedBy: request.user.userId, - }, - }); - } - } - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - messageId: message.id, - messageType: message.message_type, - mentionsCount: message.mentions.length, - attachmentsCount: message.attachments.length, - }, 'Message sent successfully'); - reply.code(201).send({ - success: true, - data: message, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to send message'); - if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to send message', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * PUT /channels/:channelId/messages/:messageId - Edit message - */ - fastify.put('/channels/:channelId/messages/:messageId', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - channelId: validation_1.UUIDSchema, - messageId: validation_1.UUIDSchema, - }), - body: UpdateMessageSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: MessageResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { channelId, messageId } = request.params; - const { content } = request.body; - // Check if user can edit this message - const existingMessage = await index_1.messageRepository.findById(messageId); - if (!existingMessage) { - throw new errors_1.NotFoundError('Message not found'); - } - if (existingMessage.user_id !== request.user.userId && request.user.role !== 'ceo') { - throw new errors_1.AuthorizationError('You can only edit your own messages'); - } - // Check if message is too old to edit (24 hours) - const messageAge = Date.now() - new Date(existingMessage.created_at).getTime(); - const twentyFourHours = 24 * 60 * 60 * 1000; - if (messageAge > twentyFourHours && request.user.role !== 'ceo') { - throw new errors_1.AuthorizationError('Messages older than 24 hours cannot be edited'); - } - const message = await messageService.updateMessage(messageId, { - content, - is_edited: true, - edited_at: new Date(), - }); - // Broadcast message edit - await utils_1.WebSocketUtils.sendToChannel(channelId, 'message_updated', { - type: 'message_updated', - channelId, - messageId, - message: message, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - timestamp: new Date().toISOString(), - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - messageId, - }, 'Message edited successfully'); - reply.send({ - success: true, - data: message, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to edit message'); - if (error instanceof errors_1.NotFoundError || error instanceof errors_1.AuthorizationError) { - reply.code(error.statusCode).send((0, errors_1.formatErrorResponse)(error)); - } - else if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to edit message', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * DELETE /channels/:channelId/messages/:messageId - Delete message - */ - fastify.delete('/channels/:channelId/messages/:messageId', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - channelId: validation_1.UUIDSchema, - messageId: validation_1.UUIDSchema, - }), - response: { - 200: validation_1.SuccessResponseSchema, - }, - }, - }, async (request, reply) => { - try { - const { channelId, messageId } = request.params; - // Check if user can delete this message - const existingMessage = await index_1.messageRepository.findById(messageId); - if (!existingMessage) { - throw new errors_1.NotFoundError('Message not found'); - } - const canDelete = existingMessage.user_id === request.user.userId || - request.user.role === 'ceo'; - if (!canDelete) { - throw new errors_1.AuthorizationError('You can only delete your own messages'); - } - const success = await index_1.messageRepository.softDelete(messageId, request.user.userId); - if (!success) { - throw new errors_1.NotFoundError('Message not found'); - } - // Clear message cache - await CacheService_1.cacheService.messages.delete(cache_decorators_1.CacheKeyUtils.messageKey(messageId)); - // Broadcast message deletion - await utils_1.WebSocketUtils.sendToChannel(channelId, 'message_deleted', { - type: 'message_deleted', - channelId, - messageId, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - timestamp: new Date().toISOString(), - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - messageId, - }, 'Message deleted successfully'); - reply.send({ - success: true, - message: 'Message deleted successfully', - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to delete message'); - if (error instanceof errors_1.NotFoundError || error instanceof errors_1.AuthorizationError) { - reply.code(error.statusCode).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to delete message', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * POST /channels/:channelId/messages/:messageId/reactions - Add reaction - */ - fastify.post('/channels/:channelId/messages/:messageId/reactions', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - channelId: validation_1.UUIDSchema, - messageId: validation_1.UUIDSchema, - }), - body: typebox_1.Type.Object({ - emoji: typebox_1.Type.String({ minLength: 1, maxLength: 10 }), - }), - response: { - 200: validation_1.SuccessResponseSchema, - }, - }, - }, async (request, reply) => { - try { - const { channelId, messageId } = request.params; - const { emoji } = request.body; - const success = await index_1.messageRepository.addReaction(messageId, request.user.userId, emoji); - if (!success) { - throw new errors_1.NotFoundError('Message not found'); - } - // Broadcast reaction - await utils_1.WebSocketUtils.sendToChannel(channelId, 'message_reaction_added', { - type: 'message_reaction_added', - channelId, - messageId, - emoji, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - timestamp: new Date().toISOString(), - }); - reply.send({ - success: true, - message: 'Reaction added successfully', - timestamp: new Date().toISOString(), - }); - } - catch (error) { - logger_1.loggers.api.error({ error }, 'Failed to add reaction'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to add reaction', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * GET /channels/:channelId/messages/:messageId/thread - Get thread messages - */ - fastify.get('/channels/:channelId/messages/:messageId/thread', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess, middleware_1.apiRateLimit], - schema: { - params: typebox_1.Type.Object({ - channelId: validation_1.UUIDSchema, - messageId: validation_1.UUIDSchema, - }), - querystring: validation_1.PaginationSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Object({ - parentMessage: MessageResponseSchema, - replies: typebox_1.Type.Array(MessageResponseSchema), - pagination: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - limit: typebox_1.Type.Integer(), - offset: typebox_1.Type.Integer(), - hasMore: typebox_1.Type.Boolean(), - }), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { channelId, messageId } = request.params; - const { limit = 50, offset = 0 } = request.query; - // Get the parent message - const parentMessage = await index_1.messageRepository.findById(messageId); - if (!parentMessage) { - throw new errors_1.NotFoundError('Message not found'); - } - // Get thread root - if this message is itself a reply, get its root - const threadRoot = parentMessage.thread_root || messageId; - // Get all replies to this thread - const replies = await index_1.messageRepository.findChannelMessages(channelId, { threadRoot }, Math.min(limit, 100), offset); - // Filter out the parent message from replies if it's included - const threadReplies = replies.filter(msg => msg.id !== threadRoot); - const total = await index_1.messageRepository.getChannelMessageCount(channelId, { threadRoot }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - messageId, - threadRoot, - repliesCount: threadReplies.length, - }, 'Thread messages retrieved'); - reply.send({ - success: true, - data: { - parentMessage, - replies: threadReplies, - pagination: { - total: total - 1, // Exclude parent message from count - limit, - offset, - hasMore: offset + limit < total - 1, - }, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve thread messages'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to retrieve thread messages', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * POST /channels/:channelId/messages/:messageId/pin - Pin/Unpin message - */ - fastify.post('/channels/:channelId/messages/:messageId/pin', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - channelId: validation_1.UUIDSchema, - messageId: validation_1.UUIDSchema, - }), - body: typebox_1.Type.Object({ - pinned: typebox_1.Type.Boolean(), - }), - response: { - 200: validation_1.SuccessResponseSchema, - }, - }, - }, async (request, reply) => { - try { - const { channelId, messageId } = request.params; - const { pinned } = request.body; - // Only channel admins/owners and CEO can pin messages - // This would need channel member role checking - if (request.user.role !== 'ceo') { - throw new errors_1.AuthorizationError('Only channel administrators can pin messages'); - } - const success = await index_1.messageRepository.updateMessage(messageId, { - is_pinned: pinned, - }); - if (!success) { - throw new errors_1.NotFoundError('Message not found'); - } - // Broadcast pin status change - await utils_1.WebSocketUtils.sendToChannel(channelId, 'message_pinned', { - type: pinned ? 'message_pinned' : 'message_unpinned', - channelId, - messageId, - pinned, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - timestamp: new Date().toISOString(), - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - messageId, - pinned, - }, `Message ${pinned ? 'pinned' : 'unpinned'} successfully`); - reply.send({ - success: true, - message: `Message ${pinned ? 'pinned' : 'unpinned'} successfully`, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to update message pin status'); - if (error instanceof errors_1.NotFoundError || error instanceof errors_1.AuthorizationError) { - reply.code(error.statusCode).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to update pin status', - code: 'SERVER_ERROR', - }, - }); - } - } - }); -}; -exports.registerMessageRoutes = registerMessageRoutes; -//# sourceMappingURL=MessageRoutes.js.map \ No newline at end of file diff --git a/backend/dist/api/routes/MessageRoutes.js.map b/backend/dist/api/routes/MessageRoutes.js.map deleted file mode 100644 index 2bc839b..0000000 --- a/backend/dist/api/routes/MessageRoutes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"MessageRoutes.js","sourceRoot":"","sources":["../../../src/api/routes/MessageRoutes.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/api/routes/NotificationRoutes.d.ts b/backend/dist/api/routes/NotificationRoutes.d.ts deleted file mode 100644 index e38f1e8..0000000 --- a/backend/dist/api/routes/NotificationRoutes.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -import { FastifyInstance } from 'fastify'; -export declare function notificationRoutes(fastify: FastifyInstance): Promise; -//# sourceMappingURL=NotificationRoutes.d.ts.map \ No newline at end of file diff --git a/backend/dist/api/routes/NotificationRoutes.d.ts.map b/backend/dist/api/routes/NotificationRoutes.d.ts.map deleted file mode 100644 index 8d5cbcd..0000000 --- a/backend/dist/api/routes/NotificationRoutes.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NotificationRoutes.d.ts","sourceRoot":"","sources":["../../../src/api/routes/NotificationRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAwBxE,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,eAAe,iBAyUhE"} \ No newline at end of file diff --git a/backend/dist/api/routes/NotificationRoutes.js b/backend/dist/api/routes/NotificationRoutes.js deleted file mode 100644 index a0d0ea2..0000000 --- a/backend/dist/api/routes/NotificationRoutes.js +++ /dev/null @@ -1,274 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.notificationRoutes = notificationRoutes; -const typebox_1 = require("@sinclair/typebox"); -const index_1 = require("@db/index"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const utils_1 = require("@websocket/utils"); -// Schema definitions -const ChannelNotificationSchema = typebox_1.Type.Object({ - channelId: typebox_1.Type.String(), - senderId: typebox_1.Type.String(), - senderName: typebox_1.Type.String(), - message: typebox_1.Type.String(), - type: typebox_1.Type.Union([typebox_1.Type.Literal('new_message'), typebox_1.Type.Literal('thread_reply')]), - parentMessageId: typebox_1.Type.Optional(typebox_1.Type.String()), -}); -async function notificationRoutes(fastify) { - /** - * POST /notifications/channel - Send push notifications to channel members - */ - fastify.post('/notifications/channel', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Notifications'], - summary: 'Send Channel Notification', - description: 'Send push notifications to all channel members except the sender', - body: ChannelNotificationSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - message: typebox_1.Type.String(), - notificationsSent: typebox_1.Type.Number(), - }), - 400: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - 500: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - const { channelId, senderId, senderName, message, type, parentMessageId } = request.body; - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - try { - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - type, - messageLength: message.length, - }, 'Sending channel notification'); - // Get channel members (excluding the sender) - const members = await index_1.channelRepository.getMembers(channelId); - const recipientMembers = members.filter(member => member.user_id !== senderId); - if (recipientMembers.length === 0) { - return reply.send({ - success: true, - message: 'No recipients to notify', - notificationsSent: 0, - }); - } - // Prepare notification data - const notificationTitle = type === 'thread_reply' - ? `${senderName} replied in thread` - : `New message in channel`; - const notificationBody = message.length > 100 - ? `${message.substring(0, 97)}...` - : message; - // Send notifications via WebSocket (which can trigger push notifications) - let notificationsSent = 0; - for (const member of recipientMembers) { - try { - // Send WebSocket notification - await utils_1.WebSocketUtils.createAndSendNotification(member.user_id, { - title: notificationTitle, - message: notificationBody, - category: 'channel', - priority: 'medium', - data: { - channelId, - senderId, - senderName, - messageId: parentMessageId || 'new', - type, - }, - }); - notificationsSent++; - } - catch (error) { - logger_1.loggers.api.warn({ error, userId: member.user_id, channelId }, 'Failed to send notification to user'); - } - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - type, - notificationsSent, - totalRecipients: recipientMembers.length, - }, 'Channel notifications sent successfully'); - reply.send({ - success: true, - message: 'Notifications sent successfully', - notificationsSent, - }); - } - catch (error) { - logger_1.loggers.api.error({ error, context }, 'Failed to send channel notifications'); - const notificationError = new errors_1.ExternalServiceError('Failed to send notifications', 'notification-service', error); - reply.code(502).send((0, errors_1.formatErrorResponse)(notificationError)); - } - }); - /** - * POST /notifications/register - Register device token for push notifications - */ - fastify.post('/notifications/register', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Notifications'], - summary: 'Register Device Token', - description: 'Register device token for push notifications', - body: typebox_1.Type.Object({ - token: typebox_1.Type.String(), - platform: typebox_1.Type.Union([typebox_1.Type.Literal('ios'), typebox_1.Type.Literal('android')]), - deviceInfo: typebox_1.Type.Optional(typebox_1.Type.Object({ - brand: typebox_1.Type.Optional(typebox_1.Type.String()), - modelName: typebox_1.Type.Optional(typebox_1.Type.String()), - osName: typebox_1.Type.Optional(typebox_1.Type.String()), - osVersion: typebox_1.Type.Optional(typebox_1.Type.String()), - })), - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - message: typebox_1.Type.String(), - }), - }, - }, - }, async (request, reply) => { - const { token, platform, deviceInfo } = request.body; - try { - logger_1.loggers.api.info({ - userId: request.user?.userId, - platform, - deviceInfo, - }, 'Registering device token'); - // Here you would typically save the token to your database - // associated with the user for later use in push notifications - // For now, just log it as this would require a device_tokens table - logger_1.loggers.api.info({ - userId: request.user?.userId, - tokenLength: token.length, - platform, - }, 'Device token registered (not persisted - requires device_tokens table)'); - reply.send({ - success: true, - message: 'Device token registered successfully', - }); - } - catch (error) { - logger_1.loggers.api.error({ error, userId: request.user?.userId }, 'Failed to register device token'); - const tokenError = new errors_1.ExternalServiceError('Failed to register device token', 'notification-service', error); - reply.code(502).send((0, errors_1.formatErrorResponse)(tokenError)); - } - }); - /** - * PUT /notifications/preferences - Update notification preferences - */ - fastify.put('/notifications/preferences', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Notifications'], - summary: 'Update Notification Preferences', - description: 'Update user notification preferences', - body: typebox_1.Type.Object({ - taskNotifications: typebox_1.Type.Boolean(), - mentionNotifications: typebox_1.Type.Boolean(), - channelNotifications: typebox_1.Type.Boolean(), - quietHoursEnabled: typebox_1.Type.Boolean(), - quietHoursStart: typebox_1.Type.Optional(typebox_1.Type.String()), - quietHoursEnd: typebox_1.Type.Optional(typebox_1.Type.String()), - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - message: typebox_1.Type.String(), - }), - }, - }, - }, async (request, reply) => { - try { - logger_1.loggers.api.info({ - userId: request.user?.userId, - preferences: request.body, - }, 'Updating notification preferences'); - // Here you would update the user's notification preferences in the database - // This would require a user_notification_preferences table or additional columns - reply.send({ - success: true, - message: 'Notification preferences updated successfully', - }); - } - catch (error) { - logger_1.loggers.api.error({ error, userId: request.user?.userId }, 'Failed to update notification preferences'); - const prefsError = new errors_1.ExternalServiceError('Failed to update notification preferences', 'preferences-service', error); - reply.code(502).send((0, errors_1.formatErrorResponse)(prefsError)); - } - }); - /** - * GET /notifications/preferences - Get notification preferences - */ - fastify.get('/notifications/preferences', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Notifications'], - summary: 'Get Notification Preferences', - description: 'Get user notification preferences', - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Object({ - taskNotifications: typebox_1.Type.Boolean(), - mentionNotifications: typebox_1.Type.Boolean(), - channelNotifications: typebox_1.Type.Boolean(), - quietHoursEnabled: typebox_1.Type.Boolean(), - quietHoursStart: typebox_1.Type.Optional(typebox_1.Type.String()), - quietHoursEnd: typebox_1.Type.Optional(typebox_1.Type.String()), - }), - }), - }, - }, - }, async (request, reply) => { - try { - // Return default preferences for now - // In a real implementation, you'd fetch these from the database - const defaultPreferences = { - taskNotifications: true, - mentionNotifications: true, - channelNotifications: true, - quietHoursEnabled: false, - quietHoursStart: '22:00', - quietHoursEnd: '08:00', - }; - reply.send({ - success: true, - data: defaultPreferences, - }); - } - catch (error) { - logger_1.loggers.api.error({ error, userId: request.user?.userId }, 'Failed to get notification preferences'); - const getPrefsError = new errors_1.ExternalServiceError('Failed to get notification preferences', 'preferences-service', error); - reply.code(502).send((0, errors_1.formatErrorResponse)(getPrefsError)); - } - }); -} -//# sourceMappingURL=NotificationRoutes.js.map \ No newline at end of file diff --git a/backend/dist/api/routes/NotificationRoutes.js.map b/backend/dist/api/routes/NotificationRoutes.js.map deleted file mode 100644 index 7dbfdca..0000000 --- a/backend/dist/api/routes/NotificationRoutes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NotificationRoutes.js","sourceRoot":"","sources":["../../../src/api/routes/NotificationRoutes.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/api/routes/TaskRoutes.d.ts b/backend/dist/api/routes/TaskRoutes.d.ts deleted file mode 100644 index 62ca1a5..0000000 --- a/backend/dist/api/routes/TaskRoutes.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { FastifyInstance } from 'fastify'; -/** - * Register task routes - */ -export declare const registerTaskRoutes: (fastify: FastifyInstance) => Promise; -//# sourceMappingURL=TaskRoutes.d.ts.map \ No newline at end of file diff --git a/backend/dist/api/routes/TaskRoutes.d.ts.map b/backend/dist/api/routes/TaskRoutes.d.ts.map deleted file mode 100644 index d633ec8..0000000 --- a/backend/dist/api/routes/TaskRoutes.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TaskRoutes.d.ts","sourceRoot":"","sources":["../../../src/api/routes/TaskRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAyJxE;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAU,SAAS,eAAe,kBA69ChE,CAAC"} \ No newline at end of file diff --git a/backend/dist/api/routes/TaskRoutes.js b/backend/dist/api/routes/TaskRoutes.js deleted file mode 100644 index ef114b7..0000000 --- a/backend/dist/api/routes/TaskRoutes.js +++ /dev/null @@ -1,1438 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerTaskRoutes = void 0; -const typebox_1 = require("@sinclair/typebox"); -const index_1 = require("@db/index"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const middleware_1 = require("@auth/middleware"); -const CacheService_1 = require("../../services/CacheService"); -const cache_decorators_1 = require("@utils/cache-decorators"); -const utils_1 = require("@websocket/utils"); -const validation_1 = require("@utils/validation"); -/** - * Task Management API Routes - * Enterprise-grade task CRUD operations with real-time updates - */ -// Request/Response Schemas -const CreateTaskSchema = typebox_1.Type.Object({ - title: typebox_1.Type.String({ minLength: 1, maxLength: 255 }), - description: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 2000 })), - channel_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - parent_task_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - assigned_to: typebox_1.Type.Optional(typebox_1.Type.Array(validation_1.UUIDSchema)), - owned_by: typebox_1.Type.Optional(validation_1.UUIDSchema), - priority: typebox_1.Type.Optional(validation_1.TaskPrioritySchema), - task_type: typebox_1.Type.Optional(typebox_1.Type.Union([ - typebox_1.Type.Literal('general'), - typebox_1.Type.Literal('project'), - typebox_1.Type.Literal('maintenance'), - typebox_1.Type.Literal('emergency'), - typebox_1.Type.Literal('research'), - typebox_1.Type.Literal('approval'), - ])), - complexity: typebox_1.Type.Optional(typebox_1.Type.Integer({ minimum: 1, maximum: 10 })), - estimated_hours: typebox_1.Type.Optional(typebox_1.Type.Number({ minimum: 0 })), - due_date: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - start_date: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - tags: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.String({ maxLength: 50 }))), - labels: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), - voice_created: typebox_1.Type.Optional(typebox_1.Type.Boolean()), - voice_command_id: typebox_1.Type.Optional(typebox_1.Type.String()), - voice_instructions: typebox_1.Type.Optional(typebox_1.Type.String()), - business_value: typebox_1.Type.Optional(validation_1.BusinessValueSchema), - acceptance_criteria: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 2000 })), -}); -const UpdateTaskSchema = typebox_1.Type.Object({ - title: typebox_1.Type.Optional(typebox_1.Type.String({ minLength: 1, maxLength: 255 })), - description: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 2000 })), - priority: typebox_1.Type.Optional(validation_1.TaskPrioritySchema), - status: typebox_1.Type.Optional(validation_1.TaskStatusSchema), - complexity: typebox_1.Type.Optional(typebox_1.Type.Integer({ minimum: 1, maximum: 10 })), - estimated_hours: typebox_1.Type.Optional(typebox_1.Type.Number({ minimum: 0 })), - due_date: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - start_date: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - tags: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.String({ maxLength: 50 }))), - labels: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), - business_value: typebox_1.Type.Optional(validation_1.BusinessValueSchema), - acceptance_criteria: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 2000 })), -}); -const TaskResponseSchema = typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - title: typebox_1.Type.String(), - description: typebox_1.Type.Optional(typebox_1.Type.String()), - channel_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - parent_task_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - created_by: validation_1.UUIDSchema, - assigned_to: typebox_1.Type.Array(validation_1.UUIDSchema), - owned_by: typebox_1.Type.Optional(validation_1.UUIDSchema), - priority: validation_1.TaskPrioritySchema, - status: validation_1.TaskStatusSchema, - task_type: typebox_1.Type.String(), - complexity: typebox_1.Type.Integer(), - estimated_hours: typebox_1.Type.Optional(typebox_1.Type.Number()), - actual_hours: typebox_1.Type.Number(), - story_points: typebox_1.Type.Optional(typebox_1.Type.Integer()), - due_date: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - start_date: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - completed_at: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - progress_percentage: typebox_1.Type.Integer(), - tags: typebox_1.Type.Array(typebox_1.Type.String()), - labels: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any()), - voice_created: typebox_1.Type.Boolean(), - voice_command_id: typebox_1.Type.Optional(typebox_1.Type.String()), - voice_instructions: typebox_1.Type.Optional(typebox_1.Type.String()), - business_value: validation_1.BusinessValueSchema, - acceptance_criteria: typebox_1.Type.Optional(typebox_1.Type.String()), - watchers: typebox_1.Type.Array(validation_1.UUIDSchema), - comments_count: typebox_1.Type.Integer(), - attachments_count: typebox_1.Type.Integer(), - created_at: typebox_1.Type.String({ format: 'date-time' }), - updated_at: typebox_1.Type.String({ format: 'date-time' }), - last_activity_at: typebox_1.Type.String({ format: 'date-time' }), -}); -const TaskStatsSchema = typebox_1.Type.Object({ - totalTasks: typebox_1.Type.Integer(), - tasksByStatus: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Integer()), - tasksByPriority: typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Integer()), - overdueTasks: typebox_1.Type.Integer(), - completedThisWeek: typebox_1.Type.Integer(), - averageCompletionTime: typebox_1.Type.Number(), -}); -/** - * Task service with caching - */ -class TaskService { - async getTaskById(taskId) { - return await index_1.taskRepository.findById(taskId); - } - async updateTask(taskId, updateData) { - return await index_1.taskRepository.update(taskId, updateData); - } - async createTask(taskData) { - return await index_1.taskRepository.createTask(taskData); - } -} -__decorate([ - (0, cache_decorators_1.Cacheable)({ - ttl: 900, // 15 minutes - namespace: 'tasks', - keyGenerator: (taskId) => cache_decorators_1.CacheKeyUtils.taskKey(taskId), - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], TaskService.prototype, "getTaskById", null); -__decorate([ - (0, cache_decorators_1.CacheEvict)({ - keys: (taskId) => [cache_decorators_1.CacheKeyUtils.taskKey(taskId)], - namespace: 'tasks', - tags: ['tasks'], - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, Object]), - __metadata("design:returntype", Promise) -], TaskService.prototype, "updateTask", null); -__decorate([ - (0, cache_decorators_1.CacheEvict)({ - allEntries: true, - namespace: 'tasks', - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], TaskService.prototype, "createTask", null); -const taskService = new TaskService(); -/** - * Register task routes - */ -const registerTaskRoutes = async (fastify) => { - /** - * GET /tasks - List tasks with filters - */ - fastify.get('/tasks', { - preHandler: [middleware_1.authenticate, middleware_1.apiRateLimit], - schema: { - querystring: typebox_1.Type.Intersect([ - validation_1.PaginationSchema, - typebox_1.Type.Object({ - status: typebox_1.Type.Optional(typebox_1.Type.Array(validation_1.TaskStatusSchema)), - priority: typebox_1.Type.Optional(typebox_1.Type.Array(validation_1.TaskPrioritySchema)), - assigned_to: typebox_1.Type.Optional(validation_1.UUIDSchema), - channel_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - created_by: typebox_1.Type.Optional(validation_1.UUIDSchema), - due_after: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - due_before: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - tags: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.String())), - overdue: typebox_1.Type.Optional(typebox_1.Type.Boolean()), - voice_created: typebox_1.Type.Optional(typebox_1.Type.Boolean()), - search: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 200 })), - }), - ]), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Array(TaskResponseSchema), - pagination: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - limit: typebox_1.Type.Integer(), - offset: typebox_1.Type.Integer(), - hasMore: typebox_1.Type.Boolean(), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { limit = 20, offset = 0, status, priority, assigned_to, channel_id, created_by, due_after, due_before, tags, overdue, voice_created, search, } = request.query; - // Debug logging for filter parameters - logger_1.loggers.api.info({ - userId: request.user?.userId, - queryParams: { - limit, - offset, - status: Array.isArray(status) ? status : [status], - priority: Array.isArray(priority) ? priority : [priority], - assigned_to, - channel_id, - created_by, - due_after, - due_before, - tags, - overdue, - voice_created, - search, - } - }, 'Task filter request received'); - // Build task filters - const filters = {}; - if (status) { - filters.status = Array.isArray(status) ? status : [status]; - } - if (priority) { - filters.priority = Array.isArray(priority) ? priority : [priority]; - } - if (assigned_to) - filters.assignedTo = [assigned_to]; - if (channel_id) - filters.channelId = channel_id; - if (due_after) - filters.dueAfter = new Date(due_after); - if (due_before) - filters.dueBefore = new Date(due_before); - if (tags) { - filters.tags = Array.isArray(tags) ? tags : [tags]; - } - if (overdue !== undefined) - filters.overdue = overdue; - if (voice_created !== undefined) - filters.voiceCreated = voice_created; - // Filter based on user permissions (non-CEO users only see their tasks unless specified) - if (request.user.role !== 'ceo' && !assigned_to && !created_by) { - filters.assignedTo = [request.user.userId]; - } - let tasks = []; - let total = 0; - if (search) { - // Use search functionality - tasks = await index_1.taskRepository.searchTasks(search, request.user.userId, Math.min(limit, 100), offset); - total = tasks.length; // Approximation for search results - } - else { - // Use filtered query - tasks = await index_1.taskRepository.findWithFilters(filters, Math.min(limit, 100), offset); - // TODO: Get total count for pagination - total = tasks.length; - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - filters, - search, - resultCount: tasks.length, - }, 'Tasks list retrieved'); - reply.send({ - success: true, - data: tasks, - pagination: { - total, - limit, - offset, - hasMore: offset + limit < total, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - // Enhanced error logging - logger_1.loggers.api.error({ - error: { - message: error instanceof Error ? error.message : String(error), - stack: error instanceof Error ? error.stack : undefined, - name: error instanceof Error ? error.name : undefined, - }, - context, - queryParams: request.query, - }, 'Failed to retrieve tasks - detailed error'); - reply.code(500).send({ - error: { - message: 'Failed to retrieve tasks', - code: 'SERVER_ERROR', - details: error instanceof Error ? error.message : String(error), - }, - }); - } - }); - /** - * GET /tasks/:id - Get task details - */ - fastify.get('/tasks/:id', { - preHandler: [middleware_1.authenticate], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: TaskResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const task = await taskService.getTaskById(id); - if (!task) { - throw new errors_1.NotFoundError('Task not found'); - } - // Check if user has access to this task - const hasAccess = request.user.role === 'ceo' || - task.assigned_to.includes(request.user.userId) || - task.created_by === request.user.userId || - task.watchers.includes(request.user.userId); - if (!hasAccess) { - throw new errors_1.AuthorizationError('You do not have access to this task'); - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - taskId: id, - }, 'Task details retrieved'); - reply.send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve task'); - if (error instanceof errors_1.NotFoundError || error instanceof errors_1.AuthorizationError) { - reply.code(error.statusCode).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to retrieve task', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * POST /tasks - Create new task - */ - fastify.post('/tasks', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorize)('tasks:create')], - schema: { - body: CreateTaskSchema, - response: { - 201: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: TaskResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const taskData = { - ...request.body, - created_by: request.user.userId, - assigned_to: request.body.assigned_to || [request.user.userId], - owned_by: request.body.owned_by || request.user.userId, - tags: request.body.tags || [], - labels: request.body.labels || {}, - due_date: request.body.due_date ? new Date(request.body.due_date) : undefined, - start_date: request.body.start_date ? new Date(request.body.start_date) : undefined, - }; - const task = await taskService.createTask(taskData); - // Create activity for task creation - try { - await index_1.activityRepository.createActivity({ - channelId: task.channel_id, - taskId: task.id, - userId: request.user.userId, - activityType: 'task_created', - title: `Task Created: ${task.title}`, - description: `New task "${task.title}" was created${task.description ? ': ' + task.description : ''}`, - priority: task.priority, - category: 'task', - metadata: { - taskId: task.id, - taskTitle: task.title, - taskStatus: task.status, - taskPriority: task.priority, - assignedTo: task.assigned_to, - channelId: task.channel_id, - createdBy: request.user.userId, - createdByName: request.user.name - } - }); - } - catch (error) { - logger_1.loggers.api.warn?.({ error, taskId: task.id }, 'Failed to create task creation activity'); - } - // Broadcast task creation - await utils_1.WebSocketUtils.broadcastTaskUpdate({ - type: 'task_created', - taskId: task.id, - channelId: task.channel_id || '', - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'create', - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - // Send notifications to assignees - if (task.assigned_to.length > 0) { - for (const assigneeId of task.assigned_to) { - if (assigneeId !== request.user.userId) { - await utils_1.WebSocketUtils.createAndSendNotification(assigneeId, { - title: 'New Task Assigned', - message: `You have been assigned to task: ${task.title}`, - category: 'task', - priority: task.priority === 'critical' || task.priority === 'urgent' ? 'high' : 'medium', - actionUrl: `/tasks/${task.id}`, - actionText: 'View Task', - data: { taskId: task.id, taskTitle: task.title }, - }); - } - } - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - taskId: task.id, - taskTitle: task.title, - assignedTo: task.assigned_to, - priority: task.priority, - }, 'Task created successfully'); - reply.code(201).send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to create task'); - if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to create task', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * PUT /tasks/:id - Update task - */ - fastify.put('/tasks/:id', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorize)('tasks:update')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - body: UpdateTaskSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: TaskResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const updateData = { - ...request.body, - due_date: request.body.due_date ? new Date(request.body.due_date) : undefined, - start_date: request.body.start_date ? new Date(request.body.start_date) : undefined, - }; - // Check if user can update this task - const existingTask = await index_1.taskRepository.findById(id); - if (!existingTask) { - throw new errors_1.NotFoundError('Task not found'); - } - const canUpdate = request.user.role === 'ceo' || - existingTask.assigned_to.includes(request.user.userId) || - existingTask.created_by === request.user.userId || - existingTask.owned_by === request.user.userId; - if (!canUpdate) { - throw new errors_1.AuthorizationError('You do not have permission to update this task'); - } - const task = await taskService.updateTask(id, updateData); - // Broadcast task update - await utils_1.WebSocketUtils.broadcastTaskUpdate({ - type: 'task_updated', - taskId: id, - channelId: task.channel_id || '', - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'update', - changes: updateData, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - taskId: id, - updatedFields: Object.keys(updateData), - }, 'Task updated successfully'); - reply.send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to update task'); - if (error instanceof errors_1.NotFoundError || error instanceof errors_1.AuthorizationError) { - reply.code(error.statusCode).send((0, errors_1.formatErrorResponse)(error)); - } - else if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to update task', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * PATCH /tasks/:id/status - Update task status - */ - fastify.patch('/tasks/:id/status', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorize)('tasks:update')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - body: typebox_1.Type.Object({ - status: validation_1.TaskStatusSchema, - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: TaskResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const { status } = request.body; - const task = await index_1.taskRepository.updateStatus(id, status, request.user.userId); - // Create activity for task status update - try { - const activityType = status === 'completed' ? 'task_completed' : 'task_updated'; - const activityTitle = status === 'completed' - ? `Task Completed: ${task.title}` - : `Task Status Updated: ${task.title}`; - const activityDescription = status === 'completed' - ? `Task "${task.title}" was marked as completed` - : `Task "${task.title}" status was updated to ${status}`; - await index_1.activityRepository.createActivity({ - channelId: task.channel_id, - taskId: task.id, - userId: request.user.userId, - activityType, - title: activityTitle, - description: activityDescription, - priority: task.priority, - category: 'task', - metadata: { - taskId: task.id, - taskTitle: task.title, - oldStatus: status, // Note: we don't have the old status here, but status is the new one - newStatus: status, - taskPriority: task.priority, - assignedTo: task.assigned_to, - channelId: task.channel_id, - updatedBy: request.user.userId, - updatedByName: request.user.name - } - }); - } - catch (error) { - logger_1.loggers.api.warn?.({ error, taskId: id }, 'Failed to create task status update activity'); - } - // Broadcast status change - await utils_1.WebSocketUtils.broadcastTaskUpdate({ - type: status === 'completed' ? 'task_completed' : 'task_updated', - taskId: id, - channelId: task.channel_id || '', - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: status === 'completed' ? 'complete' : 'update', - changes: { status }, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - // Send completion notification - if (status === 'completed') { - for (const assigneeId of task.assigned_to) { - if (assigneeId !== request.user.userId) { - await utils_1.WebSocketUtils.createAndSendNotification(assigneeId, { - title: 'Task Completed', - message: `Task "${task.title}" has been completed`, - category: 'task', - priority: 'medium', - actionUrl: `/tasks/${task.id}`, - actionText: 'View Task', - data: { taskId: task.id, taskTitle: task.title }, - }); - } - } - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - taskId: id, - newStatus: status, - }, 'Task status updated successfully'); - reply.send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to update task status'); - if (error instanceof errors_1.NotFoundError || error instanceof errors_1.AuthorizationError) { - reply.code(error.statusCode).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to update task status', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * POST /tasks/:id/assign - Assign users to task - */ - fastify.post('/tasks/:id/assign', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorize)('tasks:assign')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - body: typebox_1.Type.Object({ - user_ids: typebox_1.Type.Array(validation_1.UUIDSchema), - }), - response: { - 200: validation_1.SuccessResponseSchema, - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const { user_ids } = request.body; - const success = await index_1.taskRepository.assignUsers(id, user_ids, request.user.userId); - if (!success) { - throw new errors_1.ValidationError('Failed to assign users to task', []); - } - // Clear task cache - await CacheService_1.cacheService.tasks.delete(cache_decorators_1.CacheKeyUtils.taskKey(id)); - // Get updated task for broadcast - const task = await index_1.taskRepository.findById(id); - if (task) { - // Create activity for task assignment - try { - await index_1.activityRepository.createActivity({ - channelId: task.channel_id, - taskId: task.id, - userId: request.user.userId, - activityType: 'task_assigned', - title: `Task Assigned: ${task.title}`, - description: `Task "${task.title}" was assigned to ${user_ids.length} user(s)`, - priority: task.priority, - category: 'task', - metadata: { - taskId: task.id, - taskTitle: task.title, - assignedUserIds: user_ids, - taskPriority: task.priority, - channelId: task.channel_id, - assignedBy: request.user.userId, - assignedByName: request.user.name, - totalAssignedUsers: task.assigned_to.length - } - }); - } - catch (error) { - logger_1.loggers.api.warn?.({ error, taskId: id }, 'Failed to create task assignment activity'); - } - // Broadcast assignment - await utils_1.WebSocketUtils.broadcastTaskUpdate({ - type: 'task_updated', - taskId: id, - channelId: task.channel_id || '', - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'assign', - changes: { assigned_to: user_ids }, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - // Send notifications to newly assigned users - for (const userId of user_ids) { - await utils_1.WebSocketUtils.createAndSendNotification(userId, { - title: 'Task Assigned', - message: `You have been assigned to task: ${task.title}`, - category: 'task', - priority: task.priority === 'critical' || task.priority === 'urgent' ? 'high' : 'medium', - actionUrl: `/tasks/${task.id}`, - actionText: 'View Task', - data: { taskId: task.id, taskTitle: task.title }, - }); - } - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - taskId: id, - assignedUsers: user_ids, - }, 'Users assigned to task successfully'); - reply.send({ - success: true, - message: 'Users assigned successfully', - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to assign users to task'); - if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to assign users', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * DELETE /tasks/:id - Delete task - */ - fastify.delete('/tasks/:id', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorize)('tasks:delete')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - response: { - 200: validation_1.SuccessResponseSchema, - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const success = await index_1.taskRepository.softDelete(id, request.user.userId); - if (!success) { - throw new errors_1.NotFoundError('Task not found'); - } - // Clear task cache - await CacheService_1.cacheService.tasks.delete(cache_decorators_1.CacheKeyUtils.taskKey(id)); - // Broadcast task deletion - await utils_1.WebSocketUtils.broadcastTaskUpdate({ - type: 'task_deleted', - taskId: id, - task: {}, - action: 'delete', - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - taskId: id, - }, 'Task deleted successfully'); - reply.send({ - success: true, - message: 'Task deleted successfully', - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to delete task'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to delete task', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * GET /tasks/stats - Get task statistics - */ - fastify.get('/tasks/stats', { - preHandler: [middleware_1.authenticate], - schema: { - querystring: typebox_1.Type.Object({ - user_id: typebox_1.Type.Optional(validation_1.UUIDSchema), - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: TaskStatsSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { user_id } = request.query; - // Only CEO can view stats for other users - const targetUserId = user_id && request.user.role === 'ceo' ? user_id : request.user.userId; - const stats = await index_1.taskRepository.getTaskStats(targetUserId); - logger_1.loggers.api.info({ - userId: request.user?.userId, - targetUserId, - stats, - }, 'Task stats retrieved'); - reply.send({ - success: true, - data: stats, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve task stats'); - reply.code(500).send({ - error: { - message: 'Failed to retrieve task stats', - code: 'SERVER_ERROR', - }, - }); - } - }); - /** - * GET /channels/:channelId/tasks - Get channel tasks - */ - fastify.get('/channels/:channelId/tasks', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - channelId: validation_1.UUIDSchema, - }), - querystring: typebox_1.Type.Intersect([ - validation_1.PaginationSchema, - typebox_1.Type.Object({ - status: typebox_1.Type.Optional(typebox_1.Type.Array(validation_1.TaskStatusSchema)), - priority: typebox_1.Type.Optional(typebox_1.Type.Array(validation_1.TaskPrioritySchema)), - assigned_to: typebox_1.Type.Optional(validation_1.UUIDSchema), - }), - ]), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Array(TaskResponseSchema), - pagination: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - limit: typebox_1.Type.Integer(), - offset: typebox_1.Type.Integer(), - hasMore: typebox_1.Type.Boolean(), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { channelId } = request.params; - const { limit = 20, offset = 0, status, priority, assigned_to } = request.query; - // Build filters - const filters = { - channelId, - status, - priority, - assignedTo: assigned_to ? [assigned_to] : undefined, - }; - const tasks = await index_1.taskRepository.findWithFilters(filters, Math.min(limit, 100), offset); - const total = tasks.length; // Simplified - in production, implement proper count query - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - taskCount: tasks.length, - filters, - }, 'Channel tasks retrieved'); - reply.send({ - success: true, - data: tasks, - pagination: { - total, - limit, - offset, - hasMore: offset + limit < total, - }, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve channel tasks'); - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel tasks', - code: 'SERVER_ERROR', - }, - }); - } - }); - /** - * POST /channels/:channelId/tasks - Create task in channel - */ - fastify.post('/channels/:channelId/tasks', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess, (0, middleware_1.authorize)('tasks:create')], - schema: { - params: typebox_1.Type.Object({ - channelId: validation_1.UUIDSchema, - }), - body: typebox_1.Type.Omit(CreateTaskSchema, ['channel_id']), - response: { - 201: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: TaskResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { channelId } = request.params; - const taskData = { - ...request.body, - channel_id: channelId, - created_by: request.user.userId, - assigned_to: request.body.assigned_to || [request.user.userId], - owned_by: request.body.owned_by || request.user.userId, - tags: request.body.tags || [], - labels: request.body.labels || {}, - due_date: request.body.due_date ? new Date(request.body.due_date) : undefined, - start_date: request.body.start_date ? new Date(request.body.start_date) : undefined, - }; - const task = await taskService.createTask(taskData); - // Create activity for channel task creation - try { - await index_1.activityRepository.createActivity({ - channelId, - taskId: task.id, - userId: request.user.userId, - activityType: 'task_created', - title: `Channel Task Created: ${task.title}`, - description: `New task "${task.title}" was created in channel${task.description ? ': ' + task.description : ''}`, - priority: task.priority, - category: 'task', - metadata: { - taskId: task.id, - taskTitle: task.title, - taskStatus: task.status, - taskPriority: task.priority, - assignedTo: task.assigned_to, - channelId, - createdBy: request.user.userId, - createdByName: request.user.name, - isChannelTask: true - } - }); - } - catch (error) { - logger_1.loggers.api.warn?.({ error, taskId: task.id, channelId }, 'Failed to create channel task creation activity'); - } - // Broadcast task creation to channel and task management system - await utils_1.WebSocketUtils.broadcastTaskUpdate({ - type: 'task_created', - taskId: task.id, - channelId, - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'create', - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - // Send channel notification message - if (channelId) { - await utils_1.WebSocketUtils.broadcastChannelMessage({ - type: 'chat_message', - channelId, - messageId: `task_created_${task.id}`, - message: `Task "${task.title}" was created`, - messageType: 'system', - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - } - // Send notifications to assignees - if (task.assigned_to.length > 0) { - for (const assigneeId of task.assigned_to) { - if (assigneeId !== request.user.userId) { - await utils_1.WebSocketUtils.createAndSendNotification(assigneeId, { - title: 'New Channel Task Assigned', - message: `You have been assigned to task: ${task.title} in channel`, - category: 'task', - priority: task.priority === 'critical' || task.priority === 'urgent' ? 'high' : 'medium', - actionUrl: `/channels/${channelId}?task=${task.id}`, - actionText: 'View Task', - data: { - taskId: task.id, - taskTitle: task.title, - channelId, - }, - }); - } - } - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - taskId: task.id, - taskTitle: task.title, - assignedTo: task.assigned_to, - }, 'Channel task created successfully'); - reply.code(201).send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to create channel task'); - if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to create channel task', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * PUT /tasks/:id/channel - Link task to channel - */ - fastify.put('/tasks/:id/channel', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorize)('tasks:update')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - }), - body: typebox_1.Type.Object({ - channel_id: typebox_1.Type.Union([validation_1.UUIDSchema, typebox_1.Type.Null()]), - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: TaskResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { id } = request.params; - const { channel_id } = request.body; - // Check if user can update this task - const existingTask = await index_1.taskRepository.findById(id); - if (!existingTask) { - throw new errors_1.NotFoundError('Task not found'); - } - const canUpdate = request.user.role === 'ceo' || - existingTask.assigned_to.includes(request.user.userId) || - existingTask.created_by === request.user.userId || - existingTask.owned_by === request.user.userId; - if (!canUpdate) { - throw new errors_1.AuthorizationError('You do not have permission to update this task'); - } - // If linking to a channel, verify user has access - if (channel_id) { - const hasChannelAccess = await index_1.channelRepository.canUserAccess(channel_id, request.user.userId, request.user.role); - if (!hasChannelAccess) { - throw new errors_1.AuthorizationError('You do not have access to this channel'); - } - } - const task = await taskService.updateTask(id, { channel_id }); - // Broadcast to both old and new channels - if (existingTask.channel_id) { - await utils_1.WebSocketUtils.broadcastChannelMessage({ - type: 'chat_message', - channelId: existingTask.channel_id, - messageId: `task_unlink_${task.id}`, - message: `Task "${task.title}" was unlinked from this channel`, - messageType: 'system', - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - } - if (channel_id) { - await utils_1.WebSocketUtils.broadcastChannelMessage({ - type: 'chat_message', - channelId: channel_id, - messageId: `task_link_${task.id}`, - message: `Task "${task.title}" was linked to this channel`, - messageType: 'system', - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - } - // Also broadcast to task system - await utils_1.WebSocketUtils.broadcastTaskUpdate({ - type: 'task_updated', - taskId: id, - channelId: channel_id || '', - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'update', - changes: { channel_id }, - userId: request.user.userId, - userName: request.user.name, - userRole: request.user.role, - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - taskId: id, - oldChannelId: existingTask.channel_id, - newChannelId: channel_id, - }, 'Task channel link updated successfully'); - reply.send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to update task channel link'); - if (error instanceof errors_1.NotFoundError || error instanceof errors_1.AuthorizationError) { - reply.code(error.statusCode).send((0, errors_1.formatErrorResponse)(error)); - } - else if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to update task channel link', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * GET /channels/:channelId/tasks/stats - Get channel task statistics - */ - fastify.get('/channels/:channelId/tasks/stats', { - preHandler: [middleware_1.authenticate, middleware_1.requireChannelAccess], - schema: { - params: typebox_1.Type.Object({ - channelId: validation_1.UUIDSchema, - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: TaskStatsSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }), - }), - }, - }, - }, async (request, reply) => { - try { - const { channelId } = request.params; - // Get channel task statistics by using regular getTaskStats with channel filter - // This is a simplified implementation - in production, implement proper channel stats - const stats = await index_1.taskRepository.getTaskStats(request.user.userId); - logger_1.loggers.api.info({ - userId: request.user?.userId, - channelId, - stats, - }, 'Channel task stats retrieved'); - reply.send({ - success: true, - data: stats, - timestamp: new Date().toISOString(), - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve channel task stats'); - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel task stats', - code: 'SERVER_ERROR', - }, - }); - } - }); -}; -exports.registerTaskRoutes = registerTaskRoutes; -//# sourceMappingURL=TaskRoutes.js.map \ No newline at end of file diff --git a/backend/dist/api/routes/TaskRoutes.js.map b/backend/dist/api/routes/TaskRoutes.js.map deleted file mode 100644 index 1f1431d..0000000 --- a/backend/dist/api/routes/TaskRoutes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TaskRoutes.js","sourceRoot":"","sources":["../../../src/api/routes/TaskRoutes.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/api/routes/UserRoutes.d.ts b/backend/dist/api/routes/UserRoutes.d.ts deleted file mode 100644 index 5704dcc..0000000 --- a/backend/dist/api/routes/UserRoutes.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { FastifyInstance } from 'fastify'; -/** - * Register user routes - */ -export declare const registerUserRoutes: (fastify: FastifyInstance) => Promise; -//# sourceMappingURL=UserRoutes.d.ts.map \ No newline at end of file diff --git a/backend/dist/api/routes/UserRoutes.d.ts.map b/backend/dist/api/routes/UserRoutes.d.ts.map deleted file mode 100644 index 7877aa9..0000000 --- a/backend/dist/api/routes/UserRoutes.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"UserRoutes.d.ts","sourceRoot":"","sources":["../../../src/api/routes/UserRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AA+HxE;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAU,SAAS,eAAe,kBAynBhE,CAAC"} \ No newline at end of file diff --git a/backend/dist/api/routes/UserRoutes.js b/backend/dist/api/routes/UserRoutes.js deleted file mode 100644 index 169fea0..0000000 --- a/backend/dist/api/routes/UserRoutes.js +++ /dev/null @@ -1,704 +0,0 @@ -"use strict"; -var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { - var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; - if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); - else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; - return c > 3 && r && Object.defineProperty(target, key, r), r; -}; -var __metadata = (this && this.__metadata) || function (k, v) { - if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerUserRoutes = void 0; -const typebox_1 = require("@sinclair/typebox"); -const index_1 = require("@db/index"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const middleware_1 = require("@auth/middleware"); -const CacheService_1 = require("../../services/CacheService"); -const cache_decorators_1 = require("@utils/cache-decorators"); -const validation_1 = require("@utils/validation"); -/** - * User Management API Routes - * Enterprise-grade user CRUD operations with caching and security - */ -// Request/Response Schemas -const CreateUserSchema = typebox_1.Type.Object({ - email: validation_1.EmailSchema, - password: typebox_1.Type.String({ minLength: 8, maxLength: 128 }), - name: typebox_1.Type.String({ minLength: 1, maxLength: 255 }), - role: validation_1.UserRoleSchema, - department: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 100 })), - job_title: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 100 })), - phone: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 20 })), - avatar_url: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'uri' })), - timezone: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 50 })), - language_preference: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 10 })), -}); -const UpdateUserSchema = typebox_1.Type.Object({ - name: typebox_1.Type.Optional(typebox_1.Type.String({ minLength: 1, maxLength: 255 })), - department: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 100 })), - job_title: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 100 })), - phone: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 20 })), - avatar_url: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'uri' })), - timezone: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 50 })), - language_preference: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 10 })), - notification_settings: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), - voice_settings: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), -}); -const ChangePasswordSchema = typebox_1.Type.Object({ - currentPassword: typebox_1.Type.String(), - newPassword: typebox_1.Type.String({ minLength: 8, maxLength: 128 }), -}); -const UserResponseSchema = typebox_1.Type.Object({ - id: validation_1.UUIDSchema, - email: validation_1.EmailSchema, - name: typebox_1.Type.String(), - role: validation_1.UserRoleSchema, - department: typebox_1.Type.Optional(typebox_1.Type.String()), - job_title: typebox_1.Type.Optional(typebox_1.Type.String()), - phone: typebox_1.Type.Optional(typebox_1.Type.String()), - avatar_url: typebox_1.Type.Optional(typebox_1.Type.String()), - timezone: typebox_1.Type.Optional(typebox_1.Type.String()), - language_preference: typebox_1.Type.Optional(typebox_1.Type.String()), - notification_settings: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), - voice_settings: typebox_1.Type.Optional(typebox_1.Type.Record(typebox_1.Type.String(), typebox_1.Type.Any())), - email_verified: typebox_1.Type.Boolean(), - last_active: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - created_at: typebox_1.Type.String({ format: 'date-time' }), - updated_at: typebox_1.Type.String({ format: 'date-time' }), -}); -const UserListResponseSchema = typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Array(UserResponseSchema), - pagination: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - limit: typebox_1.Type.Integer(), - offset: typebox_1.Type.Integer(), - hasMore: typebox_1.Type.Boolean(), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }), -}); -/** - * User service class with caching decorators - */ -class UserService { - async getUserById(userId) { - return await index_1.userRepository.findById(userId); - } - async updateUser(userId, updateData) { - return await index_1.userRepository.update(userId, updateData); - } - async createUser(userData) { - return await index_1.userRepository.createUser(userData); - } -} -__decorate([ - (0, cache_decorators_1.Cacheable)({ - ttl: 3600, - namespace: 'users', - keyGenerator: (userId) => cache_decorators_1.CacheKeyUtils.userKey(userId) - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String]), - __metadata("design:returntype", Promise) -], UserService.prototype, "getUserById", null); -__decorate([ - (0, cache_decorators_1.CacheEvict)({ - keys: (userId) => [cache_decorators_1.CacheKeyUtils.userKey(userId), `${cache_decorators_1.CacheKeyUtils.userKey(userId)}:profile`], - namespace: 'users' - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [String, Object]), - __metadata("design:returntype", Promise) -], UserService.prototype, "updateUser", null); -__decorate([ - (0, cache_decorators_1.CacheEvict)({ - allEntries: true, - namespace: 'users' - }), - __metadata("design:type", Function), - __metadata("design:paramtypes", [Object]), - __metadata("design:returntype", Promise) -], UserService.prototype, "createUser", null); -const userService = new UserService(); -/** - * Register user routes - */ -const registerUserRoutes = async (fastify) => { - /** - * GET /users - List all users (Admin/Manager only) - */ - fastify.get('/users', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorizeRoles)('ceo', 'manager'), middleware_1.apiRateLimit], - schema: { - querystring: typebox_1.Type.Intersect([ - validation_1.PaginationSchema, - typebox_1.Type.Object({ - search: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 100 })), - role: typebox_1.Type.Optional(validation_1.UserRoleSchema), - department: typebox_1.Type.Optional(typebox_1.Type.String({ maxLength: 100 })), - status: typebox_1.Type.Optional(typebox_1.Type.Union([ - typebox_1.Type.Literal('active'), - typebox_1.Type.Literal('inactive'), - typebox_1.Type.Literal('suspended') - ])), - }) - ]), - response: { - 200: UserListResponseSchema, - 401: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String() - }) - }), - 403: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String() - }) - }) - } - } - }, async (request, reply) => { - try { - const { limit = 20, offset = 0, search, role, department, status } = request.query; - // Build filters - const filters = {}; - if (search) { - filters.search = search; - } - if (role) { - filters.role = role; - } - if (department) { - filters.department = department; - } - if (status) { - filters.status = status; - } - // Get users with pagination - const result = await index_1.userRepository.findMany({ - filters, - limit: Math.min(limit, 100), // Cap at 100 - offset, - orderBy: 'created_at', - orderDirection: 'DESC' - }); - logger_1.loggers.api.info({ - userId: request.user?.userId, - filters, - resultCount: result.data.length, - total: result.total - }, 'Users list retrieved'); - reply.send({ - success: true, - data: result.data.map(user => ({ - ...user, - // Remove sensitive fields - password_hash: undefined, - reset_token: undefined, - verification_token: undefined, - })), - pagination: { - total: result.total, - limit, - offset, - hasMore: offset + limit < result.total - }, - timestamp: new Date().toISOString() - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve users list'); - reply.code(500).send({ - error: { - message: 'Failed to retrieve users', - code: 'SERVER_ERROR' - } - }); - } - }); - /** - * GET /users/:id - Get user by ID - */ - fastify.get('/users/:id', { - preHandler: [middleware_1.authenticate, (0, middleware_1.requireResourceOwnership)('id')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: UserResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }) - }), - 404: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String() - }) - }) - } - } - }, async (request, reply) => { - try { - const { id } = request.params; - // Use cached service method - const user = await userService.getUserById(id); - if (!user) { - throw new errors_1.NotFoundError('User not found'); - } - logger_1.loggers.api.info({ - userId: request.user?.userId, - requestedUserId: id - }, 'User profile retrieved'); - reply.send({ - success: true, - data: { - ...user, - // Remove sensitive fields - password_hash: undefined, - reset_token: undefined, - verification_token: undefined, - }, - timestamp: new Date().toISOString() - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers - }); - logger_1.loggers.api.error({ error, context }, 'Failed to retrieve user'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to retrieve user', - code: 'SERVER_ERROR' - } - }); - } - } - }); - /** - * POST /users - Create new user (Admin only) - */ - fastify.post('/users', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorizeRoles)('ceo'), middleware_1.apiRateLimit], - schema: { - body: CreateUserSchema, - response: { - 201: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: UserResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }) - }), - 400: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - details: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.Object({ - field: typebox_1.Type.String(), - message: typebox_1.Type.String(), - value: typebox_1.Type.Optional(typebox_1.Type.Any()) - }))) - }) - }) - } - } - }, async (request, reply) => { - try { - const userData = { - ...request.body, - created_by: request.user?.userId, - }; - // Create user using cached service - const user = await userService.createUser(userData); - logger_1.loggers.api.info({ - userId: request.user?.userId, - createdUserId: user.id, - userEmail: user.email, - userRole: user.role - }, 'User created successfully'); - reply.code(201).send({ - success: true, - data: { - ...user, - password_hash: undefined, - reset_token: undefined, - verification_token: undefined, - }, - timestamp: new Date().toISOString() - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers - }); - logger_1.loggers.api.error({ error, context }, 'Failed to create user'); - if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to create user', - code: 'SERVER_ERROR' - } - }); - } - } - }); - /** - * PUT /users/:id - Update user - */ - fastify.put('/users/:id', { - preHandler: [middleware_1.authenticate, (0, middleware_1.requireResourceOwnership)('id')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema - }), - body: UpdateUserSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: UserResponseSchema, - timestamp: typebox_1.Type.String({ format: 'date-time' }) - }) - } - } - }, async (request, reply) => { - try { - const { id } = request.params; - const updateData = request.body; - // Update user using cached service (will evict cache) - const user = await userService.updateUser(id, updateData); - logger_1.loggers.api.info({ - userId: request.user?.userId, - updatedUserId: id, - updatedFields: Object.keys(updateData) - }, 'User updated successfully'); - reply.send({ - success: true, - data: { - ...user, - password_hash: undefined, - reset_token: undefined, - verification_token: undefined, - }, - timestamp: new Date().toISOString() - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers - }); - logger_1.loggers.api.error({ error, context }, 'Failed to update user'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to update user', - code: 'SERVER_ERROR' - } - }); - } - } - }); - /** - * POST /users/:id/change-password - Change user password - */ - fastify.post('/users/:id/change-password', { - preHandler: [middleware_1.authenticate, (0, middleware_1.requireResourceOwnership)('id')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema - }), - body: ChangePasswordSchema, - response: { - 200: validation_1.SuccessResponseSchema - } - } - }, async (request, reply) => { - try { - const { id } = request.params; - const { currentPassword, newPassword } = request.body; - // Verify current password - const user = await index_1.userRepository.findById(id); - if (!user) { - throw new errors_1.NotFoundError('User not found'); - } - const isCurrentPasswordValid = await index_1.userRepository.verifyPassword(user.email, currentPassword); - if (!isCurrentPasswordValid) { - throw new errors_1.ValidationError('Current password is incorrect', [ - { field: 'currentPassword', message: 'Invalid password' } - ]); - } - // Update password - const success = await index_1.userRepository.updatePassword(id, newPassword); - if (!success) { - throw new Error('Failed to update password'); - } - // Clear user cache - await CacheService_1.cacheService.users.delete(cache_decorators_1.CacheKeyUtils.userKey(id)); - logger_1.loggers.api.info({ - userId: request.user?.userId, - targetUserId: id - }, 'Password changed successfully'); - reply.send({ - success: true, - message: 'Password changed successfully', - timestamp: new Date().toISOString() - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers - }); - logger_1.loggers.api.error({ error, context }, 'Failed to change password'); - if (error instanceof errors_1.NotFoundError || error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to change password', - code: 'SERVER_ERROR' - } - }); - } - } - }); - /** - * DELETE /users/:id - Soft delete user (Admin only) - */ - fastify.delete('/users/:id', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorizeRoles)('ceo')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema - }), - response: { - 200: validation_1.SuccessResponseSchema - } - } - }, async (request, reply) => { - try { - const { id } = request.params; - // Prevent self-deletion - if (id === request.user?.userId) { - throw new errors_1.ValidationError('Cannot delete your own account', []); - } - const success = await index_1.userRepository.softDelete(id, request.user.userId); - if (!success) { - throw new errors_1.NotFoundError('User not found'); - } - // Clear user cache - await CacheService_1.cacheService.users.delete(cache_decorators_1.CacheKeyUtils.userKey(id)); - logger_1.loggers.api.info({ - userId: request.user?.userId, - deletedUserId: id - }, 'User deleted successfully'); - reply.send({ - success: true, - message: 'User deleted successfully', - timestamp: new Date().toISOString() - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers - }); - logger_1.loggers.api.error({ error, context }, 'Failed to delete user'); - if (error instanceof errors_1.NotFoundError || error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to delete user', - code: 'SERVER_ERROR' - } - }); - } - } - }); - /** - * GET /users/:id/stats - Get user statistics (Admin/Manager only) - */ - fastify.get('/users/:id/stats', { - preHandler: [middleware_1.authenticate, (0, middleware_1.authorizeRoles)('ceo', 'manager')], - schema: { - params: typebox_1.Type.Object({ - id: validation_1.UUIDSchema - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Object({ - userId: validation_1.UUIDSchema, - taskStats: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - completed: typebox_1.Type.Integer(), - inProgress: typebox_1.Type.Integer(), - overdue: typebox_1.Type.Integer(), - }), - channelStats: typebox_1.Type.Object({ - memberships: typebox_1.Type.Integer(), - messagesCount: typebox_1.Type.Integer(), - }), - activityStats: typebox_1.Type.Object({ - lastActive: typebox_1.Type.Optional(typebox_1.Type.String({ format: 'date-time' })), - totalSessions: typebox_1.Type.Integer(), - avgSessionDuration: typebox_1.Type.Number(), - }), - }), - timestamp: typebox_1.Type.String({ format: 'date-time' }) - }) - } - } - }, async (request, reply) => { - try { - const { id } = request.params; - // Get user stats from repositories - const [user, taskStats] = await Promise.all([ - index_1.userRepository.findById(id), - // taskRepository.getTaskStats(id), // TODO: Implement - Promise.resolve({ - total: 0, - completed: 0, - inProgress: 0, - overdue: 0, - }) - ]); - if (!user) { - throw new errors_1.NotFoundError('User not found'); - } - // TODO: Get channel stats, activity stats from respective repositories - const stats = { - userId: id, - taskStats, - channelStats: { - memberships: 0, - messagesCount: 0, - }, - activityStats: { - lastActive: user.last_active?.toISOString(), - totalSessions: 0, - avgSessionDuration: 0, - }, - }; - logger_1.loggers.api.info({ - userId: request.user?.userId, - requestedUserId: id - }, 'User stats retrieved'); - reply.send({ - success: true, - data: stats, - timestamp: new Date().toISOString() - }); - } - catch (error) { - const context = (0, errors_1.createErrorContext)({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers - }); - logger_1.loggers.api.error({ error, context }, 'Failed to get user stats'); - if (error instanceof errors_1.NotFoundError) { - reply.code(404).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to get user stats', - code: 'SERVER_ERROR' - } - }); - } - } - }); -}; -exports.registerUserRoutes = registerUserRoutes; -//# sourceMappingURL=UserRoutes.js.map \ No newline at end of file diff --git a/backend/dist/api/routes/UserRoutes.js.map b/backend/dist/api/routes/UserRoutes.js.map deleted file mode 100644 index ea523a7..0000000 --- a/backend/dist/api/routes/UserRoutes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"UserRoutes.js","sourceRoot":"","sources":["../../../src/api/routes/UserRoutes.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/auth/jwt.d.ts b/backend/dist/auth/jwt.d.ts deleted file mode 100644 index 134ad55..0000000 --- a/backend/dist/auth/jwt.d.ts +++ /dev/null @@ -1,135 +0,0 @@ -/** - * JWT Authentication system with refresh token support - * Enterprise-grade security with proper token management - */ -export interface TokenPayload { - userId: string; - email: string; - role: 'ceo' | 'manager' | 'staff'; - name: string; - permissions: string[]; - sessionId: string; - type: 'access' | 'refresh'; - iat: number; - exp: number; -} -export interface TokenPair { - accessToken: string; - refreshToken: string; - expiresIn: number; - refreshExpiresIn: number; -} -export interface AccessTokenData { - token: string; - payload: TokenPayload; - expiresAt: Date; -} -/** - * Role-based permissions mapping - */ -declare const ROLE_PERMISSIONS: Record; -/** - * JWT service class - */ -declare class JWTService { - private readonly accessTokenSecret; - private readonly refreshTokenSecret; - private readonly accessTokenExpiry; - private readonly refreshTokenExpiry; - constructor(); - /** - * Generate access and refresh token pair - */ - generateTokens(user: { - id: string; - email: string; - role: 'ceo' | 'manager' | 'staff'; - name: string; - }): Promise; - /** - * Verify and decode access token - */ - verifyAccessToken(token: string): Promise; - /** - * Verify and decode refresh token - */ - verifyRefreshToken(token: string): Promise; - /** - * Refresh access token using refresh token - */ - refreshTokens(refreshToken: string): Promise; - /** - * Decode token without verification (for debugging) - */ - decodeToken(token: string): TokenPayload | null; - /** - * Extract token from Authorization header - */ - extractTokenFromHeader(authHeader: string | undefined): string | null; - /** - * Check if user has required permission - */ - hasPermission(payload: TokenPayload, requiredPermission: string): boolean; - /** - * Check if user has any of the required permissions - */ - hasAnyPermission(payload: TokenPayload, requiredPermissions: string[]): boolean; - /** - * Check if user has all required permissions - */ - hasAllPermissions(payload: TokenPayload, requiredPermissions: string[]): boolean; - /** - * Get token expiration date - */ - getTokenExpiration(token: string): Date | null; - /** - * Check if token is expired - */ - isTokenExpired(token: string): boolean; - /** - * Generate password reset token (separate from auth tokens) - */ - generatePasswordResetToken(userId: string): string; - /** - * Verify password reset token - */ - verifyPasswordResetToken(token: string, userId: string): boolean; - /** - * Generate email verification token - */ - generateEmailVerificationToken(userId: string, email: string): string; - /** - * Verify email verification token - */ - verifyEmailVerificationToken(token: string, email: string): { - userId: string; - } | null; -} -export declare const jwtService: JWTService; -export default jwtService; -export declare const PERMISSIONS: { - readonly CHANNELS_CREATE: "channels:create"; - readonly CHANNELS_READ: "channels:read"; - readonly CHANNELS_UPDATE: "channels:update"; - readonly CHANNELS_DELETE: "channels:delete"; - readonly CHANNELS_MANAGE_MEMBERS: "channels:manage_members"; - readonly CHANNELS_ARCHIVE: "channels:archive"; - readonly TASKS_CREATE: "tasks:create"; - readonly TASKS_READ: "tasks:read"; - readonly TASKS_UPDATE: "tasks:update"; - readonly TASKS_DELETE: "tasks:delete"; - readonly TASKS_ASSIGN: "tasks:assign"; - readonly TASKS_MANAGE_DEPENDENCIES: "tasks:manage_dependencies"; - readonly USERS_CREATE: "users:create"; - readonly USERS_READ: "users:read"; - readonly USERS_UPDATE: "users:update"; - readonly USERS_DELETE: "users:delete"; - readonly USERS_MANAGE_ROLES: "users:manage_roles"; - readonly VOICE_COMMANDS: "voice:commands"; - readonly VOICE_TRANSCRIBE: "voice:transcribe"; - readonly VOICE_PROCESS: "voice:process"; - readonly SYSTEM_ADMIN: "system:admin"; - readonly ANALYTICS_READ: "analytics:read"; -}; -export { ROLE_PERMISSIONS }; -//# sourceMappingURL=jwt.d.ts.map \ No newline at end of file diff --git a/backend/dist/auth/jwt.d.ts.map b/backend/dist/auth/jwt.d.ts.map deleted file mode 100644 index 8a07b2c..0000000 --- a/backend/dist/auth/jwt.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/auth/jwt.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/auth/jwt.js b/backend/dist/auth/jwt.js deleted file mode 100644 index f1a1414..0000000 --- a/backend/dist/auth/jwt.js +++ /dev/null @@ -1,356 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ROLE_PERMISSIONS = exports.PERMISSIONS = exports.jwtService = void 0; -const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); -const index_1 = require("@config/index"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const crypto_1 = __importDefault(require("crypto")); -/** - * Role-based permissions mapping - */ -const ROLE_PERMISSIONS = { - ceo: [ - // Wildcard permission - CEO has access to everything - '*' - ], - manager: [ - // Channel management within scope - 'channels:create', 'channels:read', 'channels:update', - 'channels:manage_members', - // Task management - 'tasks:create', 'tasks:read', 'tasks:update', - 'tasks:assign', 'tasks:manage_dependencies', - // Limited user management - 'users:read', 'users:update_profile', - // Voice features (limited) - 'voice:commands', - // Analytics (read-only) - 'analytics:read' - ], - staff: [ - // Basic channel access - 'channels:read', 'channels:participate', - // Task participation - 'tasks:read', 'tasks:update_own', 'tasks:comment', - // Profile management - 'users:read_own', 'users:update_own_profile', - // No voice commands (CEO-only feature) - ] -}; -exports.ROLE_PERMISSIONS = ROLE_PERMISSIONS; -/** - * Generate cryptographically secure session ID - */ -const generateSessionId = () => { - return crypto_1.default.randomBytes(32).toString('hex'); -}; -/** - * Convert time string to seconds - */ -const parseTimeToSeconds = (timeStr) => { - const unit = timeStr.slice(-1); - const value = parseInt(timeStr.slice(0, -1)); - switch (unit) { - case 's': return value; - case 'm': return value * 60; - case 'h': return value * 3600; - case 'd': return value * 86400; - default: return value; - } -}; -/** - * JWT service class - */ -class JWTService { - accessTokenSecret; - refreshTokenSecret; - accessTokenExpiry; - refreshTokenExpiry; - constructor() { - this.accessTokenSecret = index_1.config.jwt.secret; - this.refreshTokenSecret = index_1.config.jwt.refreshSecret; - this.accessTokenExpiry = index_1.config.jwt.expiresIn; - this.refreshTokenExpiry = index_1.config.jwt.refreshExpiresIn; - } - /** - * Generate access and refresh token pair - */ - async generateTokens(user) { - const sessionId = generateSessionId(); - const permissions = ROLE_PERMISSIONS[user.role] || []; - const basePayload = { - userId: user.id, - email: user.email, - role: user.role, - name: user.name, - permissions, - sessionId - }; - // Generate access token - const accessToken = jsonwebtoken_1.default.sign({ ...basePayload, type: 'access' }, this.accessTokenSecret, { - expiresIn: this.accessTokenExpiry, - issuer: 'ceo-platform', - audience: 'ceo-platform-api', - subject: user.id - }); - // Generate refresh token - const refreshToken = jsonwebtoken_1.default.sign({ ...basePayload, type: 'refresh' }, this.refreshTokenSecret, { - expiresIn: this.refreshTokenExpiry, - issuer: 'ceo-platform', - audience: 'ceo-platform-api', - subject: user.id - }); - const expiresIn = parseTimeToSeconds(this.accessTokenExpiry); - const refreshExpiresIn = parseTimeToSeconds(this.refreshTokenExpiry); - logger_1.securityLogger.logAuthEvent('token_generated', { - userId: user.id, - email: user.email, - role: user.role, - sessionId, - expiresIn, - refreshExpiresIn - }); - return { - accessToken, - refreshToken, - expiresIn, - refreshExpiresIn - }; - } - /** - * Verify and decode access token - */ - async verifyAccessToken(token) { - try { - const payload = jsonwebtoken_1.default.verify(token, this.accessTokenSecret, { - issuer: 'ceo-platform', - audience: 'ceo-platform-api' - }); - if (payload.type !== 'access') { - throw new errors_1.InvalidTokenError('Token type mismatch - expected access token'); - } - return payload; - } - catch (error) { - if (error instanceof jsonwebtoken_1.default.TokenExpiredError) { - throw new errors_1.TokenExpiredError('Access token has expired'); - } - else if (error instanceof jsonwebtoken_1.default.JsonWebTokenError) { - throw new errors_1.InvalidTokenError(`Invalid access token: ${error.message}`); - } - else { - logger_1.logger.error({ error }, 'Unexpected error verifying access token'); - throw new errors_1.AuthenticationError('Token verification failed'); - } - } - } - /** - * Verify and decode refresh token - */ - async verifyRefreshToken(token) { - try { - const payload = jsonwebtoken_1.default.verify(token, this.refreshTokenSecret, { - issuer: 'ceo-platform', - audience: 'ceo-platform-api' - }); - if (payload.type !== 'refresh') { - throw new errors_1.InvalidTokenError('Token type mismatch - expected refresh token'); - } - return payload; - } - catch (error) { - if (error instanceof jsonwebtoken_1.default.TokenExpiredError) { - logger_1.securityLogger.logAuthEvent('refresh_token_expired', { - error: error.message - }); - throw new errors_1.TokenExpiredError('Refresh token has expired'); - } - else if (error instanceof jsonwebtoken_1.default.JsonWebTokenError) { - logger_1.securityLogger.logSecurityViolation('invalid_refresh_token', { - error: error.message, - token: token.substring(0, 20) + '...' - }); - throw new errors_1.InvalidTokenError(`Invalid refresh token: ${error.message}`); - } - else { - logger_1.logger.error({ error }, 'Unexpected error verifying refresh token'); - throw new errors_1.AuthenticationError('Refresh token verification failed'); - } - } - } - /** - * Refresh access token using refresh token - */ - async refreshTokens(refreshToken) { - const payload = await this.verifyRefreshToken(refreshToken); - // Generate new token pair with same user data - const user = { - id: payload.userId, - email: payload.email, - role: payload.role, - name: payload.name - }; - const newTokens = await this.generateTokens(user); - logger_1.securityLogger.logAuthEvent('token_refreshed', { - userId: payload.userId, - email: payload.email, - oldSessionId: payload.sessionId, - newSessionId: 'generated' // New session ID is in the new tokens - }); - return newTokens; - } - /** - * Decode token without verification (for debugging) - */ - decodeToken(token) { - try { - const payload = jsonwebtoken_1.default.decode(token); - return payload; - } - catch (error) { - logger_1.logger.warn({ error }, 'Failed to decode token'); - return null; - } - } - /** - * Extract token from Authorization header - */ - extractTokenFromHeader(authHeader) { - if (!authHeader || typeof authHeader !== 'string') { - return null; - } - const parts = authHeader.split(' '); - if (parts.length !== 2 || parts[0] !== 'Bearer') { - return null; - } - return parts[1] || null; - } - /** - * Check if user has required permission - */ - hasPermission(payload, requiredPermission) { - // Check for wildcard permission first (CEO has all permissions) - if (payload.permissions.includes('*')) { - return true; - } - return payload.permissions.includes(requiredPermission); - } - /** - * Check if user has any of the required permissions - */ - hasAnyPermission(payload, requiredPermissions) { - // Check for wildcard permission first (CEO has all permissions) - if (payload.permissions.includes('*')) { - return true; - } - return requiredPermissions.some(permission => payload.permissions.includes(permission)); - } - /** - * Check if user has all required permissions - */ - hasAllPermissions(payload, requiredPermissions) { - // Check for wildcard permission first (CEO has all permissions) - if (payload.permissions.includes('*')) { - return true; - } - return requiredPermissions.every(permission => payload.permissions.includes(permission)); - } - /** - * Get token expiration date - */ - getTokenExpiration(token) { - const payload = this.decodeToken(token); - if (!payload || !payload.exp) { - return null; - } - return new Date(payload.exp * 1000); - } - /** - * Check if token is expired - */ - isTokenExpired(token) { - const expiration = this.getTokenExpiration(token); - if (!expiration) { - return true; - } - return expiration.getTime() < Date.now(); - } - /** - * Generate password reset token (separate from auth tokens) - */ - generatePasswordResetToken(userId) { - return jsonwebtoken_1.default.sign({ userId, type: 'password_reset' }, this.accessTokenSecret + userId, // Include user ID in secret for security - { expiresIn: '1h' }); - } - /** - * Verify password reset token - */ - verifyPasswordResetToken(token, userId) { - try { - const payload = jsonwebtoken_1.default.verify(token, this.accessTokenSecret + userId); - return payload.userId === userId && payload.type === 'password_reset'; - } - catch (error) { - return false; - } - } - /** - * Generate email verification token - */ - generateEmailVerificationToken(userId, email) { - return jsonwebtoken_1.default.sign({ userId, email, type: 'email_verification' }, this.accessTokenSecret + email, { expiresIn: '24h' }); - } - /** - * Verify email verification token - */ - verifyEmailVerificationToken(token, email) { - try { - const payload = jsonwebtoken_1.default.verify(token, this.accessTokenSecret + email); - if (payload.email === email && payload.type === 'email_verification') { - return { userId: payload.userId }; - } - return null; - } - catch (error) { - return null; - } - } -} -// Export singleton instance -exports.jwtService = new JWTService(); -exports.default = exports.jwtService; -// Export permission constants -exports.PERMISSIONS = { - // Channel permissions - CHANNELS_CREATE: 'channels:create', - CHANNELS_READ: 'channels:read', - CHANNELS_UPDATE: 'channels:update', - CHANNELS_DELETE: 'channels:delete', - CHANNELS_MANAGE_MEMBERS: 'channels:manage_members', - CHANNELS_ARCHIVE: 'channels:archive', - // Task permissions - TASKS_CREATE: 'tasks:create', - TASKS_READ: 'tasks:read', - TASKS_UPDATE: 'tasks:update', - TASKS_DELETE: 'tasks:delete', - TASKS_ASSIGN: 'tasks:assign', - TASKS_MANAGE_DEPENDENCIES: 'tasks:manage_dependencies', - // User permissions - USERS_CREATE: 'users:create', - USERS_READ: 'users:read', - USERS_UPDATE: 'users:update', - USERS_DELETE: 'users:delete', - USERS_MANAGE_ROLES: 'users:manage_roles', - // Voice permissions - VOICE_COMMANDS: 'voice:commands', - VOICE_TRANSCRIBE: 'voice:transcribe', - VOICE_PROCESS: 'voice:process', - // System permissions - SYSTEM_ADMIN: 'system:admin', - ANALYTICS_READ: 'analytics:read', -}; -//# sourceMappingURL=jwt.js.map \ No newline at end of file diff --git a/backend/dist/auth/jwt.js.map b/backend/dist/auth/jwt.js.map deleted file mode 100644 index 072c6b7..0000000 --- a/backend/dist/auth/jwt.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/auth/jwt.ts"],"names":[],"mappings":";;;;;;AAAA,gEAA+B;AAC/B,yCAAuC;AACvC,0CAAuD;AACvD,0CAA0F;AAC1F,oDAA4B;AAgC5B;;GAEG;AACH,MAAM,gBAAgB,GAA6B;IACjD,GAAG,EAAE;QACH,qDAAqD;QACrD,GAAG;KACJ;IACD,OAAO,EAAE;QACP,kCAAkC;QAClC,iBAAiB,EAAE,eAAe,EAAE,iBAAiB;QACrD,yBAAyB;QACzB,kBAAkB;QAClB,cAAc,EAAE,YAAY,EAAE,cAAc;QAC5C,cAAc,EAAE,2BAA2B;QAC3C,0BAA0B;QAC1B,YAAY,EAAE,sBAAsB;QACpC,2BAA2B;QAC3B,gBAAgB;QAChB,wBAAwB;QACxB,gBAAgB;KACjB;IACD,KAAK,EAAE;QACL,uBAAuB;QACvB,eAAe,EAAE,sBAAsB;QACvC,qBAAqB;QACrB,YAAY,EAAE,kBAAkB,EAAE,eAAe;QACjD,qBAAqB;QACrB,gBAAgB,EAAE,0BAA0B;QAC5C,uCAAuC;KACxC;CACF,CAAC;AA6XO,4CAAgB;AA3XzB;;GAEG;AACH,MAAM,iBAAiB,GAAG,GAAW,EAAE;IACrC,OAAO,gBAAM,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,kBAAkB,GAAG,CAAC,OAAe,EAAU,EAAE;IACrD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAE7C,QAAQ,IAAI,EAAE,CAAC;QACb,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,CAAC;QACvB,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,EAAE,CAAC;QAC5B,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,IAAI,CAAC;QAC9B,KAAK,GAAG,CAAC,CAAC,OAAO,KAAK,GAAG,KAAK,CAAC;QAC/B,OAAO,CAAC,CAAC,OAAO,KAAK,CAAC;IACxB,CAAC;AACH,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU;IACG,iBAAiB,CAAS;IAC1B,kBAAkB,CAAS;IAC3B,iBAAiB,CAAS;IAC1B,kBAAkB,CAAS;IAE5C;QACE,IAAI,CAAC,iBAAiB,GAAG,cAAM,CAAC,GAAG,CAAC,MAAM,CAAC;QAC3C,IAAI,CAAC,kBAAkB,GAAG,cAAM,CAAC,GAAG,CAAC,aAAa,CAAC;QACnD,IAAI,CAAC,iBAAiB,GAAG,cAAM,CAAC,GAAG,CAAC,SAAS,CAAC;QAC9C,IAAI,CAAC,kBAAkB,GAAG,cAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACxD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,IAKpB;QACC,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAC;QACtC,MAAM,WAAW,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QAEtD,MAAM,WAAW,GAAG;YAClB,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW;YACX,SAAS;SACV,CAAC;QAEF,wBAAwB;QACxB,MAAM,WAAW,GAAG,sBAAG,CAAC,IAAI,CAC1B,EAAE,GAAG,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,EAClC,IAAI,CAAC,iBAAiB,EACtB;YACE,SAAS,EAAE,IAAI,CAAC,iBAAiB;YACjC,MAAM,EAAE,cAAc;YACtB,QAAQ,EAAE,kBAAkB;YAC5B,OAAO,EAAE,IAAI,CAAC,EAAE;SACE,CACrB,CAAC;QAEF,yBAAyB;QACzB,MAAM,YAAY,GAAG,sBAAG,CAAC,IAAI,CAC3B,EAAE,GAAG,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE,EACnC,IAAI,CAAC,kBAAkB,EACvB;YACE,SAAS,EAAE,IAAI,CAAC,kBAAkB;YAClC,MAAM,EAAE,cAAc;YACtB,QAAQ,EAAE,kBAAkB;YAC5B,OAAO,EAAE,IAAI,CAAC,EAAE;SACE,CACrB,CAAC;QAEF,MAAM,SAAS,GAAG,kBAAkB,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QAC7D,MAAM,gBAAgB,GAAG,kBAAkB,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;QAErE,uBAAc,CAAC,YAAY,CAAC,iBAAiB,EAAE;YAC7C,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,SAAS;YACT,SAAS;YACT,gBAAgB;SACjB,CAAC,CAAC;QAEH,OAAO;YACL,WAAW;YACX,YAAY;YACZ,SAAS;YACT,gBAAgB;SACjB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,iBAAiB,CAAC,KAAa;QACnC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,iBAAiB,EAAE;gBACxD,MAAM,EAAE,cAAc;gBACtB,QAAQ,EAAE,kBAAkB;aAC7B,CAAiB,CAAC;YAEnB,IAAI,OAAO,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;gBAC9B,MAAM,IAAI,0BAAiB,CAAC,6CAA6C,CAAC,CAAC;YAC7E,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,sBAAG,CAAC,iBAAiB,EAAE,CAAC;gBAC3C,MAAM,IAAI,0BAAiB,CAAC,0BAA0B,CAAC,CAAC;YAC1D,CAAC;iBAAM,IAAI,KAAK,YAAY,sBAAG,CAAC,iBAAiB,EAAE,CAAC;gBAClD,MAAM,IAAI,0BAAiB,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACxE,CAAC;iBAAM,CAAC;gBACN,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,yCAAyC,CAAC,CAAC;gBACnE,MAAM,IAAI,4BAAmB,CAAC,2BAA2B,CAAC,CAAC;YAC7D,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,kBAAkB,CAAC,KAAa;QACpC,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,kBAAkB,EAAE;gBACzD,MAAM,EAAE,cAAc;gBACtB,QAAQ,EAAE,kBAAkB;aAC7B,CAAiB,CAAC;YAEnB,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;gBAC/B,MAAM,IAAI,0BAAiB,CAAC,8CAA8C,CAAC,CAAC;YAC9E,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,YAAY,sBAAG,CAAC,iBAAiB,EAAE,CAAC;gBAC3C,uBAAc,CAAC,YAAY,CAAC,uBAAuB,EAAE;oBACnD,KAAK,EAAE,KAAK,CAAC,OAAO;iBACrB,CAAC,CAAC;gBACH,MAAM,IAAI,0BAAiB,CAAC,2BAA2B,CAAC,CAAC;YAC3D,CAAC;iBAAM,IAAI,KAAK,YAAY,sBAAG,CAAC,iBAAiB,EAAE,CAAC;gBAClD,uBAAc,CAAC,oBAAoB,CAAC,uBAAuB,EAAE;oBAC3D,KAAK,EAAE,KAAK,CAAC,OAAO;oBACpB,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK;iBACtC,CAAC,CAAC;gBACH,MAAM,IAAI,0BAAiB,CAAC,0BAA0B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACzE,CAAC;iBAAM,CAAC;gBACN,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,0CAA0C,CAAC,CAAC;gBACpE,MAAM,IAAI,4BAAmB,CAAC,mCAAmC,CAAC,CAAC;YACrE,CAAC;QACH,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,YAAoB;QACtC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC;QAE5D,8CAA8C;QAC9C,MAAM,IAAI,GAAG;YACX,EAAE,EAAE,OAAO,CAAC,MAAM;YAClB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,IAAI,EAAE,OAAO,CAAC,IAAI;SACnB,CAAC;QAEF,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,CAAC;QAElD,uBAAc,CAAC,YAAY,CAAC,iBAAiB,EAAE;YAC7C,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,YAAY,EAAE,OAAO,CAAC,SAAS;YAC/B,YAAY,EAAE,WAAW,CAAC,sCAAsC;SACjE,CAAC,CAAC;QAEH,OAAO,SAAS,CAAC;IACnB,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,KAAa;QACvB,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CAAC,KAAK,CAAiB,CAAC;YAClD,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,wBAAwB,CAAC,CAAC;YACjD,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,sBAAsB,CAAC,UAA8B;QACnD,IAAI,CAAC,UAAU,IAAI,OAAO,UAAU,KAAK,QAAQ,EAAE,CAAC;YAClD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;QACpC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC;YAChD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAC1B,CAAC;IAED;;OAEG;IACH,aAAa,CAAC,OAAqB,EAAE,kBAA0B;QAC7D,gEAAgE;QAChE,IAAI,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;IAC1D,CAAC;IAED;;OAEG;IACH,gBAAgB,CAAC,OAAqB,EAAE,mBAA6B;QACnE,gEAAgE;QAChE,IAAI,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAC3C,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,CACzC,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,iBAAiB,CAAC,OAAqB,EAAE,mBAA6B;QACpE,gEAAgE;QAChE,IAAI,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,mBAAmB,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAC5C,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,UAAU,CAAC,CACzC,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,kBAAkB,CAAC,KAAa;QAC9B,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QACxC,IAAI,CAAC,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAAC;IACtC,CAAC;IAED;;OAEG;IACH,cAAc,CAAC,KAAa;QAC1B,MAAM,UAAU,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAClD,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,UAAU,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAC3C,CAAC;IAED;;OAEG;IACH,0BAA0B,CAAC,MAAc;QACvC,OAAO,sBAAG,CAAC,IAAI,CACb,EAAE,MAAM,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAClC,IAAI,CAAC,iBAAiB,GAAG,MAAM,EAAE,yCAAyC;QAC1E,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,wBAAwB,CAAC,KAAa,EAAE,MAAc;QACpD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CACxB,KAAK,EACL,IAAI,CAAC,iBAAiB,GAAG,MAAM,CACzB,CAAC;YAET,OAAO,OAAO,CAAC,MAAM,KAAK,MAAM,IAAI,OAAO,CAAC,IAAI,KAAK,gBAAgB,CAAC;QACxE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED;;OAEG;IACH,8BAA8B,CAAC,MAAc,EAAE,KAAa;QAC1D,OAAO,sBAAG,CAAC,IAAI,CACb,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,oBAAoB,EAAE,EAC7C,IAAI,CAAC,iBAAiB,GAAG,KAAK,EAC9B,EAAE,SAAS,EAAE,KAAK,EAAE,CACrB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,4BAA4B,CAAC,KAAa,EAAE,KAAa;QACvD,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,sBAAG,CAAC,MAAM,CACxB,KAAK,EACL,IAAI,CAAC,iBAAiB,GAAG,KAAK,CACxB,CAAC;YAET,IAAI,OAAO,CAAC,KAAK,KAAK,KAAK,IAAI,OAAO,CAAC,IAAI,KAAK,oBAAoB,EAAE,CAAC;gBACrE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC;YACpC,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF;AAED,4BAA4B;AACf,QAAA,UAAU,GAAG,IAAI,UAAU,EAAE,CAAC;AAC3C,kBAAe,kBAAU,CAAC;AAE1B,8BAA8B;AACjB,QAAA,WAAW,GAAG;IACzB,sBAAsB;IACtB,eAAe,EAAE,iBAAiB;IAClC,aAAa,EAAE,eAAe;IAC9B,eAAe,EAAE,iBAAiB;IAClC,eAAe,EAAE,iBAAiB;IAClC,uBAAuB,EAAE,yBAAyB;IAClD,gBAAgB,EAAE,kBAAkB;IAEpC,mBAAmB;IACnB,YAAY,EAAE,cAAc;IAC5B,UAAU,EAAE,YAAY;IACxB,YAAY,EAAE,cAAc;IAC5B,YAAY,EAAE,cAAc;IAC5B,YAAY,EAAE,cAAc;IAC5B,yBAAyB,EAAE,2BAA2B;IAEtD,mBAAmB;IACnB,YAAY,EAAE,cAAc;IAC5B,UAAU,EAAE,YAAY;IACxB,YAAY,EAAE,cAAc;IAC5B,YAAY,EAAE,cAAc;IAC5B,kBAAkB,EAAE,oBAAoB;IAExC,oBAAoB;IACpB,cAAc,EAAE,gBAAgB;IAChC,gBAAgB,EAAE,kBAAkB;IACpC,aAAa,EAAE,eAAe;IAE9B,qBAAqB;IACrB,YAAY,EAAE,cAAc;IAC5B,cAAc,EAAE,gBAAgB;CACxB,CAAC"} \ No newline at end of file diff --git a/backend/dist/auth/middleware.d.ts b/backend/dist/auth/middleware.d.ts deleted file mode 100644 index 6493bb5..0000000 --- a/backend/dist/auth/middleware.d.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { FastifyRequest, FastifyReply } from 'fastify'; -import { TokenPayload } from './jwt'; -/** - * Authentication and Authorization middleware for Fastify - * Enterprise-grade security with rate limiting and audit logging - */ -declare module 'fastify' { - interface FastifyRequest { - user?: (TokenPayload & { - isAuthenticated: boolean; - id: string; - }) | undefined; - } -} -/** - * Authentication middleware - validates JWT tokens - */ -export declare const authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Optional authentication middleware - doesn't throw if no token - */ -export declare const optionalAuthenticate: (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Authorization middleware factory - checks permissions - */ -export declare const authorize: (...requiredPermissions: string[]) => (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Role-based authorization middleware - */ -export declare const authorizeRoles: (...allowedRoles: Array<"ceo" | "manager" | "staff">) => (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * CEO-only middleware - */ -export declare const requireCEO: (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Manager or CEO middleware - */ -export declare const requireManagerOrCEO: (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Voice commands authorization (CEO only feature) - */ -export declare const authorizeVoiceCommands: (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Rate limiting middleware - */ -export declare const rateLimit: (options: { - maxRequests: number; - windowMs: number; - keyGenerator?: (request: FastifyRequest) => string; - skipSuccessful?: boolean; - skipFailedRequests?: boolean; -}) => (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Strict rate limiting for authentication endpoints - */ -export declare const authRateLimit: (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Voice processing rate limiting - */ -export declare const voiceRateLimit: (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * API rate limiting for general endpoints - */ -export declare const apiRateLimit: (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Resource ownership middleware - ensures user can only access their own resources - */ -export declare const requireResourceOwnership: (userIdParam?: string) => (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Channel membership middleware - ensures user has access to channel - */ -export declare const requireChannelAccess: (request: FastifyRequest, reply: FastifyReply) => Promise; -/** - * Audit logging middleware - logs all requests for security audit - */ -export declare const auditLog: (request: FastifyRequest, reply: FastifyReply) => Promise; -//# sourceMappingURL=middleware.d.ts.map \ No newline at end of file diff --git a/backend/dist/auth/middleware.d.ts.map b/backend/dist/auth/middleware.d.ts.map deleted file mode 100644 index 6bc8c7b..0000000 --- a/backend/dist/auth/middleware.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvD,OAAO,EAAc,YAAY,EAAe,MAAM,OAAO,CAAC;AAK9D;;;GAGG;AAGH,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,IAAI,CAAC,EACD,CAAC,YAAY,GAAG;YACd,eAAe,EAAE,OAAO,CAAC;YACzB,EAAE,EAAE,MAAM,CAAC;SACZ,CAAC,GACF,SAAS,CAAC;KACf;CACF;AAsBD;;GAEG;AACH,eAAO,MAAM,YAAY,GAAU,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAmE7F,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAC/B,SAAS,cAAc,EACvB,OAAO,YAAY,KAClB,OAAO,CAAC,IAAI,CAWd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,GAAG,qBAAqB,MAAM,EAAE,MAC1C,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CA+B1E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,cAAc,GAAI,GAAG,cAAc,KAAK,CAAC,KAAK,GAAG,SAAS,GAAG,OAAO,CAAC,MAClE,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAyB1E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,UAAU,YA9BE,cAAc,SAAS,YAAY,KAAG,OAAO,CAAC,IAAI,CA8B5B,CAAC;AAEhD;;GAEG;AACH,eAAO,MAAM,mBAAmB,YAnCP,cAAc,SAAS,YAAY,KAAG,OAAO,CAAC,IAAI,CAmCR,CAAC;AAEpE;;GAEG;AACH,eAAO,MAAM,sBAAsB,YA7EV,cAAc,SAAS,YAAY,KAAG,OAAO,CAAC,IAAI,CA6EA,CAAC;AAE5E;;GAEG;AACH,eAAO,MAAM,SAAS,GAAI,SAAS;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,MAAM,CAAC;IACnD,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,kBAAkB,CAAC,EAAE,OAAO,CAAC;CAC9B,MASe,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CA4C1E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,YAjDD,cAAc,SAAS,YAAY,KAAG,OAAO,CAAC,IAAI,CAqDzE,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,cAAc,YA1DF,cAAc,SAAS,YAAY,KAAG,OAAO,CAAC,IAAI,CA8DzE,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,YAAY,YAnEA,cAAc,SAAS,YAAY,KAAG,OAAO,CAAC,IAAI,CAuEzE,CAAC;AAEH;;GAEG;AACH,eAAO,MAAM,wBAAwB,GAAI,cAAa,MAAiB,MACvD,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CA6B1E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,GAC/B,SAAS,cAAc,EACvB,OAAO,YAAY,KAClB,OAAO,CAAC,IAAI,CAkDd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,QAAQ,GAAU,SAAS,cAAc,EAAE,OAAO,YAAY,KAAG,OAAO,CAAC,IAAI,CAmCzF,CAAC"} \ No newline at end of file diff --git a/backend/dist/auth/middleware.js b/backend/dist/auth/middleware.js deleted file mode 100644 index d196196..0000000 --- a/backend/dist/auth/middleware.js +++ /dev/null @@ -1,373 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -exports.auditLog = exports.requireChannelAccess = exports.requireResourceOwnership = exports.apiRateLimit = exports.voiceRateLimit = exports.authRateLimit = exports.rateLimit = exports.authorizeVoiceCommands = exports.requireManagerOrCEO = exports.requireCEO = exports.authorizeRoles = exports.authorize = exports.optionalAuthenticate = exports.authenticate = void 0; -const jwt_1 = require("./jwt"); -const index_1 = require("@db/index"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -/** - * Rate limiting store (in production, use Redis) - */ -const rateLimitStore = new Map(); -/** - * Clean up expired rate limit entries - */ -const cleanupRateLimit = () => { - const now = Date.now(); - for (const [key, value] of rateLimitStore.entries()) { - if (now > value.resetTime) { - rateLimitStore.delete(key); - } - } -}; -// Cleanup every 5 minutes -setInterval(cleanupRateLimit, 5 * 60 * 1000); -/** - * Authentication middleware - validates JWT tokens - */ -const authenticate = async (request, reply) => { - try { - const authHeader = request.headers.authorization; - const token = jwt_1.jwtService.extractTokenFromHeader(authHeader); - if (!token) { - logger_1.securityLogger.logAuthEvent('missing_token', { - ip: request.ip, - userAgent: request.headers['user-agent'] ?? '', - path: request.url, - method: request.method, - }); - throw new errors_1.AuthenticationError('Access token required'); - } - // Verify token - const payload = await jwt_1.jwtService.verifyAccessToken(token); - // Additional security: verify user still exists and is active - const user = await index_1.userRepository.findById(payload.userId); - if (!user) { - logger_1.securityLogger.logSecurityViolation('token_user_not_found', { - userId: payload.userId, - ip: request.ip, - }); - throw new errors_1.AuthenticationError('User account not found'); - } - if (user.deleted_at) { - logger_1.securityLogger.logSecurityViolation('deleted_user_token', { - userId: payload.userId, - ip: request.ip, - }); - throw new errors_1.AuthenticationError('User account has been deactivated'); - } - // Update user's last active timestamp (async, don't wait) - index_1.userRepository.updateLastActive(payload.userId).catch((error) => { - logger_1.logger.warn?.({ error, userId: payload.userId }, 'Failed to update last active timestamp'); - }); - // Attach user to request - request.user = { - ...payload, - isAuthenticated: true, - id: payload.userId, // Add id alias for compatibility - }; - logger_1.securityLogger.logAuthEvent('authentication_success', { - userId: payload.userId, - email: payload.email, - role: payload.role, - ip: request.ip, - path: request.url, - method: request.method, - }); - } - catch (error) { - if (error instanceof errors_1.AuthenticationError) { - throw error; - } - logger_1.logger.error({ error }, 'Authentication middleware error'); - throw new errors_1.AuthenticationError('Authentication failed'); - } -}; -exports.authenticate = authenticate; -/** - * Optional authentication middleware - doesn't throw if no token - */ -const optionalAuthenticate = async (request, reply) => { - try { - await (0, exports.authenticate)(request, reply); - } - catch (error) { - // Don't throw, just log the attempt - logger_1.logger.debug({ error: error instanceof Error ? error.message : String(error) }, 'Optional authentication failed'); - request.user = undefined; - } -}; -exports.optionalAuthenticate = optionalAuthenticate; -/** - * Authorization middleware factory - checks permissions - */ -const authorize = (...requiredPermissions) => { - return async (request, reply) => { - if (!request.user || !request.user.isAuthenticated) { - throw new errors_1.AuthenticationError('Authentication required for this resource'); - } - const hasPermission = requiredPermissions.length === 0 || - jwt_1.jwtService.hasAnyPermission(request.user, requiredPermissions); - if (!hasPermission) { - logger_1.securityLogger.logAuthzEvent('access_denied', { - userId: request.user.userId, - requiredPermissions, - userPermissions: request.user.permissions, - resource: request.url, - action: request.method, - ip: request.ip, - }); - throw new errors_1.AuthorizationError(`Insufficient permissions. Required: ${requiredPermissions.join(', ')}`); - } - logger_1.securityLogger.logAuthzEvent('access_granted', { - userId: request.user.userId, - resource: request.url, - action: request.method, - ip: request.ip, - }); - }; -}; -exports.authorize = authorize; -/** - * Role-based authorization middleware - */ -const authorizeRoles = (...allowedRoles) => { - return async (request, reply) => { - if (!request.user || !request.user.isAuthenticated) { - throw new errors_1.AuthenticationError('Authentication required for this resource'); - } - if (!allowedRoles.includes(request.user.role)) { - logger_1.securityLogger.logAuthzEvent('access_denied', { - userId: request.user.userId, - userRole: request.user.role, - allowedRoles, - resource: request.url, - action: request.method, - ip: request.ip, - }); - throw new errors_1.AuthorizationError(`Access denied. Required roles: ${allowedRoles.join(', ')}`); - } - logger_1.securityLogger.logAuthzEvent('access_granted', { - userId: request.user.userId, - resource: request.url, - action: request.method, - ip: request.ip, - }); - }; -}; -exports.authorizeRoles = authorizeRoles; -/** - * CEO-only middleware - */ -exports.requireCEO = (0, exports.authorizeRoles)('ceo'); -/** - * Manager or CEO middleware - */ -exports.requireManagerOrCEO = (0, exports.authorizeRoles)('ceo', 'manager'); -/** - * Voice commands authorization (CEO only feature) - */ -exports.authorizeVoiceCommands = (0, exports.authorize)(jwt_1.PERMISSIONS.VOICE_COMMANDS); -/** - * Rate limiting middleware - */ -const rateLimit = (options) => { - const { maxRequests, windowMs, keyGenerator = (req) => req.ip, skipSuccessful = false, skipFailedRequests = false, } = options; - return async (request, reply) => { - const key = keyGenerator(request); - const now = Date.now(); - const resetTime = now + windowMs; - let rateLimitInfo = rateLimitStore.get(key); - if (!rateLimitInfo || now > rateLimitInfo.resetTime) { - rateLimitInfo = { count: 0, resetTime }; - rateLimitStore.set(key, rateLimitInfo); - } - rateLimitInfo.count++; - if (rateLimitInfo.count > maxRequests) { - logger_1.securityLogger.logSecurityViolation('rate_limit_exceeded', { - key, - ip: request.ip, - path: request.url, - count: rateLimitInfo.count, - maxRequests, - userId: request.user?.userId, - }); - const retryAfter = Math.ceil((rateLimitInfo.resetTime - now) / 1000); - reply.header('Retry-After', retryAfter.toString()); - reply.header('X-RateLimit-Limit', maxRequests.toString()); - reply.header('X-RateLimit-Remaining', '0'); - reply.header('X-RateLimit-Reset', Math.ceil(rateLimitInfo.resetTime / 1000).toString()); - throw new errors_1.RateLimitError('Rate limit exceeded. Too many requests.', retryAfter, { - maxRequests, - windowMs, - currentCount: rateLimitInfo.count, - }); - } - // Set rate limit headers - const remaining = Math.max(0, maxRequests - rateLimitInfo.count); - reply.header('X-RateLimit-Limit', maxRequests.toString()); - reply.header('X-RateLimit-Remaining', remaining.toString()); - reply.header('X-RateLimit-Reset', Math.ceil(rateLimitInfo.resetTime / 1000).toString()); - }; -}; -exports.rateLimit = rateLimit; -/** - * Strict rate limiting for authentication endpoints - */ -exports.authRateLimit = (0, exports.rateLimit)({ - maxRequests: 5, // 5 attempts - windowMs: 15 * 60 * 1000, // 15 minutes - keyGenerator: (req) => `auth:${req.ip}`, -}); -/** - * Voice processing rate limiting - */ -exports.voiceRateLimit = (0, exports.rateLimit)({ - maxRequests: 60, // 60 commands per hour - windowMs: 60 * 60 * 1000, // 1 hour - keyGenerator: (req) => `voice:${req.user?.userId || req.ip}`, -}); -/** - * API rate limiting for general endpoints - */ -exports.apiRateLimit = (0, exports.rateLimit)({ - maxRequests: 1000, // 1000 requests per hour - windowMs: 60 * 60 * 1000, // 1 hour - keyGenerator: (req) => `api:${req.user?.userId || req.ip}`, -}); -/** - * Resource ownership middleware - ensures user can only access their own resources - */ -const requireResourceOwnership = (userIdParam = 'userId') => { - return async (request, reply) => { - if (!request.user || !request.user.isAuthenticated) { - throw new errors_1.AuthenticationError('Authentication required'); - } - // CEO can access any resource - if (request.user.role === 'ceo') { - return; - } - const requestedUserId = request.params[userIdParam]; - if (!requestedUserId) { - throw new errors_1.AuthorizationError('Resource user ID not specified'); - } - if (request.user.userId !== requestedUserId) { - logger_1.securityLogger.logAuthzEvent('access_denied', { - userId: request.user.userId, - requestedUserId, - resource: request.url, - action: request.method, - ip: request.ip, - reason: 'resource_ownership_violation', - }); - throw new errors_1.AuthorizationError('You can only access your own resources'); - } - }; -}; -exports.requireResourceOwnership = requireResourceOwnership; -/** - * Channel membership middleware - ensures user has access to channel - */ -const requireChannelAccess = async (request, reply) => { - if (!request.user || !request.user.isAuthenticated) { - throw new errors_1.AuthenticationError('Authentication required'); - } - // Check for channel ID in different parameter names used in routes - const channelId = request.params.channelId || - request.params.id || - request.body?.channelId; - if (!channelId) { - throw new errors_1.AuthorizationError('Channel ID not specified'); - } - // CEO can access any channel - if (request.user.role === 'ceo') { - return; - } - try { - const { channelRepository } = await Promise.resolve().then(() => __importStar(require('@db/index'))); - const hasAccess = await channelRepository.canUserAccess(channelId, request.user.userId, request.user.role); - if (!hasAccess) { - logger_1.securityLogger.logAuthzEvent('access_denied', { - userId: request.user.userId, - channelId, - resource: request.url, - action: request.method, - ip: request.ip, - reason: 'channel_access_denied', - }); - throw new errors_1.AuthorizationError('You do not have access to this channel'); - } - } - catch (error) { - if (error instanceof errors_1.AuthorizationError) { - throw error; - } - logger_1.logger.error({ error, channelId, userId: request.user.userId }, 'Error checking channel access'); - throw new errors_1.AuthorizationError('Unable to verify channel access'); - } -}; -exports.requireChannelAccess = requireChannelAccess; -/** - * Audit logging middleware - logs all requests for security audit - */ -const auditLog = async (request, reply) => { - const startTime = Date.now(); - // Log request - logger_1.logger.info({ - method: request.method, - url: request.url, - ip: request.ip, - userAgent: request.headers['user-agent'], - userId: request.user?.userId, - userRole: request.user?.role, - timestamp: new Date().toISOString(), - }, 'API Request'); - // Log response when request completes - request.server.addHook('onSend', async (request, reply, payload) => { - const duration = Date.now() - startTime; - logger_1.logger.info({ - method: request.method, - url: request.url, - statusCode: reply.statusCode, - duration, - userId: request.user?.userId, - timestamp: new Date().toISOString(), - }, 'API Response'); - return payload; - }); -}; -exports.auditLog = auditLog; -//# sourceMappingURL=middleware.js.map \ No newline at end of file diff --git a/backend/dist/auth/middleware.js.map b/backend/dist/auth/middleware.js.map deleted file mode 100644 index 90051e7..0000000 --- a/backend/dist/auth/middleware.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/auth/middleware.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/auth/routes.d.ts b/backend/dist/auth/routes.d.ts deleted file mode 100644 index 8d83c2f..0000000 --- a/backend/dist/auth/routes.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { FastifyInstance } from 'fastify'; -/** - * Register authentication routes - */ -export declare const registerAuthRoutes: (fastify: FastifyInstance) => Promise; -//# sourceMappingURL=routes.d.ts.map \ No newline at end of file diff --git a/backend/dist/auth/routes.d.ts.map b/backend/dist/auth/routes.d.ts.map deleted file mode 100644 index adfee3a..0000000 --- a/backend/dist/auth/routes.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/auth/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AA6DxE;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAU,SAAS,eAAe,kBA4uBhE,CAAC"} \ No newline at end of file diff --git a/backend/dist/auth/routes.js b/backend/dist/auth/routes.js deleted file mode 100644 index 35e6e33..0000000 --- a/backend/dist/auth/routes.js +++ /dev/null @@ -1,715 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.registerAuthRoutes = void 0; -const jwt_1 = require("./jwt"); -const index_1 = require("@db/index"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const middleware_1 = require("./middleware"); -const typebox_1 = require("@sinclair/typebox"); -const crypto_1 = __importDefault(require("crypto")); -const EmailService_1 = require("@/services/EmailService"); -/** - * Authentication routes for the CEO Communication Platform - * Includes login, registration, password reset, and token management - */ -// Request/Response schemas -const LoginSchema = typebox_1.Type.Object({ - email: typebox_1.Type.String({ format: 'email' }), - password: typebox_1.Type.String({ minLength: 1 }), -}); -const RegisterSchema = typebox_1.Type.Object({ - email: typebox_1.Type.String({ format: 'email' }), - password: typebox_1.Type.String({ minLength: 8 }), - name: typebox_1.Type.String({ minLength: 1, maxLength: 255 }), - role: typebox_1.Type.Union([typebox_1.Type.Literal('ceo'), typebox_1.Type.Literal('manager'), typebox_1.Type.Literal('staff')]), - department: typebox_1.Type.Optional(typebox_1.Type.String()), - job_title: typebox_1.Type.Optional(typebox_1.Type.String()), - phone: typebox_1.Type.Optional(typebox_1.Type.String()), -}); -const RefreshTokenSchema = typebox_1.Type.Object({ - refreshToken: typebox_1.Type.String(), -}); -const PasswordResetRequestSchema = typebox_1.Type.Object({ - email: typebox_1.Type.String({ format: 'email' }), -}); -const PasswordResetSchema = typebox_1.Type.Object({ - token: typebox_1.Type.String(), - newPassword: typebox_1.Type.String({ minLength: 8 }), -}); -const ChangePasswordSchema = typebox_1.Type.Object({ - currentPassword: typebox_1.Type.String(), - newPassword: typebox_1.Type.String({ minLength: 8 }), -}); -/** - * Register authentication routes - */ -const registerAuthRoutes = async (fastify) => { - /** - * POST /auth/login - User login - */ - fastify.post('/auth/login', { - preHandler: [middleware_1.authRateLimit], - schema: { - tags: ['Authentication'], - summary: 'User Login', - description: 'Authenticate user with email and password to receive JWT tokens', - body: LoginSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Object({ - accessToken: typebox_1.Type.String(), - refreshToken: typebox_1.Type.String(), - expiresIn: typebox_1.Type.Number(), - user: typebox_1.Type.Object({ - id: typebox_1.Type.String(), - email: typebox_1.Type.String(), - name: typebox_1.Type.String(), - role: typebox_1.Type.String(), - avatar_url: typebox_1.Type.Optional(typebox_1.Type.String()), - email_verified: typebox_1.Type.Boolean(), - permissions: typebox_1.Type.Array(typebox_1.Type.String()), - }), - }), - }), - 400: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - 401: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - const { email, password } = request.body; - const context = (0, errors_1.createErrorContext)({ - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - }); - try { - logger_1.securityLogger.logAuthEvent('login_attempt', { - email, - ip: request.ip, - userAgent: request.headers['user-agent'] ?? '', - }); - // Verify credentials - const user = await index_1.userRepository.verifyPassword(email, password); - if (!user) { - logger_1.securityLogger.logAuthEvent('failed_login', { - email, - ip: request.ip, - reason: 'invalid_credentials', - }); - throw new errors_1.AuthenticationError('Invalid email or password'); - } - // Log if user is not verified (but don't prevent login) - if (!user.email_verified) { - logger_1.securityLogger.logAuthEvent('login_attempt', { - email, - ip: request.ip, - reason: 'email_not_verified', - }); - } - // Generate tokens - const tokens = await jwt_1.jwtService.generateTokens({ - id: user.id, - email: user.email, - role: user.role, - name: user.name, - }); - logger_1.securityLogger.logAuthEvent('login', { - userId: user.id, - email: user.email, - ip: request.ip, - userAgent: request.headers['user-agent'] ?? '', - }); - const responseData = { - success: true, - data: { - ...tokens, - user: { - id: user.id, - email: user.email, - name: user.name, - role: user.role, - avatar_url: user.avatar_url, - email_verified: user.email_verified, - permissions: jwt_1.jwtService.decodeToken(tokens.accessToken)?.permissions || [], - }, - }, - }; - logger_1.logger.info({ - responseData: JSON.stringify(responseData).substring(0, 200) + '...', - contentLength: JSON.stringify(responseData).length, - userId: user.id - }, 'Sending login response'); - reply.code(200).send(responseData); - } - catch (error) { - logger_1.logger.error({ error, context }, 'Login failed'); - if (error instanceof errors_1.AuthenticationError || error instanceof errors_1.ValidationError) { - reply.code(401).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Login failed due to server error', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * POST /auth/register - User registration - */ - fastify.post('/auth/register', { - preHandler: [middleware_1.authRateLimit], - schema: { - tags: ['Authentication'], - summary: 'User Registration', - description: 'Register a new user account with email verification', - body: RegisterSchema, - response: { - 201: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Object({ - user: typebox_1.Type.Object({ - id: typebox_1.Type.String(), - email: typebox_1.Type.String(), - name: typebox_1.Type.String(), - role: typebox_1.Type.String(), - }), - message: typebox_1.Type.String(), - }), - }), - 400: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - 409: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - const userData = request.body; - const context = (0, errors_1.createErrorContext)({ - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - }); - try { - logger_1.securityLogger.logAuthEvent('registration_attempt', { - email: userData.email, - role: userData.role, - ip: request.ip, - }); - // Create user - const user = await index_1.userRepository.createUser({ - ...userData, - // Self-registration: do not set created_by - }); - // Generate email verification token - const verificationToken = jwt_1.jwtService.generateEmailVerificationToken(user.id, user.email); - await index_1.userRepository.setEmailVerificationToken(user.id, verificationToken); - // Send verification email - const emailSent = await EmailService_1.emailService.sendEmailVerification({ - userEmail: user.email, - userName: user.name, - verificationToken, - }); - if (!emailSent) { - logger_1.logger.warn({ userId: user.id, email: user.email }, 'Failed to send verification email'); - } - logger_1.securityLogger.logAuthEvent('registration_success', { - userId: user.id, - email: user.email, - role: user.role, - ip: request.ip, - emailSent, - }); - reply.code(201).send({ - success: true, - data: { - user: { - id: user.id, - email: user.email, - name: user.name, - role: user.role, - }, - message: 'Account created successfully. Please check your email for verification instructions.', - }, - }); - } - catch (error) { - logger_1.logger.error({ error, context }, 'Registration failed'); - if (error instanceof errors_1.ConflictError) { - reply.code(409).send((0, errors_1.formatErrorResponse)(error)); - } - else if (error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Registration failed due to server error', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * POST /auth/refresh - Refresh access token - */ - fastify.post('/auth/refresh', { - schema: { - body: RefreshTokenSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Object({ - accessToken: typebox_1.Type.String(), - refreshToken: typebox_1.Type.String(), - expiresIn: typebox_1.Type.Number(), - }), - }), - 401: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - const { refreshToken } = request.body; - const context = (0, errors_1.createErrorContext)({ - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - }); - try { - const tokens = await jwt_1.jwtService.refreshTokens(refreshToken); - reply.code(200).send({ - success: true, - data: tokens, - }); - } - catch (error) { - logger_1.logger.error({ error, context }, 'Token refresh failed'); - if (error instanceof errors_1.AuthenticationError) { - reply.code(401).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Token refresh failed', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * POST /auth/logout - User logout (invalidate tokens - would need Redis blacklist in production) - */ - fastify.post('/auth/logout', async (request, reply) => { - // In production, add token to blacklist in Redis - // For now, just log the logout - if (request.user) { - logger_1.securityLogger.logAuthEvent('logout', { - userId: request.user.userId, - ip: request.ip, - }); - } - reply.code(200).send({ - success: true, - message: 'Logged out successfully', - }); - }); - /** - * POST /auth/password-reset-request - Request password reset - */ - fastify.post('/auth/password-reset-request', { - preHandler: [middleware_1.authRateLimit], - schema: { - body: PasswordResetRequestSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - message: typebox_1.Type.String(), - }), - }, - }, - }, async (request, reply) => { - const { email } = request.body; - try { - // Generate reset token - const resetToken = crypto_1.default.randomBytes(32).toString('hex'); - // Save token (expires in 1 hour) - const tokenSet = await index_1.userRepository.setPasswordResetToken(email, resetToken, '1 hour'); - if (tokenSet) { - // Get user details for the email - const user = await index_1.userRepository.findByEmail(email); - if (user) { - // Send password reset email - const emailSent = await EmailService_1.emailService.sendPasswordReset({ - userEmail: user.email, - userName: user.name, - resetToken, - }); - if (!emailSent) { - logger_1.logger.warn({ email, userId: user.id }, 'Failed to send password reset email'); - } - logger_1.securityLogger.logAuthEvent('password_reset_requested', { - email, - userId: user.id, - ip: request.ip, - emailSent, - }); - } - logger_1.logger.info({ email, resetToken }, 'Password reset token generated'); - } - // Always return success (don't reveal if email exists) - reply.code(200).send({ - success: true, - message: 'If the email address exists, you will receive password reset instructions.', - }); - } - catch (error) { - logger_1.logger.error({ error, email }, 'Password reset request failed'); - // Still return success to prevent email enumeration - reply.code(200).send({ - success: true, - message: 'If the email address exists, you will receive password reset instructions.', - }); - } - }); - /** - * POST /auth/password-reset - Reset password with token - */ - fastify.post('/auth/password-reset', { - preHandler: [middleware_1.authRateLimit], - schema: { - body: PasswordResetSchema, - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - message: typebox_1.Type.String(), - }), - 400: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - const { token, newPassword } = request.body; - const context = (0, errors_1.createErrorContext)({ - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - ...(request.user && - typeof request.user === 'object' && - 'email' in request.user && - 'role' in request.user - ? { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - } - : {}), - }); - try { - // Verify token - const user = await index_1.userRepository.verifyPasswordResetToken(token); - if (!user) { - throw new errors_1.AuthenticationError('Invalid or expired password reset token'); - } - // Update password - const success = await index_1.userRepository.updatePassword(user.id, newPassword); - if (!success) { - throw new errors_1.ValidationError('Failed to update password', []); - } - logger_1.securityLogger.logAuthEvent('password_reset', { - userId: user.id, - email: user.email, - ip: request.ip, - }); - reply.code(200).send({ - success: true, - message: 'Password has been reset successfully. You can now log in with your new password.', - }); - } - catch (error) { - logger_1.logger.error({ error, context }, 'Password reset failed'); - if (error instanceof errors_1.AuthenticationError || error instanceof errors_1.ValidationError) { - reply.code(400).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Password reset failed', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * GET /auth/verify-email/:token - Verify email address - */ - fastify.get('/auth/verify-email/:token', async (request, reply) => { - const { token } = request.params; - try { - const user = await index_1.userRepository.verifyEmail(token); - if (!user) { - throw new errors_1.AuthenticationError('Invalid or expired email verification token'); - } - logger_1.securityLogger.logAuthEvent('email_verified', { - userId: user.id, - email: user.email, - ip: request.ip, - }); - reply.code(200).send({ - success: true, - message: 'Email address verified successfully. You can now log in.', - }); - } - catch (error) { - logger_1.logger.error({ error, token }, 'Email verification failed'); - reply.code(400).send({ - error: { - message: 'Invalid or expired verification token', - code: 'INVALID_TOKEN', - }, - }); - } - }); - /** - * GET /auth/me - Get current user profile - */ - fastify.get('/auth/me', { - preHandler: [fastify.authenticate], - schema: { - tags: ['Authentication'], - summary: 'Get Current User', - description: 'Get the profile information of the currently authenticated user', - security: [{ BearerAuth: [] }], - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - data: typebox_1.Type.Object({ - user: typebox_1.Type.Object({ - id: typebox_1.Type.String({ format: 'uuid' }), - email: typebox_1.Type.String({ format: 'email' }), - name: typebox_1.Type.String(), - role: typebox_1.Type.String({ enum: ['ceo', 'manager', 'staff'] }), - avatar_url: typebox_1.Type.Union([typebox_1.Type.String({ format: 'uri' }), typebox_1.Type.Null()]), - department: typebox_1.Type.Union([typebox_1.Type.String(), typebox_1.Type.Null()]), - job_title: typebox_1.Type.Union([typebox_1.Type.String(), typebox_1.Type.Null()]), - phone: typebox_1.Type.Union([typebox_1.Type.String(), typebox_1.Type.Null()]), - language_preference: typebox_1.Type.Union([typebox_1.Type.String(), typebox_1.Type.Null()]), - timezone: typebox_1.Type.Union([typebox_1.Type.String(), typebox_1.Type.Null()]), - notification_settings: typebox_1.Type.Any(), - voice_settings: typebox_1.Type.Any(), - email_verified: typebox_1.Type.Boolean(), - last_active: typebox_1.Type.Union([typebox_1.Type.String({ format: 'date-time' }), typebox_1.Type.Null()]), - created_at: typebox_1.Type.String({ format: 'date-time' }), - updated_at: typebox_1.Type.Union([typebox_1.Type.String({ format: 'date-time' }), typebox_1.Type.Null()]), - permissions: typebox_1.Type.Array(typebox_1.Type.String()), - }), - }), - }), - 401: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - if (!request.user) { - throw new errors_1.AuthenticationError('Authentication required'); - } - try { - const user = await index_1.userRepository.findById(request.user.userId); - if (!user) { - throw new errors_1.AuthenticationError('User not found'); - } - reply.code(200).send({ - success: true, - data: { - user: { - id: user.id, - email: user.email, - name: user.name, - role: user.role, - avatar_url: user.avatar_url, - department: user.department, - job_title: user.job_title, - phone: user.phone, - language_preference: user.language_preference, - timezone: user.timezone, - notification_settings: user.notification_settings, - voice_settings: user.voice_settings, - email_verified: user.email_verified, - last_active: user.last_active, - created_at: user.created_at, - updated_at: user.updated_at, - permissions: request.user.permissions, - }, - }, - }); - } - catch (error) { - logger_1.logger.error({ error, userId: request.user.userId }, 'Get profile failed'); - if (error instanceof errors_1.AuthenticationError) { - reply.code(401).send((0, errors_1.formatErrorResponse)(error)); - } - else { - reply.code(500).send({ - error: { - message: 'Failed to get user profile', - code: 'SERVER_ERROR', - }, - }); - } - } - }); - /** - * POST /auth/resend-verification - Resend email verification - */ - fastify.post('/auth/resend-verification', { - preHandler: [middleware_1.authRateLimit], - schema: { - tags: ['Authentication'], - summary: 'Resend Email Verification', - description: 'Resend email verification link to the specified email address', - body: typebox_1.Type.Object({ - email: typebox_1.Type.String({ format: 'email' }), - }), - response: { - 200: typebox_1.Type.Object({ - success: typebox_1.Type.Boolean(), - message: typebox_1.Type.String(), - }), - 400: typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - }), - }), - }, - }, - }, async (request, reply) => { - const { email } = request.body; - const context = (0, errors_1.createErrorContext)({ - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers, - }); - try { - // Check if user exists with this email - const user = await index_1.userRepository.findByEmail(email); - if (!user) { - // Don't reveal that the email doesn't exist for security reasons - reply.code(200).send({ - success: true, - message: 'If the email address exists, a new verification email will be sent.', - }); - return; - } - // Check if user is already verified - if (user.email_verified) { - reply.code(200).send({ - success: true, - message: 'Email address is already verified.', - }); - return; - } - // Generate new email verification token - const verificationToken = jwt_1.jwtService.generateEmailVerificationToken(user.id, user.email); - await index_1.userRepository.setEmailVerificationToken(user.id, verificationToken); - // Send verification email - const emailSent = await EmailService_1.emailService.sendEmailVerification({ - userEmail: user.email, - userName: user.name, - verificationToken, - }); - if (!emailSent) { - logger_1.logger.warn({ userId: user.id, email: user.email }, 'Failed to send verification email'); - } - logger_1.securityLogger.logAuthEvent('email_verified', { - userId: user.id, - email: user.email, - ip: request.ip, - emailSent, - }); - reply.code(200).send({ - success: true, - message: 'Verification email sent successfully. Please check your inbox and spam folder.', - }); - } - catch (error) { - logger_1.logger.error({ error, context, email }, 'Resend verification failed'); - reply.code(500).send({ - error: { - message: 'Failed to resend verification email', - code: 'SERVER_ERROR', - }, - }); - } - }); -}; -exports.registerAuthRoutes = registerAuthRoutes; -//# sourceMappingURL=routes.js.map \ No newline at end of file diff --git a/backend/dist/auth/routes.js.map b/backend/dist/auth/routes.js.map deleted file mode 100644 index 1d8fc96..0000000 --- a/backend/dist/auth/routes.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/auth/routes.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/config/database.d.ts b/backend/dist/config/database.d.ts deleted file mode 100644 index fc5b178..0000000 --- a/backend/dist/config/database.d.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { Pool, PoolClient } from 'pg'; -/** - * Initialize the database connection pool - */ -export declare const initializeDatabase: () => Promise; -/** - * Get the database connection pool - */ -export declare const getPool: () => Pool; -/** - * Execute a query with performance monitoring - */ -export declare const query: (text: string, params?: any[], client?: PoolClient) => Promise<{ - rows: T[]; - rowCount: number; -}>; -/** - * Execute a transaction with automatic rollback on errors - */ -export declare const transaction: (callback: (client: PoolClient) => Promise) => Promise; -/** - * Check database health and connection status - */ -export declare const healthCheck: () => Promise<{ - status: "healthy" | "unhealthy"; - details: { - connected: boolean; - poolStats: { - total: number; - idle: number; - waiting: number; - }; - queryTime?: number; - error?: string; - }; -}>; -/** - * Gracefully close the database connection pool - */ -export declare const closeDatabase: () => Promise; -/** - * Get current pool statistics for monitoring - */ -export declare const getPoolStats: () => { - total: number; - idle: number; - waiting: number; - connected: boolean; -}; -export declare const databaseMetrics: { - /** - * Log slow queries for optimization - */ - logSlowQuery: (queryText: string, duration: number, params?: any[]) => void; - /** - * Monitor connection pool health - */ - monitorPoolHealth: () => void; -}; -export type DatabaseClient = PoolClient; -export type QueryResult = { - rows: T[]; - rowCount: number; -}; -//# sourceMappingURL=database.d.ts.map \ No newline at end of file diff --git a/backend/dist/config/database.d.ts.map b/backend/dist/config/database.d.ts.map deleted file mode 100644 index 7fec64b..0000000 --- a/backend/dist/config/database.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/config/database.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,UAAU,EAAc,MAAM,IAAI,CAAC;AAyClD;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC,IAAI,CAyDvD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,OAAO,QAAO,IAK1B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,KAAK,GAAU,CAAC,GAAG,GAAG,EACjC,MAAM,MAAM,EACZ,SAAS,GAAG,EAAE,EACd,SAAS,UAAU,KAClB,OAAO,CAAC;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CA8CzC,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,GAAU,CAAC,EAAE,UAAU,CAAC,MAAM,EAAE,UAAU,KAAK,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO,CAAC,CAAC,CA2B5F,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,QAAa,OAAO,CAAC;IAC3C,MAAM,EAAE,SAAS,GAAG,WAAW,CAAC;IAChC,OAAO,EAAE;QACP,SAAS,EAAE,OAAO,CAAC;QACnB,SAAS,EAAE;YACT,KAAK,EAAE,MAAM,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,MAAM,CAAC;SACjB,CAAC;QACF,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CA6CA,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,aAAa,QAAa,OAAO,CAAC,IAAI,CAOlD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,YAAY;;;;;CAgBxB,CAAC;AAGF,eAAO,MAAM,eAAe;IAC1B;;OAEG;8BACuB,MAAM,YAAY,MAAM,WAAW,GAAG,EAAE;IAelE;;OAEG;;CA0BJ,CAAC;AAGF,MAAM,MAAM,cAAc,GAAG,UAAU,CAAC;AACxC,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,GAAG,IAAI;IAAE,IAAI,EAAE,CAAC,EAAE,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC"} \ No newline at end of file diff --git a/backend/dist/config/database.js b/backend/dist/config/database.js deleted file mode 100644 index d3833f2..0000000 --- a/backend/dist/config/database.js +++ /dev/null @@ -1,268 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.databaseMetrics = exports.getPoolStats = exports.closeDatabase = exports.healthCheck = exports.transaction = exports.query = exports.getPool = exports.initializeDatabase = void 0; -const pg_1 = require("pg"); -const index_1 = require("./index"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -/** - * Database configuration and connection management - * Optimized for high-performance concurrent operations with voice processing - */ -// Enhanced pool configuration for CEO platform requirements -const poolConfig = { - connectionString: index_1.config.database.url, - min: index_1.config.database.pool.min, - max: index_1.config.database.pool.max, - // Connection lifecycle settings - idleTimeoutMillis: 30000, // 30 seconds idle timeout - connectionTimeoutMillis: 10000, // 10 seconds connection timeout - maxUses: 7500, // Maximum uses per connection before recycling - // SSL configuration for production and AWS RDS - ssl: index_1.config.app.isProduction || index_1.config.database.url.includes('rds.amazonaws.com') - ? { - rejectUnauthorized: false, // Configure properly in production - } - : false, - // Query timeout settings - query_timeout: 30000, // 30 seconds max query time - statement_timeout: 30000, // Statement timeout - // Performance optimizations - application_name: 'ceo-communication-platform', - // Error handling -}; -// Global connection pool -let pool = null; -/** - * Initialize the database connection pool - */ -const initializeDatabase = async () => { - try { - pool = new pg_1.Pool(poolConfig); - // Set up connection pool event handlers - pool.on('connect', (client) => { - logger_1.logger.debug({ - totalCount: pool?.totalCount, - idleCount: pool?.idleCount, - waitingCount: pool?.waitingCount, - }, 'Database client connected'); - }); - pool.on('acquire', (client) => { - logger_1.logger.debug('Database client acquired from pool'); - }); - pool.on('remove', (client) => { - logger_1.logger.debug('Database client removed from pool'); - }); - pool.on('error', (err, client) => { - logger_1.logger.error({ error: err }, 'Database pool error'); - }); - // Test the connection - const client = await pool.connect(); - // Optimize connection settings for performance - await client.query(` - SET timezone = 'UTC'; - SET statement_timeout = '30s'; - SET lock_timeout = '10s'; - SET idle_in_transaction_session_timeout = '60s'; - SET search_path = app, public; - `); - // Test query to verify everything is working - const result = await client.query('SELECT NOW() as current_time, version() as pg_version'); - client.release(); - logger_1.logger.info({ - poolSize: pool.totalCount, - currentTime: result.rows[0].current_time, - postgresVersion: result.rows[0].pg_version, - }, 'Database initialized'); - } - catch (error) { - logger_1.logger.error({ error }, 'Failed to initialize database connection pool'); - throw new errors_1.DatabaseConnectionError('Failed to connect to database', { error }); - } -}; -exports.initializeDatabase = initializeDatabase; -/** - * Get the database connection pool - */ -const getPool = () => { - if (!pool) { - throw new errors_1.DatabaseConnectionError('Database pool not initialized'); - } - return pool; -}; -exports.getPool = getPool; -/** - * Execute a query with performance monitoring - */ -const query = async (text, params, client) => { - const queryClient = client || pool; - if (!queryClient) { - throw new errors_1.DatabaseConnectionError('No database connection available'); - } - return logger_1.performanceLogger.trackAsyncOperation(async () => { - try { - const result = await queryClient.query(text, params); - if (index_1.config.development.debugSql) { - logger_1.logger.debug({ - query: text, - params: params, - rowCount: result.rowCount, - }, 'SQL Query executed'); - } - return { - rows: result.rows, - rowCount: result.rowCount || 0, - }; - } - catch (error) { - logger_1.logger.error({ - error, - query: text, - params: params, - }, 'Database query failed'); - throw new errors_1.DatabaseError(`Database query failed: ${error instanceof Error ? error.message : 'Unknown error'}`, { query: text, params }); - } - }, 'database-query', { query: text.substring(0, 100) + (text.length > 100 ? '...' : '') }); -}; -exports.query = query; -/** - * Execute a transaction with automatic rollback on errors - */ -const transaction = async (callback) => { - const client = await (0, exports.getPool)().connect(); - try { - await client.query('BEGIN'); - logger_1.logger.debug('Database transaction started'); - const result = await logger_1.performanceLogger.trackAsyncOperation(() => callback(client), 'database-transaction'); - await client.query('COMMIT'); - logger_1.logger.debug('Database transaction committed'); - return result; - } - catch (error) { - await client.query('ROLLBACK'); - logger_1.logger.error({ error }, 'Database transaction rolled back'); - throw new errors_1.DatabaseError(`Transaction failed: ${error instanceof Error ? error.message : 'Unknown error'}`, { originalError: error }); - } - finally { - client.release(); - } -}; -exports.transaction = transaction; -/** - * Check database health and connection status - */ -const healthCheck = async () => { - try { - const startTime = Date.now(); - if (!pool) { - return { - status: 'unhealthy', - details: { - connected: false, - poolStats: { total: 0, idle: 0, waiting: 0 }, - error: 'Pool not initialized', - }, - }; - } - // Test with a simple query - await pool.query('SELECT 1 as health_check'); - const queryTime = Date.now() - startTime; - return { - status: 'healthy', - details: { - connected: true, - poolStats: { - total: pool.totalCount, - idle: pool.idleCount, - waiting: pool.waitingCount, - }, - queryTime, - }, - }; - } - catch (error) { - return { - status: 'unhealthy', - details: { - connected: false, - poolStats: { - total: pool?.totalCount || 0, - idle: pool?.idleCount || 0, - waiting: pool?.waitingCount || 0, - }, - error: error instanceof Error ? error.message : 'Unknown error', - }, - }; - } -}; -exports.healthCheck = healthCheck; -/** - * Gracefully close the database connection pool - */ -const closeDatabase = async () => { - if (pool) { - logger_1.logger.info('Closing database connection pool...'); - await pool.end(); - pool = null; - logger_1.logger.info('Database connection pool closed'); - } -}; -exports.closeDatabase = closeDatabase; -/** - * Get current pool statistics for monitoring - */ -const getPoolStats = () => { - if (!pool) { - return { - total: 0, - idle: 0, - waiting: 0, - connected: false, - }; - } - return { - total: pool.totalCount, - idle: pool.idleCount, - waiting: pool.waitingCount, - connected: true, - }; -}; -exports.getPoolStats = getPoolStats; -// Performance monitoring utilities -exports.databaseMetrics = { - /** - * Log slow queries for optimization - */ - logSlowQuery: (queryText, duration, params) => { - if (duration > 1000) { - // Log queries taking more than 1 second - logger_1.logger.warn({ - query: queryText, - duration, - params, - threshold: '1000ms', - }, 'Slow query detected'); - } - }, - /** - * Monitor connection pool health - */ - monitorPoolHealth: () => { - if (!pool) - return; - const stats = (0, exports.getPoolStats)(); - const utilizationRate = (stats.total - stats.idle) / stats.total; - if (utilizationRate > 0.8) { - logger_1.logger.warn({ - stats, - utilizationRate, - }, 'High database connection pool utilization'); - } - if (stats.waiting > 0) { - logger_1.logger.warn({ - stats, - }, 'Clients waiting for database connections'); - } - }, -}; -//# sourceMappingURL=database.js.map \ No newline at end of file diff --git a/backend/dist/config/database.js.map b/backend/dist/config/database.js.map deleted file mode 100644 index 91af2f0..0000000 --- a/backend/dist/config/database.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"database.js","sourceRoot":"","sources":["../../src/config/database.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/config/index.d.ts b/backend/dist/config/index.d.ts deleted file mode 100644 index 0c1dfb0..0000000 --- a/backend/dist/config/index.d.ts +++ /dev/null @@ -1,82 +0,0 @@ -export declare const config: { - readonly app: { - readonly env: "development" | "test" | "production"; - readonly port: number; - readonly host: string; - readonly isDevelopment: boolean; - readonly isProduction: boolean; - readonly isTest: boolean; - }; - readonly database: { - readonly url: string; - readonly pool: { - readonly min: number; - readonly max: number; - }; - readonly options: { - readonly parseInputDatesAsUTC: true; - readonly ssl: boolean; - }; - }; - readonly redis: { - readonly host: string; - readonly port: number; - readonly password: string | undefined; - readonly db: number; - readonly pubSubDb: number; - readonly options: { - readonly retryDelayOnFailover: 100; - readonly enableReadyCheck: false; - readonly maxRetriesPerRequest: null; - readonly lazyConnect: true; - }; - }; - readonly jwt: { - readonly secret: string; - readonly refreshSecret: string; - readonly expiresIn: string; - readonly refreshExpiresIn: string; - }; - readonly api: { - readonly prefix: string; - readonly version: string; - readonly cors: { - readonly origin: string[]; - readonly credentials: true; - }; - readonly rateLimit: { - readonly max: number; - readonly timeWindow: "15 minutes"; - }; - }; - readonly security: { - readonly bcryptRounds: number; - readonly maxLoginAttempts: number; - readonly lockoutDuration: string; - }; - readonly performance: { - readonly cache: { - readonly ttl: { - readonly short: string; - readonly medium: string; - readonly long: string; - }; - }; - readonly timeouts: { - readonly query: string; - readonly request: string; - }; - }; - readonly logging: { - readonly level: "error" | "warn" | "info" | "debug"; - readonly format: "json" | "pretty"; - }; - readonly development: { - readonly seedDatabase: boolean; - readonly debugSql: boolean; - readonly debugWebSocket: boolean; - }; -}; -export type Config = typeof config; -export type AppEnv = typeof config.app.env; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/backend/dist/config/index.d.ts.map b/backend/dist/config/index.d.ts.map deleted file mode 100644 index dc8abf8..0000000 --- a/backend/dist/config/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"AAiEA,eAAO,MAAM,MAAM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+FT,CAAC;AAGX,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC;AACnC,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC"} \ No newline at end of file diff --git a/backend/dist/config/index.js b/backend/dist/config/index.js deleted file mode 100644 index 22c219b..0000000 --- a/backend/dist/config/index.js +++ /dev/null @@ -1,144 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.config = void 0; -const dotenv_1 = require("dotenv"); -const zod_1 = require("zod"); -// Load environment variables -(0, dotenv_1.config)(); -// Environment validation schema -const envSchema = zod_1.z.object({ - // Application - NODE_ENV: zod_1.z.enum(['development', 'test', 'production']).default('development'), - PORT: zod_1.z.union([zod_1.z.string().transform(Number), zod_1.z.number()]).default(3000), - HOST: zod_1.z.string().default('0.0.0.0'), - // Database - DATABASE_URL: zod_1.z.string(), - DB_POOL_MIN: zod_1.z.string().transform(Number).default('2'), - DB_POOL_MAX: zod_1.z.string().transform(Number).default('20'), - // Redis - REDIS_URL: zod_1.z.string().default('redis://localhost:6379'), - // JWT - JWT_SECRET: zod_1.z.string().min(32), - JWT_REFRESH_SECRET: zod_1.z.string().min(32), - JWT_EXPIRES_IN: zod_1.z.string().default('15m'), - JWT_REFRESH_EXPIRES_IN: zod_1.z.string().default('7d'), - // API - API_PREFIX: zod_1.z.string().default('/api'), - API_VERSION: zod_1.z.string().default('v1'), - CORS_ORIGIN: zod_1.z.string().default('*'), - RATE_LIMIT_MAX: zod_1.z.string().transform(Number).default('100'), - // Security - BCRYPT_ROUNDS: zod_1.z.string().transform(Number).default('12'), - MAX_LOGIN_ATTEMPTS: zod_1.z.string().transform(Number).default('5'), - LOCKOUT_DURATION: zod_1.z.string().default('15m'), - // Redis - REDIS_HOST: zod_1.z.string().default('localhost'), - REDIS_PORT: zod_1.z.coerce.number().default(6379), - REDIS_PASSWORD: zod_1.z.string().optional(), - REDIS_DB: zod_1.z.coerce.number().default(0), - REDIS_PUBSUB_DB: zod_1.z.coerce.number().default(1), - // Performance - CACHE_TTL_SHORT: zod_1.z.string().default('5m'), - CACHE_TTL_MEDIUM: zod_1.z.string().default('1h'), - CACHE_TTL_LONG: zod_1.z.string().default('24h'), - QUERY_TIMEOUT: zod_1.z.string().default('30s'), - REQUEST_TIMEOUT: zod_1.z.string().default('30s'), - // Logging - LOG_LEVEL: zod_1.z.enum(['error', 'warn', 'info', 'debug']).default('info'), - LOG_FORMAT: zod_1.z.enum(['json', 'pretty']).default('json'), - // Development - SEED_DATABASE: zod_1.z.string().transform(Boolean).default('false'), - DEBUG_SQL: zod_1.z.string().transform(Boolean).default('false'), - DEBUG_WEBSOCKET: zod_1.z.string().transform(Boolean).default('false'), -}); -// Validate and export configuration -const env = envSchema.parse(process.env); -exports.config = { - // Application settings - app: { - env: env.NODE_ENV, - port: env.PORT, - host: env.HOST, - isDevelopment: env.NODE_ENV === 'development', - isProduction: env.NODE_ENV === 'production', - isTest: env.NODE_ENV === 'test', - }, - // Database configuration - database: { - url: env.DATABASE_URL, - pool: { - min: env.DB_POOL_MIN, - max: env.DB_POOL_MAX, - }, - options: { - parseInputDatesAsUTC: true, - ssl: env.NODE_ENV === 'production', - }, - }, - // Redis configuration - redis: { - host: env.REDIS_HOST, - port: env.REDIS_PORT, - password: env.REDIS_PASSWORD, - db: env.REDIS_DB, - pubSubDb: env.REDIS_PUBSUB_DB, - options: { - retryDelayOnFailover: 100, - enableReadyCheck: false, - maxRetriesPerRequest: null, - lazyConnect: true, - }, - }, - // JWT configuration - jwt: { - secret: env.JWT_SECRET, - refreshSecret: env.JWT_REFRESH_SECRET, - expiresIn: env.JWT_EXPIRES_IN, - refreshExpiresIn: env.JWT_REFRESH_EXPIRES_IN, - }, - // API configuration - api: { - prefix: env.API_PREFIX, - version: env.API_VERSION, - cors: { - origin: env.CORS_ORIGIN.split(','), - credentials: true, - }, - rateLimit: { - max: env.RATE_LIMIT_MAX, - timeWindow: '15 minutes', - }, - }, - // Security configuration - security: { - bcryptRounds: env.BCRYPT_ROUNDS, - maxLoginAttempts: env.MAX_LOGIN_ATTEMPTS, - lockoutDuration: env.LOCKOUT_DURATION, - }, - // Performance configuration - performance: { - cache: { - ttl: { - short: env.CACHE_TTL_SHORT, - medium: env.CACHE_TTL_MEDIUM, - long: env.CACHE_TTL_LONG, - }, - }, - timeouts: { - query: env.QUERY_TIMEOUT, - request: env.REQUEST_TIMEOUT, - }, - }, - // Logging configuration - logging: { - level: env.LOG_LEVEL, - format: env.LOG_FORMAT, - }, - // Development configuration - development: { - seedDatabase: env.SEED_DATABASE, - debugSql: env.DEBUG_SQL, - debugWebSocket: env.DEBUG_WEBSOCKET, - }, -}; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/backend/dist/config/index.js.map b/backend/dist/config/index.js.map deleted file mode 100644 index 9a2c95e..0000000 --- a/backend/dist/config/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/config/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/config/redis.d.ts b/backend/dist/config/redis.d.ts deleted file mode 100644 index e95093b..0000000 --- a/backend/dist/config/redis.d.ts +++ /dev/null @@ -1,86 +0,0 @@ -import Redis from 'ioredis'; -/** - * Redis configuration and connection management - * Enterprise-grade caching with performance monitoring - */ -export interface CacheMetrics { - hits: number; - misses: number; - sets: number; - deletes: number; - errors: number; - totalOperations: number; - hitRate: number; -} -declare class RedisManager { - private client; - private subscriber; - private publisher; - private isConnected; - private reconnectAttempts; - private maxReconnectAttempts; - private metrics; - /** - * Initialize Redis connections - */ - initialize(): Promise; - /** - * Setup Redis event handlers - */ - private setupEventHandlers; - /** - * Get the main Redis client - */ - getClient(): Redis; - /** - * Get the subscriber client - */ - getSubscriber(): Redis; - /** - * Get the publisher client - */ - getPublisher(): Redis; - /** - * Check if Redis is connected - */ - isRedisConnected(): boolean; - /** - * Health check for Redis - */ - healthCheck(): Promise; - /** - * Get cache metrics - */ - getMetrics(): CacheMetrics; - /** - * Reset metrics - */ - resetMetrics(): void; - /** - * Start metrics collection - */ - private startMetricsCollection; - /** - * Increment hit counter - */ - incrementHits(): void; - /** - * Increment miss counter - */ - incrementMisses(): void; - /** - * Increment sets counter - */ - incrementSets(): void; - /** - * Increment deletes counter - */ - incrementDeletes(): void; - /** - * Close all Redis connections - */ - close(): Promise; -} -export declare const redisManager: RedisManager; -export default redisManager; -//# sourceMappingURL=redis.d.ts.map \ No newline at end of file diff --git a/backend/dist/config/redis.d.ts.map b/backend/dist/config/redis.d.ts.map deleted file mode 100644 index f00ec25..0000000 --- a/backend/dist/config/redis.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"redis.d.ts","sourceRoot":"","sources":["../../src/config/redis.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,SAAS,CAAC;AAK5B;;;GAGG;AAEH,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,cAAM,YAAY;IAChB,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,oBAAoB,CAAK;IACjC,OAAO,CAAC,OAAO,CAQb;IAEF;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAwDjC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IA4C1B;;OAEG;IACH,SAAS,IAAI,KAAK;IAOlB;;OAEG;IACH,aAAa,IAAI,KAAK;IAOtB;;OAEG;IACH,YAAY,IAAI,KAAK;IAOrB;;OAEG;IACH,gBAAgB,IAAI,OAAO;IAI3B;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAYrC;;OAEG;IACH,UAAU,IAAI,YAAY;IAS1B;;OAEG;IACH,YAAY,IAAI,IAAI;IAYpB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IA2B9B;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;OAEG;IACH,eAAe,IAAI,IAAI;IAIvB;;OAEG;IACH,aAAa,IAAI,IAAI;IAIrB;;OAEG;IACH,gBAAgB,IAAI,IAAI;IAIxB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;CAoB7B;AAGD,eAAO,MAAM,YAAY,cAAqB,CAAC;AAC/C,eAAe,YAAY,CAAC"} \ No newline at end of file diff --git a/backend/dist/config/redis.js b/backend/dist/config/redis.js deleted file mode 100644 index 9ab0e15..0000000 --- a/backend/dist/config/redis.js +++ /dev/null @@ -1,259 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.redisManager = void 0; -const ioredis_1 = __importDefault(require("ioredis")); -const index_1 = require("./index"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -class RedisManager { - client = null; - subscriber = null; - publisher = null; - isConnected = false; - reconnectAttempts = 0; - maxReconnectAttempts = 5; - metrics = { - hits: 0, - misses: 0, - sets: 0, - deletes: 0, - errors: 0, - totalOperations: 0, - hitRate: 0, - }; - /** - * Initialize Redis connections - */ - async initialize() { - try { - logger_1.logger.info('Initializing Redis connections...'); - const redisOptions = { - host: index_1.config.redis?.host || 'localhost', - port: index_1.config.redis?.port || 6379, - db: index_1.config.redis?.db || 0, - retryDelayOnFailover: 100, - maxRetriesPerRequest: 3, - connectTimeout: 5000, - lazyConnect: true, - keepAlive: 30000, - }; - if (typeof index_1.config.redis?.password === 'string') { - redisOptions.password = index_1.config.redis.password; - } - // Main client for general operations - this.client = new ioredis_1.default(redisOptions); - // Subscriber for pub/sub operations - this.subscriber = new ioredis_1.default({ - ...redisOptions, - db: index_1.config.redis?.pubSubDb || 1, - }); - // Publisher for pub/sub operations - this.publisher = new ioredis_1.default({ - ...redisOptions, - db: index_1.config.redis?.pubSubDb || 1, - }); - // Setup event handlers - this.setupEventHandlers(); - // Connect all clients - await Promise.all([ - this.client.connect(), - this.subscriber.connect(), - this.publisher.connect(), - ]); - this.isConnected = true; - this.reconnectAttempts = 0; - logger_1.logger.info('Redis initialized'); - // Start metrics collection - this.startMetricsCollection(); - } - catch (error) { - logger_1.logger.error({ error }, 'Failed to initialize Redis connections'); - throw new errors_1.CacheError('Redis initialization failed', { error }); - } - } - /** - * Setup Redis event handlers - */ - setupEventHandlers() { - if (!this.client) - return; - this.client.on('connect', () => { - logger_1.loggers.cache.info('Redis client connected'); - this.isConnected = true; - this.reconnectAttempts = 0; - }); - this.client.on('ready', () => { - logger_1.loggers.cache.info('Redis client ready for operations'); - }); - this.client.on('error', (error) => { - logger_1.loggers.cache.error({ error }, 'Redis client error'); - this.metrics.errors++; - this.isConnected = false; - }); - this.client.on('close', () => { - logger_1.loggers.cache.warn('Redis client connection closed'); - this.isConnected = false; - }); - this.client.on('reconnecting', () => { - this.reconnectAttempts++; - logger_1.loggers.cache.warn({ attempt: this.reconnectAttempts }, 'Redis client reconnecting'); - if (this.reconnectAttempts >= this.maxReconnectAttempts) { - logger_1.loggers.cache.error('Max reconnection attempts reached'); - this.client?.disconnect(); - } - }); - // Setup similar handlers for subscriber and publisher - this.subscriber?.on('error', (error) => { - logger_1.loggers.cache.error({ error, type: 'subscriber' }, 'Redis subscriber error'); - }); - this.publisher?.on('error', (error) => { - logger_1.loggers.cache.error({ error, type: 'publisher' }, 'Redis publisher error'); - }); - } - /** - * Get the main Redis client - */ - getClient() { - if (!this.client) { - throw new errors_1.CacheError('Redis client not initialized'); - } - return this.client; - } - /** - * Get the subscriber client - */ - getSubscriber() { - if (!this.subscriber) { - throw new errors_1.CacheError('Redis subscriber not initialized'); - } - return this.subscriber; - } - /** - * Get the publisher client - */ - getPublisher() { - if (!this.publisher) { - throw new errors_1.CacheError('Redis publisher not initialized'); - } - return this.publisher; - } - /** - * Check if Redis is connected - */ - isRedisConnected() { - return this.isConnected && this.client?.status === 'ready'; - } - /** - * Health check for Redis - */ - async healthCheck() { - try { - if (!this.client) - return false; - const result = await this.client.ping(); - return result === 'PONG'; - } - catch (error) { - logger_1.loggers.cache.error({ error }, 'Redis health check failed'); - return false; - } - } - /** - * Get cache metrics - */ - getMetrics() { - const totalOperations = this.metrics.hits + this.metrics.misses; - return { - ...this.metrics, - totalOperations, - hitRate: totalOperations > 0 ? (this.metrics.hits / totalOperations) * 100 : 0, - }; - } - /** - * Reset metrics - */ - resetMetrics() { - this.metrics = { - hits: 0, - misses: 0, - sets: 0, - deletes: 0, - errors: 0, - totalOperations: 0, - hitRate: 0, - }; - } - /** - * Start metrics collection - */ - startMetricsCollection() { - // Log metrics every 5 minutes - setInterval(() => { - const metrics = this.getMetrics(); - logger_1.loggers.cache.info({ - metrics, - isConnected: this.isConnected, - status: this.client?.status, - }, 'Redis cache metrics'); - // Log warnings for poor performance - if (metrics.hitRate < 80 && metrics.totalOperations > 100) { - logger_1.loggers.cache.warn({ hitRate: metrics.hitRate }, 'Low cache hit rate detected'); - } - if (metrics.errors > 10) { - logger_1.loggers.cache.warn({ errors: metrics.errors }, 'High error rate in Redis operations'); - } - }, 5 * 60 * 1000); - } - /** - * Increment hit counter - */ - incrementHits() { - this.metrics.hits++; - } - /** - * Increment miss counter - */ - incrementMisses() { - this.metrics.misses++; - } - /** - * Increment sets counter - */ - incrementSets() { - this.metrics.sets++; - } - /** - * Increment deletes counter - */ - incrementDeletes() { - this.metrics.deletes++; - } - /** - * Close all Redis connections - */ - async close() { - try { - logger_1.logger.info('Closing Redis connections...'); - await Promise.allSettled([ - this.client?.quit(), - this.subscriber?.quit(), - this.publisher?.quit(), - ]); - this.client = null; - this.subscriber = null; - this.publisher = null; - this.isConnected = false; - logger_1.logger.info('Redis connections closed successfully'); - } - catch (error) { - logger_1.logger.error({ error }, 'Error closing Redis connections'); - } - } -} -// Export singleton instance -exports.redisManager = new RedisManager(); -exports.default = exports.redisManager; -//# sourceMappingURL=redis.js.map \ No newline at end of file diff --git a/backend/dist/config/redis.js.map b/backend/dist/config/redis.js.map deleted file mode 100644 index 9278eea..0000000 --- a/backend/dist/config/redis.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"redis.js","sourceRoot":"","sources":["../../src/config/redis.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/ActivityRepository.d.ts b/backend/dist/db/ActivityRepository.d.ts deleted file mode 100644 index 380a3d2..0000000 --- a/backend/dist/db/ActivityRepository.d.ts +++ /dev/null @@ -1,135 +0,0 @@ -import { DatabaseClient } from '../config/database'; -import BaseRepository, { BaseEntity } from './BaseRepository'; -export interface Activity extends BaseEntity { - channel_id?: string; - task_id?: string; - user_id: string; - activity_type: 'message' | 'task_created' | 'task_updated' | 'task_completed' | 'task_assigned' | 'member_joined' | 'member_left' | 'file_uploaded' | 'channel_updated' | 'channel_created' | 'reaction_added' | 'mention' | 'voice_command' | 'ai_response'; - title: string; - description?: string; - metadata: Record; - read_by: string[]; - priority: 'low' | 'medium' | 'high'; - category: 'task' | 'channel' | 'system' | 'social'; - referenced_entity_id?: string; - referenced_entity_type?: 'task' | 'message' | 'channel' | 'user' | 'file'; -} -export interface CreateActivityData { - channelId?: string; - taskId?: string; - userId: string; - activityType: Activity['activity_type']; - title: string; - description?: string; - metadata?: Record; - priority?: Activity['priority']; - category?: Activity['category']; - referencedEntityId?: string; - referencedEntityType?: Activity['referenced_entity_type']; -} -export interface ActivityWithUser extends Activity { - user_name: string; - user_email: string; - user_avatar?: string; - user_role: string; - channel_name?: string; - task_title?: string; -} -declare class ActivityRepository extends BaseRepository { - constructor(); - /** - * Create new activity with validation - */ - findMany(filters: { - type?: string[]; - channel_id?: string; - user_id?: string; - from_date?: string; - to_date?: string; - limit?: number; - offset?: number; - includeUser?: boolean; - }): Promise; - count(filters: any): Promise; - getUserFeed(userId: string, limit: number, offset: number): Promise; - getUserFeedCount(userId: string): Promise; - getActivityStats(options: { - userId?: string; - period?: string; - channelId?: string; - }): Promise; - createActivity(activityData: CreateActivityData, client?: DatabaseClient): Promise; - /** - * Find activities for a specific user - */ - findUserActivities(userId: string, filters?: { - activityType?: string; - category?: string; - unreadOnly?: boolean; - channelId?: string; - taskId?: string; - after?: Date; - before?: Date; - }, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Find activities for a channel - */ - findChannelActivities(channelId: string, filters?: { - activityType?: string; - userId?: string; - after?: Date; - before?: Date; - }, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Get channel activity count - */ - getChannelActivityCount(channelId: string, filters?: { - activityType?: string; - userId?: string; - after?: Date; - before?: Date; - }, client?: DatabaseClient): Promise; - /** - * Get user activity count - */ - getUserActivityCount(userId: string, unreadOnly?: boolean, client?: DatabaseClient): Promise; - /** - * Mark activity as read by user - */ - markAsRead(activityId: string, userId: string, client?: DatabaseClient): Promise; - /** - * Mark multiple activities as read - */ - markMultipleAsRead(activityIds: string[], userId: string, client?: DatabaseClient): Promise; - /** - * Mark all user activities as read - */ - markAllAsRead(userId: string, client?: DatabaseClient): Promise; - /** - * Get activity statistics for a user - */ - getUserActivityStats(userId: string, daysSince?: number, client?: DatabaseClient): Promise<{ - totalActivities: number; - unreadActivities: number; - activitiesByType: Record; - activitiesByCategory: Record; - recentActivity: Array<{ - date: string; - count: number; - }>; - }>; - /** - * Determine priority based on activity type - */ - private determinePriority; - /** - * Determine category based on activity type - */ - private determineCategory; - /** - * Clean up old activities - */ - cleanupOldActivities(daysToKeep?: number, client?: DatabaseClient): Promise; -} -export default ActivityRepository; -//# sourceMappingURL=ActivityRepository.d.ts.map \ No newline at end of file diff --git a/backend/dist/db/ActivityRepository.d.ts.map b/backend/dist/db/ActivityRepository.d.ts.map deleted file mode 100644 index 80a1e50..0000000 --- a/backend/dist/db/ActivityRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ActivityRepository.d.ts","sourceRoot":"","sources":["../../src/db/ActivityRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/ActivityRepository.js b/backend/dist/db/ActivityRepository.js deleted file mode 100644 index b48ed5d..0000000 --- a/backend/dist/db/ActivityRepository.js +++ /dev/null @@ -1,620 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const logger_1 = require("../utils/logger"); -const BaseRepository_1 = __importDefault(require("./BaseRepository")); -class ActivityRepository extends BaseRepository_1.default { - constructor() { - super('activities'); - } - /** - * Create new activity with validation - */ - // Add methods needed for the API routes - async findMany(filters) { - let whereConditions = ['a.deleted_at IS NULL']; - let params = []; - let paramIndex = 1; - if (filters.type) { - whereConditions.push(`a.activity_type = ANY($${paramIndex})`); - params.push(filters.type); - paramIndex++; - } - if (filters.channel_id) { - whereConditions.push(`a.channel_id = $${paramIndex}`); - params.push(filters.channel_id); - paramIndex++; - } - if (filters.user_id) { - whereConditions.push(`a.user_id = $${paramIndex}`); - params.push(filters.user_id); - paramIndex++; - } - if (filters.from_date) { - whereConditions.push(`a.created_at >= $${paramIndex}`); - params.push(filters.from_date); - paramIndex++; - } - if (filters.to_date) { - whereConditions.push(`a.created_at <= $${paramIndex}`); - params.push(filters.to_date); - paramIndex++; - } - const userJoin = filters.includeUser ? ` - LEFT JOIN users u ON a.user_id = u.id AND u.deleted_at IS NULL - ` : ''; - const userFields = filters.includeUser ? ` - u.name as user_name, - u.email as user_email, - u.avatar_url, - u.role as user_role - ` : ''; - const sql = ` - SELECT - a.*, - ${userFields} - FROM ${this.tableName} a - ${userJoin} - WHERE ${whereConditions.join(' AND ')} - ORDER BY a.created_at DESC - ${filters.limit ? `LIMIT $${paramIndex}` : ''} - ${filters.offset && filters.limit ? `OFFSET $${paramIndex + 1}` : ''} - `; - if (filters.limit) { - params.push(filters.limit); - if (filters.offset) { - params.push(filters.offset); - } - } - const result = await this.executeRawQuery(sql, params); - return result.rows.map(row => ({ - ...row, - user: filters.includeUser && row.user_name ? { - id: row.user_id, - name: row.user_name, - email: row.user_email, - avatar_url: row.avatar_url, - } : undefined - })); - } - async count(filters) { - let whereConditions = ['deleted_at IS NULL']; - let params = []; - let paramIndex = 1; - if (filters.type) { - whereConditions.push(`activity_type = ANY($${paramIndex})`); - params.push(filters.type); - paramIndex++; - } - if (filters.channel_id) { - whereConditions.push(`channel_id = $${paramIndex}`); - params.push(filters.channel_id); - paramIndex++; - } - if (filters.user_id) { - whereConditions.push(`user_id = $${paramIndex}`); - params.push(filters.user_id); - paramIndex++; - } - const sql = ` - SELECT COUNT(*) as count - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - `; - const result = await this.executeRawQuery(sql, params); - return parseInt(result.rows[0]?.count || '0', 10); - } - async getUserFeed(userId, limit, offset) { - return this.findMany({ - limit, - offset, - includeUser: true, - }); - } - async getUserFeedCount(userId) { - return this.count({}); - } - async getActivityStats(options) { - return { - totalActivities: 0, - activitiesByType: {}, - activitiesByPeriod: {}, - }; - } - async createActivity(activityData, client) { - // Set defaults - const activityToCreate = { - channel_id: activityData.channelId, - task_id: activityData.taskId, - user_id: activityData.userId, - activity_type: activityData.activityType, - title: activityData.title, - description: activityData.description || '', - metadata: activityData.metadata || {}, - read_by: [], - priority: activityData.priority || this.determinePriority(activityData.activityType), - category: activityData.category || this.determineCategory(activityData.activityType), - referenced_entity_id: activityData.referencedEntityId, - referenced_entity_type: activityData.referencedEntityType, - }; - const activity = await this.create(activityToCreate, client); - logger_1.logger.info({ - activityId: activity.id, - activityType: activity.activity_type, - userId: activity.user_id, - channelId: activity.channel_id, - taskId: activity.task_id, - }, 'Activity created successfully'); - return activity; - } - /** - * Find activities for a specific user - */ - async findUserActivities(userId, filters, limit = 50, offset = 0, client) { - let whereConditions = ['a.deleted_at IS NULL']; - let params = []; - let paramIndex = 1; - // User should see activities where: - // 1. They are the actor (created the activity) - // 2. They are mentioned/assigned in tasks - // 3. Activities in channels they are members of - // 4. Activities on tasks they are assigned to - whereConditions.push(`( - a.user_id = $${paramIndex} - OR a.metadata::text LIKE '%"' || $${paramIndex} || '"%' - OR EXISTS ( - SELECT 1 FROM channels c - WHERE c.id = a.channel_id - AND $${paramIndex} = ANY(c.members) - AND c.deleted_at IS NULL - ) - OR EXISTS ( - SELECT 1 FROM tasks t - WHERE t.id = a.task_id - AND $${paramIndex} = ANY(t.assigned_to) - AND t.deleted_at IS NULL - ) - )`); - params.push(userId); - paramIndex++; - // Add filters - if (filters?.activityType) { - whereConditions.push(`a.activity_type = $${paramIndex}`); - params.push(filters.activityType); - paramIndex++; - } - if (filters?.category) { - whereConditions.push(`a.category = $${paramIndex}`); - params.push(filters.category); - paramIndex++; - } - if (filters?.channelId) { - whereConditions.push(`a.channel_id = $${paramIndex}`); - params.push(filters.channelId); - paramIndex++; - } - if (filters?.taskId) { - whereConditions.push(`a.task_id = $${paramIndex}`); - params.push(filters.taskId); - paramIndex++; - } - if (filters?.unreadOnly) { - whereConditions.push(`NOT ($${paramIndex - params.length + 1} = ANY(a.read_by))`); - } - if (filters?.after) { - whereConditions.push(`a.created_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - if (filters?.before) { - whereConditions.push(`a.created_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - const sql = ` - SELECT - a.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - c.name as channel_name, - t.title as task_title - FROM ${this.tableName} a - LEFT JOIN users u ON a.user_id = u.id - LEFT JOIN channels c ON a.channel_id = c.id - LEFT JOIN tasks t ON a.task_id = t.id - WHERE ${whereConditions.join(' AND ')} - ORDER BY a.created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - params.push(limit, offset); - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Find activities for a channel - */ - async findChannelActivities(channelId, filters, limit = 50, offset = 0, client) { - let whereConditions = ['a.channel_id = $1', 'a.deleted_at IS NULL']; - let params = [channelId]; - let paramIndex = 2; - // Add filters - if (filters?.activityType) { - whereConditions.push(`a.activity_type = $${paramIndex}`); - params.push(filters.activityType); - paramIndex++; - } - if (filters?.userId) { - whereConditions.push(`a.user_id = $${paramIndex}`); - params.push(filters.userId); - paramIndex++; - } - if (filters?.after) { - whereConditions.push(`a.created_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - if (filters?.before) { - whereConditions.push(`a.created_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - const sql = ` - SELECT - a.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - c.name as channel_name, - t.title as task_title - FROM ${this.tableName} a - LEFT JOIN users u ON a.user_id = u.id - LEFT JOIN channels c ON a.channel_id = c.id - LEFT JOIN tasks t ON a.task_id = t.id - WHERE ${whereConditions.join(' AND ')} - ORDER BY a.created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - params.push(limit, offset); - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Get channel activity count - */ - async getChannelActivityCount(channelId, filters, client) { - let whereConditions = ['channel_id = $1', 'deleted_at IS NULL']; - let params = [channelId]; - let paramIndex = 2; - // Add filters - if (filters?.activityType) { - whereConditions.push(`activity_type = $${paramIndex}`); - params.push(filters.activityType); - paramIndex++; - } - if (filters?.userId) { - whereConditions.push(`user_id = $${paramIndex}`); - params.push(filters.userId); - paramIndex++; - } - if (filters?.after) { - whereConditions.push(`created_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - if (filters?.before) { - whereConditions.push(`created_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - const sql = ` - SELECT COUNT(*) as count - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - `; - const result = await this.executeRawQuery(sql, params, client); - return parseInt(result.rows[0]?.count || '0', 10); - } - /** - * Get user activity count - */ - async getUserActivityCount(userId, unreadOnly = false, client) { - let whereConditions = ['deleted_at IS NULL']; - let params = []; - let paramIndex = 1; - // User should see activities where they are involved - whereConditions.push(`( - user_id = $${paramIndex} - OR metadata::text LIKE '%"' || $${paramIndex} || '"%' - OR EXISTS ( - SELECT 1 FROM channels c - WHERE c.id = channel_id - AND $${paramIndex} = ANY(c.members) - AND c.deleted_at IS NULL - ) - OR EXISTS ( - SELECT 1 FROM tasks t - WHERE t.id = task_id - AND $${paramIndex} = ANY(t.assigned_to) - AND t.deleted_at IS NULL - ) - )`); - params.push(userId); - paramIndex++; - if (unreadOnly) { - whereConditions.push(`NOT ($${paramIndex - 1} = ANY(read_by))`); - } - const sql = ` - SELECT COUNT(*) as count - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - `; - const result = await this.executeRawQuery(sql, params, client); - return parseInt(result.rows[0]?.count || '0', 10); - } - /** - * Mark activity as read by user - */ - async markAsRead(activityId, userId, client) { - const sql = ` - UPDATE ${this.tableName} - SET read_by = CASE - WHEN $2 = ANY(read_by) THEN read_by - ELSE array_append(read_by, $2) - END - WHERE id = $1 AND deleted_at IS NULL - RETURNING id - `; - try { - const result = await this.executeRawQuery(sql, [activityId, userId], client); - if (result.rowCount > 0) { - logger_1.logger.debug({ - activityId, - userId, - }, 'Activity marked as read'); - return true; - } - return false; - } - catch (error) { - logger_1.logger.error({ error, activityId, userId }, 'Failed to mark activity as read'); - return false; - } - } - /** - * Mark multiple activities as read - */ - async markMultipleAsRead(activityIds, userId, client) { - if (activityIds.length === 0) - return 0; - const placeholders = activityIds.map((_, index) => `$${index + 2}`).join(','); - const sql = ` - UPDATE ${this.tableName} - SET read_by = CASE - WHEN $1 = ANY(read_by) THEN read_by - ELSE array_append(read_by, $1) - END - WHERE id = ANY(ARRAY[${placeholders}]) AND deleted_at IS NULL - `; - try { - const result = await this.executeRawQuery(sql, [userId, ...activityIds], client); - logger_1.logger.info({ - userId, - markedCount: result.rowCount, - totalIds: activityIds.length, - }, 'Multiple activities marked as read'); - return result.rowCount; - } - catch (error) { - logger_1.logger.error({ error, userId, activityIds }, 'Failed to mark multiple activities as read'); - return 0; - } - } - /** - * Mark all user activities as read - */ - async markAllAsRead(userId, client) { - const sql = ` - UPDATE ${this.tableName} - SET read_by = CASE - WHEN $1 = ANY(read_by) THEN read_by - ELSE array_append(read_by, $1) - END - WHERE deleted_at IS NULL - AND NOT ($1 = ANY(read_by)) - AND ( - user_id = $1 - OR metadata::text LIKE '%"' || $1 || '"%' - OR EXISTS ( - SELECT 1 FROM channels c - WHERE c.id = channel_id - AND $1 = ANY(c.members) - AND c.deleted_at IS NULL - ) - OR EXISTS ( - SELECT 1 FROM tasks t - WHERE t.id = task_id - AND $1 = ANY(t.assigned_to) - AND t.deleted_at IS NULL - ) - ) - `; - try { - const result = await this.executeRawQuery(sql, [userId], client); - logger_1.logger.info({ - userId, - markedCount: result.rowCount, - }, 'All user activities marked as read'); - return result.rowCount; - } - catch (error) { - logger_1.logger.error({ error, userId }, 'Failed to mark all activities as read'); - return 0; - } - } - /** - * Get activity statistics for a user - */ - async getUserActivityStats(userId, daysSince = 30, client) { - const statsSql = ` - WITH user_activities AS ( - SELECT * - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND created_at >= NOW() - INTERVAL '${daysSince} days' - AND ( - user_id = $1 - OR metadata::text LIKE '%"' || $1 || '"%' - OR EXISTS ( - SELECT 1 FROM channels c - WHERE c.id = channel_id - AND $1 = ANY(c.members) - AND c.deleted_at IS NULL - ) - OR EXISTS ( - SELECT 1 FROM tasks t - WHERE t.id = task_id - AND $1 = ANY(t.assigned_to) - AND t.deleted_at IS NULL - ) - ) - ) - SELECT - COUNT(*) as total_activities, - COUNT(*) FILTER (WHERE NOT ($1 = ANY(read_by))) as unread_activities, - COUNT(*) FILTER (WHERE activity_type = 'message') as message_activities, - COUNT(*) FILTER (WHERE activity_type = 'task_created') as task_created_activities, - COUNT(*) FILTER (WHERE activity_type = 'task_updated') as task_updated_activities, - COUNT(*) FILTER (WHERE activity_type = 'task_completed') as task_completed_activities, - COUNT(*) FILTER (WHERE category = 'task') as task_category, - COUNT(*) FILTER (WHERE category = 'channel') as channel_category, - COUNT(*) FILTER (WHERE category = 'system') as system_category, - COUNT(*) FILTER (WHERE category = 'social') as social_category - FROM user_activities - `; - const recentActivitySql = ` - SELECT - DATE(created_at) as date, - COUNT(*) as count - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND created_at >= NOW() - INTERVAL '${daysSince} days' - AND ( - user_id = $1 - OR metadata::text LIKE '%"' || $1 || '"%' - OR EXISTS ( - SELECT 1 FROM channels c - WHERE c.id = channel_id - AND $1 = ANY(c.members) - AND c.deleted_at IS NULL - ) - OR EXISTS ( - SELECT 1 FROM tasks t - WHERE t.id = task_id - AND $1 = ANY(t.assigned_to) - AND t.deleted_at IS NULL - ) - ) - GROUP BY DATE(created_at) - ORDER BY date DESC - `; - const [statsResult, recentActivityResult] = await Promise.all([ - this.executeRawQuery(statsSql, [userId], client), - this.executeRawQuery(recentActivitySql, [userId], client), - ]); - const stats = statsResult.rows[0]; - return { - totalActivities: parseInt(stats.total_activities, 10), - unreadActivities: parseInt(stats.unread_activities, 10), - activitiesByType: { - message: parseInt(stats.message_activities, 10), - task_created: parseInt(stats.task_created_activities, 10), - task_updated: parseInt(stats.task_updated_activities, 10), - task_completed: parseInt(stats.task_completed_activities, 10), - }, - activitiesByCategory: { - task: parseInt(stats.task_category, 10), - channel: parseInt(stats.channel_category, 10), - system: parseInt(stats.system_category, 10), - social: parseInt(stats.social_category, 10), - }, - recentActivity: recentActivityResult.rows.map((row) => ({ - date: row.date, - count: parseInt(row.count, 10), - })), - }; - } - /** - * Determine priority based on activity type - */ - determinePriority(activityType) { - switch (activityType) { - case 'task_created': - case 'task_assigned': - return 'medium'; - case 'task_completed': - return 'low'; - case 'mention': - return 'high'; - case 'voice_command': - return 'high'; - default: - return 'low'; - } - } - /** - * Determine category based on activity type - */ - determineCategory(activityType) { - switch (activityType) { - case 'task_created': - case 'task_updated': - case 'task_completed': - case 'task_assigned': - return 'task'; - case 'message': - case 'member_joined': - case 'member_left': - case 'channel_updated': - case 'channel_created': - return 'channel'; - case 'mention': - case 'reaction_added': - return 'social'; - case 'file_uploaded': - case 'voice_command': - case 'ai_response': - return 'system'; - default: - return 'system'; - } - } - /** - * Clean up old activities - */ - async cleanupOldActivities(daysToKeep = 90, client) { - const sql = ` - DELETE FROM ${this.tableName} - WHERE created_at < NOW() - INTERVAL '${daysToKeep} days' - AND category != 'task' -- Keep task-related activities longer - `; - try { - const result = await this.executeRawQuery(sql, [], client); - logger_1.logger.info({ - deletedCount: result.rowCount, - daysToKeep, - }, 'Old activities cleaned up'); - return result.rowCount; - } - catch (error) { - logger_1.logger.error({ error, daysToKeep }, 'Failed to clean up old activities'); - return 0; - } - } -} -exports.default = ActivityRepository; -//# sourceMappingURL=ActivityRepository.js.map \ No newline at end of file diff --git a/backend/dist/db/ActivityRepository.js.map b/backend/dist/db/ActivityRepository.js.map deleted file mode 100644 index 8fa562e..0000000 --- a/backend/dist/db/ActivityRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ActivityRepository.js","sourceRoot":"","sources":["../../src/db/ActivityRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/AnnouncementRepository.d.ts b/backend/dist/db/AnnouncementRepository.d.ts deleted file mode 100644 index 1a8f742..0000000 --- a/backend/dist/db/AnnouncementRepository.d.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import { BaseRepository, BaseEntity } from './BaseRepository'; -export interface Announcement extends BaseEntity { - id: string; - title: string; - content: string; - type: 'info' | 'warning' | 'success' | 'error' | 'feature' | 'maintenance'; - priority: 'low' | 'medium' | 'high' | 'critical'; - target_audience: 'all' | 'admins' | 'developers' | 'designers' | 'managers'; - scheduled_for?: Date | null; - expires_at?: Date | null; - action_button_text?: string | null; - action_button_url?: string | null; - image_url?: string | null; - created_by: string; - published: boolean; - read_by: string[]; -} -export interface CreateAnnouncementData { - title: string; - content: string; - type: Announcement['type']; - priority: Announcement['priority']; - target_audience: Announcement['target_audience']; - scheduled_for?: Date | undefined; - expires_at?: Date | undefined; - action_button_text?: string | undefined; - action_button_url?: string | undefined; - image_url?: string | undefined; - created_by: string; - published?: boolean | undefined; -} -export interface UpdateAnnouncementData { - title?: string | undefined; - content?: string | undefined; - type?: Announcement['type'] | undefined; - priority?: Announcement['priority'] | undefined; - target_audience?: Announcement['target_audience'] | undefined; - scheduled_for?: Date | null | undefined; - expires_at?: Date | null | undefined; - action_button_text?: string | null | undefined; - action_button_url?: string | null | undefined; - image_url?: string | null | undefined; - published?: boolean | undefined; -} -export interface AnnouncementFilter { - type?: Announcement['type'][] | undefined; - priority?: Announcement['priority'][] | undefined; - target_audience?: Announcement['target_audience'][] | undefined; - published?: boolean | undefined; - created_by?: string[] | undefined; - date_from?: Date | undefined; - date_to?: Date | undefined; -} -export default class AnnouncementRepository extends BaseRepository { - constructor(); - create(data: CreateAnnouncementData, client?: DatabaseClient): Promise; - update(id: string, data: UpdateAnnouncementData, client?: DatabaseClient): Promise; - findForUser(userId: string, userRole: string, includeRead?: boolean, client?: DatabaseClient): Promise; - markAsRead(announcementId: string, userId: string, client?: DatabaseClient): Promise; - findWithFilter(filter: AnnouncementFilter, limit?: number, offset?: number, client?: DatabaseClient): Promise<{ - data: Announcement[]; - total: number; - }>; - getStats(client?: DatabaseClient): Promise<{ - total: number; - published: number; - scheduled: number; - expired: number; - byType: Record; - byPriority: Record; - byAudience: Record; - }>; - private mapRoleToAudience; - private transformRow; -} -//# sourceMappingURL=AnnouncementRepository.d.ts.map \ No newline at end of file diff --git a/backend/dist/db/AnnouncementRepository.d.ts.map b/backend/dist/db/AnnouncementRepository.d.ts.map deleted file mode 100644 index a1e7a55..0000000 --- a/backend/dist/db/AnnouncementRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"AnnouncementRepository.d.ts","sourceRoot":"","sources":["../../src/db/AnnouncementRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/AnnouncementRepository.js b/backend/dist/db/AnnouncementRepository.js deleted file mode 100644 index 1b29fb3..0000000 --- a/backend/dist/db/AnnouncementRepository.js +++ /dev/null @@ -1,314 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -const BaseRepository_1 = require("./BaseRepository"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -class AnnouncementRepository extends BaseRepository_1.BaseRepository { - constructor() { - super('announcements'); - this.selectFields = [ - 'id', 'title', 'content', 'type', 'priority', 'target_audience', - 'scheduled_for', 'expires_at', 'action_button_text', 'action_button_url', - 'image_url', 'created_by', 'published', 'read_by', 'created_at', - 'updated_at', 'version', 'deleted_at', 'deleted_by' - ]; - } - async create(data, client) { - const now = new Date(); - // Validation - if (!data.title?.trim()) { - throw new errors_1.ValidationError('Title is required'); - } - if (!data.content?.trim()) { - throw new errors_1.ValidationError('Content is required'); - } - if (!data.created_by) { - throw new errors_1.ValidationError('Created by user ID is required'); - } - const announcementData = { - ...data, - published: data.published ?? false, - read_by: [], - created_at: now, - updated_at: now, - version: 1 - }; - const query = ` - INSERT INTO ${this.tableName} ( - title, content, type, priority, target_audience, scheduled_for, - expires_at, action_button_text, action_button_url, image_url, - created_by, published, read_by, created_at, updated_at, version - ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16 - ) RETURNING * - `; - const values = [ - announcementData.title, - announcementData.content, - announcementData.type, - announcementData.priority, - announcementData.target_audience, - announcementData.scheduled_for || null, - announcementData.expires_at || null, - announcementData.action_button_text || null, - announcementData.action_button_url || null, - announcementData.image_url || null, - announcementData.created_by, - announcementData.published, - JSON.stringify(announcementData.read_by), - announcementData.created_at, - announcementData.updated_at, - announcementData.version - ]; - try { - const result = await this.query(query, values, client); - logger_1.logger.info(`Created announcement: ${result.rows[0].id}`); - return this.transformRow(result.rows[0]); - } - catch (error) { - logger_1.logger.error('Failed to create announcement:', error); - throw error; - } - } - async update(id, data, client) { - const existing = await this.findById(id, client); - if (!existing) { - throw new errors_1.NotFoundError(`Announcement with ID ${id} not found`); - } - const now = new Date(); - const updateFields = []; - const updateValues = []; - let paramIndex = 1; - // Build dynamic update query - Object.entries(data).forEach(([key, value]) => { - if (value !== undefined) { - updateFields.push(`${key} = $${paramIndex}`); - updateValues.push(value); - paramIndex++; - } - }); - if (updateFields.length === 0) { - return existing; - } - // Add updated_at and version - updateFields.push(`updated_at = $${paramIndex}`); - updateValues.push(now); - paramIndex++; - updateFields.push(`version = $${paramIndex}`); - updateValues.push(existing.version + 1); - paramIndex++; - // Add WHERE clause - updateValues.push(id); - updateValues.push(existing.version); - const query = ` - UPDATE ${this.tableName} - SET ${updateFields.join(', ')} - WHERE id = $${paramIndex - 1} AND version = $${paramIndex} AND deleted_at IS NULL - RETURNING * - `; - try { - const result = await this.query(query, updateValues, client); - if (result.rows.length === 0) { - throw new Error('Concurrent update detected - announcement was modified by another process'); - } - logger_1.logger.info(`Updated announcement: ${id}`); - return this.transformRow(result.rows[0]); - } - catch (error) { - logger_1.logger.error(`Failed to update announcement ${id}:`, error); - throw error; - } - } - async findForUser(userId, userRole, includeRead = true, client) { - let whereConditions = ['deleted_at IS NULL', 'published = true']; - const queryParams = []; - let paramIndex = 1; - // Filter by expiration - whereConditions.push('(expires_at IS NULL OR expires_at > NOW())'); - // Filter by schedule - whereConditions.push('(scheduled_for IS NULL OR scheduled_for <= NOW())'); - // Filter by target audience - if (userRole !== 'ceo') { - whereConditions.push(`(target_audience = 'all' OR target_audience = $${paramIndex})`); - queryParams.push(this.mapRoleToAudience(userRole)); - paramIndex++; - } - // Filter by read status if requested - if (!includeRead) { - whereConditions.push(`NOT (read_by ? $${paramIndex})`); - queryParams.push(userId); - paramIndex++; - } - const query = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - ORDER BY - CASE priority - WHEN 'critical' THEN 1 - WHEN 'high' THEN 2 - WHEN 'medium' THEN 3 - WHEN 'low' THEN 4 - END, - created_at DESC - `; - try { - const result = await this.query(query, queryParams, client); - return result.rows.map(row => this.transformRow(row)); - } - catch (error) { - logger_1.logger.error(`Failed to find announcements for user ${userId}:`, error); - throw error; - } - } - async markAsRead(announcementId, userId, client) { - const query = ` - UPDATE ${this.tableName} - SET read_by = read_by || $1::jsonb, - updated_at = NOW(), - version = version + 1 - WHERE id = $2 - AND deleted_at IS NULL - AND NOT (read_by ? $3) - `; - const values = [JSON.stringify([userId]), announcementId, userId]; - try { - await this.query(query, values, client); - logger_1.logger.info(`Marked announcement ${announcementId} as read by user ${userId}`); - } - catch (error) { - logger_1.logger.error(`Failed to mark announcement ${announcementId} as read:`, error); - throw error; - } - } - async findWithFilter(filter, limit = 50, offset = 0, client) { - const whereConditions = ['deleted_at IS NULL']; - const queryParams = []; - let paramIndex = 1; - // Apply filters - if (filter.type && filter.type.length > 0) { - whereConditions.push(`type = ANY($${paramIndex})`); - queryParams.push(filter.type); - paramIndex++; - } - if (filter.priority && filter.priority.length > 0) { - whereConditions.push(`priority = ANY($${paramIndex})`); - queryParams.push(filter.priority); - paramIndex++; - } - if (filter.target_audience && filter.target_audience.length > 0) { - whereConditions.push(`target_audience = ANY($${paramIndex})`); - queryParams.push(filter.target_audience); - paramIndex++; - } - if (filter.published !== undefined) { - whereConditions.push(`published = $${paramIndex}`); - queryParams.push(filter.published); - paramIndex++; - } - if (filter.created_by && filter.created_by.length > 0) { - whereConditions.push(`created_by = ANY($${paramIndex})`); - queryParams.push(filter.created_by); - paramIndex++; - } - if (filter.date_from) { - whereConditions.push(`created_at >= $${paramIndex}`); - queryParams.push(filter.date_from); - paramIndex++; - } - if (filter.date_to) { - whereConditions.push(`created_at <= $${paramIndex}`); - queryParams.push(filter.date_to); - paramIndex++; - } - const whereClause = whereConditions.join(' AND '); - // Count query - const countQuery = `SELECT COUNT(*) FROM ${this.tableName} WHERE ${whereClause}`; - const countResult = await this.query(countQuery, queryParams, client); - const total = parseInt(countResult.rows[0].count); - // Data query - queryParams.push(limit, offset); - const dataQuery = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ${whereClause} - ORDER BY created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - const dataResult = await this.query(dataQuery, queryParams, client); - const data = dataResult.rows.map(row => this.transformRow(row)); - return { data, total }; - } - async getStats(client) { - const query = ` - SELECT - COUNT(*) as total, - COUNT(CASE WHEN published = true THEN 1 END) as published, - COUNT(CASE WHEN scheduled_for > NOW() THEN 1 END) as scheduled, - COUNT(CASE WHEN expires_at < NOW() THEN 1 END) as expired, - jsonb_object_agg(type, type_count) as by_type, - jsonb_object_agg(priority, priority_count) as by_priority, - jsonb_object_agg(target_audience, audience_count) as by_audience - FROM ${this.tableName} - CROSS JOIN LATERAL ( - SELECT type, COUNT(*) as type_count - FROM ${this.tableName} t2 - WHERE t2.type = ${this.tableName}.type AND deleted_at IS NULL - GROUP BY type - ) type_stats - CROSS JOIN LATERAL ( - SELECT priority, COUNT(*) as priority_count - FROM ${this.tableName} t3 - WHERE t3.priority = ${this.tableName}.priority AND deleted_at IS NULL - GROUP BY priority - ) priority_stats - CROSS JOIN LATERAL ( - SELECT target_audience, COUNT(*) as audience_count - FROM ${this.tableName} t4 - WHERE t4.target_audience = ${this.tableName}.target_audience AND deleted_at IS NULL - GROUP BY target_audience - ) audience_stats - WHERE deleted_at IS NULL - `; - try { - const result = await this.query(query, [], client); - const row = result.rows[0]; - return { - total: parseInt(row.total) || 0, - published: parseInt(row.published) || 0, - scheduled: parseInt(row.scheduled) || 0, - expired: parseInt(row.expired) || 0, - byType: row.by_type || {}, - byPriority: row.by_priority || {}, - byAudience: row.by_audience || {}, - }; - } - catch (error) { - logger_1.logger.error('Failed to get announcement stats:', error); - throw error; - } - } - mapRoleToAudience(role) { - const roleMapping = { - 'admin': 'admins', - 'developer': 'developers', - 'designer': 'designers', - 'manager': 'managers', - 'staff': 'all' - }; - return roleMapping[role] || 'all'; - } - transformRow(row) { - return { - ...row, - read_by: Array.isArray(row.read_by) ? row.read_by : (row.read_by ? JSON.parse(row.read_by) : []), - created_at: new Date(row.created_at), - updated_at: new Date(row.updated_at), - scheduled_for: row.scheduled_for ? new Date(row.scheduled_for) : null, - expires_at: row.expires_at ? new Date(row.expires_at) : null, - deleted_at: row.deleted_at ? new Date(row.deleted_at) : null, - }; - } -} -exports.default = AnnouncementRepository; -//# sourceMappingURL=AnnouncementRepository.js.map \ No newline at end of file diff --git a/backend/dist/db/AnnouncementRepository.js.map b/backend/dist/db/AnnouncementRepository.js.map deleted file mode 100644 index f3a37f3..0000000 --- a/backend/dist/db/AnnouncementRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"AnnouncementRepository.js","sourceRoot":"","sources":["../../src/db/AnnouncementRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/BaseRepository.d.ts b/backend/dist/db/BaseRepository.d.ts deleted file mode 100644 index 94c903d..0000000 --- a/backend/dist/db/BaseRepository.d.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { PoolClient } from 'pg'; -import { DatabaseClient } from '@config/database'; -/** - * Base Repository class providing common CRUD operations - * Implements enterprise patterns: soft delete, audit trail, optimistic locking - */ -export interface BaseEntity { - id: string; - created_at: Date; - updated_at: Date; - version: number; - deleted_at?: Date | null; - deleted_by?: string | null; -} -export interface FilterOptions { - limit?: number; - offset?: number; - orderBy?: string; - orderDirection?: 'ASC' | 'DESC'; - includeDeleted?: boolean; - filters?: Record; -} -export interface PaginatedResult { - data: T[]; - total: number; - limit: number; - offset: number; - hasMore: boolean; -} -export declare abstract class BaseRepository { - protected tableName: string; - protected primaryKey: string; - protected selectFields: string[]; - constructor(tableName: string); - /** - * Create a new entity - */ - create(data: Partial>, client?: DatabaseClient): Promise; - /** - * Find entity by ID - */ - findById(id: string, includeDeleted?: boolean, client?: DatabaseClient): Promise; - /** - * Find entity by ID or throw NotFoundError - */ - findByIdOrThrow(id: string, includeDeleted?: boolean, client?: DatabaseClient): Promise; - /** - * Update entity by ID with optimistic locking - */ - update(id: string, data: Partial>, expectedVersion?: number, client?: DatabaseClient): Promise; - /** - * Soft delete entity - */ - softDelete(id: string, deletedBy: string, client?: DatabaseClient): Promise; - /** - * Restore soft deleted entity - */ - restore(id: string, client?: DatabaseClient): Promise; - /** - * Hard delete entity (use with caution) - */ - hardDelete(id: string, client?: DatabaseClient): Promise; - /** - * Find multiple entities with filtering and pagination - */ - findMany(options?: FilterOptions, client?: DatabaseClient): Promise>; - /** - * Count entities with optional filters - */ - count(filters?: Record, includeDeleted?: boolean, client?: DatabaseClient): Promise; - /** - * Check if entity exists - */ - exists(id: string, includeDeleted?: boolean, client?: DatabaseClient): Promise; - /** - * Execute raw SQL query within repository context - */ - protected executeRawQuery(sql: string, params?: any[], client?: DatabaseClient): Promise<{ - rows: R[]; - rowCount: number; - }>; - /** - * Execute multiple operations in a transaction - */ - executeInTransaction(callback: (client: PoolClient) => Promise): Promise; - /** - * Bulk insert entities (for seeding and imports) - */ - bulkCreate(entities: Partial>[], client?: DatabaseClient): Promise; - /** - * Get repository statistics - */ - getStats(client?: DatabaseClient): Promise<{ - total: number; - active: number; - deleted: number; - createdToday: number; - updatedToday: number; - }>; - /** - * Direct query method for complex database operations - */ - query(sql: string, params?: any[], client?: DatabaseClient): Promise<{ - rows: R[]; - rowCount: number; - }>; -} -export default BaseRepository; -//# sourceMappingURL=BaseRepository.d.ts.map \ No newline at end of file diff --git a/backend/dist/db/BaseRepository.d.ts.map b/backend/dist/db/BaseRepository.d.ts.map deleted file mode 100644 index ae16590..0000000 --- a/backend/dist/db/BaseRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"BaseRepository.d.ts","sourceRoot":"","sources":["../../src/db/BaseRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/BaseRepository.js b/backend/dist/db/BaseRepository.js deleted file mode 100644 index 77a43e8..0000000 --- a/backend/dist/db/BaseRepository.js +++ /dev/null @@ -1,368 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.BaseRepository = void 0; -const database_1 = require("@config/database"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -class BaseRepository { - tableName; - primaryKey = 'id'; - selectFields = ['*']; - constructor(tableName) { - this.tableName = tableName; - } - /** - * Create a new entity - */ - async create(data, client) { - const fields = Object.keys(data); - const values = Object.values(data); - const placeholders = fields.map((_, index) => `$${index + 1}`).join(', '); - const sql = ` - INSERT INTO ${this.tableName} (${fields.join(', ')}) - VALUES (${placeholders}) - RETURNING ${this.selectFields.join(', ')} - `; - return logger_1.performanceLogger.trackAsyncOperation(async () => { - const result = await (0, database_1.query)(sql, values, client); - if (result.rows.length === 0 || result.rows[0] === undefined) { - throw new errors_1.DatabaseError('Failed to create entity - no rows returned'); - } - logger_1.logger.debug({ - table: this.tableName, - operation: 'create', - entityId: result.rows[0].id, - }, 'Entity created successfully'); - // Ensure result.rows[0] is not undefined before returning - if (result.rows[0] === undefined) { - throw new errors_1.DatabaseError('Failed to create entity - undefined row returned'); - } - return result.rows[0]; - }, `repository-create-${this.tableName}`, { entityType: this.tableName }); - } - /** - * Find entity by ID - */ - async findById(id, includeDeleted = false, client) { - const deletedCondition = includeDeleted ? '' : 'AND deleted_at IS NULL'; - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ${this.primaryKey} = $1 ${deletedCondition} - `; - return logger_1.performanceLogger.trackAsyncOperation(async () => { - const result = await (0, database_1.query)(sql, [id], client); - return result.rows[0] || null; - }, `repository-findById-${this.tableName}`, { entityId: id }); - } - /** - * Find entity by ID or throw NotFoundError - */ - async findByIdOrThrow(id, includeDeleted = false, client) { - const entity = await this.findById(id, includeDeleted, client); - if (!entity) { - throw new errors_1.NotFoundError(`${this.tableName} with id ${id} not found`); - } - return entity; - } - /** - * Update entity by ID with optimistic locking - */ - async update(id, data, expectedVersion, client) { - // Exclude audit fields from update data - const { created_at, updated_at, version, deleted_at, deleted_by, ...updateData } = data; - const fields = Object.keys(updateData); - const values = Object.values(updateData); - if (fields.length === 0) { - throw new errors_1.ValidationError('No fields provided for update', []); - } - const setClause = fields.map((field, index) => `${field} = $${index + 2}`).join(', '); - const versionCondition = expectedVersion ? 'AND version = $' + (fields.length + 3) : ''; - const versionParams = expectedVersion ? [expectedVersion] : []; - const sql = ` - UPDATE ${this.tableName} - SET ${setClause} - WHERE ${this.primaryKey} = $1 - AND deleted_at IS NULL - ${versionCondition} - RETURNING ${this.selectFields.join(', ')} - `; - return logger_1.performanceLogger.trackAsyncOperation(async () => { - const result = await (0, database_1.query)(sql, [id, ...values, ...versionParams], client); - if (result.rows.length === 0) { - if (expectedVersion) { - throw new errors_1.ValidationError('Optimistic locking failure - entity was modified by another transaction', []); - } - throw new errors_1.NotFoundError(`${this.tableName} with id ${id} not found or already deleted`); - } - logger_1.logger.debug({ - table: this.tableName, - operation: 'update', - entityId: id, - fields: fields, - }, 'Entity updated successfully'); - if (result.rows[0] === undefined) { - throw new errors_1.DatabaseError('Failed to update entity - undefined row returned'); - } - return result.rows[0]; - }, `repository-update-${this.tableName}`, { entityId: id, fieldsUpdated: fields.length }); - } - /** - * Soft delete entity - */ - async softDelete(id, deletedBy, client) { - const sql = ` - UPDATE ${this.tableName} - SET deleted_at = NOW(), deleted_by = $2 - WHERE ${this.primaryKey} = $1 AND deleted_at IS NULL - `; - return logger_1.performanceLogger.trackAsyncOperation(async () => { - const result = await (0, database_1.query)(sql, [id, deletedBy], client); - const wasDeleted = result.rowCount > 0; - if (wasDeleted) { - logger_1.logger.info({ - table: this.tableName, - operation: 'softDelete', - entityId: id, - deletedBy: deletedBy, - }, 'Entity soft deleted successfully'); - } - return wasDeleted; - }, `repository-softDelete-${this.tableName}`, { entityId: id }); - } - /** - * Restore soft deleted entity - */ - async restore(id, client) { - const sql = ` - UPDATE ${this.tableName} - SET deleted_at = NULL, deleted_by = NULL - WHERE ${this.primaryKey} = $1 AND deleted_at IS NOT NULL - `; - return logger_1.performanceLogger.trackAsyncOperation(async () => { - const result = await (0, database_1.query)(sql, [id], client); - const wasRestored = result.rowCount > 0; - if (wasRestored) { - logger_1.logger.info({ - table: this.tableName, - operation: 'restore', - entityId: id, - }, 'Entity restored successfully'); - } - return wasRestored; - }, `repository-restore-${this.tableName}`, { entityId: id }); - } - /** - * Hard delete entity (use with caution) - */ - async hardDelete(id, client) { - const sql = `DELETE FROM ${this.tableName} WHERE ${this.primaryKey} = $1`; - return logger_1.performanceLogger.trackAsyncOperation(async () => { - const result = await (0, database_1.query)(sql, [id], client); - const wasDeleted = result.rowCount > 0; - if (wasDeleted) { - logger_1.logger.warn({ - table: this.tableName, - operation: 'hardDelete', - entityId: id, - }, 'Entity permanently deleted'); - } - return wasDeleted; - }, `repository-hardDelete-${this.tableName}`, { entityId: id }); - } - /** - * Find multiple entities with filtering and pagination - */ - async findMany(options = {}, client) { - const { limit = 20, offset = 0, orderBy = 'created_at', orderDirection = 'DESC', includeDeleted = false, filters = {}, } = options; - // Build WHERE conditions - const whereConditions = []; - const values = []; - let paramCounter = 1; - // Soft delete condition - if (!includeDeleted) { - whereConditions.push('deleted_at IS NULL'); - } - // Additional filters - Object.entries(filters).forEach(([field, value]) => { - if (value !== undefined && value !== null) { - if (Array.isArray(value)) { - whereConditions.push(`${field} = ANY($${paramCounter})`); - values.push(value); - } - else { - whereConditions.push(`${field} = $${paramCounter}`); - values.push(value); - } - paramCounter++; - } - }); - const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''; - // Count query - const countSql = ` - SELECT COUNT(*) as total - FROM ${this.tableName} - ${whereClause} - `; - // Data query - const dataSql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - ${whereClause} - ORDER BY ${orderBy} ${orderDirection} - LIMIT $${paramCounter} OFFSET $${paramCounter + 1} - `; - return logger_1.performanceLogger.trackAsyncOperation(async () => { - // Execute both queries in parallel for better performance - const [countResult, dataResult] = await Promise.all([ - (0, database_1.query)(countSql, values, client), - (0, database_1.query)(dataSql, [...values, limit, offset], client), - ]); - const total = countResult.rows[0]?.total ? parseInt(countResult.rows[0].total, 10) : 0; - const data = dataResult.rows; - const hasMore = offset + limit < total; - return { - data, - total, - limit, - offset, - hasMore, - }; - }, `repository-findMany-${this.tableName}`, { limit, offset, filterCount: Object.keys(filters).length }); - } - /** - * Count entities with optional filters - */ - async count(filters = {}, includeDeleted = false, client) { - const whereConditions = []; - const values = []; - let paramCounter = 1; - // Soft delete condition - if (!includeDeleted) { - whereConditions.push('deleted_at IS NULL'); - } - // Additional filters - Object.entries(filters).forEach(([field, value]) => { - if (value !== undefined && value !== null) { - whereConditions.push(`${field} = $${paramCounter}`); - values.push(value); - paramCounter++; - } - }); - const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''; - const sql = ` - SELECT COUNT(*) as total - FROM ${this.tableName} - ${whereClause} - `; - return logger_1.performanceLogger.trackAsyncOperation(async () => { - const result = await (0, database_1.query)(sql, values, client); - return result.rows[0]?.total ? parseInt(result.rows[0].total, 10) : 0; - }, `repository-count-${this.tableName}`, { filterCount: Object.keys(filters).length }); - } - /** - * Check if entity exists - */ - async exists(id, includeDeleted = false, client) { - const deletedCondition = includeDeleted ? '' : 'AND deleted_at IS NULL'; - const sql = ` - SELECT 1 FROM ${this.tableName} - WHERE ${this.primaryKey} = $1 ${deletedCondition} - LIMIT 1 - `; - const result = await (0, database_1.query)(sql, [id], client); - return result.rows.length > 0; - } - /** - * Execute raw SQL query within repository context - */ - async executeRawQuery(sql, params = [], client) { - return logger_1.performanceLogger.trackAsyncOperation(async () => { - const result = await (0, database_1.query)(sql, params, client); - logger_1.logger.debug({ - table: this.tableName, - operation: 'rawQuery', - sql: sql.substring(0, 100) + (sql.length > 100 ? '...' : ''), - paramCount: params.length, - rowCount: result.rowCount, - }, 'Raw query executed'); - return result; - }, `repository-rawQuery-${this.tableName}`, { paramCount: params.length }); - } - /** - * Execute multiple operations in a transaction - */ - async executeInTransaction(callback) { - return (0, database_1.transaction)(callback); - } - /** - * Bulk insert entities (for seeding and imports) - */ - async bulkCreate(entities, client) { - if (entities.length === 0) { - return []; - } - // Get all unique field names - const allFields = new Set(); - entities.forEach((entity) => { - Object.keys(entity).forEach((field) => allFields.add(field)); - }); - const fields = Array.from(allFields); - const values = []; - const valuePlaceholders = []; - entities.forEach((entity, entityIndex) => { - const entityValues = fields.map((field) => entity[field] ?? null); - values.push(...entityValues); - const startParam = entityIndex * fields.length + 1; - const entityPlaceholders = fields.map((_, fieldIndex) => `$${startParam + fieldIndex}`); - valuePlaceholders.push(`(${entityPlaceholders.join(', ')})`); - }); - const sql = ` - INSERT INTO ${this.tableName} (${fields.join(', ')}) - VALUES ${valuePlaceholders.join(', ')} - RETURNING ${this.selectFields.join(', ')} - `; - return logger_1.performanceLogger.trackAsyncOperation(async () => { - const result = await (0, database_1.query)(sql, values, client); - logger_1.logger.info({ - table: this.tableName, - operation: 'bulkCreate', - entityCount: entities.length, - fieldsCount: fields.length, - }, 'Bulk create completed successfully'); - return result.rows; - }, `repository-bulkCreate-${this.tableName}`, { entityCount: entities.length }); - } - /** - * Get repository statistics - */ - async getStats(client) { - const sql = ` - SELECT - COUNT(*) as total, - COUNT(*) FILTER (WHERE deleted_at IS NULL) as active, - COUNT(*) FILTER (WHERE deleted_at IS NOT NULL) as deleted, - COUNT(*) FILTER (WHERE DATE(created_at) = CURRENT_DATE) as created_today, - COUNT(*) FILTER (WHERE DATE(updated_at) = CURRENT_DATE AND updated_at::date > created_at::date) as updated_today - FROM ${this.tableName} - `; - const result = await (0, database_1.query)(sql, [], client); - const stats = result.rows[0]; - return { - total: parseInt(stats.total, 10), - active: parseInt(stats.active, 10), - deleted: parseInt(stats.deleted, 10), - createdToday: parseInt(stats.created_today, 10), - updatedToday: parseInt(stats.updated_today, 10), - }; - } - /** - * Direct query method for complex database operations - */ - async query(sql, params = [], client) { - return this.executeRawQuery(sql, params, client); - } -} -exports.BaseRepository = BaseRepository; -exports.default = BaseRepository; -//# sourceMappingURL=BaseRepository.js.map \ No newline at end of file diff --git a/backend/dist/db/BaseRepository.js.map b/backend/dist/db/BaseRepository.js.map deleted file mode 100644 index f66b79d..0000000 --- a/backend/dist/db/BaseRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"BaseRepository.js","sourceRoot":"","sources":["../../src/db/BaseRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/ChannelRepository.d.ts b/backend/dist/db/ChannelRepository.d.ts deleted file mode 100644 index ca25b30..0000000 --- a/backend/dist/db/ChannelRepository.d.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import BaseRepository, { BaseEntity } from './BaseRepository'; -export interface Channel extends BaseEntity { - name: string; - description?: string; - category_id?: string; - channel_type: 'project' | 'department' | 'initiative' | 'temporary' | 'emergency' | 'announcement'; - privacy_level: 'public' | 'private' | 'restricted'; - status: 'active' | 'archived' | 'paused' | 'completed'; - created_by: string; - owned_by: string; - moderators: string[]; - members: string[]; - member_count: number; - max_members: number; - auto_join_roles: string[]; - settings: Record; - integrations: Record; - activity_stats: Record; - project_info: Record; - schedule: Record; - archived_at?: Date; - archived_by?: string; - archive_reason?: string; - retention_until?: Date; - last_activity_at: Date; -} -export interface CreateChannelData { - name: string; - description?: string; - category_id?: string; - channel_type?: Channel['channel_type']; - privacy_level?: Channel['privacy_level']; - created_by: string; - owned_by?: string; - members?: string[]; - moderators?: string[]; - max_members?: number; - auto_join_roles?: string[]; - settings?: Record; - project_info?: Record; - integrations?: Record; -} -export interface ChannelWithDetails extends Channel { - category_name?: string; - owner_name?: string; - member_details?: Array<{ - id: string; - name: string; - email: string; - role: string; - avatar_url?: string; - }>; -} -declare class ChannelRepository extends BaseRepository { - constructor(); - /** - * Create new channel with validation - */ - createChannel(channelData: CreateChannelData, client?: DatabaseClient): Promise; - /** - * Find channel by category and name - */ - findByCategoryAndName(categoryId: string, name: string, client?: DatabaseClient): Promise; - /** - * Find channels by category - */ - findByCategory(categoryId: string, includeDeleted?: boolean, client?: DatabaseClient): Promise; - /** - * Find channels where user is a member - */ - findByMember(userId: string, includeDeleted?: boolean, client?: DatabaseClient): Promise; - /** - * Find channels accessible by user (considering role and privacy) - */ - findAccessibleByUser(userId: string, userRole: string, client?: DatabaseClient): Promise; - /** - * Add member to channel - */ - addMember(channelId: string, userId: string, addedBy: string, client?: DatabaseClient): Promise; - /** - * Remove member from channel - */ - removeMember(channelId: string, userId: string, removedBy: string, client?: DatabaseClient): Promise; - /** - * Add multiple members to channel - */ - addMembers(channelId: string, userIds: string[], addedBy: string, client?: DatabaseClient): Promise; - /** - * Get channel with detailed member information - */ - findWithMembers(channelId: string, client?: DatabaseClient): Promise; - /** - * Search channels by name or description - */ - searchChannels(searchTerm: string, userId?: string, userRole?: string, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Get channels by status - */ - findByStatus(status: Channel['status'], limit?: number, client?: DatabaseClient): Promise; - /** - * Archive channel - */ - archiveChannel(channelId: string, archivedBy: string, reason?: string, client?: DatabaseClient): Promise; - /** - * Restore archived channel - */ - restoreChannel(channelId: string, client?: DatabaseClient): Promise; - /** - * Update channel activity timestamp - */ - updateActivity(channelId: string, client?: DatabaseClient): Promise; - /** - * Get channels requiring attention (no recent activity) - */ - getInactiveChannels(daysSinceActivity?: number, client?: DatabaseClient): Promise; - /** - * Get channel statistics - */ - getChannelStats(client?: DatabaseClient): Promise<{ - totalChannels: number; - activeChannels: number; - channelsByType: Record; - channelsByPrivacy: Record; - averageMembers: number; - mostActiveChannels: Array<{ - id: string; - name: string; - activity_count: number; - }>; - }>; - /** - * Find channels for a specific user - */ - findUserChannels(userId: string, client?: DatabaseClient): Promise; - /** - * Get channel members - */ - getMembers(channelId: string, client?: DatabaseClient): Promise; - /** - * Check if user can access channel - */ - canUserAccess(channelId: string, userId: string, userRole: string, client?: DatabaseClient): Promise; -} -export default ChannelRepository; -//# sourceMappingURL=ChannelRepository.d.ts.map \ No newline at end of file diff --git a/backend/dist/db/ChannelRepository.d.ts.map b/backend/dist/db/ChannelRepository.d.ts.map deleted file mode 100644 index dd019be..0000000 --- a/backend/dist/db/ChannelRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ChannelRepository.d.ts","sourceRoot":"","sources":["../../src/db/ChannelRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/ChannelRepository.js b/backend/dist/db/ChannelRepository.js deleted file mode 100644 index 7eeefe4..0000000 --- a/backend/dist/db/ChannelRepository.js +++ /dev/null @@ -1,476 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const BaseRepository_1 = __importDefault(require("./BaseRepository")); -class ChannelRepository extends BaseRepository_1.default { - constructor() { - super('channels'); - } - /** - * Create new channel with validation - */ - async createChannel(channelData, client) { - // Check for duplicate name within category - if (channelData.category_id) { - const existingChannel = await this.findByCategoryAndName(channelData.category_id, channelData.name, client); - if (existingChannel) { - throw new errors_1.ConflictError('Channel name already exists in this category', { - name: channelData.name, - categoryId: channelData.category_id, - }); - } - } - // Set defaults - const channelToCreate = { - ...channelData, - owned_by: channelData.owned_by || channelData.created_by, - channel_type: channelData.channel_type || 'project', - privacy_level: channelData.privacy_level || 'public', - status: 'active', - members: channelData.members || [channelData.created_by], - moderators: channelData.moderators || [channelData.created_by], - member_count: channelData.members?.length || 1, - max_members: channelData.max_members || 100, - auto_join_roles: channelData.auto_join_roles || [], - settings: { - allow_voice_commands: true, - voice_command_roles: ['ceo', 'manager'], - allow_file_uploads: true, - ...channelData.settings, - }, - integrations: { - ai_assistant_enabled: true, - task_creation_enabled: true, - ...channelData.integrations, - }, - activity_stats: { - total_messages: 0, - total_files: 0, - total_tasks: 0, - last_activity: null, - }, - project_info: channelData.project_info || {}, - schedule: { - timezone: 'UTC', - working_hours: { - monday: { start: '09:00', end: '17:00' }, - tuesday: { start: '09:00', end: '17:00' }, - wednesday: { start: '09:00', end: '17:00' }, - thursday: { start: '09:00', end: '17:00' }, - friday: { start: '09:00', end: '17:00' }, - saturday: null, - sunday: null, - }, - }, - }; - const channel = await this.create(channelToCreate, client); - logger_1.logger.info({ - channelId: channel.id, - name: channel.name, - createdBy: channel.created_by, - memberCount: channel.member_count, - }, 'Channel created successfully'); - return channel; - } - /** - * Find channel by category and name - */ - async findByCategoryAndName(categoryId, name, client) { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE category_id = $1 - AND LOWER(name) = LOWER($2) - AND deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [categoryId, name], client); - return result.rows[0] || null; - } - /** - * Find channels by category - */ - async findByCategory(categoryId, includeDeleted = false, client) { - const options = { - filters: { category_id: categoryId }, - includeDeleted, - orderBy: 'last_activity_at', - orderDirection: 'DESC', - }; - const result = await this.findMany(options, client); - return result.data; - } - /** - * Find channels where user is a member - */ - async findByMember(userId, includeDeleted = false, client) { - const deletedCondition = includeDeleted ? '' : 'AND deleted_at IS NULL'; - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE $1 = ANY(members) ${deletedCondition} - ORDER BY last_activity_at DESC - `; - const result = await this.executeRawQuery(sql, [userId], client); - return result.rows; - } - /** - * Find channels accessible by user (considering role and privacy) - */ - async findAccessibleByUser(userId, userRole, client) { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND ( - -- Public channels - privacy_level = 'public' - -- User is a member - OR $1 = ANY(members) - -- CEO can access all channels - OR $2 = 'ceo' - -- Restricted channels with role access - OR (privacy_level = 'restricted' AND $2 = ANY(auto_join_roles)) - ) - ORDER BY - CASE WHEN $1 = ANY(members) THEN 1 ELSE 2 END, - last_activity_at DESC - `; - const result = await this.executeRawQuery(sql, [userId, userRole], client); - return result.rows; - } - /** - * Add member to channel - */ - async addMember(channelId, userId, addedBy, client) { - const sql = ` - SELECT add_channel_member($1, $2, $3) - `; - try { - const result = await this.executeRawQuery(sql, [channelId, userId, addedBy], client); - const success = result.rows[0]?.add_channel_member || false; - if (success) { - logger_1.logger.info({ - channelId, - userId, - addedBy, - }, 'Member added to channel'); - } - return success; - } - catch (error) { - logger_1.logger.error({ error, channelId, userId }, 'Failed to add member to channel'); - throw error; - } - } - /** - * Remove member from channel - */ - async removeMember(channelId, userId, removedBy, client) { - const sql = ` - SELECT remove_channel_member($1, $2, $3) - `; - try { - const result = await this.executeRawQuery(sql, [channelId, userId, removedBy], client); - const success = result.rows[0]?.remove_channel_member || false; - if (success) { - logger_1.logger.info({ - channelId, - userId, - removedBy, - }, 'Member removed from channel'); - } - return success; - } - catch (error) { - logger_1.logger.error({ error, channelId, userId }, 'Failed to remove member from channel'); - throw error; - } - } - /** - * Add multiple members to channel - */ - async addMembers(channelId, userIds, addedBy, client) { - const successfulAdds = []; - for (const userId of userIds) { - try { - const success = await this.addMember(channelId, userId, addedBy, client); - if (success) { - successfulAdds.push(userId); - } - } - catch (error) { - logger_1.logger.warn({ error, channelId, userId }, 'Failed to add individual member'); - } - } - return successfulAdds; - } - /** - * Get channel with detailed member information - */ - async findWithMembers(channelId, client) { - const sql = ` - SELECT - c.*, - cat.name as category_name, - owner.name as owner_name, - ARRAY( - SELECT json_build_object( - 'id', u.id, - 'name', u.name, - 'email', u.email, - 'role', u.role, - 'avatar_url', u.avatar_url - ) - FROM users u - WHERE u.id = ANY(c.members) - AND u.deleted_at IS NULL - ORDER BY u.name - ) as member_details - FROM channels c - LEFT JOIN categories cat ON c.category_id = cat.id - LEFT JOIN users owner ON c.owned_by = owner.id - WHERE c.id = $1 AND c.deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [channelId], client); - return result.rows[0] || null; - } - /** - * Search channels by name or description - */ - async searchChannels(searchTerm, userId, userRole, limit = 20, offset = 0, client) { - let accessCondition = ''; - let params = [`%${searchTerm}%`, limit, offset]; - if (userId && userRole) { - accessCondition = ` - AND ( - privacy_level = 'public' - OR $4 = ANY(members) - OR $5 = 'ceo' - OR (privacy_level = 'restricted' AND $5 = ANY(auto_join_roles)) - ) - `; - params.push(userId, userRole); - } - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ( - LOWER(name) LIKE LOWER($1) OR - LOWER(description) LIKE LOWER($1) - ) - AND deleted_at IS NULL - AND status = 'active' - ${accessCondition} - ORDER BY - CASE WHEN LOWER(name) LIKE LOWER($1) THEN 1 ELSE 2 END, - last_activity_at DESC - LIMIT $2 OFFSET $3 - `; - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Get channels by status - */ - async findByStatus(status, limit, client) { - const options = { - filters: { status }, - orderBy: 'last_activity_at', - orderDirection: 'DESC', - ...(limit !== undefined ? { limit } : {}), - }; - const result = await this.findMany(options, client); - return result.data; - } - /** - * Archive channel - */ - async archiveChannel(channelId, archivedBy, reason, client) { - const sql = ` - UPDATE ${this.tableName} - SET - status = 'archived', - archived_at = NOW(), - archived_by = $2, - archive_reason = $3 - WHERE id = $1 AND deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [channelId, archivedBy, reason || 'Manual archive'], client); - if (result.rowCount > 0) { - logger_1.logger.info({ - channelId, - archivedBy, - reason, - }, 'Channel archived successfully'); - } - return result.rowCount > 0; - } - /** - * Restore archived channel - */ - async restoreChannel(channelId, client) { - const sql = ` - UPDATE ${this.tableName} - SET - status = 'active', - archived_at = NULL, - archived_by = NULL, - archive_reason = NULL - WHERE id = $1 AND deleted_at IS NULL AND status = 'archived' - `; - const result = await this.executeRawQuery(sql, [channelId], client); - if (result.rowCount > 0) { - logger_1.logger.info({ channelId }, 'Channel restored successfully'); - } - return result.rowCount > 0; - } - /** - * Update channel activity timestamp - */ - async updateActivity(channelId, client) { - const sql = ` - UPDATE ${this.tableName} - SET last_activity_at = NOW() - WHERE id = $1 AND deleted_at IS NULL - `; - await this.executeRawQuery(sql, [channelId], client); - } - /** - * Get channels requiring attention (no recent activity) - */ - async getInactiveChannels(daysSinceActivity = 30, client) { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE last_activity_at < NOW() - INTERVAL '${daysSinceActivity} days' - AND status = 'active' - AND deleted_at IS NULL - ORDER BY last_activity_at ASC - `; - const result = await this.executeRawQuery(sql, [], client); - return result.rows; - } - /** - * Get channel statistics - */ - async getChannelStats(client) { - const statsSql = ` - SELECT - COUNT(*) as total_channels, - COUNT(*) FILTER (WHERE status = 'active') as active_channels, - COUNT(*) FILTER (WHERE channel_type = 'project') as project_channels, - COUNT(*) FILTER (WHERE channel_type = 'department') as department_channels, - COUNT(*) FILTER (WHERE channel_type = 'initiative') as initiative_channels, - COUNT(*) FILTER (WHERE channel_type = 'temporary') as temporary_channels, - COUNT(*) FILTER (WHERE privacy_level = 'public') as public_channels, - COUNT(*) FILTER (WHERE privacy_level = 'private') as private_channels, - COUNT(*) FILTER (WHERE privacy_level = 'restricted') as restricted_channels, - AVG(member_count) as average_members - FROM ${this.tableName} - WHERE deleted_at IS NULL - `; - const mostActiveSql = ` - SELECT id, name, - COALESCE((activity_stats->>'total_messages')::int, 0) + - COALESCE((activity_stats->>'total_tasks')::int, 0) as activity_count - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND status = 'active' - ORDER BY activity_count DESC - LIMIT 10 - `; - const [statsResult, activeResult] = await Promise.all([ - this.executeRawQuery(statsSql, [], client), - this.executeRawQuery(mostActiveSql, [], client), - ]); - const stats = statsResult.rows[0]; - return { - totalChannels: parseInt(stats.total_channels, 10), - activeChannels: parseInt(stats.active_channels, 10), - channelsByType: { - project: parseInt(stats.project_channels, 10), - department: parseInt(stats.department_channels, 10), - initiative: parseInt(stats.initiative_channels, 10), - temporary: parseInt(stats.temporary_channels, 10), - }, - channelsByPrivacy: { - public: parseInt(stats.public_channels, 10), - private: parseInt(stats.private_channels, 10), - restricted: parseInt(stats.restricted_channels, 10), - }, - averageMembers: parseFloat(stats.average_members || '0'), - mostActiveChannels: activeResult.rows.map((row) => ({ - id: row.id, - name: row.name, - activity_count: parseInt(row.activity_count, 10), - })), - }; - } - /** - * Find channels for a specific user - */ - async findUserChannels(userId, client) { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE $1 = ANY(members) - AND deleted_at IS NULL - AND status = 'active' - ORDER BY last_activity_at DESC - `; - const result = await this.executeRawQuery(sql, [userId], client); - return result.rows; - } - /** - * Get channel members - */ - async getMembers(channelId, client) { - const sql = ` - SELECT u.id, u.name, u.email, u.role, u.avatar_url - FROM users u - JOIN ${this.tableName} c ON u.id = ANY(c.members) - WHERE c.id = $1 AND c.deleted_at IS NULL - AND u.deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [channelId], client); - return result.rows; - } - /** - * Check if user can access channel - */ - async canUserAccess(channelId, userId, userRole, client) { - const sql = ` - SELECT id, privacy_level, members, created_by - FROM ${this.tableName} - WHERE id = $1 AND deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [channelId], client); - if (result.rows.length === 0) { - return false; - } - const channel = result.rows[0]; - // CEO can access any channel - if (userRole === 'ceo') { - return true; - } - // Creator can always access - if (channel.created_by === userId) { - return true; - } - // Check if user is a member - if (channel.members && channel.members.includes(userId)) { - return true; - } - // Public channels are accessible to all - if (channel.privacy_level === 'public') { - return true; - } - return false; - } -} -exports.default = ChannelRepository; -//# sourceMappingURL=ChannelRepository.js.map \ No newline at end of file diff --git a/backend/dist/db/ChannelRepository.js.map b/backend/dist/db/ChannelRepository.js.map deleted file mode 100644 index 9d18b4a..0000000 --- a/backend/dist/db/ChannelRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ChannelRepository.js","sourceRoot":"","sources":["../../src/db/ChannelRepository.ts"],"names":[],"mappings":";;;;;AACA,0CAAuC;AACvC,0CAA8E;AAC9E,sEAA6E;AA+D7E,MAAM,iBAAkB,SAAQ,wBAAuB;IACrD;QACE,KAAK,CAAC,UAAU,CAAC,CAAC;IACpB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,WAA8B,EAAE,MAAuB;QACzE,2CAA2C;QAC3C,IAAI,WAAW,CAAC,WAAW,EAAE,CAAC;YAC5B,MAAM,eAAe,GAAG,MAAM,IAAI,CAAC,qBAAqB,CACtD,WAAW,CAAC,WAAW,EACvB,WAAW,CAAC,IAAI,EAChB,MAAM,CACP,CAAC;YACF,IAAI,eAAe,EAAE,CAAC;gBACpB,MAAM,IAAI,sBAAa,CAAC,8CAA8C,EAAE;oBACtE,IAAI,EAAE,WAAW,CAAC,IAAI;oBACtB,UAAU,EAAE,WAAW,CAAC,WAAW;iBACpC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,eAAe;QACf,MAAM,eAAe,GAAG;YACtB,GAAG,WAAW;YACd,QAAQ,EAAE,WAAW,CAAC,QAAQ,IAAI,WAAW,CAAC,UAAU;YACxD,YAAY,EAAE,WAAW,CAAC,YAAY,IAAI,SAAS;YACnD,aAAa,EAAE,WAAW,CAAC,aAAa,IAAI,QAAQ;YACpD,MAAM,EAAE,QAAiB;YACzB,OAAO,EAAE,WAAW,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC;YACxD,UAAU,EAAE,WAAW,CAAC,UAAU,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC;YAC9D,YAAY,EAAE,WAAW,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC;YAC9C,WAAW,EAAE,WAAW,CAAC,WAAW,IAAI,GAAG;YAC3C,eAAe,EAAE,WAAW,CAAC,eAAe,IAAI,EAAE;YAClD,QAAQ,EAAE;gBACR,oBAAoB,EAAE,IAAI;gBAC1B,mBAAmB,EAAE,CAAC,KAAK,EAAE,SAAS,CAAC;gBACvC,kBAAkB,EAAE,IAAI;gBACxB,GAAG,WAAW,CAAC,QAAQ;aACxB;YACD,YAAY,EAAE;gBACZ,oBAAoB,EAAE,IAAI;gBAC1B,qBAAqB,EAAE,IAAI;gBAC3B,GAAG,WAAW,CAAC,YAAY;aAC5B;YACD,cAAc,EAAE;gBACd,cAAc,EAAE,CAAC;gBACjB,WAAW,EAAE,CAAC;gBACd,WAAW,EAAE,CAAC;gBACd,aAAa,EAAE,IAAI;aACpB;YACD,YAAY,EAAE,WAAW,CAAC,YAAY,IAAI,EAAE;YAC5C,QAAQ,EAAE;gBACR,QAAQ,EAAE,KAAK;gBACf,aAAa,EAAE;oBACb,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE;oBACxC,OAAO,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE;oBACzC,SAAS,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE;oBAC3C,QAAQ,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE;oBAC1C,MAAM,EAAE,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE;oBACxC,QAAQ,EAAE,IAAI;oBACd,MAAM,EAAE,IAAI;iBACb;aACF;SACF,CAAC;QAEF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;QAE3D,eAAM,CAAC,IAAI,CACT;YACE,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,SAAS,EAAE,OAAO,CAAC,UAAU;YAC7B,WAAW,EAAE,OAAO,CAAC,YAAY;SAClC,EACD,8BAA8B,CAC/B,CAAC;QAEF,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,qBAAqB,CACzB,UAAkB,EAClB,IAAY,EACZ,MAAuB;QAEvB,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;aAC9B,IAAI,CAAC,SAAS;;;;KAItB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAU,GAAG,EAAE,CAAC,UAAU,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,CAAC;QACpF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAClB,UAAkB,EAClB,iBAA0B,KAAK,EAC/B,MAAuB;QAEvB,MAAM,OAAO,GAAkB;YAC7B,OAAO,EAAE,EAAE,WAAW,EAAE,UAAU,EAAE;YACpC,cAAc;YACd,OAAO,EAAE,kBAAkB;YAC3B,cAAc,EAAE,MAAM;SACvB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACpD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,MAAc,EACd,iBAA0B,KAAK,EAC/B,MAAuB;QAEvB,MAAM,gBAAgB,GAAG,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,wBAAwB,CAAC;QAExE,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;aAC9B,IAAI,CAAC,SAAS;gCACK,gBAAgB;;KAE3C,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAU,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QAC1E,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,oBAAoB,CACxB,MAAc,EACd,QAAgB,EAChB,MAAuB;QAEvB,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;aAC9B,IAAI,CAAC,SAAS;;;;;;;;;;;;;;;KAetB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAU,GAAG,EAAE,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,CAAC;QACpF,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,SAAS,CACb,SAAiB,EACjB,MAAc,EACd,OAAe,EACf,MAAuB;QAEvB,MAAM,GAAG,GAAG;;KAEX,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CACvC,GAAG,EACH,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAC5B,MAAM,CACP,CAAC;YAEF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,kBAAkB,IAAI,KAAK,CAAC;YAE5D,IAAI,OAAO,EAAE,CAAC;gBACZ,eAAM,CAAC,IAAI,CACT;oBACE,SAAS;oBACT,MAAM;oBACN,OAAO;iBACR,EACD,yBAAyB,CAC1B,CAAC;YACJ,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,iCAAiC,CAAC,CAAC;YAC9E,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,SAAiB,EACjB,MAAc,EACd,SAAiB,EACjB,MAAuB;QAEvB,MAAM,GAAG,GAAG;;KAEX,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CACvC,GAAG,EACH,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAC9B,MAAM,CACP,CAAC;YAEF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,qBAAqB,IAAI,KAAK,CAAC;YAE/D,IAAI,OAAO,EAAE,CAAC;gBACZ,eAAM,CAAC,IAAI,CACT;oBACE,SAAS;oBACT,MAAM;oBACN,SAAS;iBACV,EACD,6BAA6B,CAC9B,CAAC;YACJ,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,sCAAsC,CAAC,CAAC;YACnF,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CACd,SAAiB,EACjB,OAAiB,EACjB,OAAe,EACf,MAAuB;QAEvB,MAAM,cAAc,GAAa,EAAE,CAAC;QAEpC,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC;gBACzE,IAAI,OAAO,EAAE,CAAC;oBACZ,cAAc,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;gBAC9B,CAAC;YACH,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,eAAM,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE,iCAAiC,CAAC,CAAC;YAC/E,CAAC;QACH,CAAC;QAED,OAAO,cAAc,CAAC;IACxB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CACnB,SAAiB,EACjB,MAAuB;QAEvB,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;KAsBX,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAqB,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;QACxF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAClB,UAAkB,EAClB,MAAe,EACf,QAAiB,EACjB,QAAgB,EAAE,EAClB,SAAiB,CAAC,EAClB,MAAuB;QAEvB,IAAI,eAAe,GAAG,EAAE,CAAC;QACzB,IAAI,MAAM,GAAG,CAAC,IAAI,UAAU,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAEhD,IAAI,MAAM,IAAI,QAAQ,EAAE,CAAC;YACvB,eAAe,GAAG;;;;;;;OAOjB,CAAC;YACF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QAChC,CAAC;QAED,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;aAC9B,IAAI,CAAC,SAAS;;;;;;;QAOnB,eAAe;;;;;KAKlB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAU,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACxE,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAChB,MAAyB,EACzB,KAAc,EACd,MAAuB;QAEvB,MAAM,OAAO,GAAkB;YAC7B,OAAO,EAAE,EAAE,MAAM,EAAE;YACnB,OAAO,EAAE,kBAAkB;YAC3B,cAAc,EAAE,MAAM;YACtB,GAAG,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC1C,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACpD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAClB,SAAiB,EACjB,UAAkB,EAClB,MAAe,EACf,MAAuB;QAEvB,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,SAAS;;;;;;;KAOxB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CACvC,GAAG,EACH,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,IAAI,gBAAgB,CAAC,EACnD,MAAM,CACP,CAAC;QAEF,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;YACxB,eAAM,CAAC,IAAI,CACT;gBACE,SAAS;gBACT,UAAU;gBACV,MAAM;aACP,EACD,+BAA+B,CAChC,CAAC;QACJ,CAAC;QAED,OAAO,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,SAAiB,EAAE,MAAuB;QAC7D,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,SAAS;;;;;;;KAOxB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;QAEpE,IAAI,MAAM,CAAC,QAAQ,GAAG,CAAC,EAAE,CAAC;YACxB,eAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,EAAE,+BAA+B,CAAC,CAAC;QAC9D,CAAC;QAED,OAAO,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,SAAiB,EAAE,MAAuB;QAC7D,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,SAAS;;;KAGxB,CAAC;QAEF,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;IACvD,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,mBAAmB,CACvB,oBAA4B,EAAE,EAC9B,MAAuB;QAEvB,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;aAC9B,IAAI,CAAC,SAAS;mDACwB,iBAAiB;;;;KAI/D,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAU,GAAG,EAAE,EAAE,EAAE,MAAM,CAAC,CAAC;QACpE,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,MAAuB;QAQ3C,MAAM,QAAQ,GAAG;;;;;;;;;;;;aAYR,IAAI,CAAC,SAAS;;KAEtB,CAAC;QAEF,MAAM,aAAa,GAAG;;;;aAIb,IAAI,CAAC,SAAS;;;;;KAKtB,CAAC;QAEF,MAAM,CAAC,WAAW,EAAE,YAAY,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;YACpD,IAAI,CAAC,eAAe,CAAM,QAAQ,EAAE,EAAE,EAAE,MAAM,CAAC;YAC/C,IAAI,CAAC,eAAe,CAAM,aAAa,EAAE,EAAE,EAAE,MAAM,CAAC;SACrD,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAElC,OAAO;YACL,aAAa,EAAE,QAAQ,CAAC,KAAK,CAAC,cAAc,EAAE,EAAE,CAAC;YACjD,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;YACnD,cAAc,EAAE;gBACd,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,gBAAgB,EAAE,EAAE,CAAC;gBAC7C,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,CAAC;gBACnD,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,CAAC;gBACnD,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,kBAAkB,EAAE,EAAE,CAAC;aAClD;YACD,iBAAiB,EAAE;gBACjB,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;gBAC3C,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,gBAAgB,EAAE,EAAE,CAAC;gBAC7C,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,CAAC;aACpD;YACD,cAAc,EAAE,UAAU,CAAC,KAAK,CAAC,eAAe,IAAI,GAAG,CAAC;YACxD,kBAAkB,EAAE,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAQ,EAAE,EAAE,CAAC,CAAC;gBACvD,EAAE,EAAE,GAAG,CAAC,EAAE;gBACV,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,cAAc,EAAE,QAAQ,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,CAAC;aACjD,CAAC,CAAC;SACJ,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,gBAAgB,CAAC,MAAc,EAAE,MAAuB;QAC5D,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;aAC9B,IAAI,CAAC,SAAS;;;;;KAKtB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QACjE,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,SAAiB,EAAE,MAAuB;QACzD,MAAM,GAAG,GAAG;;;aAGH,IAAI,CAAC,SAAS;;;KAGtB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;QACpE,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CACjB,SAAiB,EACjB,MAAc,EACd,QAAgB,EAChB,MAAuB;QAEvB,MAAM,GAAG,GAAG;;aAEH,IAAI,CAAC,SAAS;;KAEtB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;QACpE,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE/B,6BAA6B;QAC7B,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;YACvB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,4BAA4B;QAC5B,IAAI,OAAO,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;YAClC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,4BAA4B;QAC5B,IAAI,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YACxD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,wCAAwC;QACxC,IAAI,OAAO,CAAC,aAAa,KAAK,QAAQ,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAED,kBAAe,iBAAiB,CAAC"} \ No newline at end of file diff --git a/backend/dist/db/FileRepository.d.ts b/backend/dist/db/FileRepository.d.ts deleted file mode 100644 index 1d755bc..0000000 --- a/backend/dist/db/FileRepository.d.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import BaseRepository, { BaseEntity } from './BaseRepository'; -export interface FileEntity extends BaseEntity { - filename: string; - original_name: string; - mime_type: string; - size: number; - url: string; - download_url?: string; - thumbnail_url?: string; - uploaded_by: string; - channel_id?: string; - task_id?: string; - message_id?: string; - file_path: string; - storage_provider: 'local' | 'aws_s3' | 'gcs' | 'azure'; - storage_key: string; - checksum: string; - metadata: Record; - access_count: number; - last_accessed_at?: Date; - virus_scan_status: 'pending' | 'clean' | 'infected' | 'error'; - virus_scan_result?: Record; - expires_at?: Date; - is_public: boolean; - download_count: number; -} -export interface CreateFileData { - filename: string; - originalName: string; - mimeType: string; - size: number; - url: string; - downloadUrl?: string; - thumbnailUrl?: string; - uploadedBy: string; - channelId?: string; - taskId?: string; - messageId?: string; - filePath: string; - storageProvider?: FileEntity['storage_provider']; - storageKey: string; - checksum: string; - metadata?: Record; - isPublic?: boolean; - expiresAt?: Date; -} -export interface FileWithUploader extends FileEntity { - uploader_name: string; - uploader_email: string; - uploader_avatar?: string; - channel_name?: string; - task_title?: string; -} -declare class FileRepository extends BaseRepository { - constructor(); - /** - * Create new file record with validation - */ - createFile(fileData: CreateFileData, client?: DatabaseClient): Promise; - /** - * Find files by channel - */ - findChannelFiles(channelId: string, filters?: { - fileType?: string; - uploadedBy?: string; - search?: string; - after?: Date; - before?: Date; - }, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Get channel file count - */ - getChannelFileCount(channelId: string, filters?: { - fileType?: string; - uploadedBy?: string; - search?: string; - after?: Date; - before?: Date; - }, client?: DatabaseClient): Promise; - /** - * Find files by task - */ - findTaskFiles(taskId: string, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Find files by user - */ - findUserFiles(userId: string, filters?: { - channelId?: string; - taskId?: string; - fileType?: string; - }, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Search files by name or content - */ - searchFiles(searchTerm: string, options?: { - userId?: string; - channelId?: string; - taskId?: string; - fileType?: string; - }, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Update file access tracking - */ - recordFileAccess(fileId: string, client?: DatabaseClient): Promise; - /** - * Update file download count - */ - recordFileDownload(fileId: string, client?: DatabaseClient): Promise; - /** - * Update virus scan status - */ - updateVirusScanStatus(fileId: string, status: FileEntity['virus_scan_status'], result?: Record, client?: DatabaseClient): Promise; - /** - * Get file statistics - */ - getFileStats(client?: DatabaseClient): Promise<{ - totalFiles: number; - totalSize: number; - filesByType: Record; - filesByProvider: Record; - virusScannedFiles: number; - infectedFiles: number; - mostDownloadedFiles: Array<{ - id: string; - filename: string; - downloadCount: number; - }>; - }>; - /** - * Clean up expired files - */ - cleanupExpiredFiles(client?: DatabaseClient): Promise; - /** - * Get allowed mime types - */ - private getAllowedMimeTypes; - /** - * Check if mime type is allowed - */ - isMimeTypeAllowed(mimeType: string): boolean; - /** - * Get file category from mime type - */ - getFileCategory(mimeType: string): string; -} -export default FileRepository; -//# sourceMappingURL=FileRepository.d.ts.map \ No newline at end of file diff --git a/backend/dist/db/FileRepository.d.ts.map b/backend/dist/db/FileRepository.d.ts.map deleted file mode 100644 index 109a82b..0000000 --- a/backend/dist/db/FileRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"FileRepository.d.ts","sourceRoot":"","sources":["../../src/db/FileRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/FileRepository.js b/backend/dist/db/FileRepository.js deleted file mode 100644 index 8d86937..0000000 --- a/backend/dist/db/FileRepository.js +++ /dev/null @@ -1,505 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const BaseRepository_1 = __importDefault(require("./BaseRepository")); -class FileRepository extends BaseRepository_1.default { - constructor() { - super('files'); - } - /** - * Create new file record with validation - */ - async createFile(fileData, client) { - // Validate file size (100MB default max) - const maxSize = 100 * 1024 * 1024; // 100MB - if (fileData.size > maxSize) { - throw new errors_1.ValidationError('File size exceeds maximum allowed size', [ - { field: 'size', message: `File size must be less than ${maxSize} bytes` }, - ]); - } - // Validate mime type - const allowedTypes = this.getAllowedMimeTypes(); - if (!allowedTypes.includes(fileData.mimeType)) { - throw new errors_1.ValidationError('Unsupported file type', [ - { field: 'mimeType', message: `File type ${fileData.mimeType} is not allowed` }, - ]); - } - const fileToCreate = { - filename: fileData.filename, - original_name: fileData.originalName, - mime_type: fileData.mimeType, - size: fileData.size, - url: fileData.url, - download_url: fileData.downloadUrl ?? '', - thumbnail_url: fileData.thumbnailUrl ?? '', - uploaded_by: fileData.uploadedBy, - channel_id: fileData.channelId ?? '', - task_id: fileData.taskId ?? '', - message_id: fileData.messageId ?? '', - file_path: fileData.filePath, - storage_provider: fileData.storageProvider || 'local', - storage_key: fileData.storageKey, - checksum: fileData.checksum, - metadata: fileData.metadata || {}, - access_count: 0, - virus_scan_status: 'pending', - is_public: fileData.isPublic || false, - download_count: 0, - ...(fileData.expiresAt ? { expires_at: fileData.expiresAt } : {}), - }; - const file = await this.create(fileToCreate, client); - logger_1.logger.info({ - fileId: file.id, - filename: file.filename, - size: file.size, - uploadedBy: file.uploaded_by, - channelId: file.channel_id, - taskId: file.task_id, - }, 'File created successfully'); - return file; - } - /** - * Find files by channel - */ - async findChannelFiles(channelId, filters, limit = 50, offset = 0, client) { - let whereConditions = ['fel.entity_type = \'channel\'', 'fel.entity_id = $1', 'f.deleted_at IS NULL']; - let params = [channelId]; - let paramIndex = 2; - // Add filters - if (filters?.fileType) { - whereConditions.push(`f.mime_type LIKE $${paramIndex}`); - params.push(`${filters.fileType}%`); - paramIndex++; - } - if (filters?.uploadedBy) { - whereConditions.push(`f.uploaded_by = $${paramIndex}`); - params.push(filters.uploadedBy); - paramIndex++; - } - if (filters?.search) { - whereConditions.push(`( - LOWER(f.filename) LIKE LOWER($${paramIndex}) OR - LOWER(f.original_name) LIKE LOWER($${paramIndex}) - )`); - params.push(`%${filters.search}%`); - paramIndex++; - } - if (filters?.after) { - whereConditions.push(`f.uploaded_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - if (filters?.before) { - whereConditions.push(`f.uploaded_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - const sql = ` - SELECT - f.*, - u.name as uploader_name, - u.email as uploader_email, - u.avatar_url as uploader_avatar, - c.name as channel_name, - t.title as task_title - FROM ${this.tableName} f - INNER JOIN file_entity_links fel ON f.id = fel.file_id - LEFT JOIN users u ON f.uploaded_by = u.id - LEFT JOIN channels c ON fel.entity_id = c.id AND fel.entity_type = 'channel' - LEFT JOIN tasks t ON fel.entity_id = t.id AND fel.entity_type = 'task' - WHERE ${whereConditions.join(' AND ')} - ORDER BY f.uploaded_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - params.push(limit, offset); - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Get channel file count - */ - async getChannelFileCount(channelId, filters, client) { - let whereConditions = ['fel.entity_type = \'channel\'', 'fel.entity_id = $1', 'f.deleted_at IS NULL']; - let params = [channelId]; - let paramIndex = 2; - // Add filters - if (filters?.fileType) { - whereConditions.push(`f.mime_type LIKE $${paramIndex}`); - params.push(`${filters.fileType}%`); - paramIndex++; - } - if (filters?.uploadedBy) { - whereConditions.push(`f.uploaded_by = $${paramIndex}`); - params.push(filters.uploadedBy); - paramIndex++; - } - if (filters?.search) { - whereConditions.push(`( - LOWER(f.filename) LIKE LOWER($${paramIndex}) OR - LOWER(f.original_name) LIKE LOWER($${paramIndex}) - )`); - params.push(`%${filters.search}%`); - paramIndex++; - } - if (filters?.after) { - whereConditions.push(`f.uploaded_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - if (filters?.before) { - whereConditions.push(`f.uploaded_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - const sql = ` - SELECT COUNT(*) as count - FROM ${this.tableName} f - INNER JOIN file_entity_links fel ON f.id = fel.file_id - WHERE ${whereConditions.join(' AND ')} - `; - const result = await this.executeRawQuery(sql, params, client); - return parseInt(result.rows[0]?.count || '0', 10); - } - /** - * Find files by task - */ - async findTaskFiles(taskId, limit = 50, offset = 0, client) { - const sql = ` - SELECT - f.*, - u.name as uploader_name, - u.email as uploader_email, - u.avatar_url as uploader_avatar, - t.title as task_title - FROM ${this.tableName} f - LEFT JOIN users u ON f.uploaded_by = u.id - LEFT JOIN tasks t ON f.task_id = t.id - WHERE f.task_id = $1 AND f.deleted_at IS NULL - ORDER BY f.created_at DESC - LIMIT $2 OFFSET $3 - `; - const result = await this.executeRawQuery(sql, [taskId, limit, offset], client); - return result.rows; - } - /** - * Find files by user - */ - async findUserFiles(userId, filters, limit = 50, offset = 0, client) { - let whereConditions = ['f.uploaded_by = $1', 'f.deleted_at IS NULL']; - let params = [userId]; - let paramIndex = 2; - // Add filters - if (filters?.channelId) { - whereConditions.push(`f.channel_id = $${paramIndex}`); - params.push(filters.channelId); - paramIndex++; - } - if (filters?.taskId) { - whereConditions.push(`f.task_id = $${paramIndex}`); - params.push(filters.taskId); - paramIndex++; - } - if (filters?.fileType) { - whereConditions.push(`f.mime_type LIKE $${paramIndex}`); - params.push(`${filters.fileType}%`); - paramIndex++; - } - const sql = ` - SELECT - f.*, - u.name as uploader_name, - u.email as uploader_email, - u.avatar_url as uploader_avatar, - c.name as channel_name, - t.title as task_title - FROM ${this.tableName} f - LEFT JOIN users u ON f.uploaded_by = u.id - LEFT JOIN channels c ON f.channel_id = c.id - LEFT JOIN tasks t ON f.task_id = t.id - WHERE ${whereConditions.join(' AND ')} - ORDER BY f.created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - params.push(limit, offset); - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Search files by name or content - */ - async searchFiles(searchTerm, options, limit = 50, offset = 0, client) { - let whereConditions = [ - 'f.deleted_at IS NULL', - '(LOWER(f.filename) LIKE LOWER($1) OR LOWER(f.original_name) LIKE LOWER($1))', - ]; - let params = [`%${searchTerm}%`]; - let paramIndex = 2; - // Add filters - if (options?.userId) { - whereConditions.push(`f.uploaded_by = $${paramIndex}`); - params.push(options.userId); - paramIndex++; - } - if (options?.channelId) { - whereConditions.push(`f.channel_id = $${paramIndex}`); - params.push(options.channelId); - paramIndex++; - } - if (options?.taskId) { - whereConditions.push(`f.task_id = $${paramIndex}`); - params.push(options.taskId); - paramIndex++; - } - if (options?.fileType) { - whereConditions.push(`f.mime_type LIKE $${paramIndex}`); - params.push(`${options.fileType}%`); - paramIndex++; - } - const sql = ` - SELECT - f.*, - u.name as uploader_name, - u.email as uploader_email, - u.avatar_url as uploader_avatar, - c.name as channel_name, - t.title as task_title - FROM ${this.tableName} f - LEFT JOIN users u ON f.uploaded_by = u.id - LEFT JOIN channels c ON f.channel_id = c.id - LEFT JOIN tasks t ON f.task_id = t.id - WHERE ${whereConditions.join(' AND ')} - ORDER BY f.created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - params.push(limit, offset); - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Update file access tracking - */ - async recordFileAccess(fileId, client) { - const sql = ` - UPDATE ${this.tableName} - SET - access_count = access_count + 1, - last_accessed_at = NOW() - WHERE id = $1 AND deleted_at IS NULL - `; - try { - await this.executeRawQuery(sql, [fileId], client); - logger_1.logger.debug({ - fileId, - }, 'File access recorded'); - } - catch (error) { - logger_1.logger.error({ error, fileId }, 'Failed to record file access'); - } - } - /** - * Update file download count - */ - async recordFileDownload(fileId, client) { - const sql = ` - UPDATE ${this.tableName} - SET download_count = download_count + 1 - WHERE id = $1 AND deleted_at IS NULL - `; - try { - await this.executeRawQuery(sql, [fileId], client); - logger_1.logger.debug({ - fileId, - }, 'File download recorded'); - } - catch (error) { - logger_1.logger.error({ error, fileId }, 'Failed to record file download'); - } - } - /** - * Update virus scan status - */ - async updateVirusScanStatus(fileId, status, result, client) { - const sql = ` - UPDATE ${this.tableName} - SET - virus_scan_status = $2, - virus_scan_result = $3 - WHERE id = $1 AND deleted_at IS NULL - RETURNING id - `; - try { - const queryResult = await this.executeRawQuery(sql, [fileId, status, JSON.stringify(result || {})], client); - if (queryResult.rowCount > 0) { - logger_1.logger.info({ - fileId, - status, - hasResult: !!result, - }, 'Virus scan status updated'); - return true; - } - return false; - } - catch (error) { - logger_1.logger.error({ error, fileId, status }, 'Failed to update virus scan status'); - return false; - } - } - /** - * Get file statistics - */ - async getFileStats(client) { - const statsSql = ` - SELECT - COUNT(*) as total_files, - SUM(size) as total_size, - COUNT(*) FILTER (WHERE mime_type LIKE 'image/%') as image_files, - COUNT(*) FILTER (WHERE mime_type LIKE 'video/%') as video_files, - COUNT(*) FILTER (WHERE mime_type LIKE 'audio/%') as audio_files, - COUNT(*) FILTER (WHERE mime_type = 'application/pdf') as pdf_files, - COUNT(*) FILTER (WHERE storage_provider = 'local') as local_files, - COUNT(*) FILTER (WHERE storage_provider = 'aws_s3') as s3_files, - COUNT(*) FILTER (WHERE virus_scan_status = 'clean') as clean_files, - COUNT(*) FILTER (WHERE virus_scan_status = 'infected') as infected_files - FROM ${this.tableName} - WHERE deleted_at IS NULL - `; - const topFilesSql = ` - SELECT id, filename, download_count - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND download_count > 0 - ORDER BY download_count DESC - LIMIT 10 - `; - const [statsResult, topFilesResult] = await Promise.all([ - this.executeRawQuery(statsSql, [], client), - this.executeRawQuery(topFilesSql, [], client), - ]); - const stats = statsResult.rows[0]; - return { - totalFiles: parseInt(stats.total_files, 10), - totalSize: parseInt(stats.total_size, 10) || 0, - filesByType: { - image: parseInt(stats.image_files, 10), - video: parseInt(stats.video_files, 10), - audio: parseInt(stats.audio_files, 10), - pdf: parseInt(stats.pdf_files, 10), - }, - filesByProvider: { - local: parseInt(stats.local_files, 10), - aws_s3: parseInt(stats.s3_files, 10), - }, - virusScannedFiles: parseInt(stats.clean_files, 10), - infectedFiles: parseInt(stats.infected_files, 10), - mostDownloadedFiles: topFilesResult.rows.map((row) => ({ - id: row.id, - filename: row.filename, - downloadCount: parseInt(row.download_count, 10), - })), - }; - } - /** - * Clean up expired files - */ - async cleanupExpiredFiles(client) { - const sql = ` - UPDATE ${this.tableName} - SET deleted_at = NOW() - WHERE expires_at IS NOT NULL - AND expires_at < NOW() - AND deleted_at IS NULL - `; - try { - const result = await this.executeRawQuery(sql, [], client); - logger_1.logger.info({ - deletedCount: result.rowCount, - }, 'Expired files cleaned up'); - return result.rowCount; - } - catch (error) { - logger_1.logger.error({ error }, 'Failed to clean up expired files'); - return 0; - } - } - /** - * Get allowed mime types - */ - getAllowedMimeTypes() { - return [ - // Images - 'image/jpeg', - 'image/png', - 'image/gif', - 'image/webp', - 'image/svg+xml', - 'image/bmp', - 'image/tiff', - // Documents - 'application/pdf', - 'application/msword', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'application/vnd.ms-excel', - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'application/vnd.ms-powerpoint', - 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'text/plain', - 'text/csv', - 'text/markdown', - 'application/json', - 'application/xml', - // Archives - 'application/zip', - 'application/x-rar-compressed', - 'application/x-7z-compressed', - 'application/x-tar', - 'application/gzip', - // Media - 'video/mp4', - 'video/mpeg', - 'video/quicktime', - 'video/webm', - 'audio/mpeg', - 'audio/wav', - 'audio/mp4', - 'audio/webm', - 'audio/ogg', - // Code files - 'text/javascript', - 'text/css', - 'text/html', - 'application/javascript', - ]; - } - /** - * Check if mime type is allowed - */ - isMimeTypeAllowed(mimeType) { - return this.getAllowedMimeTypes().includes(mimeType); - } - /** - * Get file category from mime type - */ - getFileCategory(mimeType) { - if (mimeType.startsWith('image/')) - return 'image'; - if (mimeType.startsWith('video/')) - return 'video'; - if (mimeType.startsWith('audio/')) - return 'audio'; - if (mimeType === 'application/pdf') - return 'document'; - if (mimeType.includes('word') || mimeType.includes('excel') || mimeType.includes('powerpoint')) - return 'office'; - if (mimeType.startsWith('text/') || mimeType.includes('json') || mimeType.includes('xml')) - return 'text'; - if (mimeType.includes('zip') || mimeType.includes('rar') || mimeType.includes('tar')) - return 'archive'; - return 'other'; - } -} -exports.default = FileRepository; -//# sourceMappingURL=FileRepository.js.map \ No newline at end of file diff --git a/backend/dist/db/FileRepository.js.map b/backend/dist/db/FileRepository.js.map deleted file mode 100644 index a74c9b5..0000000 --- a/backend/dist/db/FileRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"FileRepository.js","sourceRoot":"","sources":["../../src/db/FileRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/MessageRepository.d.ts b/backend/dist/db/MessageRepository.d.ts deleted file mode 100644 index ef2c5ec..0000000 --- a/backend/dist/db/MessageRepository.d.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import BaseRepository, { BaseEntity } from './BaseRepository'; -export interface Message extends BaseEntity { - channel_id: string; - task_id?: string; - user_id: string; - content: string; - message_type: 'text' | 'voice' | 'file' | 'system' | 'command_result' | 'ai_response'; - voice_data?: Record; - transcription?: string; - attachments: any[]; - reply_to?: string; - thread_root?: string; - is_edited: boolean; - is_pinned: boolean; - is_announcement: boolean; - reactions: Record; - mentions: string[]; - ai_generated: boolean; - ai_context?: Record; - command_execution_id?: string; - metadata: Record; - formatting: Record; - edited_at?: Date; - user_name?: string; - user_avatar?: string; -} -export interface CreateMessageData { - channel_id: string; - task_id?: string; - user_id: string; - content: string; - message_type?: Message['message_type']; - voice_data?: Record; - transcription?: string; - attachments?: any[]; - reply_to?: string; - thread_root?: string; - mentions?: string[]; - ai_generated?: boolean; - ai_context?: Record; - command_execution_id?: string; - metadata?: Record; - formatting?: Record; -} -export interface MessageWithUser extends Message { - user_name: string; - user_email: string; - user_avatar?: string; - user_role: string; - reply_count?: number; - last_reply_timestamp?: Date; - deleted_by_name?: string; -} -declare class MessageRepository extends BaseRepository { - constructor(); - /** - * Create new message with validation - */ - createMessage(messageData: CreateMessageData, client?: DatabaseClient): Promise; - /** - * Find messages in a channel - */ - findChannelMessages(channelId: string, filters?: { - threadRoot?: string; - messageType?: string; - before?: Date; - after?: Date; - includeThreadReplies?: boolean; - }, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Get channel message count - */ - getChannelMessageCount(channelId: string, filters?: { - threadRoot?: string; - messageType?: string; - before?: Date; - after?: Date; - includeThreadReplies?: boolean; - }, client?: DatabaseClient): Promise; - /** - * Search messages in channel - */ - searchMessages(channelId: string, searchTerm: string, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Update message content - */ - updateMessage(messageId: string, updateData: Partial, client?: DatabaseClient): Promise; - /** - * Add reaction to message - */ - addReaction(messageId: string, userId: string, emoji: string, client?: DatabaseClient): Promise; - /** - * Remove reaction from message - */ - removeReaction(messageId: string, userId: string, emoji: string, client?: DatabaseClient): Promise; - /** - * Get message reactions - */ - getMessageReactions(messageId: string, client?: DatabaseClient): Promise>; - /** - * Update user's last read message in channel - */ - updateLastRead(channelId: string, userId: string, messageId?: string, client?: DatabaseClient): Promise; - /** - * Get latest message ID in channel - */ - private getLatestMessageId; - /** - * Get unread message count for user in channel - */ - getUnreadCount(channelId: string, userId: string, client?: DatabaseClient): Promise; - /** - * Get message thread - */ - getMessageThread(threadRootId: string, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Get pinned messages in channel - */ - getPinnedMessages(channelId: string, limit?: number, client?: DatabaseClient): Promise; - /** - * Get channel message statistics - */ - getChannelMessageStats(channelId: string, client?: DatabaseClient): Promise<{ - totalMessages: number; - messagesByType: Record; - topUsers: Array<{ - userId: string; - userName: string; - messageCount: number; - }>; - dailyActivity: Array<{ - date: string; - messageCount: number; - }>; - }>; -} -export default MessageRepository; -//# sourceMappingURL=MessageRepository.d.ts.map \ No newline at end of file diff --git a/backend/dist/db/MessageRepository.d.ts.map b/backend/dist/db/MessageRepository.d.ts.map deleted file mode 100644 index 6d90f4e..0000000 --- a/backend/dist/db/MessageRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"MessageRepository.d.ts","sourceRoot":"","sources":["../../src/db/MessageRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/MessageRepository.js b/backend/dist/db/MessageRepository.js deleted file mode 100644 index 5eab2d6..0000000 --- a/backend/dist/db/MessageRepository.js +++ /dev/null @@ -1,444 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const logger_1 = require("@utils/logger"); -const BaseRepository_1 = __importDefault(require("./BaseRepository")); -class MessageRepository extends BaseRepository_1.default { - constructor() { - super('messages'); - } - /** - * Create new message with validation - */ - async createMessage(messageData, client) { - // Set defaults - const messageToCreate = { - ...messageData, - message_type: messageData.message_type || 'text', - attachments: messageData.attachments || [], - mentions: messageData.mentions || [], - metadata: messageData.metadata || {}, - formatting: messageData.formatting || {}, - reactions: {}, - is_edited: false, - is_pinned: false, - is_announcement: false, - ai_generated: messageData.ai_generated || false, - }; - const message = await this.create(messageToCreate, client); - logger_1.logger.info({ - messageId: message.id, - channelId: message.channel_id, - userId: message.user_id, - messageType: message.message_type, - contentLength: message.content.length, - }, 'Message created successfully'); - return message; - } - /** - * Find messages in a channel - */ - async findChannelMessages(channelId, filters, limit = 50, offset = 0, client) { - let whereConditions = ['m.channel_id = $1', 'm.deleted_at IS NULL']; - let params = [channelId]; - let paramIndex = 2; - // Add filters - if (filters?.threadRoot) { - whereConditions.push(`m.thread_root = $${paramIndex}`); - params.push(filters.threadRoot); - paramIndex++; - } - else if (!filters?.includeThreadReplies) { - // For main channel messages, exclude thread replies (messages with thread_root) - // unless explicitly requested to include them - whereConditions.push(`m.thread_root IS NULL`); - } - if (filters?.messageType) { - whereConditions.push(`m.message_type = $${paramIndex}`); - params.push(filters.messageType); - paramIndex++; - } - if (filters?.before) { - whereConditions.push(`m.created_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - if (filters?.after) { - whereConditions.push(`m.created_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - const sql = ` - SELECT - m.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - COALESCE(thread_stats.reply_count, 0) as reply_count, - thread_stats.last_reply_timestamp, - deleter.name as deleted_by_name - FROM ${this.tableName} m - LEFT JOIN users u ON m.user_id = u.id - LEFT JOIN users deleter ON m.deleted_by = deleter.id - LEFT JOIN ( - SELECT - thread_root, - COUNT(*) as reply_count, - MAX(created_at) as last_reply_timestamp - FROM ${this.tableName} - WHERE thread_root IS NOT NULL - AND deleted_at IS NULL - GROUP BY thread_root - ) thread_stats ON m.id = thread_stats.thread_root - WHERE ${whereConditions.join(' AND ')} - ORDER BY m.created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - params.push(limit, offset); - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Get channel message count - */ - async getChannelMessageCount(channelId, filters, client) { - let whereConditions = ['channel_id = $1', 'deleted_at IS NULL']; - let params = [channelId]; - let paramIndex = 2; - // Add filters - if (filters?.threadRoot) { - whereConditions.push(`thread_root = $${paramIndex}`); - params.push(filters.threadRoot); - paramIndex++; - } - else if (!filters?.includeThreadReplies) { - // For main channel messages, exclude thread replies (messages with thread_root) - // unless explicitly requested to include them - whereConditions.push(`thread_root IS NULL`); - } - if (filters?.messageType) { - whereConditions.push(`message_type = $${paramIndex}`); - params.push(filters.messageType); - paramIndex++; - } - if (filters?.before) { - whereConditions.push(`created_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - if (filters?.after) { - whereConditions.push(`created_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - const sql = ` - SELECT COUNT(*) as count - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - `; - const result = await this.executeRawQuery(sql, params, client); - return parseInt(result.rows[0]?.count || '0', 10); - } - /** - * Search messages in channel - */ - async searchMessages(channelId, searchTerm, limit = 50, offset = 0, client) { - const sql = ` - SELECT - m.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - deleter.name as deleted_by_name - FROM ${this.tableName} m - LEFT JOIN users u ON m.user_id = u.id - LEFT JOIN users deleter ON m.deleted_by = deleter.id - WHERE m.channel_id = $1 - AND m.deleted_at IS NULL - AND ( - LOWER(m.content) LIKE LOWER($2) - OR LOWER(m.transcription) LIKE LOWER($2) - ) - ORDER BY m.created_at DESC - LIMIT $3 OFFSET $4 - `; - const result = await this.executeRawQuery(sql, [channelId, `%${searchTerm}%`, limit, offset], client); - return result.rows; - } - /** - * Update message content - */ - async updateMessage(messageId, updateData, client) { - const message = await this.update(messageId, updateData, client); - if (message) { - logger_1.logger.info({ - messageId, - updatedFields: Object.keys(updateData), - }, 'Message updated successfully'); - } - return message; - } - /** - * Add reaction to message - */ - async addReaction(messageId, userId, emoji, client) { - const sql = ` - UPDATE ${this.tableName} - SET reactions = COALESCE(reactions, '{}'::jsonb) || - jsonb_build_object($2, - COALESCE((reactions->$2)::jsonb, '[]'::jsonb) || - CASE - WHEN (reactions->$2)::text LIKE '%"' || $3 || '"%' - THEN '[]'::jsonb - ELSE jsonb_build_array($3) - END - ) - WHERE id = $1 AND deleted_at IS NULL - RETURNING id - `; - try { - const result = await this.executeRawQuery(sql, [messageId, emoji, userId], client); - if (result.rowCount > 0) { - logger_1.logger.info({ - messageId, - userId, - emoji, - }, 'Reaction added to message'); - return true; - } - return false; - } - catch (error) { - logger_1.logger.error({ error, messageId, userId, emoji }, 'Failed to add reaction'); - return false; - } - } - /** - * Remove reaction from message - */ - async removeReaction(messageId, userId, emoji, client) { - const sql = ` - UPDATE ${this.tableName} - SET reactions = COALESCE(reactions, '{}'::jsonb) || - jsonb_build_object($2, - COALESCE( - (SELECT jsonb_agg(elem) - FROM jsonb_array_elements_text((reactions->$2)::jsonb) AS elem - WHERE elem != $3), - '[]'::jsonb - ) - ) - WHERE id = $1 AND deleted_at IS NULL - RETURNING id - `; - try { - const result = await this.executeRawQuery(sql, [messageId, emoji, userId], client); - if (result.rowCount > 0) { - logger_1.logger.info({ - messageId, - userId, - emoji, - }, 'Reaction removed from message'); - return true; - } - return false; - } - catch (error) { - logger_1.logger.error({ error, messageId, userId, emoji }, 'Failed to remove reaction'); - return false; - } - } - /** - * Get message reactions - */ - async getMessageReactions(messageId, client) { - const sql = ` - SELECT reactions - FROM ${this.tableName} - WHERE id = $1 AND deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [messageId], client); - return result.rows[0]?.reactions || {}; - } - /** - * Update user's last read message in channel - */ - async updateLastRead(channelId, userId, messageId, client) { - // If no messageId provided, use the latest message - const latestMessageId = messageId || await this.getLatestMessageId(channelId, client); - if (!latestMessageId) - return; - const sql = ` - INSERT INTO channel_read_status (channel_id, user_id, last_read_message_id, last_read_at) - VALUES ($1, $2, $3, NOW()) - ON CONFLICT (channel_id, user_id) - DO UPDATE SET - last_read_message_id = EXCLUDED.last_read_message_id, - last_read_at = EXCLUDED.last_read_at - `; - try { - await this.executeRawQuery(sql, [channelId, userId, latestMessageId], client); - logger_1.logger.debug({ - channelId, - userId, - messageId: latestMessageId, - }, 'Last read message updated'); - } - catch (error) { - logger_1.logger.error({ error, channelId, userId }, 'Failed to update last read'); - } - } - /** - * Get latest message ID in channel - */ - async getLatestMessageId(channelId, client) { - const sql = ` - SELECT id - FROM ${this.tableName} - WHERE channel_id = $1 AND deleted_at IS NULL - ORDER BY created_at DESC - LIMIT 1 - `; - const result = await this.executeRawQuery(sql, [channelId], client); - return result.rows[0]?.id || null; - } - /** - * Get unread message count for user in channel - */ - async getUnreadCount(channelId, userId, client) { - const sql = ` - WITH last_read AS ( - SELECT last_read_message_id, last_read_at - FROM channel_read_status - WHERE channel_id = $1 AND user_id = $2 - ) - SELECT COUNT(*) as count - FROM ${this.tableName} m - LEFT JOIN last_read lr ON true - WHERE m.channel_id = $1 - AND m.deleted_at IS NULL - AND m.user_id != $2 - AND ( - lr.last_read_message_id IS NULL - OR m.created_at > lr.last_read_at - ) - `; - const result = await this.executeRawQuery(sql, [channelId, userId], client); - return parseInt(result.rows[0]?.count || '0', 10); - } - /** - * Get message thread - */ - async getMessageThread(threadRootId, limit = 50, offset = 0, client) { - const sql = ` - SELECT - m.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - deleter.name as deleted_by_name - FROM ${this.tableName} m - LEFT JOIN users u ON m.user_id = u.id - LEFT JOIN users deleter ON m.deleted_by = deleter.id - WHERE (m.id = $1 OR m.thread_root = $1) - AND m.deleted_at IS NULL - ORDER BY m.created_at ASC - LIMIT $2 OFFSET $3 - `; - const result = await this.executeRawQuery(sql, [threadRootId, limit, offset], client); - return result.rows; - } - /** - * Get pinned messages in channel - */ - async getPinnedMessages(channelId, limit = 20, client) { - const sql = ` - SELECT - m.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - deleter.name as deleted_by_name - FROM ${this.tableName} m - LEFT JOIN users u ON m.user_id = u.id - LEFT JOIN users deleter ON m.deleted_by = deleter.id - WHERE m.channel_id = $1 - AND m.is_pinned = true - AND m.deleted_at IS NULL - ORDER BY m.created_at DESC - LIMIT $2 - `; - const result = await this.executeRawQuery(sql, [channelId, limit], client); - return result.rows; - } - /** - * Get channel message statistics - */ - async getChannelMessageStats(channelId, client) { - const statsSql = ` - SELECT - COUNT(*) as total_messages, - COUNT(*) FILTER (WHERE message_type = 'text') as text_messages, - COUNT(*) FILTER (WHERE message_type = 'voice') as voice_messages, - COUNT(*) FILTER (WHERE message_type = 'file') as file_messages, - COUNT(*) FILTER (WHERE message_type = 'system') as system_messages - FROM ${this.tableName} - WHERE channel_id = $1 AND deleted_at IS NULL - `; - const topUsersSql = ` - SELECT - m.user_id, - u.name as user_name, - COUNT(*) as message_count - FROM ${this.tableName} m - LEFT JOIN users u ON m.user_id = u.id - WHERE m.channel_id = $1 AND m.deleted_at IS NULL - GROUP BY m.user_id, u.name - ORDER BY message_count DESC - LIMIT 10 - `; - const dailyActivitySql = ` - SELECT - DATE(created_at) as date, - COUNT(*) as message_count - FROM ${this.tableName} - WHERE channel_id = $1 - AND deleted_at IS NULL - AND created_at >= NOW() - INTERVAL '30 days' - GROUP BY DATE(created_at) - ORDER BY date DESC - `; - const [statsResult, topUsersResult, dailyActivityResult] = await Promise.all([ - this.executeRawQuery(statsSql, [channelId], client), - this.executeRawQuery(topUsersSql, [channelId], client), - this.executeRawQuery(dailyActivitySql, [channelId], client), - ]); - const stats = statsResult.rows[0]; - return { - totalMessages: parseInt(stats.total_messages, 10), - messagesByType: { - text: parseInt(stats.text_messages, 10), - voice: parseInt(stats.voice_messages, 10), - file: parseInt(stats.file_messages, 10), - system: parseInt(stats.system_messages, 10), - }, - topUsers: topUsersResult.rows.map((row) => ({ - userId: row.user_id, - userName: row.user_name, - messageCount: parseInt(row.message_count, 10), - })), - dailyActivity: dailyActivityResult.rows.map((row) => ({ - date: row.date, - messageCount: parseInt(row.message_count, 10), - })), - }; - } -} -exports.default = MessageRepository; -//# sourceMappingURL=MessageRepository.js.map \ No newline at end of file diff --git a/backend/dist/db/MessageRepository.js.map b/backend/dist/db/MessageRepository.js.map deleted file mode 100644 index b02eb29..0000000 --- a/backend/dist/db/MessageRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"MessageRepository.js","sourceRoot":"","sources":["../../src/db/MessageRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/TaskRepository.d.ts b/backend/dist/db/TaskRepository.d.ts deleted file mode 100644 index 05d51ac..0000000 --- a/backend/dist/db/TaskRepository.d.ts +++ /dev/null @@ -1,169 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import BaseRepository, { BaseEntity } from './BaseRepository'; -export interface Task extends BaseEntity { - title: string; - description?: string; - channel_id?: string; - parent_task_id?: string; - created_by: string; - assigned_to: string[]; - owned_by?: string; - priority: 'low' | 'medium' | 'high' | 'urgent' | 'critical'; - status: 'pending' | 'in_progress' | 'review' | 'completed' | 'cancelled' | 'on_hold'; - task_type: 'general' | 'project' | 'maintenance' | 'emergency' | 'research' | 'approval'; - complexity: number; - estimated_hours?: number; - actual_hours: number; - story_points?: number; - due_date?: Date; - start_date?: Date; - completed_at?: Date; - progress_percentage: number; - tags: string[]; - labels: Record; - custom_fields: Record; - voice_created: boolean; - voice_command_id?: string; - voice_instructions?: string; - ai_generated: boolean; - ai_suggestions: Record; - automation_rules: Record; - watchers: string[]; - comments_count: number; - attachments_count: number; - business_value: 'low' | 'medium' | 'high' | 'critical'; - cost_center?: string; - budget_impact?: number; - acceptance_criteria?: string; - definition_of_done?: string; - quality_score?: number; - external_references: Record; - integrations: Record; - recurrence_pattern?: Record; - recurrence_parent_id?: string; - is_recurring: boolean; - last_activity_at: Date; -} -export interface CreateTaskData { - title: string; - description?: string; - channel_id?: string; - parent_task_id?: string; - created_by: string; - assigned_to?: string[]; - owned_by?: string; - priority?: Task['priority']; - task_type?: Task['task_type']; - complexity?: number; - estimated_hours?: number; - due_date?: Date; - start_date?: Date; - tags?: string[]; - labels?: Record; - voice_created?: boolean; - voice_command_id?: string; - voice_instructions?: string; - business_value?: Task['business_value']; - acceptance_criteria?: string; -} -export interface TaskWithDetails extends Task { - channel_name?: string; - owner_name?: string; - assignee_details?: Array<{ - id: string; - name: string; - email: string; - avatar_url?: string; - }>; - subtask_count?: number; - dependency_count?: number; -} -export interface TaskFilter { - status?: Task['status'][]; - priority?: Task['priority'][]; - assignedTo?: string[]; - channelId?: string; - dueAfter?: Date; - dueBefore?: Date; - tags?: string[]; - voiceCreated?: boolean; - overdue?: boolean; -} -declare class TaskRepository extends BaseRepository { - constructor(); - /** - * Create new task with validation - */ - createTask(taskData: CreateTaskData, client?: DatabaseClient): Promise; - /** - * Assign users to task - */ - assignUsers(taskId: string, userIds: string[], assignedBy: string, client?: DatabaseClient): Promise; - /** - * Unassign users from task - */ - unassignUsers(taskId: string, userIds: string[], unassignedBy: string, client?: DatabaseClient): Promise; - /** - * Find tasks assigned to user - */ - findByAssignee(userId: string, status?: Task['status'][], includeWatching?: boolean, client?: DatabaseClient): Promise; - /** - * Find tasks in channel - */ - findByChannel(channelId: string, includeCompleted?: boolean, client?: DatabaseClient): Promise; - /** - * Find overdue tasks - */ - findOverdue(userId?: string, client?: DatabaseClient): Promise; - /** - * Find tasks with filters - */ - findWithFilters(filters: TaskFilter, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Update task progress - */ - updateProgress(taskId: string, progressPercentage: number, updatedBy: string, client?: DatabaseClient): Promise; - /** - * Update task status - */ - updateStatus(taskId: string, status: Task['status'], updatedBy: string, client?: DatabaseClient): Promise; - /** - * Get task with details (channel, assignees, subtasks) - */ - findWithDetails(taskId: string, client?: DatabaseClient): Promise; - /** - * Get task hierarchy (parent and children) - */ - getTaskHierarchy(taskId: string, client?: DatabaseClient): Promise>; - /** - * Search tasks by title or description - */ - searchTasks(searchTerm: string, userId?: string, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Get task statistics - */ - getTaskStats(userId?: string, client?: DatabaseClient): Promise<{ - totalTasks: number; - tasksByStatus: Record; - tasksByPriority: Record; - overdueTasks: number; - completedThisWeek: number; - averageCompletionTime: number; - }>; - /** - * Add watcher to task - */ - addWatcher(taskId: string, userId: string, client?: DatabaseClient): Promise; - /** - * Remove watcher from task - */ - removeWatcher(taskId: string, userId: string, client?: DatabaseClient): Promise; -} -export default TaskRepository; -//# sourceMappingURL=TaskRepository.d.ts.map \ No newline at end of file diff --git a/backend/dist/db/TaskRepository.d.ts.map b/backend/dist/db/TaskRepository.d.ts.map deleted file mode 100644 index 6e73cc3..0000000 --- a/backend/dist/db/TaskRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TaskRepository.d.ts","sourceRoot":"","sources":["../../src/db/TaskRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/TaskRepository.js b/backend/dist/db/TaskRepository.js deleted file mode 100644 index ba434d5..0000000 --- a/backend/dist/db/TaskRepository.js +++ /dev/null @@ -1,460 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const BaseRepository_1 = __importDefault(require("./BaseRepository")); -class TaskRepository extends BaseRepository_1.default { - constructor() { - super('tasks'); - } - /** - * Create new task with validation - */ - async createTask(taskData, client) { - // Validate parent task if specified - if (taskData.parent_task_id) { - const parentTask = await this.findById(taskData.parent_task_id, false, client); - if (!parentTask) { - throw new errors_1.ValidationError('Parent task not found', [ - { field: 'parent_task_id', message: 'Parent task does not exist', value: taskData.parent_task_id } - ]); - } - } - // Set defaults - const taskToCreate = { - ...taskData, - assigned_to: taskData.assigned_to || [taskData.created_by], - owned_by: taskData.owned_by || taskData.assigned_to?.[0] || taskData.created_by, - priority: taskData.priority || 'medium', - status: 'pending', - task_type: taskData.task_type || 'general', - complexity: taskData.complexity || 1, - actual_hours: 0, - progress_percentage: 0, - tags: taskData.tags || [], - labels: taskData.labels || {}, - custom_fields: {}, - voice_created: taskData.voice_created || false, - ai_generated: false, - ai_suggestions: {}, - automation_rules: {}, - watchers: [], - comments_count: 0, - attachments_count: 0, - business_value: taskData.business_value || 'medium', - external_references: {}, - integrations: { - calendar_event: null, - email_thread: null, - document_links: [], - meeting_recordings: [] - }, - is_recurring: false, - last_activity_at: new Date() - }; - const task = await this.create(taskToCreate, client); - logger_1.logger.info({ - taskId: task.id, - title: task.title, - createdBy: task.created_by, - assignedTo: task.assigned_to, - channelId: task.channel_id - }, 'Task created successfully'); - return task; - } - /** - * Assign users to task - */ - async assignUsers(taskId, userIds, assignedBy, client) { - const sql = ` - SELECT assign_task($1, $2, $3) - `; - try { - const result = await this.executeRawQuery(sql, [taskId, userIds, assignedBy], client); - const success = result.rows[0]?.assign_task || false; - if (success) { - logger_1.logger.info({ - taskId, - userIds, - assignedBy - }, 'Users assigned to task'); - } - return success; - } - catch (error) { - logger_1.logger.error({ error, taskId, userIds }, 'Failed to assign users to task'); - throw error; - } - } - /** - * Unassign users from task - */ - async unassignUsers(taskId, userIds, unassignedBy, client) { - const sql = ` - SELECT unassign_task($1, $2, $3) - `; - try { - const result = await this.executeRawQuery(sql, [taskId, userIds, unassignedBy], client); - const success = result.rows[0]?.unassign_task || false; - if (success) { - logger_1.logger.info({ - taskId, - userIds, - unassignedBy - }, 'Users unassigned from task'); - } - return success; - } - catch (error) { - logger_1.logger.error({ error, taskId, userIds }, 'Failed to unassign users from task'); - throw error; - } - } - /** - * Find tasks assigned to user - */ - async findByAssignee(userId, status, includeWatching = true, client) { - const sql = ` - SELECT * FROM get_user_tasks($1, $2, $3) - `; - const result = await this.executeRawQuery(sql, [userId, status, includeWatching], client); - return result.rows; - } - /** - * Find tasks in channel - */ - async findByChannel(channelId, includeCompleted = true, client) { - const statusFilter = includeCompleted ? {} : { status: ['pending', 'in_progress', 'review', 'on_hold'] }; - const options = { - filters: { channel_id: channelId, ...statusFilter }, - orderBy: 'created_at', - orderDirection: 'DESC' - }; - const result = await this.findMany(options, client); - return result.data; - } - /** - * Find overdue tasks - */ - async findOverdue(userId, client) { - let sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE due_date < NOW() - AND status NOT IN ('completed', 'cancelled') - AND deleted_at IS NULL - `; - let params = []; - if (userId) { - sql += ` AND $1 = ANY(assigned_to)`; - params = [userId]; - } - sql += ` ORDER BY due_date ASC`; - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Find tasks with filters - */ - async findWithFilters(filters, limit = 50, offset = 0, client) { - const whereConditions = ['deleted_at IS NULL']; - const params = []; - let paramCounter = 1; - // Status filter - if (filters.status && filters.status.length > 0) { - whereConditions.push(`status = ANY($${paramCounter})`); - params.push(filters.status); - paramCounter++; - } - // Priority filter - if (filters.priority && filters.priority.length > 0) { - whereConditions.push(`priority = ANY($${paramCounter})`); - params.push(filters.priority); - paramCounter++; - } - // Assignee filter - if (filters.assignedTo && filters.assignedTo.length > 0) { - whereConditions.push(`assigned_to && $${paramCounter}`); - params.push(filters.assignedTo); - paramCounter++; - } - // Channel filter - if (filters.channelId) { - whereConditions.push(`channel_id = $${paramCounter}`); - params.push(filters.channelId); - paramCounter++; - } - // Due date filters - if (filters.dueAfter) { - whereConditions.push(`due_date >= $${paramCounter}`); - params.push(filters.dueAfter); - paramCounter++; - } - if (filters.dueBefore) { - whereConditions.push(`due_date <= $${paramCounter}`); - params.push(filters.dueBefore); - paramCounter++; - } - // Tags filter - if (filters.tags && filters.tags.length > 0) { - whereConditions.push(`tags && $${paramCounter}`); - params.push(filters.tags); - paramCounter++; - } - // Voice created filter - if (filters.voiceCreated !== undefined) { - whereConditions.push(`voice_created = $${paramCounter}`); - params.push(filters.voiceCreated); - paramCounter++; - } - // Overdue filter - if (filters.overdue) { - whereConditions.push(`due_date < NOW() AND status NOT IN ('completed', 'cancelled')`); - } - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - ORDER BY - CASE priority - WHEN 'critical' THEN 5 - WHEN 'urgent' THEN 4 - WHEN 'high' THEN 3 - WHEN 'medium' THEN 2 - ELSE 1 - END DESC, - due_date ASC NULLS LAST, - created_at DESC - LIMIT $${paramCounter} OFFSET $${paramCounter + 1} - `; - params.push(limit, offset); - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Update task progress - */ - async updateProgress(taskId, progressPercentage, updatedBy, client) { - if (progressPercentage < 0 || progressPercentage > 100) { - throw new errors_1.ValidationError('Progress percentage must be between 0 and 100', [ - { field: 'progress_percentage', message: 'Invalid progress value', value: progressPercentage } - ]); - } - const updateData = { - progress_percentage: progressPercentage, - last_activity_at: new Date() - }; - const task = await this.update(taskId, updateData, undefined, client); - logger_1.logger.info({ - taskId, - progressPercentage, - updatedBy - }, 'Task progress updated'); - return task; - } - /** - * Update task status - */ - async updateStatus(taskId, status, updatedBy, client) { - const updateData = { - status, - last_activity_at: new Date() - }; - // Set completion timestamp for completed tasks - if (status === 'completed') { - updateData.completed_at = new Date(); - updateData.progress_percentage = 100; - } - // Clear completion timestamp when moving away from completed - const currentTask = await this.findById(taskId, false, client); - if (currentTask?.status === 'completed' && status !== 'completed') { - updateData.completed_at = null; - } - const task = await this.update(taskId, updateData, undefined, client); - logger_1.logger.info({ - taskId, - status, - updatedBy, - previousStatus: currentTask?.status - }, 'Task status updated'); - return task; - } - /** - * Get task with details (channel, assignees, subtasks) - */ - async findWithDetails(taskId, client) { - const sql = ` - SELECT - t.*, - c.name as channel_name, - owner.name as owner_name, - ARRAY( - SELECT json_build_object( - 'id', u.id, - 'name', u.name, - 'email', u.email, - 'avatar_url', u.avatar_url - ) - FROM users u - WHERE u.id = ANY(t.assigned_to) - AND u.deleted_at IS NULL - ORDER BY u.name - ) as assignee_details, - ( - SELECT COUNT(*) - FROM tasks subtasks - WHERE subtasks.parent_task_id = t.id - AND subtasks.deleted_at IS NULL - ) as subtask_count, - ( - SELECT COUNT(*) - FROM task_dependencies td - WHERE td.task_id = t.id - AND td.deleted_at IS NULL - AND td.is_active = true - ) as dependency_count - FROM tasks t - LEFT JOIN channels c ON t.channel_id = c.id - LEFT JOIN users owner ON t.owned_by = owner.id - WHERE t.id = $1 AND t.deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [taskId], client); - return result.rows[0] || null; - } - /** - * Get task hierarchy (parent and children) - */ - async getTaskHierarchy(taskId, client) { - const sql = ` - SELECT * FROM get_task_hierarchy($1) - `; - const result = await this.executeRawQuery(sql, [taskId], client); - return result.rows; - } - /** - * Search tasks by title or description - */ - async searchTasks(searchTerm, userId, limit = 20, offset = 0, client) { - let userCondition = ''; - let params = [`%${searchTerm}%`, limit, offset]; - if (userId) { - userCondition = 'AND ($4 = ANY(assigned_to) OR $4 = ANY(watchers) OR created_by = $4)'; - params.push(userId); - } - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ( - LOWER(title) LIKE LOWER($1) OR - LOWER(description) LIKE LOWER($1) OR - LOWER(array_to_string(tags, ' ')) LIKE LOWER($1) - ) - AND deleted_at IS NULL - AND status != 'cancelled' - ${userCondition} - ORDER BY - CASE WHEN LOWER(title) LIKE LOWER($1) THEN 1 ELSE 2 END, - CASE priority - WHEN 'critical' THEN 5 - WHEN 'urgent' THEN 4 - WHEN 'high' THEN 3 - WHEN 'medium' THEN 2 - ELSE 1 - END DESC, - created_at DESC - LIMIT $2 OFFSET $3 - `; - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Get task statistics - */ - async getTaskStats(userId, client) { - let userCondition = ''; - let params = []; - if (userId) { - userCondition = 'WHERE ($1 = ANY(assigned_to) OR created_by = $1) AND deleted_at IS NULL'; - params = [userId]; - } - else { - userCondition = 'WHERE deleted_at IS NULL'; - } - const sql = ` - SELECT - COUNT(*) as total_tasks, - COUNT(*) FILTER (WHERE status = 'pending') as pending_tasks, - COUNT(*) FILTER (WHERE status = 'in_progress') as in_progress_tasks, - COUNT(*) FILTER (WHERE status = 'review') as review_tasks, - COUNT(*) FILTER (WHERE status = 'completed') as completed_tasks, - COUNT(*) FILTER (WHERE status = 'cancelled') as cancelled_tasks, - COUNT(*) FILTER (WHERE status = 'on_hold') as on_hold_tasks, - COUNT(*) FILTER (WHERE priority = 'low') as low_priority, - COUNT(*) FILTER (WHERE priority = 'medium') as medium_priority, - COUNT(*) FILTER (WHERE priority = 'high') as high_priority, - COUNT(*) FILTER (WHERE priority = 'urgent') as urgent_priority, - COUNT(*) FILTER (WHERE priority = 'critical') as critical_priority, - COUNT(*) FILTER (WHERE due_date < NOW() AND status NOT IN ('completed', 'cancelled')) as overdue_tasks, - COUNT(*) FILTER (WHERE status = 'completed' AND completed_at >= NOW() - INTERVAL '7 days') as completed_this_week, - AVG(EXTRACT(EPOCH FROM (completed_at - created_at)) / 3600) FILTER (WHERE completed_at IS NOT NULL) as avg_completion_hours - FROM ${this.tableName} - ${userCondition} - `; - const result = await this.executeRawQuery(sql, params, client); - const stats = result.rows[0]; - return { - totalTasks: parseInt(stats.total_tasks, 10), - tasksByStatus: { - pending: parseInt(stats.pending_tasks, 10), - in_progress: parseInt(stats.in_progress_tasks, 10), - review: parseInt(stats.review_tasks, 10), - completed: parseInt(stats.completed_tasks, 10), - cancelled: parseInt(stats.cancelled_tasks, 10), - on_hold: parseInt(stats.on_hold_tasks, 10) - }, - tasksByPriority: { - low: parseInt(stats.low_priority, 10), - medium: parseInt(stats.medium_priority, 10), - high: parseInt(stats.high_priority, 10), - urgent: parseInt(stats.urgent_priority, 10), - critical: parseInt(stats.critical_priority, 10) - }, - overdueTasks: parseInt(stats.overdue_tasks, 10), - completedThisWeek: parseInt(stats.completed_this_week, 10), - averageCompletionTime: parseFloat(stats.avg_completion_hours || '0') - }; - } - /** - * Add watcher to task - */ - async addWatcher(taskId, userId, client) { - const sql = ` - UPDATE ${this.tableName} - SET watchers = array_append(watchers, $2) - WHERE id = $1 - AND deleted_at IS NULL - AND NOT ($2 = ANY(watchers)) - `; - const result = await this.executeRawQuery(sql, [taskId, userId], client); - return result.rowCount > 0; - } - /** - * Remove watcher from task - */ - async removeWatcher(taskId, userId, client) { - const sql = ` - UPDATE ${this.tableName} - SET watchers = array_remove(watchers, $2) - WHERE id = $1 - AND deleted_at IS NULL - AND $2 = ANY(watchers) - `; - const result = await this.executeRawQuery(sql, [taskId, userId], client); - return result.rowCount > 0; - } -} -exports.default = TaskRepository; -//# sourceMappingURL=TaskRepository.js.map \ No newline at end of file diff --git a/backend/dist/db/TaskRepository.js.map b/backend/dist/db/TaskRepository.js.map deleted file mode 100644 index b02ed29..0000000 --- a/backend/dist/db/TaskRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TaskRepository.js","sourceRoot":"","sources":["../../src/db/TaskRepository.ts"],"names":[],"mappings":";;;;;AACA,0CAAuC;AACvC,0CAAmF;AACnF,sEAA6E;AA+F7E,MAAM,cAAe,SAAQ,wBAAoB;IAC/C;QACE,KAAK,CAAC,OAAO,CAAC,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,QAAwB,EAAE,MAAuB;QAChE,oCAAoC;QACpC,IAAI,QAAQ,CAAC,cAAc,EAAE,CAAC;YAC5B,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,cAAc,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;YAC/E,IAAI,CAAC,UAAU,EAAE,CAAC;gBAChB,MAAM,IAAI,wBAAe,CAAC,uBAAuB,EAAE;oBACjD,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,4BAA4B,EAAE,KAAK,EAAE,QAAQ,CAAC,cAAc,EAAE;iBACnG,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,eAAe;QACf,MAAM,YAAY,GAAG;YACnB,GAAG,QAAQ;YACX,WAAW,EAAE,QAAQ,CAAC,WAAW,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;YAC1D,QAAQ,EAAE,QAAQ,CAAC,QAAQ,IAAI,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,QAAQ,CAAC,UAAU;YAC/E,QAAQ,EAAE,QAAQ,CAAC,QAAQ,IAAI,QAAQ;YACvC,MAAM,EAAE,SAAkB;YAC1B,SAAS,EAAE,QAAQ,CAAC,SAAS,IAAI,SAAS;YAC1C,UAAU,EAAE,QAAQ,CAAC,UAAU,IAAI,CAAC;YACpC,YAAY,EAAE,CAAC;YACf,mBAAmB,EAAE,CAAC;YACtB,IAAI,EAAE,QAAQ,CAAC,IAAI,IAAI,EAAE;YACzB,MAAM,EAAE,QAAQ,CAAC,MAAM,IAAI,EAAE;YAC7B,aAAa,EAAE,EAAE;YACjB,aAAa,EAAE,QAAQ,CAAC,aAAa,IAAI,KAAK;YAC9C,YAAY,EAAE,KAAK;YACnB,cAAc,EAAE,EAAE;YAClB,gBAAgB,EAAE,EAAE;YACpB,QAAQ,EAAE,EAAE;YACZ,cAAc,EAAE,CAAC;YACjB,iBAAiB,EAAE,CAAC;YACpB,cAAc,EAAE,QAAQ,CAAC,cAAc,IAAI,QAAQ;YACnD,mBAAmB,EAAE,EAAE;YACvB,YAAY,EAAE;gBACZ,cAAc,EAAE,IAAI;gBACpB,YAAY,EAAE,IAAI;gBAClB,cAAc,EAAE,EAAE;gBAClB,kBAAkB,EAAE,EAAE;aACvB;YACD,YAAY,EAAE,KAAK;YACnB,gBAAgB,EAAE,IAAI,IAAI,EAAE;SAC7B,CAAC;QAEF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;QAErD,eAAM,CAAC,IAAI,CAAC;YACV,MAAM,EAAE,IAAI,CAAC,EAAE;YACf,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,SAAS,EAAE,IAAI,CAAC,UAAU;YAC1B,UAAU,EAAE,IAAI,CAAC,WAAW;YAC5B,SAAS,EAAE,IAAI,CAAC,UAAU;SAC3B,EAAE,2BAA2B,CAAC,CAAC;QAEhC,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,MAAc,EAAE,OAAiB,EAAE,UAAkB,EAAE,MAAuB;QAC9F,MAAM,GAAG,GAAG;;KAEX,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CACvC,GAAG,EACH,CAAC,MAAM,EAAE,OAAO,EAAE,UAAU,CAAC,EAC7B,MAAM,CACP,CAAC;YAEF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,WAAW,IAAI,KAAK,CAAC;YAErD,IAAI,OAAO,EAAE,CAAC;gBACZ,eAAM,CAAC,IAAI,CAAC;oBACV,MAAM;oBACN,OAAO;oBACP,UAAU;iBACX,EAAE,wBAAwB,CAAC,CAAC;YAC/B,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,gCAAgC,CAAC,CAAC;YAC3E,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,MAAc,EAAE,OAAiB,EAAE,YAAoB,EAAE,MAAuB;QAClG,MAAM,GAAG,GAAG;;KAEX,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CACvC,GAAG,EACH,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,EAC/B,MAAM,CACP,CAAC;YAEF,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,aAAa,IAAI,KAAK,CAAC;YAEvD,IAAI,OAAO,EAAE,CAAC;gBACZ,eAAM,CAAC,IAAI,CAAC;oBACV,MAAM;oBACN,OAAO;oBACP,YAAY;iBACb,EAAE,4BAA4B,CAAC,CAAC;YACnC,CAAC;YAED,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,oCAAoC,CAAC,CAAC;YAC/E,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,MAAc,EAAE,MAAyB,EAAE,kBAA2B,IAAI,EAAE,MAAuB;QACtH,MAAM,GAAG,GAAG;;KAEX,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAM,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,CAAC,CAAC;QAC/F,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,SAAiB,EAAE,mBAA4B,IAAI,EAAE,MAAuB;QAC9F,MAAM,YAAY,GAAG,gBAAgB,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,SAAS,EAAE,aAAa,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,CAAC;QAEzG,MAAM,OAAO,GAAkB;YAC7B,OAAO,EAAE,EAAE,UAAU,EAAE,SAAS,EAAE,GAAG,YAAY,EAAE;YACnD,OAAO,EAAE,YAAY;YACrB,cAAc,EAAE,MAAM;SACvB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACpD,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CAAC,MAAe,EAAE,MAAuB;QACxD,IAAI,GAAG,GAAG;eACC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;aAC9B,IAAI,CAAC,SAAS;;;;KAItB,CAAC;QAEF,IAAI,MAAM,GAAU,EAAE,CAAC;QAEvB,IAAI,MAAM,EAAE,CAAC;YACX,GAAG,IAAI,4BAA4B,CAAC;YACpC,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;QAED,GAAG,IAAI,wBAAwB,CAAC;QAEhC,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAO,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACrE,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,OAAmB,EAAE,QAAgB,EAAE,EAAE,SAAiB,CAAC,EAAE,MAAuB;QACxG,MAAM,eAAe,GAAG,CAAC,oBAAoB,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAU,EAAE,CAAC;QACzB,IAAI,YAAY,GAAG,CAAC,CAAC;QAErB,gBAAgB;QAChB,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAChD,eAAe,CAAC,IAAI,CAAC,iBAAiB,YAAY,GAAG,CAAC,CAAC;YACvD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;YAC5B,YAAY,EAAE,CAAC;QACjB,CAAC;QAED,kBAAkB;QAClB,IAAI,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACpD,eAAe,CAAC,IAAI,CAAC,mBAAmB,YAAY,GAAG,CAAC,CAAC;YACzD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC9B,YAAY,EAAE,CAAC;QACjB,CAAC;QAED,kBAAkB;QAClB,IAAI,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxD,eAAe,CAAC,IAAI,CAAC,mBAAmB,YAAY,EAAE,CAAC,CAAC;YACxD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;YAChC,YAAY,EAAE,CAAC;QACjB,CAAC;QAED,iBAAiB;QACjB,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,eAAe,CAAC,IAAI,CAAC,iBAAiB,YAAY,EAAE,CAAC,CAAC;YACtD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC/B,YAAY,EAAE,CAAC;QACjB,CAAC;QAED,mBAAmB;QACnB,IAAI,OAAO,CAAC,QAAQ,EAAE,CAAC;YACrB,eAAe,CAAC,IAAI,CAAC,gBAAgB,YAAY,EAAE,CAAC,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC9B,YAAY,EAAE,CAAC;QACjB,CAAC;QAED,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;YACtB,eAAe,CAAC,IAAI,CAAC,gBAAgB,YAAY,EAAE,CAAC,CAAC;YACrD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YAC/B,YAAY,EAAE,CAAC;QACjB,CAAC;QAED,cAAc;QACd,IAAI,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5C,eAAe,CAAC,IAAI,CAAC,YAAY,YAAY,EAAE,CAAC,CAAC;YACjD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAC1B,YAAY,EAAE,CAAC;QACjB,CAAC;QAED,uBAAuB;QACvB,IAAI,OAAO,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;YACvC,eAAe,CAAC,IAAI,CAAC,oBAAoB,YAAY,EAAE,CAAC,CAAC;YACzD,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC;YAClC,YAAY,EAAE,CAAC;QACjB,CAAC;QAED,iBAAiB;QACjB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,eAAe,CAAC,IAAI,CAAC,+DAA+D,CAAC,CAAC;QACxF,CAAC;QAED,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;aAC9B,IAAI,CAAC,SAAS;cACb,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;;;;;;;;;;;eAW5B,YAAY,YAAY,YAAY,GAAG,CAAC;KAClD,CAAC;QAEF,MAAM,CAAC,IAAI,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;QAE3B,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAO,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACrE,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,cAAc,CAAC,MAAc,EAAE,kBAA0B,EAAE,SAAiB,EAAE,MAAuB;QACzG,IAAI,kBAAkB,GAAG,CAAC,IAAI,kBAAkB,GAAG,GAAG,EAAE,CAAC;YACvD,MAAM,IAAI,wBAAe,CAAC,+CAA+C,EAAE;gBACzE,EAAE,KAAK,EAAE,qBAAqB,EAAE,OAAO,EAAE,wBAAwB,EAAE,KAAK,EAAE,kBAAkB,EAAE;aAC/F,CAAC,CAAC;QACL,CAAC;QAED,MAAM,UAAU,GAAG;YACjB,mBAAmB,EAAE,kBAAkB;YACvC,gBAAgB,EAAE,IAAI,IAAI,EAAE;SAC7B,CAAC;QAEF,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAEtE,eAAM,CAAC,IAAI,CAAC;YACV,MAAM;YACN,kBAAkB;YAClB,SAAS;SACV,EAAE,uBAAuB,CAAC,CAAC;QAE5B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,MAAc,EAAE,MAAsB,EAAE,SAAiB,EAAE,MAAuB;QACnG,MAAM,UAAU,GAAQ;YACtB,MAAM;YACN,gBAAgB,EAAE,IAAI,IAAI,EAAE;SAC7B,CAAC;QAEF,+CAA+C;QAC/C,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,UAAU,CAAC,YAAY,GAAG,IAAI,IAAI,EAAE,CAAC;YACrC,UAAU,CAAC,mBAAmB,GAAG,GAAG,CAAC;QACvC,CAAC;QAED,6DAA6D;QAC7D,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAC/D,IAAI,WAAW,EAAE,MAAM,KAAK,WAAW,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAClE,UAAU,CAAC,YAAY,GAAG,IAAI,CAAC;QACjC,CAAC;QAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAEtE,eAAM,CAAC,IAAI,CAAC;YACV,MAAM;YACN,MAAM;YACN,SAAS;YACT,cAAc,EAAE,WAAW,EAAE,MAAM;SACpC,EAAE,qBAAqB,CAAC,CAAC;QAE1B,OAAO,IAAI,CAAC;IACd,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,eAAe,CAAC,MAAc,EAAE,MAAuB;QAC3D,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;KAkCX,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAkB,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QAClF,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAChC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,gBAAgB,CAAC,MAAc,EAAE,MAAuB;QAO5D,MAAM,GAAG,GAAG;;KAEX,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAM,GAAG,EAAE,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QACtE,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,WAAW,CACf,UAAkB,EAClB,MAAe,EACf,QAAgB,EAAE,EAClB,SAAiB,CAAC,EAClB,MAAuB;QAEvB,IAAI,aAAa,GAAG,EAAE,CAAC;QACvB,IAAI,MAAM,GAAG,CAAC,IAAI,UAAU,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;QAEhD,IAAI,MAAM,EAAE,CAAC;YACX,aAAa,GAAG,sEAAsE,CAAC;YACvF,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtB,CAAC;QAED,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC;aAC9B,IAAI,CAAC,SAAS;;;;;;;;QAQnB,aAAa;;;;;;;;;;;;KAYhB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAO,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACrE,OAAO,MAAM,CAAC,IAAI,CAAC;IACrB,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,YAAY,CAAC,MAAe,EAAE,MAAuB;QAQzD,IAAI,aAAa,GAAG,EAAE,CAAC;QACvB,IAAI,MAAM,GAAU,EAAE,CAAC;QAEvB,IAAI,MAAM,EAAE,CAAC;YACX,aAAa,GAAG,yEAAyE,CAAC;YAC1F,MAAM,GAAG,CAAC,MAAM,CAAC,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,aAAa,GAAG,0BAA0B,CAAC;QAC7C,CAAC;QAED,MAAM,GAAG,GAAG;;;;;;;;;;;;;;;;;aAiBH,IAAI,CAAC,SAAS;QACnB,aAAa;KAChB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAM,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;QACpE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAE7B,OAAO;YACL,UAAU,EAAE,QAAQ,CAAC,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YAC3C,aAAa,EAAE;gBACb,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC;gBAC1C,WAAW,EAAE,QAAQ,CAAC,KAAK,CAAC,iBAAiB,EAAE,EAAE,CAAC;gBAClD,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,CAAC;gBACxC,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;gBAC9C,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;gBAC9C,OAAO,EAAE,QAAQ,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC;aAC3C;YACD,eAAe,EAAE;gBACf,GAAG,EAAE,QAAQ,CAAC,KAAK,CAAC,YAAY,EAAE,EAAE,CAAC;gBACrC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;gBAC3C,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC;gBACvC,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,CAAC;gBAC3C,QAAQ,EAAE,QAAQ,CAAC,KAAK,CAAC,iBAAiB,EAAE,EAAE,CAAC;aAChD;YACD,YAAY,EAAE,QAAQ,CAAC,KAAK,CAAC,aAAa,EAAE,EAAE,CAAC;YAC/C,iBAAiB,EAAE,QAAQ,CAAC,KAAK,CAAC,mBAAmB,EAAE,EAAE,CAAC;YAC1D,qBAAqB,EAAE,UAAU,CAAC,KAAK,CAAC,oBAAoB,IAAI,GAAG,CAAC;SACrE,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,MAAc,EAAE,MAAc,EAAE,MAAuB;QACtE,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,SAAS;;;;;KAKxB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QACzE,OAAO,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;IAC7B,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,aAAa,CAAC,MAAc,EAAE,MAAc,EAAE,MAAuB;QACzE,MAAM,GAAG,GAAG;eACD,IAAI,CAAC,SAAS;;;;;KAKxB,CAAC;QAEF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,eAAe,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;QACzE,OAAO,MAAM,CAAC,QAAQ,GAAG,CAAC,CAAC;IAC7B,CAAC;CACF;AAED,kBAAe,cAAc,CAAC"} \ No newline at end of file diff --git a/backend/dist/db/UserRepository.d.ts b/backend/dist/db/UserRepository.d.ts deleted file mode 100644 index 08b498d..0000000 --- a/backend/dist/db/UserRepository.d.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import BaseRepository, { BaseEntity } from './BaseRepository'; -export interface User extends BaseEntity { - email: string; - name: string; - password_hash: string; - role: 'ceo' | 'manager' | 'staff'; - avatar_url?: string; - phone?: string; - department?: string; - job_title?: string; - language_preference: string; - timezone: string; - notification_settings: Record; - voice_settings: Record; - failed_login_attempts: number; - account_locked_until?: Date; - password_reset_token?: string; - password_reset_expires?: Date; - email_verification_token?: string; - email_verified: boolean; - last_active?: Date; - last_login?: Date; - login_count: number; - created_by?: string; -} -export interface CreateUserData { - email: string; - name: string; - password: string; - role: 'ceo' | 'manager' | 'staff'; - avatar_url?: string; - phone?: string; - department?: string; - job_title?: string; - language_preference?: string; - timezone?: string; - notification_settings?: Record; - voice_settings?: Record; - created_by?: string; -} -export interface UpdateUserData { - name?: string; - avatar_url?: string; - phone?: string; - department?: string; - job_title?: string; - language_preference?: string; - timezone?: string; - notification_settings?: Record; - voice_settings?: Record; -} -export interface UserStats { - totalUsers: number; - activeUsers: number; - usersByRole: Record; - recentLogins: number; - averageLoginCount: number; -} -declare class UserRepository extends BaseRepository { - constructor(); - /** - * Create new user with password hashing - */ - createUser(userData: CreateUserData, client?: DatabaseClient): Promise; - /** - * Find user by email - */ - findByEmail(email: string, includeDeleted?: boolean, client?: DatabaseClient): Promise; - /** - * Find user by email for authentication (includes password hash) - */ - findByEmailForAuth(email: string, client?: DatabaseClient): Promise<(User & { - password_hash: string; - }) | null>; - /** - * Find users by role - */ - findByRole(role: User['role'], includeDeleted?: boolean, client?: DatabaseClient): Promise; - /** - * Find users by department - */ - findByDepartment(department: string, includeDeleted?: boolean, client?: DatabaseClient): Promise; - /** - * Update user password - */ - updatePassword(userId: string, newPassword: string, expectedVersion?: number, client?: DatabaseClient): Promise; - /** - * Verify user password - */ - verifyPassword(email: string, password: string, client?: DatabaseClient): Promise; - /** - * Record successful login - */ - recordSuccessfulLogin(userId: string, client?: DatabaseClient): Promise; - /** - * Increment failed login attempts and potentially lock account - */ - incrementFailedLoginAttempts(userId: string, client?: DatabaseClient): Promise; - /** - * Update last active timestamp - */ - updateLastActive(userId: string, client?: DatabaseClient): Promise; - /** - * Set password reset token - */ - setPasswordResetToken(email: string, token: string, expiresIn?: string, client?: DatabaseClient): Promise; - /** - * Verify password reset token - */ - verifyPasswordResetToken(token: string, client?: DatabaseClient): Promise; - /** - * Set email verification token - */ - setEmailVerificationToken(userId: string, token: string, client?: DatabaseClient): Promise; - /** - * Verify email with token - */ - verifyEmail(token: string, client?: DatabaseClient): Promise; - /** - * Search users by name or email - */ - searchUsers(searchTerm: string, limit?: number, offset?: number, client?: DatabaseClient): Promise; - /** - * Get active users (logged in recently) - */ - getActiveUsers(withinHours?: number, client?: DatabaseClient): Promise; - /** - * Get user statistics - */ - getUserStats(client?: DatabaseClient): Promise; - /** - * Update user profile - */ - updateProfile(userId: string, profileData: UpdateUserData, expectedVersion?: number, client?: DatabaseClient): Promise; - /** - * Unlock user account - */ - unlockAccount(userId: string, client?: DatabaseClient): Promise; - /** - * Get users for channel assignment suggestions - */ - getUsersForChannelAssignment(channelId: string, excludeUserIds?: string[], client?: DatabaseClient): Promise; - /** - * Find all unverified users - */ - findUnverifiedUsers(client?: DatabaseClient): Promise; - /** - * Delete all unverified users (hard delete for cleanup) - */ - deleteUnverifiedUsers(client?: DatabaseClient): Promise; -} -export default UserRepository; -//# sourceMappingURL=UserRepository.d.ts.map \ No newline at end of file diff --git a/backend/dist/db/UserRepository.d.ts.map b/backend/dist/db/UserRepository.d.ts.map deleted file mode 100644 index e7d214f..0000000 --- a/backend/dist/db/UserRepository.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"UserRepository.d.ts","sourceRoot":"","sources":["../../src/db/UserRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/UserRepository.js b/backend/dist/db/UserRepository.js deleted file mode 100644 index 8662dcb..0000000 --- a/backend/dist/db/UserRepository.js +++ /dev/null @@ -1,427 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -const BaseRepository_1 = __importDefault(require("./BaseRepository")); -const bcryptjs_1 = __importDefault(require("bcryptjs")); -const index_1 = require("@config/index"); -class UserRepository extends BaseRepository_1.default { - constructor() { - super('users'); - // Exclude sensitive fields from default select - this.selectFields = [ - 'id', - 'email', - 'name', - 'role', - 'avatar_url', - 'phone', - 'department', - 'job_title', - 'language_preference', - 'timezone', - 'notification_settings', - 'voice_settings', - 'email_verified', - 'last_active', - 'last_login', - 'login_count', - 'created_at', - 'updated_at', - 'version', - 'deleted_at', - 'created_by', - ]; - } - /** - * Create new user with password hashing - */ - async createUser(userData, client) { - // Validate unique email - const existingUser = await this.findByEmail(userData.email, false, client); - if (existingUser) { - throw new errors_1.ConflictError('Email already exists', { email: userData.email }); - } - // Validate CEO uniqueness - if (userData.role === 'ceo') { - const existingCEO = await this.findByRole('ceo', false, client); - if (existingCEO.length > 0) { - throw new errors_1.ConflictError('CEO role already exists', { role: 'ceo' }); - } - } - // Hash password - const password_hash = await bcryptjs_1.default.hash(userData.password, index_1.config.security.bcryptRounds); - // Prepare user data - const { password, ...userDataWithoutPassword } = userData; - const userToCreate = { - ...userDataWithoutPassword, - password_hash, - email_verified: false, - failed_login_attempts: 0, - login_count: 0, - language_preference: userData.language_preference || 'en', - timezone: userData.timezone || 'UTC', - }; - const user = await this.create(userToCreate, client); - logger_1.logger.info({ - userId: user.id, - email: user.email, - role: user.role, - }, 'User created successfully'); - return user; - } - /** - * Find user by email - */ - async findByEmail(email, includeDeleted = false, client) { - const deletedCondition = includeDeleted ? '' : 'AND deleted_at IS NULL'; - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE LOWER(email) = LOWER($1) ${deletedCondition} - `; - const result = await this.executeRawQuery(sql, [email], client); - return result.rows[0] || null; - } - /** - * Find user by email for authentication (includes password hash) - */ - async findByEmailForAuth(email, client) { - const sql = ` - SELECT id, email, name, role, password_hash, email_verified, - failed_login_attempts, account_locked_until, version, - last_active, last_login, login_count - FROM ${this.tableName} - WHERE LOWER(email) = LOWER($1) AND deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [email], client); - return result.rows[0] || null; - } - /** - * Find users by role - */ - async findByRole(role, includeDeleted = false, client) { - const options = { - filters: { role }, - includeDeleted, - orderBy: 'name', - orderDirection: 'ASC', - }; - const result = await this.findMany(options, client); - return result.data; - } - /** - * Find users by department - */ - async findByDepartment(department, includeDeleted = false, client) { - const options = { - filters: { department }, - includeDeleted, - orderBy: 'name', - orderDirection: 'ASC', - }; - const result = await this.findMany(options, client); - return result.data; - } - /** - * Update user password - */ - async updatePassword(userId, newPassword, expectedVersion, client) { - const password_hash = await bcryptjs_1.default.hash(newPassword, index_1.config.security.bcryptRounds); - const sql = ` - UPDATE ${this.tableName} - SET password_hash = $2, password_reset_token = NULL, password_reset_expires = NULL - WHERE id = $1 AND deleted_at IS NULL - ${expectedVersion ? 'AND version = $3' : ''} - RETURNING id - `; - const params = expectedVersion - ? [userId, password_hash, expectedVersion] - : [userId, password_hash]; - const result = await this.executeRawQuery(sql, params, client); - const success = result.rows.length > 0; - if (success) { - logger_1.logger.info({ userId }, 'User password updated successfully'); - } - return success; - } - /** - * Verify user password - */ - async verifyPassword(email, password, client) { - const user = await this.findByEmailForAuth(email, client); - if (!user) { - return null; - } - // Check account lock - if (user.account_locked_until && user.account_locked_until > new Date()) { - throw new errors_1.ValidationError('Account is temporarily locked', [ - { - field: 'account', - message: 'Account locked due to failed login attempts', - value: user.account_locked_until, - }, - ]); - } - const isValidPassword = await bcryptjs_1.default.compare(password, user.password_hash); - if (!isValidPassword) { - // Increment failed attempts - await this.incrementFailedLoginAttempts(user.id, client); - return null; - } - // Reset failed attempts and update login info - await this.recordSuccessfulLogin(user.id, client); - // Return user without password hash - const { password_hash, ...userWithoutPassword } = user; - return userWithoutPassword; - } - /** - * Record successful login - */ - async recordSuccessfulLogin(userId, client) { - const sql = ` - UPDATE ${this.tableName} - SET - failed_login_attempts = 0, - account_locked_until = NULL, - last_login = NOW(), - last_active = NOW(), - login_count = login_count + 1 - WHERE id = $1 - `; - await this.executeRawQuery(sql, [userId], client); - } - /** - * Increment failed login attempts and potentially lock account - */ - async incrementFailedLoginAttempts(userId, client) { - const maxAttempts = index_1.config.security.maxLoginAttempts; - const lockoutDuration = index_1.config.security.lockoutDuration; - const sql = ` - UPDATE ${this.tableName} - SET - failed_login_attempts = failed_login_attempts + 1, - account_locked_until = CASE - WHEN failed_login_attempts + 1 >= $2 THEN NOW() + INTERVAL '${lockoutDuration}' - ELSE account_locked_until - END - WHERE id = $1 - `; - await this.executeRawQuery(sql, [userId, maxAttempts], client); - logger_1.logger.warn({ userId }, 'Failed login attempt recorded'); - } - /** - * Update last active timestamp - */ - async updateLastActive(userId, client) { - const sql = ` - UPDATE ${this.tableName} - SET last_active = NOW() - WHERE id = $1 AND deleted_at IS NULL - `; - await this.executeRawQuery(sql, [userId], client); - } - /** - * Set password reset token - */ - async setPasswordResetToken(email, token, expiresIn = '1 hour', client) { - const sql = ` - UPDATE ${this.tableName} - SET - password_reset_token = $2, - password_reset_expires = NOW() + INTERVAL '${expiresIn}' - WHERE LOWER(email) = LOWER($1) AND deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [email, token], client); - return result.rowCount > 0; - } - /** - * Verify password reset token - */ - async verifyPasswordResetToken(token, client) { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE password_reset_token = $1 - AND password_reset_expires > NOW() - AND deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [token], client); - return result.rows[0] || null; - } - /** - * Set email verification token - */ - async setEmailVerificationToken(userId, token, client) { - const sql = ` - UPDATE ${this.tableName} - SET email_verification_token = $2 - WHERE id = $1 AND deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [userId, token], client); - return result.rowCount > 0; - } - /** - * Verify email with token - */ - async verifyEmail(token, client) { - const sql = ` - UPDATE ${this.tableName} - SET - email_verified = true, - email_verification_token = NULL - WHERE email_verification_token = $1 AND deleted_at IS NULL - RETURNING ${this.selectFields.join(', ')} - `; - const result = await this.executeRawQuery(sql, [token], client); - if (result.rows.length > 0 && result.rows[0]) { - logger_1.logger.info({ userId: result.rows[0].id }, 'Email verified successfully'); - } - return result.rows[0] || null; - } - /** - * Search users by name or email - */ - async searchUsers(searchTerm, limit = 20, offset = 0, client) { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ( - LOWER(name) LIKE LOWER($1) OR - LOWER(email) LIKE LOWER($1) OR - LOWER(department) LIKE LOWER($1) OR - LOWER(job_title) LIKE LOWER($1) - ) - AND deleted_at IS NULL - ORDER BY - CASE WHEN LOWER(name) LIKE LOWER($1) THEN 1 ELSE 2 END, - name ASC - LIMIT $2 OFFSET $3 - `; - const searchPattern = `%${searchTerm}%`; - const result = await this.executeRawQuery(sql, [searchPattern, limit, offset], client); - return result.rows; - } - /** - * Get active users (logged in recently) - */ - async getActiveUsers(withinHours = 24, client) { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE last_active > NOW() - INTERVAL '${withinHours} hours' - AND deleted_at IS NULL - ORDER BY last_active DESC - `; - const result = await this.executeRawQuery(sql, [], client); - return result.rows; - } - /** - * Get user statistics - */ - async getUserStats(client) { - const sql = ` - SELECT - COUNT(*) as total_users, - COUNT(*) FILTER (WHERE last_active > NOW() - INTERVAL '7 days') as active_users, - COUNT(*) FILTER (WHERE role = 'ceo') as ceo_count, - COUNT(*) FILTER (WHERE role = 'manager') as manager_count, - COUNT(*) FILTER (WHERE role = 'staff') as staff_count, - COUNT(*) FILTER (WHERE last_login > NOW() - INTERVAL '24 hours') as recent_logins, - AVG(login_count) as average_login_count - FROM ${this.tableName} - WHERE deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [], client); - const stats = result.rows[0]; - return { - totalUsers: parseInt(stats.total_users, 10), - activeUsers: parseInt(stats.active_users, 10), - usersByRole: { - ceo: parseInt(stats.ceo_count, 10), - manager: parseInt(stats.manager_count, 10), - staff: parseInt(stats.staff_count, 10), - }, - recentLogins: parseInt(stats.recent_logins, 10), - averageLoginCount: parseFloat(stats.average_login_count || '0'), - }; - } - /** - * Update user profile - */ - async updateProfile(userId, profileData, expectedVersion, client) { - return this.update(userId, profileData, expectedVersion, client); - } - /** - * Unlock user account - */ - async unlockAccount(userId, client) { - const sql = ` - UPDATE ${this.tableName} - SET - failed_login_attempts = 0, - account_locked_until = NULL - WHERE id = $1 AND deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [userId], client); - if (result.rowCount > 0) { - logger_1.logger.info({ userId }, 'User account unlocked'); - } - return result.rowCount > 0; - } - /** - * Get users for channel assignment suggestions - */ - async getUsersForChannelAssignment(channelId, excludeUserIds = [], client) { - const excludeCondition = excludeUserIds.length > 0 ? 'AND id != ALL($2)' : ''; - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND email_verified = true - ${excludeCondition} - ORDER BY - CASE role - WHEN 'ceo' THEN 1 - WHEN 'manager' THEN 2 - ELSE 3 - END, - last_active DESC NULLS LAST, - name ASC - LIMIT 50 - `; - const params = excludeUserIds.length > 0 ? [excludeUserIds] : []; - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - /** - * Find all unverified users - */ - async findUnverifiedUsers(client) { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE email_verified = false AND deleted_at IS NULL - ORDER BY created_at DESC - `; - const result = await this.executeRawQuery(sql, [], client); - return result.rows; - } - /** - * Delete all unverified users (hard delete for cleanup) - */ - async deleteUnverifiedUsers(client) { - const sql = ` - DELETE FROM ${this.tableName} - WHERE email_verified = false AND deleted_at IS NULL - `; - const result = await this.executeRawQuery(sql, [], client); - logger_1.logger.info({ deleteCount: result.rowCount }, 'Deleted unverified users'); - return result.rowCount || 0; - } -} -exports.default = UserRepository; -//# sourceMappingURL=UserRepository.js.map \ No newline at end of file diff --git a/backend/dist/db/UserRepository.js.map b/backend/dist/db/UserRepository.js.map deleted file mode 100644 index 9b079f9..0000000 --- a/backend/dist/db/UserRepository.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"UserRepository.js","sourceRoot":"","sources":["../../src/db/UserRepository.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/index.d.ts b/backend/dist/db/index.d.ts deleted file mode 100644 index 3feba7d..0000000 --- a/backend/dist/db/index.d.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Database layer exports - * Centralized access to all repositories and database utilities - */ -export { default as BaseRepository } from './BaseRepository'; -export type { BaseEntity, FilterOptions, PaginatedResult } from './BaseRepository'; -export { default as UserRepository } from './UserRepository'; -export type { User, CreateUserData, UpdateUserData, UserStats } from './UserRepository'; -export { default as ChannelRepository } from './ChannelRepository'; -export type { Channel, CreateChannelData, ChannelWithDetails } from './ChannelRepository'; -export { default as TaskRepository } from './TaskRepository'; -export type { Task, CreateTaskData, TaskWithDetails, TaskFilter } from './TaskRepository'; -export { default as MessageRepository } from './MessageRepository'; -export type { Message, CreateMessageData, MessageWithUser } from './MessageRepository'; -export { default as ActivityRepository } from './ActivityRepository'; -export type { Activity, CreateActivityData, ActivityWithUser } from './ActivityRepository'; -export { default as FileRepository } from './FileRepository'; -export type { FileEntity, CreateFileData, FileWithUploader } from './FileRepository'; -export { default as AnnouncementRepository } from './AnnouncementRepository'; -export type { Announcement, CreateAnnouncementData, UpdateAnnouncementData, AnnouncementFilter } from './AnnouncementRepository'; -import UserRepository from './UserRepository'; -import ChannelRepository from './ChannelRepository'; -import TaskRepository from './TaskRepository'; -import MessageRepository from './MessageRepository'; -import ActivityRepository from './ActivityRepository'; -import FileRepository from './FileRepository'; -import AnnouncementRepository from './AnnouncementRepository'; -export declare const userRepository: UserRepository; -export declare const channelRepository: ChannelRepository; -export declare const taskRepository: TaskRepository; -export declare const messageRepository: MessageRepository; -export declare const activityRepository: ActivityRepository; -export declare const fileRepository: FileRepository; -export declare const announcementRepository: AnnouncementRepository; -export declare const repositories: { - readonly users: UserRepository; - readonly channels: ChannelRepository; - readonly tasks: TaskRepository; - readonly messages: MessageRepository; - readonly activities: ActivityRepository; - readonly files: FileRepository; - readonly announcements: AnnouncementRepository; -}; -export type Repositories = typeof repositories; -export { initializeDatabase, closeDatabase, getPool, query, transaction, healthCheck, getPoolStats, databaseMetrics } from '../config/database'; -export declare class DatabaseManager { - private static instance; - static getInstance(): DatabaseManager; - query(text: string, params?: any[], client?: any): Promise; - one(text: string, params?: any[], client?: any): Promise; - many(text: string, params?: any[], client?: any): Promise; - none(text: string, params?: any[], client?: any): Promise; - transaction(callback: (client: any) => Promise): Promise; -} -export { runMigrations, rollbackLastMigration, getMigrationStatus, validateMigrations } from './migrator'; -export default repositories; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/backend/dist/db/index.d.ts.map b/backend/dist/db/index.d.ts.map deleted file mode 100644 index a0fd56b..0000000 --- a/backend/dist/db/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/index.js b/backend/dist/db/index.js deleted file mode 100644 index d74fa17..0000000 --- a/backend/dist/db/index.js +++ /dev/null @@ -1,138 +0,0 @@ -"use strict"; -/** - * Database layer exports - * Centralized access to all repositories and database utilities - */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.validateMigrations = exports.getMigrationStatus = exports.rollbackLastMigration = exports.runMigrations = exports.DatabaseManager = exports.databaseMetrics = exports.getPoolStats = exports.healthCheck = exports.transaction = exports.query = exports.getPool = exports.closeDatabase = exports.initializeDatabase = exports.repositories = exports.announcementRepository = exports.fileRepository = exports.activityRepository = exports.messageRepository = exports.taskRepository = exports.channelRepository = exports.userRepository = exports.AnnouncementRepository = exports.FileRepository = exports.ActivityRepository = exports.MessageRepository = exports.TaskRepository = exports.ChannelRepository = exports.UserRepository = exports.BaseRepository = void 0; -// Base repository -var BaseRepository_1 = require("./BaseRepository"); -Object.defineProperty(exports, "BaseRepository", { enumerable: true, get: function () { return __importDefault(BaseRepository_1).default; } }); -// Specialized repositories -var UserRepository_1 = require("./UserRepository"); -Object.defineProperty(exports, "UserRepository", { enumerable: true, get: function () { return __importDefault(UserRepository_1).default; } }); -var ChannelRepository_1 = require("./ChannelRepository"); -Object.defineProperty(exports, "ChannelRepository", { enumerable: true, get: function () { return __importDefault(ChannelRepository_1).default; } }); -var TaskRepository_1 = require("./TaskRepository"); -Object.defineProperty(exports, "TaskRepository", { enumerable: true, get: function () { return __importDefault(TaskRepository_1).default; } }); -var MessageRepository_1 = require("./MessageRepository"); -Object.defineProperty(exports, "MessageRepository", { enumerable: true, get: function () { return __importDefault(MessageRepository_1).default; } }); -var ActivityRepository_1 = require("./ActivityRepository"); -Object.defineProperty(exports, "ActivityRepository", { enumerable: true, get: function () { return __importDefault(ActivityRepository_1).default; } }); -var FileRepository_1 = require("./FileRepository"); -Object.defineProperty(exports, "FileRepository", { enumerable: true, get: function () { return __importDefault(FileRepository_1).default; } }); -var AnnouncementRepository_1 = require("./AnnouncementRepository"); -Object.defineProperty(exports, "AnnouncementRepository", { enumerable: true, get: function () { return __importDefault(AnnouncementRepository_1).default; } }); -// Repository instances for dependency injection -const UserRepository_2 = __importDefault(require("./UserRepository")); -const ChannelRepository_2 = __importDefault(require("./ChannelRepository")); -const TaskRepository_2 = __importDefault(require("./TaskRepository")); -const MessageRepository_2 = __importDefault(require("./MessageRepository")); -const ActivityRepository_2 = __importDefault(require("./ActivityRepository")); -const FileRepository_2 = __importDefault(require("./FileRepository")); -const AnnouncementRepository_2 = __importDefault(require("./AnnouncementRepository")); -// Create singleton instances -exports.userRepository = new UserRepository_2.default(); -exports.channelRepository = new ChannelRepository_2.default(); -exports.taskRepository = new TaskRepository_2.default(); -exports.messageRepository = new MessageRepository_2.default(); -exports.activityRepository = new ActivityRepository_2.default(); -exports.fileRepository = new FileRepository_2.default(); -exports.announcementRepository = new AnnouncementRepository_2.default(); -// Repository collection for easy access -exports.repositories = { - users: exports.userRepository, - channels: exports.channelRepository, - tasks: exports.taskRepository, - messages: exports.messageRepository, - activities: exports.activityRepository, - files: exports.fileRepository, - announcements: exports.announcementRepository, -}; -// Database configuration and utilities -var database_1 = require("../config/database"); -Object.defineProperty(exports, "initializeDatabase", { enumerable: true, get: function () { return database_1.initializeDatabase; } }); -Object.defineProperty(exports, "closeDatabase", { enumerable: true, get: function () { return database_1.closeDatabase; } }); -Object.defineProperty(exports, "getPool", { enumerable: true, get: function () { return database_1.getPool; } }); -Object.defineProperty(exports, "query", { enumerable: true, get: function () { return database_1.query; } }); -Object.defineProperty(exports, "transaction", { enumerable: true, get: function () { return database_1.transaction; } }); -Object.defineProperty(exports, "healthCheck", { enumerable: true, get: function () { return database_1.healthCheck; } }); -Object.defineProperty(exports, "getPoolStats", { enumerable: true, get: function () { return database_1.getPoolStats; } }); -Object.defineProperty(exports, "databaseMetrics", { enumerable: true, get: function () { return database_1.databaseMetrics; } }); -// DatabaseManager class for Phase 2 compatibility -class DatabaseManager { - static instance; - static getInstance() { - if (!DatabaseManager.instance) { - DatabaseManager.instance = new DatabaseManager(); - } - return DatabaseManager.instance; - } - async query(text, params, client) { - const { query } = await Promise.resolve().then(() => __importStar(require('../config/database'))); - return await query(text, params, client); - } - async one(text, params, client) { - const result = await this.query(text, params, client); - if (result.rows.length === 0) { - throw new Error('No rows returned'); - } - return result.rows[0]; - } - async many(text, params, client) { - const result = await this.query(text, params, client); - return result.rows; - } - async none(text, params, client) { - await this.query(text, params, client); - } - async transaction(callback) { - const { transaction } = await Promise.resolve().then(() => __importStar(require('../config/database'))); - return await transaction(callback); - } -} -exports.DatabaseManager = DatabaseManager; -// Migration system -var migrator_1 = require("./migrator"); -Object.defineProperty(exports, "runMigrations", { enumerable: true, get: function () { return migrator_1.runMigrations; } }); -Object.defineProperty(exports, "rollbackLastMigration", { enumerable: true, get: function () { return migrator_1.rollbackLastMigration; } }); -Object.defineProperty(exports, "getMigrationStatus", { enumerable: true, get: function () { return migrator_1.getMigrationStatus; } }); -Object.defineProperty(exports, "validateMigrations", { enumerable: true, get: function () { return migrator_1.validateMigrations; } }); -exports.default = exports.repositories; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/backend/dist/db/index.js.map b/backend/dist/db/index.js.map deleted file mode 100644 index 60c14ff..0000000 --- a/backend/dist/db/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/db/index.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/db/migrator.d.ts b/backend/dist/db/migrator.d.ts deleted file mode 100644 index f1e518b..0000000 --- a/backend/dist/db/migrator.d.ts +++ /dev/null @@ -1,32 +0,0 @@ -interface MigrationRecord { - id: string; - filename: string; - executed_at: Date; - checksum: string; -} -/** - * Run all pending migrations - */ -export declare const runMigrations: () => Promise; -/** - * Rollback the last migration (for development only) - */ -export declare const rollbackLastMigration: () => Promise; -/** - * Get migration status for health checks - */ -export declare const getMigrationStatus: () => Promise<{ - total: number; - executed: number; - pending: number; - lastExecuted: MigrationRecord | undefined; -}>; -/** - * Validate migration integrity - */ -export declare const validateMigrations: () => Promise<{ - valid: boolean; - errors: string[]; -}>; -export {}; -//# sourceMappingURL=migrator.d.ts.map \ No newline at end of file diff --git a/backend/dist/db/migrator.d.ts.map b/backend/dist/db/migrator.d.ts.map deleted file mode 100644 index 412431c..0000000 --- a/backend/dist/db/migrator.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"migrator.d.ts","sourceRoot":"","sources":["../../src/db/migrator.ts"],"names":[],"mappings":"AAkBA,UAAU,eAAe;IACvB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,IAAI,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AA4GD;;GAEG;AACH,eAAO,MAAM,aAAa,QAAa,OAAO,CAAC,IAAI,CA8ClD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,qBAAqB,QAAa,OAAO,CAAC,IAAI,CAoD1D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,eAAe,GAAG,SAAS,CAAC;CAC3C,CAeA,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,QAAa,OAAO,CAAC;IAClD,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB,CA2CA,CAAC"} \ No newline at end of file diff --git a/backend/dist/db/migrator.js b/backend/dist/db/migrator.js deleted file mode 100644 index e9f0bf1..0000000 --- a/backend/dist/db/migrator.js +++ /dev/null @@ -1,239 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.validateMigrations = exports.getMigrationStatus = exports.rollbackLastMigration = exports.runMigrations = void 0; -const promises_1 = require("fs/promises"); -const path_1 = require("path"); -const database_1 = require("@config/database"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -/** - * Initialize migration tracking table - */ -const initializeMigrationTable = async () => { - const createTableSql = ` - CREATE TABLE IF NOT EXISTS migrations ( - id VARCHAR(255) PRIMARY KEY, - filename VARCHAR(255) NOT NULL, - executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - checksum VARCHAR(64) NOT NULL, - execution_time_ms INTEGER, - UNIQUE(filename) - ); - - CREATE INDEX IF NOT EXISTS idx_migrations_executed_at ON migrations(executed_at); - `; - await (0, database_1.query)(createTableSql); - logger_1.logger.info('Migration tracking table initialized'); -}; -/** - * Load migration files from the migrations directory - */ -const loadMigrationFiles = async () => { - try { - const migrationsDir = (0, path_1.join)(process.cwd(), 'migrations'); - const files = await (0, promises_1.readdir)(migrationsDir); - const sqlFiles = files.filter((file) => file.endsWith('.sql')).sort(); // Ensure proper ordering - const migrations = []; - for (const filename of sqlFiles) { - const filepath = (0, path_1.join)(migrationsDir, filename); - const sql = await (0, promises_1.readFile)(filepath, 'utf-8'); - // Extract timestamp from filename (format: YYYYMMDDHHMMSS_name.sql) - const timestampMatch = filename.match(/^(\d{14})/); - const timestamp = timestampMatch && timestampMatch[1] ? parseInt(timestampMatch[1], 10) : 0; - migrations.push({ - id: filename.replace('.sql', ''), - filename, - timestamp, - sql: sql.trim(), - }); - } - logger_1.logger.info({ count: migrations.length }, 'Loaded migration files'); - return migrations; - } - catch (error) { - logger_1.logger.error({ error }, 'Failed to load migration files'); - throw new errors_1.DatabaseError('Failed to load migration files'); - } -}; -/** - * Get executed migrations from database - */ -const getExecutedMigrations = async () => { - const result = await (0, database_1.query)('SELECT * FROM migrations ORDER BY executed_at ASC'); - return result.rows; -}; -/** - * Calculate checksum for migration content - */ -const calculateChecksum = (content) => { - const crypto = require('crypto'); - return crypto.createHash('sha256').update(content).digest('hex'); -}; -/** - * Execute a single migration - */ -const executeMigration = async (migration) => { - const startTime = Date.now(); - await (0, database_1.transaction)(async (client) => { - logger_1.logger.info({ migration: migration.filename }, 'Executing migration'); - // Execute the migration SQL - await client.query(migration.sql); - // Record the migration execution - const executionTime = Date.now() - startTime; - const checksum = calculateChecksum(migration.sql); - await client.query(`INSERT INTO migrations (id, filename, executed_at, checksum, execution_time_ms) - VALUES ($1, $2, NOW(), $3, $4)`, [migration.id, migration.filename, checksum, executionTime]); - logger_1.logger.info({ - migration: migration.filename, - executionTime, - }, 'Migration executed successfully'); - }); -}; -/** - * Run all pending migrations - */ -const runMigrations = async () => { - try { - logger_1.logger.info('Starting database migrations...'); - // Initialize migration tracking - await initializeMigrationTable(); - // Load all migration files - const allMigrations = await loadMigrationFiles(); - // Get executed migrations - const executedMigrations = await getExecutedMigrations(); - const executedIds = new Set(executedMigrations.map((m) => m.id)); - // Find pending migrations - const pendingMigrations = allMigrations.filter((m) => !executedIds.has(m.id)); - if (pendingMigrations.length === 0) { - logger_1.logger.info('No pending migrations found'); - return; - } - logger_1.logger.info({ - total: allMigrations.length, - executed: executedMigrations.length, - pending: pendingMigrations.length, - }, 'Migration status'); - // Execute pending migrations in order - for (const migration of pendingMigrations) { - await executeMigration(migration); - } - logger_1.logger.info({ - executedCount: pendingMigrations.length, - }, 'All migrations completed successfully'); - } - catch (error) { - logger_1.logger.error({ error }, 'Migration failed'); - throw new errors_1.DatabaseError('Migration execution failed'); - } -}; -exports.runMigrations = runMigrations; -/** - * Rollback the last migration (for development only) - */ -const rollbackLastMigration = async () => { - if (process.env.NODE_ENV === 'production') { - throw new errors_1.DatabaseError('Rollback not allowed in production'); - } - try { - const executedMigrations = await getExecutedMigrations(); - if (executedMigrations.length === 0) { - logger_1.logger.info('No migrations to rollback'); - return; - } - const lastMigration = executedMigrations[executedMigrations.length - 1]; - if (!lastMigration) { - logger_1.logger.info('No migrations to rollback'); - return; - } - // Check if rollback SQL exists - const rollbackFilename = lastMigration.filename.replace('.sql', '.rollback.sql'); - const rollbackPath = (0, path_1.join)(process.cwd(), 'migrations', rollbackFilename); - try { - const rollbackSql = await (0, promises_1.readFile)(rollbackPath, 'utf-8'); - await (0, database_1.transaction)(async (client) => { - logger_1.logger.warn({ migration: lastMigration.filename }, 'Rolling back migration'); - // Execute rollback SQL - await client.query(rollbackSql); - // Remove from migration records - await client.query('DELETE FROM migrations WHERE id = $1', [lastMigration.id]); - logger_1.logger.warn({ - migration: lastMigration.filename, - }, 'Migration rolled back successfully'); - }); - } - catch (rollbackError) { - throw new errors_1.DatabaseError(`Rollback file not found or invalid: ${rollbackFilename}`, { - lastMigration: lastMigration.filename, - }); - } - } - catch (error) { - logger_1.logger.error({ error }, 'Rollback failed'); - throw new errors_1.DatabaseError('Migration rollback failed'); - } -}; -exports.rollbackLastMigration = rollbackLastMigration; -/** - * Get migration status for health checks - */ -const getMigrationStatus = async () => { - try { - const allMigrations = await loadMigrationFiles(); - const executedMigrations = await getExecutedMigrations(); - return { - total: allMigrations.length, - executed: executedMigrations.length, - pending: allMigrations.length - executedMigrations.length, - lastExecuted: executedMigrations[executedMigrations.length - 1], - }; - } - catch (error) { - logger_1.logger.error({ error }, 'Failed to get migration status'); - throw new errors_1.DatabaseError('Failed to get migration status'); - } -}; -exports.getMigrationStatus = getMigrationStatus; -/** - * Validate migration integrity - */ -const validateMigrations = async () => { - const errors = []; - try { - const allMigrations = await loadMigrationFiles(); - const executedMigrations = await getExecutedMigrations(); - // Check for checksum mismatches - for (const executed of executedMigrations) { - const migration = allMigrations.find((m) => m.id === executed.id); - if (!migration) { - errors.push(`Migration ${executed.filename} was executed but file no longer exists`); - continue; - } - const currentChecksum = calculateChecksum(migration.sql); - if (currentChecksum !== executed.checksum) { - errors.push(`Migration ${executed.filename} has been modified after execution`); - } - } - // Check for gaps in migration sequence - const timestamps = allMigrations - .map((m) => m.timestamp) - .filter((t) => typeof t === 'number') - .sort(); - for (let i = 1; i < timestamps.length; i++) { - const current = timestamps[i]; - const previous = timestamps[i - 1]; - if (typeof current === 'number' && typeof previous === 'number' && current <= previous) { - errors.push(`Migration timestamp ordering issue detected`); - break; - } - } - } - catch (error) { - errors.push(`Validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); - } - return { - valid: errors.length === 0, - errors, - }; -}; -exports.validateMigrations = validateMigrations; -//# sourceMappingURL=migrator.js.map \ No newline at end of file diff --git a/backend/dist/db/migrator.js.map b/backend/dist/db/migrator.js.map deleted file mode 100644 index 91b8723..0000000 --- a/backend/dist/db/migrator.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"migrator.js","sourceRoot":"","sources":["../../src/db/migrator.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/files/management/FileMetadataManager.d.ts b/backend/dist/files/management/FileMetadataManager.d.ts deleted file mode 100644 index 67df58a..0000000 --- a/backend/dist/files/management/FileMetadataManager.d.ts +++ /dev/null @@ -1,113 +0,0 @@ -/** - * File Metadata Manager - Phase 2 File Management System - * Manages file metadata in database with entity linking capabilities - * - * Success Criteria: - * - File record creation before upload - * - Status tracking (pending, uploading, completed, failed) - * - Entity linking (channels, tasks, users) - * - Metadata indexing for fast search - */ -export interface FileRecord { - id: string; - name: string; - originalName: string; - size: number; - contentType: string; - s3Key: string; - uploadedBy: string; - organizationId: string; - status: FileStatus; - description?: string; - tags: string[]; - uploadedAt: Date; - updatedAt: Date; - downloadCount: number; - lastDownloaded?: Date; -} -export declare enum FileStatus { - PENDING = "pending", - UPLOADING = "uploading", - COMPLETED = "completed", - FAILED = "failed", - DELETED = "deleted" -} -export interface FileEntityLink { - fileId: string; - entityType: 'channel' | 'task' | 'user' | 'project'; - entityId: string; - linkType: 'attachment' | 'share' | 'reference'; - linkedBy: string; - linkedAt: Date; -} -export interface FileSearchQuery { - organizationId: string; - userId?: string; - name?: string; - contentType?: string; - tags?: string[]; - entityType?: string; - entityId?: string; - status?: FileStatus; - uploadedAfter?: Date; - uploadedBefore?: Date; - limit?: number; - offset?: number; -} -export interface FileSearchResult { - files: FileRecord[]; - totalCount: number; - hasMore: boolean; -} -export declare class FileMetadataManager { - private db; - private performanceMetrics; - constructor(); - /** - * Create file record before upload process - * Target: <200ms creation time - */ - createFileRecord(name: string, originalName: string, size: number, contentType: string, s3Key: string, uploadedBy: string, organizationId: string, description?: string, tags?: string[]): Promise; - /** - * Update file status during upload lifecycle - */ - updateFileStatus(fileId: string, status: FileStatus, error?: string): Promise; - /** - * Link file to entity (channel, task, user, project) - */ - linkFileToEntity(fileId: string, entityType: FileEntityLink['entityType'], entityId: string, linkType: FileEntityLink["linkType"] | undefined, linkedBy: string): Promise; - /** - * Remove file-entity link - */ - unlinkFileFromEntity(fileId: string, entityType: FileEntityLink['entityType'], entityId: string, linkType?: FileEntityLink['linkType']): Promise; - /** - * Get file record by ID - */ - getFileRecord(fileId: string): Promise; - /** - * Search files with advanced filtering - */ - searchFiles(query: FileSearchQuery): Promise; - /** - * Get files linked to a specific entity - */ - getFilesForEntity(entityType: FileEntityLink['entityType'], entityId: string, linkType?: FileEntityLink['linkType']): Promise; - /** - * Increment download counter for analytics - */ - recordDownload(fileId: string): Promise; - /** - * Soft delete file record - */ - deleteFileRecord(fileId: string): Promise; - /** - * Get performance statistics - */ - getPerformanceStats(): { - averageQueryTime: number; - p95QueryTime: number; - totalQueries: number; - }; - private recordPerformance; -} -//# sourceMappingURL=FileMetadataManager.d.ts.map \ No newline at end of file diff --git a/backend/dist/files/management/FileMetadataManager.d.ts.map b/backend/dist/files/management/FileMetadataManager.d.ts.map deleted file mode 100644 index 758beff..0000000 --- a/backend/dist/files/management/FileMetadataManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"FileMetadataManager.d.ts","sourceRoot":"","sources":["../../../src/files/management/FileMetadataManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,UAAU,EAAE,IAAI,CAAC;IACjB,SAAS,EAAE,IAAI,CAAC;IAChB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,IAAI,CAAC;CACvB;AAED,oBAAY,UAAU;IACpB,OAAO,YAAY;IACnB,SAAS,cAAc;IACvB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,OAAO,YAAY;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,SAAS,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IACpD,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,YAAY,GAAG,OAAO,GAAG,WAAW,CAAC;IAC/C,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,IAAI,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,UAAU,CAAC;IACpB,aAAa,CAAC,EAAE,IAAI,CAAC;IACrB,cAAc,CAAC,EAAE,IAAI,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,UAAU,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,EAAE,CAAkB;IAC5B,OAAO,CAAC,kBAAkB,CAAgB;;IAO1C;;;OAGG;IACG,gBAAgB,CACpB,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,EACZ,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,UAAU,EAAE,MAAM,EAClB,cAAc,EAAE,MAAM,EACtB,WAAW,CAAC,EAAE,MAAM,EACpB,IAAI,GAAE,MAAM,EAAO,GAClB,OAAO,CAAC,UAAU,CAAC;IA2DtB;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA+C5F;;OAEG;IACG,gBAAgB,CACpB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,EACxC,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,cAAc,CAAC,UAAU,CAAC,YAAe,EACnD,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC;IAwCnB;;OAEG;IACG,oBAAoB,CACxB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,EACxC,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,GACpC,OAAO,CAAC,OAAO,CAAC;IAqCnB;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAoC/D;;OAEG;IACG,WAAW,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IA0IpE;;OAEG;IACG,iBAAiB,CACrB,UAAU,EAAE,cAAc,CAAC,YAAY,CAAC,EACxC,QAAQ,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,cAAc,CAAC,UAAU,CAAC,GACpC,OAAO,CAAC,UAAU,EAAE,CAAC;IA+CxB;;OAEG;IACG,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAgBnD;;OAEG;IACG,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA+BxD;;OAEG;IACH,mBAAmB,IAAI;QACrB,gBAAgB,EAAE,MAAM,CAAC;QACzB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB;IAoBD,OAAO,CAAC,iBAAiB;CAQ1B"} \ No newline at end of file diff --git a/backend/dist/files/management/FileMetadataManager.js b/backend/dist/files/management/FileMetadataManager.js deleted file mode 100644 index 559b64a..0000000 --- a/backend/dist/files/management/FileMetadataManager.js +++ /dev/null @@ -1,466 +0,0 @@ -"use strict"; -/** - * File Metadata Manager - Phase 2 File Management System - * Manages file metadata in database with entity linking capabilities - * - * Success Criteria: - * - File record creation before upload - * - Status tracking (pending, uploading, completed, failed) - * - Entity linking (channels, tasks, users) - * - Metadata indexing for fast search - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FileMetadataManager = exports.FileStatus = void 0; -const perf_hooks_1 = require("perf_hooks"); -const db_1 = require("../../db"); -const logger_1 = require("../../utils/logger"); -var FileStatus; -(function (FileStatus) { - FileStatus["PENDING"] = "pending"; - FileStatus["UPLOADING"] = "uploading"; - FileStatus["COMPLETED"] = "completed"; - FileStatus["FAILED"] = "failed"; - FileStatus["DELETED"] = "deleted"; -})(FileStatus || (exports.FileStatus = FileStatus = {})); -class FileMetadataManager { - db; - performanceMetrics = []; - constructor() { - this.db = db_1.DatabaseManager.getInstance(); - logger_1.logger.info('File Metadata Manager initialized'); - } - /** - * Create file record before upload process - * Target: <200ms creation time - */ - async createFileRecord(name, originalName, size, contentType, s3Key, uploadedBy, organizationId, description, tags = []) { - const startTime = perf_hooks_1.performance.now(); - try { - const result = await this.db.query(` - INSERT INTO files ( - name, original_name, size, content_type, s3_key, - uploaded_by, organization_id, status, description, - tags, uploaded_at, updated_at, download_count - ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, - NOW(), NOW(), 0 - ) - RETURNING - id, name, original_name as "originalName", size, content_type as "contentType", - s3_key as "s3Key", uploaded_by as "uploadedBy", organization_id as "organizationId", - status, description, tags, uploaded_at as "uploadedAt", - updated_at as "updatedAt", download_count as "downloadCount" - `, [ - name, originalName, size, contentType, s3Key, - uploadedBy, organizationId, FileStatus.PENDING, - description, JSON.stringify(tags) - ]); - const fileRecord = result.rows[0]; - // Parse JSON tags back to array - fileRecord.tags = typeof fileRecord.tags === 'string' - ? JSON.parse(fileRecord.tags) - : fileRecord.tags; - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - logger_1.logger.info('File record created successfully', { - fileId: fileRecord.id, - name: fileRecord.name, - size: `${Math.round(fileRecord.size / 1024)}KB`, - processingTime: `${processingTime.toFixed(2)}ms` - }); - return fileRecord; - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - logger_1.logger.error('Failed to create file record', { - error: error.message, - name, - uploadedBy, - organizationId, - processingTime: `${processingTime.toFixed(2)}ms` - }); - throw new Error(`Failed to create file record: ${error.message}`); - } - } - /** - * Update file status during upload lifecycle - */ - async updateFileStatus(fileId, status, error) { - const startTime = perf_hooks_1.performance.now(); - try { - let query = ` - UPDATE files - SET status = $2, updated_at = NOW() - `; - const params = [fileId, status]; - if (error && status === FileStatus.FAILED) { - query += `, error_message = $3`; - params.push(error); - } - query += ` WHERE id = $1 RETURNING id`; - const result = await this.db.query(query, params); - if (result.rowCount === 0) { - logger_1.logger.warn('File not found for status update', { fileId, status }); - return false; - } - const processingTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.debug('File status updated', { - fileId, - status, - processingTime: `${processingTime.toFixed(2)}ms`, - error: error ? error.substring(0, 100) : undefined - }); - return true; - } - catch (dbError) { - logger_1.logger.error('Failed to update file status', { - error: dbError.message, - fileId, - status - }); - return false; - } - } - /** - * Link file to entity (channel, task, user, project) - */ - async linkFileToEntity(fileId, entityType, entityId, linkType = 'attachment', linkedBy) { - const startTime = perf_hooks_1.performance.now(); - try { - await this.db.query(` - INSERT INTO file_entity_links ( - file_id, entity_type, entity_id, link_type, linked_by, linked_at - ) VALUES ($1, $2, $3, $4, $5, NOW()) - ON CONFLICT (file_id, entity_type, entity_id, link_type) - DO UPDATE SET - linked_by = EXCLUDED.linked_by, - linked_at = EXCLUDED.linked_at - `, [fileId, entityType, entityId, linkType, linkedBy]); - const processingTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.info('File linked to entity', { - fileId, - entityType, - entityId, - linkType, - linkedBy, - processingTime: `${processingTime.toFixed(2)}ms` - }); - return true; - } - catch (error) { - logger_1.logger.error('Failed to link file to entity', { - error: error.message, - fileId, - entityType, - entityId, - linkType - }); - return false; - } - } - /** - * Remove file-entity link - */ - async unlinkFileFromEntity(fileId, entityType, entityId, linkType) { - try { - let query = ` - DELETE FROM file_entity_links - WHERE file_id = $1 AND entity_type = $2 AND entity_id = $3 - `; - const params = [fileId, entityType, entityId]; - if (linkType) { - query += ` AND link_type = $4`; - params.push(linkType); - } - const result = await this.db.query(query, params); - logger_1.logger.info('File unlinked from entity', { - fileId, - entityType, - entityId, - linkType, - removedLinks: result.rowCount - }); - return result.rowCount > 0; - } - catch (error) { - logger_1.logger.error('Failed to unlink file from entity', { - error: error.message, - fileId, - entityType, - entityId - }); - return false; - } - } - /** - * Get file record by ID - */ - async getFileRecord(fileId) { - try { - const result = await this.db.query(` - SELECT - id, name, original_name as "originalName", size, content_type as "contentType", - s3_key as "s3Key", uploaded_by as "uploadedBy", organization_id as "organizationId", - status, description, tags, uploaded_at as "uploadedAt", - updated_at as "updatedAt", download_count as "downloadCount", - last_downloaded as "lastDownloaded" - FROM files - WHERE id = $1 AND status != $2 - `, [fileId, FileStatus.DELETED]); - if (result.rows.length === 0) { - return null; - } - const fileRecord = result.rows[0]; - // Parse JSON tags - fileRecord.tags = typeof fileRecord.tags === 'string' - ? JSON.parse(fileRecord.tags) - : fileRecord.tags || []; - return fileRecord; - } - catch (error) { - logger_1.logger.error('Failed to get file record', { - error: error.message, - fileId - }); - return null; - } - } - /** - * Search files with advanced filtering - */ - async searchFiles(query) { - const startTime = perf_hooks_1.performance.now(); - try { - const limit = query.limit || 20; - const offset = query.offset || 0; - // Build dynamic WHERE clause - const conditions = ['f.organization_id = $1', 'f.status != $2']; - const params = [query.organizationId, FileStatus.DELETED]; - let paramIndex = 3; - if (query.userId) { - conditions.push(`f.uploaded_by = $${paramIndex}`); - params.push(query.userId); - paramIndex++; - } - if (query.name) { - conditions.push(`(f.name ILIKE $${paramIndex} OR f.original_name ILIKE $${paramIndex})`); - params.push(`%${query.name}%`); - paramIndex++; - } - if (query.contentType) { - conditions.push(`f.content_type ILIKE $${paramIndex}`); - params.push(`%${query.contentType}%`); - paramIndex++; - } - if (query.status) { - conditions.push(`f.status = $${paramIndex}`); - params.push(query.status); - paramIndex++; - } - if (query.tags && query.tags.length > 0) { - conditions.push(`f.tags::jsonb ?| $${paramIndex}`); - params.push(query.tags); - paramIndex++; - } - if (query.uploadedAfter) { - conditions.push(`f.uploaded_at >= $${paramIndex}`); - params.push(query.uploadedAfter); - paramIndex++; - } - if (query.uploadedBefore) { - conditions.push(`f.uploaded_at <= $${paramIndex}`); - params.push(query.uploadedBefore); - paramIndex++; - } - // Add entity filter if specified - let joinClause = ''; - if (query.entityType && query.entityId) { - joinClause = ` - INNER JOIN file_entity_links fel ON f.id = fel.file_id - AND fel.entity_type = $${paramIndex} - AND fel.entity_id = $${paramIndex + 1} - `; - params.push(query.entityType, query.entityId); - paramIndex += 2; - } - const whereClause = conditions.join(' AND '); - // Count total results - const countQuery = ` - SELECT COUNT(DISTINCT f.id) as total - FROM files f ${joinClause} - WHERE ${whereClause} - `; - const countResult = await this.db.query(countQuery, params); - const totalCount = parseInt(countResult.rows[0].total); - // Get paginated results - const dataQuery = ` - SELECT DISTINCT - f.id, f.name, f.original_name as "originalName", f.size, - f.content_type as "contentType", f.s3_key as "s3Key", - f.uploaded_by as "uploadedBy", f.organization_id as "organizationId", - f.status, f.description, f.tags, f.uploaded_at as "uploadedAt", - f.updated_at as "updatedAt", f.download_count as "downloadCount", - f.last_downloaded as "lastDownloaded" - FROM files f ${joinClause} - WHERE ${whereClause} - ORDER BY f.uploaded_at DESC, f.id DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - params.push(limit, offset); - const dataResult = await this.db.query(dataQuery, params); - // Parse tags for each file - const files = dataResult.rows.map((row) => ({ - ...row, - tags: typeof row.tags === 'string' ? JSON.parse(row.tags) : (row.tags || []) - })); - const processingTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.debug('File search completed', { - totalFound: totalCount, - returnedCount: files.length, - processingTime: `${processingTime.toFixed(2)}ms`, - query: { - organizationId: query.organizationId, - userId: query.userId, - name: query.name, - entityType: query.entityType, - entityId: query.entityId - } - }); - return { - files, - totalCount, - hasMore: offset + files.length < totalCount - }; - } - catch (error) { - logger_1.logger.error('File search failed', { - error: error.message, - query - }); - return { - files: [], - totalCount: 0, - hasMore: false - }; - } - } - /** - * Get files linked to a specific entity - */ - async getFilesForEntity(entityType, entityId, linkType) { - try { - let query = ` - SELECT - f.id, f.name, f.original_name as "originalName", f.size, - f.content_type as "contentType", f.s3_key as "s3Key", - f.uploaded_by as "uploadedBy", f.organization_id as "organizationId", - f.status, f.description, f.tags, f.uploaded_at as "uploadedAt", - f.updated_at as "updatedAt", f.download_count as "downloadCount", - f.last_downloaded as "lastDownloaded" - FROM files f - INNER JOIN file_entity_links fel ON f.id = fel.file_id - WHERE fel.entity_type = $1 AND fel.entity_id = $2 - AND f.status = $3 - `; - const params = [entityType, entityId, FileStatus.COMPLETED]; - if (linkType) { - query += ` AND fel.link_type = $4`; - params.push(linkType); - } - query += ` ORDER BY fel.linked_at DESC`; - const result = await this.db.query(query, params); - // Parse tags for each file - const files = result.rows.map((row) => ({ - ...row, - tags: typeof row.tags === 'string' ? JSON.parse(row.tags) : (row.tags || []) - })); - return files; - } - catch (error) { - logger_1.logger.error('Failed to get files for entity', { - error: error.message, - entityType, - entityId, - linkType - }); - return []; - } - } - /** - * Increment download counter for analytics - */ - async recordDownload(fileId) { - try { - await this.db.query(` - UPDATE files - SET download_count = download_count + 1, last_downloaded = NOW() - WHERE id = $1 - `, [fileId]); - } - catch (error) { - logger_1.logger.warn('Failed to record download', { - error: error.message, - fileId - }); - } - } - /** - * Soft delete file record - */ - async deleteFileRecord(fileId) { - try { - const result = await this.db.query(` - UPDATE files - SET status = $2, updated_at = NOW() - WHERE id = $1 - RETURNING id - `, [fileId, FileStatus.DELETED]); - if (result.rowCount > 0) { - // Also remove entity links - await this.db.query(` - DELETE FROM file_entity_links WHERE file_id = $1 - `, [fileId]); - logger_1.logger.info('File record deleted', { fileId }); - return true; - } - return false; - } - catch (error) { - logger_1.logger.error('Failed to delete file record', { - error: error.message, - fileId - }); - return false; - } - } - /** - * Get performance statistics - */ - getPerformanceStats() { - if (this.performanceMetrics.length === 0) { - return { - averageQueryTime: 0, - p95QueryTime: 0, - totalQueries: 0 - }; - } - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - return { - averageQueryTime: Math.round(average * 100) / 100, - p95QueryTime: Math.round(p95 * 100) / 100, - totalQueries: this.performanceMetrics.length - }; - } - recordPerformance(time) { - this.performanceMetrics.push(time); - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} -exports.FileMetadataManager = FileMetadataManager; -//# sourceMappingURL=FileMetadataManager.js.map \ No newline at end of file diff --git a/backend/dist/files/management/FileMetadataManager.js.map b/backend/dist/files/management/FileMetadataManager.js.map deleted file mode 100644 index c987d60..0000000 --- a/backend/dist/files/management/FileMetadataManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"FileMetadataManager.js","sourceRoot":"","sources":["../../../src/files/management/FileMetadataManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/files/management/S3FileManager.d.ts b/backend/dist/files/management/S3FileManager.d.ts deleted file mode 100644 index f0f1fa1..0000000 --- a/backend/dist/files/management/S3FileManager.d.ts +++ /dev/null @@ -1,90 +0,0 @@ -/** - * S3 File Manager - Phase 2 File Management System - * Provides secure, scalable file operations with AWS S3 integration - * - * Success Criteria: - * - Upload URL generation in <2 seconds - * - Support for files up to 100MB - * - 99%+ S3 operation success rate - * - Secure presigned URL generation with 15-minute expiry - */ -export interface S3Config { - region: string; - bucket: string; - accessKeyId?: string | undefined; - secretAccessKey?: string | undefined; - maxFileSize: number; - allowedFileTypes: string[]; - presignedUrlTTL: number; -} -export interface FileMetadata { - userId: string; - organizationId: string; - originalName: string; - contentType: string; - size: number; - uploadedAt: string; - description?: string | undefined; - tags?: string[] | undefined; -} -export interface PresignedUploadResult { - uploadUrl: string; - downloadUrl?: string; - key: string; - expiresAt: Date; - fileId: string; -} -export interface FileValidationResult { - valid: boolean; - error?: string | undefined; - recommendations?: string[] | undefined; -} -export declare class S3FileManager { - private client; - private config; - private performanceMetrics; - constructor(config?: Partial); - /** - * Generate presigned upload URL with comprehensive validation - * Target: <2 second generation time - */ - generatePresignedUploadUrl(fileName: string, contentType: string, fileSize: number, metadata: FileMetadata): Promise; - /** - * Generate presigned download URL for file access - */ - generatePresignedDownloadUrl(key: string, expiresIn?: number): Promise; - /** - * Delete file from S3 - */ - deleteFile(key: string): Promise; - /** - * Get file metadata from S3 - */ - getFileMetadata(key: string): Promise; - /** - * Validate file against security and size constraints - */ - validateFile(fileName: string, contentType: string, fileSize: number): FileValidationResult; - /** - * Get performance statistics for monitoring - */ - getPerformanceStats(): { - averageGenerationTime: number; - p95GenerationTime: number; - p99GenerationTime: number; - totalOperations: number; - }; - /** - * Get service health status - */ - getHealthStatus(): Promise<{ - status: 'healthy' | 'degraded' | 'unhealthy'; - details: Record; - }>; - private generateFileId; - private generateFileKey; - private sanitizeFileName; - private containsUnsafeCharacters; - private recordPerformance; -} -//# sourceMappingURL=S3FileManager.d.ts.map \ No newline at end of file diff --git a/backend/dist/files/management/S3FileManager.d.ts.map b/backend/dist/files/management/S3FileManager.d.ts.map deleted file mode 100644 index 31e3da2..0000000 --- a/backend/dist/files/management/S3FileManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"S3FileManager.d.ts","sourceRoot":"","sources":["../../../src/files/management/S3FileManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAQH,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CAC7B;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,IAAI,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,OAAO,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;CACxC;AAED,qBAAa,aAAa;IACxB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,MAAM,CAAW;IACzB,OAAO,CAAC,kBAAkB,CAAgB;gBAE9B,MAAM,CAAC,EAAE,OAAO,CAAC,QAAQ,CAAC;IA0CtC;;;OAGG;IACG,0BAA0B,CAC9B,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,YAAY,GACrB,OAAO,CAAC,qBAAqB,CAAC;IAkFjC;;OAEG;IACG,4BAA4B,CAChC,GAAG,EAAE,MAAM,EACX,SAAS,GAAE,MAAa,GACvB,OAAO,CAAC,MAAM,CAAC;IA0ClB;;OAEG;IACG,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAsB/C;;OAEG;IACG,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAoChE;;OAEG;IACH,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,oBAAoB;IAqE3F;;OAEG;IACH,mBAAmB,IAAI;QACrB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,eAAe,EAAE,MAAM,CAAC;KACzB;IAuBD;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC;QAC/B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;QAC7C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAC9B,CAAC;IAyDF,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,gBAAgB;IAOxB,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,iBAAiB;CAQ1B"} \ No newline at end of file diff --git a/backend/dist/files/management/S3FileManager.js b/backend/dist/files/management/S3FileManager.js deleted file mode 100644 index e021fb3..0000000 --- a/backend/dist/files/management/S3FileManager.js +++ /dev/null @@ -1,395 +0,0 @@ -"use strict"; -/** - * S3 File Manager - Phase 2 File Management System - * Provides secure, scalable file operations with AWS S3 integration - * - * Success Criteria: - * - Upload URL generation in <2 seconds - * - Support for files up to 100MB - * - 99%+ S3 operation success rate - * - Secure presigned URL generation with 15-minute expiry - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.S3FileManager = void 0; -const client_s3_1 = require("@aws-sdk/client-s3"); -const s3_request_presigner_1 = require("@aws-sdk/s3-request-presigner"); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -class S3FileManager { - client; - config; - performanceMetrics = []; - constructor(config) { - this.config = { - region: process.env.AWS_REGION || 'us-east-1', - bucket: process.env.S3_BUCKET_NAME || 'ceo-platform-files', - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, - maxFileSize: 100 * 1024 * 1024, // 100MB - allowedFileTypes: [ - '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', - '.jpg', '.jpeg', '.png', '.gif', '.webp', - '.mp4', '.mov', '.avi', '.mkv', - '.mp3', '.wav', '.flac', - '.zip', '.rar', '.7z', - '.txt', '.csv', '.json', '.xml' - ], - presignedUrlTTL: 900, // 15 minutes - ...config - }; - this.client = new client_s3_1.S3Client({ - region: this.config.region, - ...(this.config.accessKeyId && this.config.secretAccessKey && { - credentials: { - accessKeyId: this.config.accessKeyId, - secretAccessKey: this.config.secretAccessKey, - } - }), - requestHandler: { - connectionTimeout: 5000, - socketTimeout: 10000, - }, - maxAttempts: 3, - }); - logger_1.logger.info('S3 File Manager initialized', { - region: this.config.region, - bucket: this.config.bucket, - maxFileSize: `${Math.round(this.config.maxFileSize / (1024 * 1024))}MB`, - allowedTypes: this.config.allowedFileTypes.length - }); - } - /** - * Generate presigned upload URL with comprehensive validation - * Target: <2 second generation time - */ - async generatePresignedUploadUrl(fileName, contentType, fileSize, metadata) { - const startTime = perf_hooks_1.performance.now(); - try { - // Validate file before generating URL - const validation = this.validateFile(fileName, contentType, fileSize); - if (!validation.valid) { - throw new Error(`File validation failed: ${validation.error}`); - } - // Generate unique file key - const fileId = this.generateFileId(); - const key = this.generateFileKey(fileId, fileName, metadata.userId, metadata.organizationId); - // Create PUT command with metadata - const putCommand = new client_s3_1.PutObjectCommand({ - Bucket: this.config.bucket, - Key: key, - ContentType: contentType, - ContentLength: fileSize, - Metadata: { - userId: metadata.userId, - organizationId: metadata.organizationId, - originalName: fileName, - uploadedAt: metadata.uploadedAt, - description: metadata.description || '', - tags: metadata.tags ? JSON.stringify(metadata.tags) : '', - fileId - }, - ServerSideEncryption: 'AES256', // Enable encryption - StorageClass: 'STANDARD_IA' // Cost-optimized storage - }); - // Generate presigned upload URL - const uploadUrl = await (0, s3_request_presigner_1.getSignedUrl)(this.client, putCommand, { - expiresIn: this.config.presignedUrlTTL - }); - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - // Log performance warning if target exceeded - if (processingTime > 2000) { - logger_1.logger.warn('Presigned URL generation exceeded 2 second target', { - processingTime: `${processingTime.toFixed(2)}ms`, - fileName, - fileSize - }); - } - const result = { - uploadUrl, - key, - fileId, - expiresAt: new Date(Date.now() + this.config.presignedUrlTTL * 1000) - }; - logger_1.logger.info('Presigned upload URL generated successfully', { - fileId, - fileName, - fileSize: `${Math.round(fileSize / 1024)}KB`, - processingTime: `${processingTime.toFixed(2)}ms`, - expiresIn: `${this.config.presignedUrlTTL}s` - }); - return result; - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - logger_1.logger.error('Failed to generate presigned upload URL', { - error: error.message, - fileName, - fileSize, - processingTime: `${processingTime.toFixed(2)}ms` - }); - throw new Error(`Failed to generate upload URL: ${error.message}`); - } - } - /** - * Generate presigned download URL for file access - */ - async generatePresignedDownloadUrl(key, expiresIn = 3600) { - const startTime = perf_hooks_1.performance.now(); - try { - // Verify file exists - const headCommand = new client_s3_1.HeadObjectCommand({ - Bucket: this.config.bucket, - Key: key - }); - await this.client.send(headCommand); - // Generate download URL - const getCommand = new client_s3_1.GetObjectCommand({ - Bucket: this.config.bucket, - Key: key - }); - const downloadUrl = await (0, s3_request_presigner_1.getSignedUrl)(this.client, getCommand, { - expiresIn - }); - const processingTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.debug('Presigned download URL generated', { - key, - processingTime: `${processingTime.toFixed(2)}ms`, - expiresIn: `${expiresIn}s` - }); - return downloadUrl; - } - catch (error) { - logger_1.logger.error('Failed to generate presigned download URL', { - error: error.message, - key - }); - throw new Error(`Failed to generate download URL: ${error.message}`); - } - } - /** - * Delete file from S3 - */ - async deleteFile(key) { - try { - const deleteCommand = new client_s3_1.DeleteObjectCommand({ - Bucket: this.config.bucket, - Key: key - }); - await this.client.send(deleteCommand); - logger_1.logger.info('File deleted from S3', { key }); - return true; - } - catch (error) { - logger_1.logger.error('Failed to delete file from S3', { - error: error.message, - key - }); - return false; - } - } - /** - * Get file metadata from S3 - */ - async getFileMetadata(key) { - try { - const headCommand = new client_s3_1.HeadObjectCommand({ - Bucket: this.config.bucket, - Key: key - }); - const response = await this.client.send(headCommand); - if (!response.Metadata) { - return null; - } - const metadata = { - userId: response.Metadata.userId || '', - organizationId: response.Metadata.organizationId || '', - originalName: response.Metadata.originalName || '', - contentType: response.ContentType || '', - size: response.ContentLength || 0, - uploadedAt: response.Metadata.uploadedAt || new Date().toISOString(), - description: response.Metadata.description || undefined, - tags: response.Metadata.tags ? JSON.parse(response.Metadata.tags) : undefined - }; - return metadata; - } - catch (error) { - logger_1.logger.error('Failed to get file metadata', { - error: error.message, - key - }); - return null; - } - } - /** - * Validate file against security and size constraints - */ - validateFile(fileName, contentType, fileSize) { - const recommendations = []; - // File size validation - if (fileSize <= 0) { - return { - valid: false, - error: 'File size must be greater than 0' - }; - } - if (fileSize > this.config.maxFileSize) { - return { - valid: false, - error: `File size exceeds maximum allowed size of ${Math.round(this.config.maxFileSize / (1024 * 1024))}MB` - }; - } - // File type validation - const fileExtension = fileName.toLowerCase().split('.').pop(); - if (!fileExtension) { - return { - valid: false, - error: 'File must have a valid extension' - }; - } - const fullExtension = `.${fileExtension}`; - if (!this.config.allowedFileTypes.includes(fullExtension)) { - return { - valid: false, - error: `File type '${fullExtension}' is not allowed`, - recommendations: [ - `Allowed types: ${this.config.allowedFileTypes.join(', ')}`, - 'Consider converting to an allowed format', - 'Contact administrator for additional file type support' - ] - }; - } - // Content type validation - if (!contentType || contentType === 'application/octet-stream') { - recommendations.push('Consider specifying a more specific content type'); - } - // File name security validation - if (this.containsUnsafeCharacters(fileName)) { - return { - valid: false, - error: 'File name contains unsafe characters', - recommendations: [ - 'Remove special characters like <>:"|?*', - 'Use only letters, numbers, hyphens, and underscores', - 'Avoid extremely long file names' - ] - }; - } - // Size recommendations - if (fileSize > 50 * 1024 * 1024) { // 50MB - recommendations.push('Large files may take longer to upload and process'); - } - return { - valid: true, - recommendations: recommendations.length > 0 ? recommendations : undefined - }; - } - /** - * Get performance statistics for monitoring - */ - getPerformanceStats() { - if (this.performanceMetrics.length === 0) { - return { - averageGenerationTime: 0, - p95GenerationTime: 0, - p99GenerationTime: 0, - totalOperations: 0 - }; - } - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - return { - averageGenerationTime: Math.round(average * 100) / 100, - p95GenerationTime: Math.round(p95 * 100) / 100, - p99GenerationTime: Math.round(p99 * 100) / 100, - totalOperations: this.performanceMetrics.length - }; - } - /** - * Get service health status - */ - async getHealthStatus() { - try { - // Test S3 connectivity with a simple head request - const testKey = 'health-check-test'; - const headCommand = new client_s3_1.HeadObjectCommand({ - Bucket: this.config.bucket, - Key: testKey - }); - const startTime = perf_hooks_1.performance.now(); - try { - await this.client.send(headCommand); - } - catch (error) { - // Expect 404 for non-existent test file - this means S3 is reachable - if (error.name !== 'NotFound') { - throw error; - } - } - const responseTime = perf_hooks_1.performance.now() - startTime; - const stats = this.getPerformanceStats(); - let status = 'healthy'; - // Check performance degradation - if (stats.averageGenerationTime > 3000 || responseTime > 5000) { - status = 'unhealthy'; - } - else if (stats.averageGenerationTime > 2000 || responseTime > 2000) { - status = 'degraded'; - } - return { - status, - details: { - s3ResponseTime: Math.round(responseTime), - averageGenerationTime: stats.averageGenerationTime, - totalOperations: stats.totalOperations, - bucket: this.config.bucket, - region: this.config.region - } - }; - } - catch (error) { - logger_1.logger.error('S3 health check failed', { error: error.message }); - return { - status: 'unhealthy', - details: { - error: error.message, - bucket: this.config.bucket, - region: this.config.region - } - }; - } - } - generateFileId() { - const timestamp = Date.now().toString(36); - const random = Math.random().toString(36).substring(2, 8); - return `file_${timestamp}_${random}`; - } - generateFileKey(fileId, originalName, userId, organizationId) { - const sanitizedName = this.sanitizeFileName(originalName); - const year = new Date().getFullYear(); - const month = String(new Date().getMonth() + 1).padStart(2, '0'); - return `organizations/${organizationId}/users/${userId}/${year}/${month}/${fileId}_${sanitizedName}`; - } - sanitizeFileName(fileName) { - return fileName - .replace(/[^a-zA-Z0-9.-]/g, '_') // Replace unsafe characters with underscore - .replace(/_{2,}/g, '_') // Replace multiple underscores with single - .substring(0, 100); // Limit length - } - containsUnsafeCharacters(fileName) { - const unsafePattern = /[<>:"|?*\x00-\x1f]/; - return unsafePattern.test(fileName) || fileName.length > 255; - } - recordPerformance(time) { - this.performanceMetrics.push(time); - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} -exports.S3FileManager = S3FileManager; -//# sourceMappingURL=S3FileManager.js.map \ No newline at end of file diff --git a/backend/dist/files/management/S3FileManager.js.map b/backend/dist/files/management/S3FileManager.js.map deleted file mode 100644 index 4f633d5..0000000 --- a/backend/dist/files/management/S3FileManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"S3FileManager.js","sourceRoot":"","sources":["../../../src/files/management/S3FileManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/files/upload/FileCommandParser.d.ts b/backend/dist/files/upload/FileCommandParser.d.ts deleted file mode 100644 index cfb8c7e..0000000 --- a/backend/dist/files/upload/FileCommandParser.d.ts +++ /dev/null @@ -1,127 +0,0 @@ -/** - * File Command Parser - Phase 2 Voice File Operations - * Parses file-related voice commands and extracts parameters - * - * Success Criteria: - * - Supports upload, share, organize, delete operations - * - Extracts file names, descriptions, target entities - * - Handles file type inference - * - Context-aware target resolution - */ -import { ContextData, UserContext } from '../../voice/types'; -export interface ParsedFileCommand { - id: string; - operation: FileOperation; - fileName?: string | undefined; - description?: string | undefined; - targetChannels: ResolvedEntity[]; - targetTasks: ResolvedEntity[]; - targetUsers: ResolvedEntity[]; - fileType?: string | undefined; - tags: string[]; - priority: 'low' | 'medium' | 'high'; - processingTime: number; - confidence: number; -} -export declare enum FileOperation { - UPLOAD = "upload", - SHARE = "share", - ORGANIZE = "organize", - DELETE = "delete", - SEARCH = "search", - DOWNLOAD = "download" -} -export interface ResolvedEntity { - id: string; - name: string; - type: string; - confidence: number; -} -export interface FileParameters { - fileName?: string | undefined; - description?: string | undefined; - targetEntities: string[]; - fileType?: string | undefined; - operation: FileOperation; - tags: string[]; - priority: string; -} -export declare class FileCommandParser { - private performanceMetrics; - constructor(); - /** - * Parse file-related voice command - * Target: <500ms parsing time - */ - parseFileCommand(transcript: string, context: ContextData, userContext: UserContext): Promise; - /** - * Extract file parameters from transcript - */ - extractFileParameters(transcript: string): FileParameters; - /** - * Resolve file targets with context awareness - */ - resolveFileTargets(targetEntities: string[], context: ContextData): Promise<{ - channels: ResolvedEntity[]; - tasks: ResolvedEntity[]; - users: ResolvedEntity[]; - }>; - /** - * Identify the primary file operation from transcript - */ - private identifyFileOperation; - /** - * Extract target entities (channels, tasks, users) from transcript - */ - private extractTargetEntities; - /** - * Extract file type from transcript - */ - private extractFileType; - /** - * Infer file type from context and file name - */ - private inferFileType; - /** - * Extract tags from transcript - */ - private extractTags; - /** - * Generate contextual tags based on transcript content - */ - private generateContextualTags; - /** - * Determine priority level from transcript - */ - private determinePriority; - /** - * Find best matching channel from context - */ - private findBestChannelMatch; - /** - * Find best matching task from context - */ - private findBestTaskMatch; - /** - * Find best matching user from context - */ - private findBestUserMatch; - /** - * Calculate match confidence between two strings - */ - private calculateMatchConfidence; - /** - * Calculate overall confidence for the parsed command - */ - private calculateConfidence; - /** - * Get performance statistics - */ - getPerformanceStats(): { - averageParsingTime: number; - p95ParsingTime: number; - totalCommandsParsed: number; - }; - private recordPerformance; -} -//# sourceMappingURL=FileCommandParser.d.ts.map \ No newline at end of file diff --git a/backend/dist/files/upload/FileCommandParser.d.ts.map b/backend/dist/files/upload/FileCommandParser.d.ts.map deleted file mode 100644 index c93f6a3..0000000 --- a/backend/dist/files/upload/FileCommandParser.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"FileCommandParser.d.ts","sourceRoot":"","sources":["../../../src/files/upload/FileCommandParser.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAE7D,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,aAAa,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,cAAc,EAAE,cAAc,EAAE,CAAC;IACjC,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,WAAW,EAAE,cAAc,EAAE,CAAC;IAC9B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,oBAAY,aAAa;IACvB,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,QAAQ,aAAa;IACrB,MAAM,WAAW;IACjB,MAAM,WAAW;IACjB,QAAQ,aAAa;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B,SAAS,EAAE,aAAa,CAAC;IACzB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,kBAAkB,CAAgB;;IAM1C;;;OAGG;IACG,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,WAAW,GACvB,OAAO,CAAC,iBAAiB,CAAC;IAsF7B;;OAEG;IACH,qBAAqB,CAAC,UAAU,EAAE,MAAM,GAAG,cAAc;IAkEzD;;OAEG;IACG,kBAAkB,CACtB,cAAc,EAAE,MAAM,EAAE,EACxB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC;QACT,QAAQ,EAAE,cAAc,EAAE,CAAC;QAC3B,KAAK,EAAE,cAAc,EAAE,CAAC;QACxB,KAAK,EAAE,cAAc,EAAE,CAAC;KACzB,CAAC;IAiDF;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAqC7B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAqD7B;;OAEG;IACH,OAAO,CAAC,eAAe;IAqBvB;;OAEG;IACH,OAAO,CAAC,aAAa;IAkCrB;;OAEG;IACH,OAAO,CAAC,WAAW;IA2BnB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAoC9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAczB;;OAEG;IACH,OAAO,CAAC,oBAAoB;IAe5B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAezB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAezB;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA8BhC;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAmD3B;;OAEG;IACH,mBAAmB,IAAI;QACrB,kBAAkB,EAAE,MAAM,CAAC;QAC3B,cAAc,EAAE,MAAM,CAAC;QACvB,mBAAmB,EAAE,MAAM,CAAC;KAC7B;IAqBD,OAAO,CAAC,iBAAiB;CAQ1B"} \ No newline at end of file diff --git a/backend/dist/files/upload/FileCommandParser.js b/backend/dist/files/upload/FileCommandParser.js deleted file mode 100644 index 484f7fe..0000000 --- a/backend/dist/files/upload/FileCommandParser.js +++ /dev/null @@ -1,539 +0,0 @@ -"use strict"; -/** - * File Command Parser - Phase 2 Voice File Operations - * Parses file-related voice commands and extracts parameters - * - * Success Criteria: - * - Supports upload, share, organize, delete operations - * - Extracts file names, descriptions, target entities - * - Handles file type inference - * - Context-aware target resolution - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FileCommandParser = exports.FileOperation = void 0; -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -var FileOperation; -(function (FileOperation) { - FileOperation["UPLOAD"] = "upload"; - FileOperation["SHARE"] = "share"; - FileOperation["ORGANIZE"] = "organize"; - FileOperation["DELETE"] = "delete"; - FileOperation["SEARCH"] = "search"; - FileOperation["DOWNLOAD"] = "download"; -})(FileOperation || (exports.FileOperation = FileOperation = {})); -class FileCommandParser { - performanceMetrics = []; - constructor() { - logger_1.logger.info('File Command Parser initialized'); - } - /** - * Parse file-related voice command - * Target: <500ms parsing time - */ - async parseFileCommand(transcript, context, userContext) { - const startTime = perf_hooks_1.performance.now(); - const commandId = `file_cmd_${Date.now()}_${Math.random().toString(36).substring(7)}`; - try { - logger_1.logger.debug('Parsing file command', { - commandId, - transcript: transcript.substring(0, 100), - userId: userContext.userId, - }); - // Step 1: Identify file operation - const operation = this.identifyFileOperation(transcript); - // Step 2: Extract file parameters - const parameters = this.extractFileParameters(transcript); - // Step 3: Resolve target entities with context - const resolvedTargets = await this.resolveFileTargets(parameters.targetEntities, context); - // Step 4: Infer file type if not explicitly mentioned - const inferredFileType = this.inferFileType(transcript, parameters.fileName); - // Step 5: Extract tags and metadata - const tags = this.extractTags(transcript); - const priority = this.determinePriority(transcript); - // Step 6: Calculate confidence score - const confidence = this.calculateConfidence(transcript, operation, resolvedTargets); - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - const parsedCommand = { - id: commandId, - operation, - fileName: parameters.fileName, - description: parameters.description, - targetChannels: resolvedTargets.channels, - targetTasks: resolvedTargets.tasks, - targetUsers: resolvedTargets.users, - fileType: inferredFileType || parameters.fileType, - tags, - priority: priority, - processingTime, - confidence, - }; - logger_1.logger.info('File command parsed successfully', { - commandId, - operation, - fileName: parameters.fileName, - targetChannels: resolvedTargets.channels.length, - targetTasks: resolvedTargets.tasks.length, - targetUsers: resolvedTargets.users.length, - confidence, - processingTime: `${processingTime.toFixed(2)}ms`, - }); - return parsedCommand; - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - logger_1.logger.error('File command parsing failed', { - commandId, - error: error.message, - transcript: transcript.substring(0, 100), - processingTime: `${processingTime.toFixed(2)}ms`, - }); - // Return default command with low confidence - return { - id: commandId, - operation: FileOperation.UPLOAD, - targetChannels: [], - targetTasks: [], - targetUsers: [], - tags: [], - priority: 'medium', - processingTime, - confidence: 0.1, - }; - } - } - /** - * Extract file parameters from transcript - */ - extractFileParameters(transcript) { - const lowerTranscript = transcript.toLowerCase(); - // Extract file name patterns - const fileNamePatterns = [ - /(?:file|document|report|presentation)\s+(?:called|named|titled)\s+"([^"]+)"/i, - /(?:file|document|report|presentation)\s+"([^"]+)"/i, - /(?:upload|share|send)\s+(?:the\s+)?([a-zA-Z0-9\s\.-]+\.(?:pdf|doc|docx|xls|xlsx|ppt|pptx|jpg|png|mp4|zip))/i, - /"([^"]+\.[a-zA-Z0-9]+)"/i, - ]; - let fileName; - for (const pattern of fileNamePatterns) { - const match = transcript.match(pattern); - if (match) { - if (match[1] !== undefined) { - fileName = match[1].trim(); - break; - } - } - } - // Extract description patterns - const descriptionPatterns = [ - /description\s+"([^"]+)"/i, - /(?:about|regarding|for)\s+"([^"]+)"/i, - /(?:with\s+)?(?:description|notes?)\s+([^,.!?]+)/i, - ]; - let description; - for (const pattern of descriptionPatterns) { - const match = transcript.match(pattern); - if (match) { - if (match[1] !== undefined) { - description = match[1].trim(); - break; - } - } - } - // Extract target entities (channels, tasks, users) - const targetEntities = this.extractTargetEntities(transcript); - // Determine file type from context - const fileType = this.extractFileType(transcript); - // Determine operation - const operation = this.identifyFileOperation(transcript); - // Extract tags - const tags = this.extractTags(transcript); - // Determine priority - const priority = this.determinePriority(transcript); - return { - fileName, - description, - targetEntities, - fileType, - operation, - tags, - priority, - }; - } - /** - * Resolve file targets with context awareness - */ - async resolveFileTargets(targetEntities, context) { - const resolved = { - channels: [], - tasks: [], - users: [], - }; - for (const entityName of targetEntities) { - const lowerName = entityName.toLowerCase(); - // Try to resolve as channel - const channel = this.findBestChannelMatch(lowerName, context.activeChannels); - if (channel) { - resolved.channels.push({ - id: channel.id, - name: channel.name, - type: 'channel', - confidence: this.calculateMatchConfidence(lowerName, channel.name), - }); - continue; - } - // Try to resolve as task - const task = this.findBestTaskMatch(lowerName, context.recentTasks); - if (task) { - resolved.tasks.push({ - id: task.id, - name: task.title, - type: 'task', - confidence: this.calculateMatchConfidence(lowerName, task.title), - }); - continue; - } - // Try to resolve as user - const user = this.findBestUserMatch(lowerName, context.teamMembers); - if (user) { - resolved.users.push({ - id: user.id, - name: user.name, - type: 'user', - confidence: this.calculateMatchConfidence(lowerName, user.name), - }); - } - } - return resolved; - } - /** - * Identify the primary file operation from transcript - */ - identifyFileOperation(transcript) { - const lowerTranscript = transcript.toLowerCase(); - // Upload patterns - if (/\b(?:upload|send|attach|add|share)\b/i.test(lowerTranscript)) { - return FileOperation.UPLOAD; - } - // Share patterns - if (/\b(?:share|distribute|send to|forward)\b/i.test(lowerTranscript)) { - return FileOperation.SHARE; - } - // Organize patterns - if (/\b(?:organize|sort|categorize|group|move)\b/i.test(lowerTranscript)) { - return FileOperation.ORGANIZE; - } - // Delete patterns - if (/\b(?:delete|remove|trash|discard)\b/i.test(lowerTranscript)) { - return FileOperation.DELETE; - } - // Search patterns - if (/\b(?:find|search|look for|locate)\b/i.test(lowerTranscript)) { - return FileOperation.SEARCH; - } - // Download patterns - if (/\b(?:download|get|retrieve|access)\b/i.test(lowerTranscript)) { - return FileOperation.DOWNLOAD; - } - // Default to upload if unclear - return FileOperation.UPLOAD; - } - /** - * Extract target entities (channels, tasks, users) from transcript - */ - extractTargetEntities(transcript) { - const entities = []; - // Channel patterns - const channelPatterns = [ - /(?:to|in|on)\s+(?:the\s+)?([a-zA-Z0-9\s]+)\s+channel/gi, - /(?:channel\s+)([a-zA-Z0-9\s]+)/gi, - /(?:to|with)\s+([a-zA-Z0-9\s]+)\s+(?:team|group)/gi, - ]; - channelPatterns.forEach((pattern) => { - let match; - while ((match = pattern.exec(transcript)) !== null) { - if (match[1] !== undefined) { - entities.push(match[1].trim()); - } - } - }); - // Task patterns - const taskPatterns = [ - /(?:for|to)\s+(?:the\s+)?([a-zA-Z0-9\s]+)\s+(?:task|project)/gi, - /(?:task\s+)([a-zA-Z0-9\s]+)/gi, - ]; - taskPatterns.forEach((pattern) => { - let match; - while ((match = pattern.exec(transcript)) !== null) { - entities.push(match[1].trim()); - } - }); - // User patterns - const userPatterns = [ - /(?:to|with|for)\s+([A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)?)/gi, - /(?:send|share)\s+(?:to|with)\s+([A-Z][a-zA-Z]+)/gi, - ]; - userPatterns.forEach((pattern) => { - let match; - while ((match = pattern.exec(transcript)) !== null) { - if (match[1] !== undefined) { - const name = match[1].trim(); - if (name.length > 2 && !['the', 'and', 'for', 'to'].includes(name.toLowerCase())) { - entities.push(name); - } - } - } - }); - return [...new Set(entities)]; // Remove duplicates - } - /** - * Extract file type from transcript - */ - extractFileType(transcript) { - const typePatterns = [ - /\b(pdf|document|doc|docx)\b/i, - /\b(spreadsheet|excel|xls|xlsx)\b/i, - /\b(presentation|powerpoint|ppt|pptx)\b/i, - /\b(image|photo|picture|jpg|jpeg|png)\b/i, - /\b(video|movie|mp4|mov|avi)\b/i, - /\b(audio|sound|music|mp3|wav)\b/i, - /\b(zip|archive|compressed)\b/i, - ]; - for (const pattern of typePatterns) { - const match = transcript.match(pattern); - if (match) { - return match[1] ? match[1].toLowerCase() : undefined; - } - } - return undefined; - } - /** - * Infer file type from context and file name - */ - inferFileType(transcript, fileName) { - if (fileName) { - const extension = fileName.split('.').pop()?.toLowerCase(); - if (extension) { - return extension; - } - } - // Infer from context words - const lowerTranscript = transcript.toLowerCase(); - if (/\b(?:report|document|letter|memo)\b/.test(lowerTranscript)) { - return 'pdf'; - } - if (/\b(?:budget|financial|numbers|data|spreadsheet)\b/.test(lowerTranscript)) { - return 'xlsx'; - } - if (/\b(?:slides|presentation|deck|pitch)\b/.test(lowerTranscript)) { - return 'pptx'; - } - if (/\b(?:photo|image|screenshot|picture)\b/.test(lowerTranscript)) { - return 'jpg'; - } - if (/\b(?:video|recording|demo|clip)\b/.test(lowerTranscript)) { - return 'mp4'; - } - return undefined; - } - /** - * Extract tags from transcript - */ - extractTags(transcript) { - const tags = []; - // Tag patterns - const tagPatterns = [ - /(?:tag|tags|tagged|label|labels)\s+(?:as\s+|with\s+)?([a-zA-Z0-9,\s]+)/gi, - /(?:category|categories)\s+([a-zA-Z0-9,\s]+)/gi, - ]; - tagPatterns.forEach((pattern) => { - const match = transcript.match(pattern); - if (match && match[1]) { - const extractedTags = match[1] - .split(/[,\s]+/) - .map((tag) => tag.trim().toLowerCase()) - .filter((tag) => tag.length > 0); - tags.push(...extractedTags); - } - }); - // Auto-generate contextual tags - const contextTags = this.generateContextualTags(transcript); - tags.push(...contextTags); - return [...new Set(tags)]; // Remove duplicates - } - /** - * Generate contextual tags based on transcript content - */ - generateContextualTags(transcript) { - const tags = []; - const lowerTranscript = transcript.toLowerCase(); - // Business context tags - if (/\b(?:urgent|priority|asap|immediately)\b/.test(lowerTranscript)) { - tags.push('urgent'); - } - if (/\b(?:confidential|private|internal|sensitive)\b/.test(lowerTranscript)) { - tags.push('confidential'); - } - if (/\b(?:public|external|client|customer)\b/.test(lowerTranscript)) { - tags.push('external'); - } - if (/\b(?:draft|preliminary|wip|work in progress)\b/.test(lowerTranscript)) { - tags.push('draft'); - } - if (/\b(?:final|approved|completed|done)\b/.test(lowerTranscript)) { - tags.push('final'); - } - // Department tags - const departments = ['marketing', 'sales', 'engineering', 'finance', 'hr', 'operations']; - departments.forEach((dept) => { - if (new RegExp(`\\b${dept}\\b`, 'i').test(transcript)) { - tags.push(dept); - } - }); - return tags; - } - /** - * Determine priority level from transcript - */ - determinePriority(transcript) { - const lowerTranscript = transcript.toLowerCase(); - if (/\b(?:urgent|critical|emergency|asap|immediately|high priority)\b/.test(lowerTranscript)) { - return 'high'; - } - if (/\b(?:low priority|later|when you have time|no rush)\b/.test(lowerTranscript)) { - return 'low'; - } - return 'medium'; - } - /** - * Find best matching channel from context - */ - findBestChannelMatch(name, channels) { - let bestMatch = null; - let bestScore = 0; - for (const channel of channels) { - const score = this.calculateMatchConfidence(name, channel.name); - if (score > bestScore && score > 0.6) { - bestScore = score; - bestMatch = channel; - } - } - return bestMatch; - } - /** - * Find best matching task from context - */ - findBestTaskMatch(name, tasks) { - let bestMatch = null; - let bestScore = 0; - for (const task of tasks) { - const score = this.calculateMatchConfidence(name, task.title); - if (score > bestScore && score > 0.6) { - bestScore = score; - bestMatch = task; - } - } - return bestMatch; - } - /** - * Find best matching user from context - */ - findBestUserMatch(name, users) { - let bestMatch = null; - let bestScore = 0; - for (const user of users) { - const score = this.calculateMatchConfidence(name, user.name); - if (score > bestScore && score > 0.7) { - bestScore = score; - bestMatch = user; - } - } - return bestMatch; - } - /** - * Calculate match confidence between two strings - */ - calculateMatchConfidence(input, target) { - const inputLower = input.toLowerCase().trim(); - const targetLower = target.toLowerCase().trim(); - // Exact match - if (inputLower === targetLower) { - return 1.0; - } - // Contains match - if (targetLower.includes(inputLower) || inputLower.includes(targetLower)) { - return 0.8; - } - // Fuzzy matching using simple word overlap - const inputWords = inputLower.split(/\s+/); - const targetWords = targetLower.split(/\s+/); - const commonWords = inputWords.filter((word) => targetWords.some((targetWord) => targetWord.includes(word) || word.includes(targetWord))); - if (commonWords.length === 0) { - return 0; - } - const overlap = commonWords.length / Math.max(inputWords.length, targetWords.length); - return overlap * 0.7; // Scale down fuzzy matches - } - /** - * Calculate overall confidence for the parsed command - */ - calculateConfidence(transcript, operation, resolvedTargets) { - let confidence = 0.5; // Base confidence - // Operation identification confidence - const operationKeywords = { - [FileOperation.UPLOAD]: ['upload', 'send', 'attach', 'add'], - [FileOperation.SHARE]: ['share', 'distribute', 'forward'], - [FileOperation.ORGANIZE]: ['organize', 'sort', 'categorize'], - [FileOperation.DELETE]: ['delete', 'remove', 'trash'], - [FileOperation.SEARCH]: ['find', 'search', 'look for'], - [FileOperation.DOWNLOAD]: ['download', 'get', 'retrieve'], - }; - const keywords = operationKeywords[operation] || []; - const hasOperationKeyword = keywords.some((keyword) => transcript.toLowerCase().includes(keyword)); - if (hasOperationKeyword) { - confidence += 0.2; - } - // Entity resolution confidence - const totalTargets = resolvedTargets.channels.length + resolvedTargets.tasks.length + resolvedTargets.users.length; - if (totalTargets > 0) { - const avgEntityConfidence = [...resolvedTargets.channels, ...resolvedTargets.tasks, ...resolvedTargets.users].reduce((sum, entity) => sum + entity.confidence, 0) / totalTargets; - confidence += avgEntityConfidence * 0.3; - } - // File name extraction confidence - if (/\b[a-zA-Z0-9\s\.-]+\.[a-zA-Z0-9]{2,4}\b/.test(transcript)) { - confidence += 0.1; - } - return Math.min(confidence, 1.0); - } - /** - * Get performance statistics - */ - getPerformanceStats() { - if (this.performanceMetrics.length === 0) { - return { - averageParsingTime: 0, - p95ParsingTime: 0, - totalCommandsParsed: 0, - }; - } - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - return { - averageParsingTime: Math.round(average * 100) / 100, - p95ParsingTime: Math.round(p95 * 100) / 100, - totalCommandsParsed: this.performanceMetrics.length, - }; - } - recordPerformance(time) { - this.performanceMetrics.push(time); - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} -exports.FileCommandParser = FileCommandParser; -//# sourceMappingURL=FileCommandParser.js.map \ No newline at end of file diff --git a/backend/dist/files/upload/FileCommandParser.js.map b/backend/dist/files/upload/FileCommandParser.js.map deleted file mode 100644 index 7066957..0000000 --- a/backend/dist/files/upload/FileCommandParser.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"FileCommandParser.js","sourceRoot":"","sources":["../../../src/files/upload/FileCommandParser.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/files/upload/VoiceFileUploadService.d.ts b/backend/dist/files/upload/VoiceFileUploadService.d.ts deleted file mode 100644 index 279fedc..0000000 --- a/backend/dist/files/upload/VoiceFileUploadService.d.ts +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Voice File Upload Service - Phase 2 Voice-Driven File Operations - * Integrates voice commands with file upload workflow - * - * Success Criteria: - * - End-to-end voice upload in <5 seconds - * - Automatic entity linking based on command context - * - Upload confirmation to user - * - Real-time notifications to affected channels - */ -import { EventEmitter } from 'events'; -import { FileStatus } from '../management/FileMetadataManager'; -import { UserContext } from '../../voice/types'; -export interface VoiceFileUploadRequest { - fileName: string; - contentType: string; - fileSize: number; - userContext: UserContext; - description?: string; - targetChannels?: string[]; - targetTasks?: string[]; - targetUsers?: string[]; - tags?: string[]; -} -export interface VoiceFileUploadResult { - success: boolean; - uploadUrl?: string; - fileId?: string; - downloadUrl?: string | undefined; - expiresAt?: Date; - linkedEntities?: { - channels: string[]; - tasks: string[]; - users: string[]; - }; - processingTime: number; - error?: string; -} -export interface FileUploadProgress { - fileId: string; - fileName: string; - progress: number; - status: FileStatus; - uploadSpeed?: number | undefined; - eta?: number | undefined; - error?: string | undefined; -} -export declare class VoiceFileUploadService extends EventEmitter { - private s3Manager; - private metadataManager; - private activeUploads; - private performanceMetrics; - constructor(); - /** - * Initiate voice-driven file upload workflow - * Target: Complete workflow in <5 seconds - */ - initiateVoiceUpload(request: VoiceFileUploadRequest): Promise; - /** - * Handle upload completion notification (typically from S3 webhook or polling) - */ - handleUploadCompletion(fileId: string, success: boolean, error?: string): Promise; - /** - * Get upload progress for a file - */ - getUploadProgress(fileId: string): FileUploadProgress | null; - /** - * Get all active uploads for monitoring - */ - getActiveUploads(): FileUploadProgress[]; - /** - * Cancel an active upload - */ - cancelUpload(fileId: string, userId: string): Promise; - /** - * Get service performance statistics - */ - getPerformanceStats(): { - averageInitiationTime: number; - p95InitiationTime: number; - p99InitiationTime: number; - totalUploadsInitiated: number; - activeUploadsCount: number; - }; - private validateUploadRequest; - private generateS3Key; - private scheduleUploadTimeoutCheck; - private sendUploadNotifications; - private recordPerformance; -} -//# sourceMappingURL=VoiceFileUploadService.d.ts.map \ No newline at end of file diff --git a/backend/dist/files/upload/VoiceFileUploadService.d.ts.map b/backend/dist/files/upload/VoiceFileUploadService.d.ts.map deleted file mode 100644 index 214f550..0000000 --- a/backend/dist/files/upload/VoiceFileUploadService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"VoiceFileUploadService.d.ts","sourceRoot":"","sources":["../../../src/files/upload/VoiceFileUploadService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAGtC,OAAO,EAAuB,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAEpF,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,SAAS,CAAC,EAAE,IAAI,CAAC;IACjB,cAAc,CAAC,EAAE;QACf,QAAQ,EAAE,MAAM,EAAE,CAAC;QACnB,KAAK,EAAE,MAAM,EAAE,CAAC;QAChB,KAAK,EAAE,MAAM,EAAE,CAAC;KACjB,CAAC;IACF,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,qBAAa,sBAAuB,SAAQ,YAAY;IACtD,OAAO,CAAC,SAAS,CAAgB;IACjC,OAAO,CAAC,eAAe,CAAsB;IAC7C,OAAO,CAAC,aAAa,CAA8C;IACnE,OAAO,CAAC,kBAAkB,CAAgB;;IA4D1C;;;OAGG;IACG,mBAAmB,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC;IAuN1F;;OAEG;IACG,sBAAsB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA6D7F;;OAEG;IACH,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;IAI5D;;OAEG;IACH,gBAAgB,IAAI,kBAAkB,EAAE;IAIxC;;OAEG;IACG,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA4CpE;;OAEG;IACH,mBAAmB,IAAI;QACrB,qBAAqB,EAAE,MAAM,CAAC;QAC9B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,qBAAqB,EAAE,MAAM,CAAC;QAC9B,kBAAkB,EAAE,MAAM,CAAC;KAC5B;YA0Ba,qBAAqB;IA2BnC,OAAO,CAAC,aAAa;IAUrB,OAAO,CAAC,0BAA0B;YAgBpB,uBAAuB;IAuCrC,OAAO,CAAC,iBAAiB;CAQ1B"} \ No newline at end of file diff --git a/backend/dist/files/upload/VoiceFileUploadService.js b/backend/dist/files/upload/VoiceFileUploadService.js deleted file mode 100644 index 4cea7f7..0000000 --- a/backend/dist/files/upload/VoiceFileUploadService.js +++ /dev/null @@ -1,445 +0,0 @@ -"use strict"; -/** - * Voice File Upload Service - Phase 2 Voice-Driven File Operations - * Integrates voice commands with file upload workflow - * - * Success Criteria: - * - End-to-end voice upload in <5 seconds - * - Automatic entity linking based on command context - * - Upload confirmation to user - * - Real-time notifications to affected channels - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VoiceFileUploadService = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const S3FileManager_1 = require("../management/S3FileManager"); -const FileMetadataManager_1 = require("../management/FileMetadataManager"); -const logger_1 = require("../../utils/logger"); -class VoiceFileUploadService extends events_1.EventEmitter { - s3Manager; - metadataManager; - activeUploads = new Map(); - performanceMetrics = []; - constructor() { - super(); - this.s3Manager = new S3FileManager_1.S3FileManager({ - maxFileSize: 100 * 1024 * 1024, // 100MB - presignedUrlTTL: 900, // 15 minutes - allowedFileTypes: [ - // Documents - '.pdf', - '.doc', - '.docx', - '.xls', - '.xlsx', - '.ppt', - '.pptx', - '.txt', - '.csv', - '.json', - '.xml', - '.rtf', - // Images - '.jpg', - '.jpeg', - '.png', - '.gif', - '.webp', - '.svg', - '.bmp', - // Media - '.mp4', - '.mov', - '.avi', - '.mkv', - '.webm', - '.mp3', - '.wav', - '.flac', - '.aac', - '.ogg', - // Archives - '.zip', - '.rar', - '.7z', - '.tar', - '.gz', - // Design files - '.psd', - '.ai', - '.sketch', - '.fig', - ], - }); - this.metadataManager = new FileMetadataManager_1.FileMetadataManager(); - logger_1.logger.info('Voice File Upload Service initialized'); - } - /** - * Initiate voice-driven file upload workflow - * Target: Complete workflow in <5 seconds - */ - async initiateVoiceUpload(request) { - const startTime = perf_hooks_1.performance.now(); - const uploadId = `upload_${Date.now()}_${Math.random().toString(36).substring(7)}`; - logger_1.logger.info('Starting voice file upload', { - uploadId, - fileName: request.fileName, - fileSize: `${Math.round(request.fileSize / 1024)}KB`, - userId: request.userContext.userId, - targetChannels: request.targetChannels?.length || 0, - targetTasks: request.targetTasks?.length || 0, - }); - try { - // Step 1: Validate file and user permissions (Target: <100ms) - const validationStart = perf_hooks_1.performance.now(); - await this.validateUploadRequest(request); - const validationTime = perf_hooks_1.performance.now() - validationStart; - // Step 2: Create file metadata record (Target: <200ms) - const metadataStart = perf_hooks_1.performance.now(); - const s3Key = this.generateS3Key(request.fileName, request.userContext); - const fileRecord = await this.metadataManager.createFileRecord(request.fileName, request.fileName, request.fileSize, request.contentType, s3Key, request.userContext.userId, request.userContext.organizationId, request.description, request.tags || []); - const metadataTime = perf_hooks_1.performance.now() - metadataStart; - // Step 3: Generate S3 presigned upload URL (Target: <2000ms) - const urlGenerationStart = perf_hooks_1.performance.now(); - const fileMetadata = { - userId: request.userContext.userId, - organizationId: request.userContext.organizationId, - originalName: request.fileName, - contentType: request.contentType, - size: request.fileSize, - uploadedAt: new Date().toISOString(), - description: request.description ?? '', - tags: request.tags, - }; - const uploadResult = await this.s3Manager.generatePresignedUploadUrl(request.fileName, request.contentType, request.fileSize, fileMetadata); - const urlGenerationTime = perf_hooks_1.performance.now() - urlGenerationStart; - // Step 4: Link file to entities (Target: <500ms) - const linkingStart = perf_hooks_1.performance.now(); - const linkedEntities = { - channels: [], - tasks: [], - users: [], - }; - // Link to channels - if (request.targetChannels && request.targetChannels.length > 0) { - for (const channelId of request.targetChannels) { - await this.metadataManager.linkFileToEntity(fileRecord.id, 'channel', channelId, 'attachment', request.userContext.userId); - linkedEntities.channels.push(channelId); - } - } - // Link to tasks - if (request.targetTasks && request.targetTasks.length > 0) { - for (const taskId of request.targetTasks) { - await this.metadataManager.linkFileToEntity(fileRecord.id, 'task', taskId, 'attachment', request.userContext.userId); - linkedEntities.tasks.push(taskId); - } - } - // Link to users (for sharing) - if (request.targetUsers && request.targetUsers.length > 0) { - for (const userId of request.targetUsers) { - await this.metadataManager.linkFileToEntity(fileRecord.id, 'user', userId, 'share', request.userContext.userId); - linkedEntities.users.push(userId); - } - } - const linkingTime = perf_hooks_1.performance.now() - linkingStart; - // Step 5: Initialize upload progress tracking - const uploadProgress = { - fileId: fileRecord.id, - fileName: request.fileName, - progress: 0, - status: FileMetadataManager_1.FileStatus.UPLOADING, - uploadSpeed: 0, - eta: Math.ceil(request.fileSize / 1000000), // Rough estimate: 1MB/sec - }; - this.activeUploads.set(fileRecord.id, uploadProgress); - // Update file status to uploading - await this.metadataManager.updateFileStatus(fileRecord.id, FileMetadataManager_1.FileStatus.UPLOADING); - const totalTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(totalTime); - // Check performance target - if (totalTime > 5000) { - logger_1.logger.warn('Voice upload initiation exceeded 5 second target', { - uploadId, - totalTime: `${totalTime.toFixed(2)}ms`, - breakdown: { - validation: `${validationTime.toFixed(2)}ms`, - metadata: `${metadataTime.toFixed(2)}ms`, - urlGeneration: `${urlGenerationTime.toFixed(2)}ms`, - linking: `${linkingTime.toFixed(2)}ms`, - }, - }); - } - // Emit upload initiated event - this.emit('upload_initiated', { - uploadId, - fileId: fileRecord.id, - fileName: request.fileName, - userId: request.userContext.userId, - linkedEntities, - processingTime: totalTime, - }); - // Generate download URL for immediate access after upload - const downloadUrl = await this.s3Manager - .generatePresignedDownloadUrl(uploadResult.key, 3600 // 1 hour - ) - .catch(() => undefined); // Don't fail if download URL generation fails - logger_1.logger.info('Voice file upload initiated successfully', { - uploadId, - fileId: fileRecord.id, - fileName: request.fileName, - uploadUrl: uploadResult.uploadUrl ? 'generated' : 'failed', - downloadUrl: downloadUrl ? 'generated' : 'not_generated', - linkedEntities, - totalTime: `${totalTime.toFixed(2)}ms`, - }); - const result = { - success: true, - uploadUrl: uploadResult.uploadUrl, - fileId: fileRecord.id, - downloadUrl, - expiresAt: uploadResult.expiresAt, - linkedEntities, - processingTime: totalTime, - }; - // Schedule upload timeout check - this.scheduleUploadTimeoutCheck(fileRecord.id, 30 * 60 * 1000); // 30 minutes - return result; - } - catch (error) { - const totalTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(totalTime); - logger_1.logger.error('Voice file upload initiation failed', { - uploadId, - error: error.message, - fileName: request.fileName, - userId: request.userContext.userId, - totalTime: `${totalTime.toFixed(2)}ms`, - }); - this.emit('upload_failed', { - uploadId, - fileName: request.fileName, - userId: request.userContext.userId, - error: error.message, - processingTime: totalTime, - }); - return { - success: false, - error: error.message, - processingTime: totalTime, - }; - } - } - /** - * Handle upload completion notification (typically from S3 webhook or polling) - */ - async handleUploadCompletion(fileId, success, error) { - const uploadProgress = this.activeUploads.get(fileId); - if (!uploadProgress) { - logger_1.logger.warn('Upload completion received for unknown file', { fileId }); - return; - } - try { - if (success) { - // Update file status to completed - await this.metadataManager.updateFileStatus(fileId, FileMetadataManager_1.FileStatus.COMPLETED); - uploadProgress.progress = 100; - uploadProgress.status = FileMetadataManager_1.FileStatus.COMPLETED; - logger_1.logger.info('File upload completed successfully', { - fileId, - fileName: uploadProgress.fileName, - }); - this.emit('upload_completed', { - fileId, - fileName: uploadProgress.fileName, - status: 'success', - }); - // Send notifications to linked entities - await this.sendUploadNotifications(fileId); - } - else { - // Update file status to failed - await this.metadataManager.updateFileStatus(fileId, FileMetadataManager_1.FileStatus.FAILED, error); - uploadProgress.status = FileMetadataManager_1.FileStatus.FAILED; - uploadProgress.error = error; - logger_1.logger.error('File upload failed', { - fileId, - fileName: uploadProgress.fileName, - error, - }); - this.emit('upload_failed', { - fileId, - fileName: uploadProgress.fileName, - error, - }); - } - } - catch (dbError) { - logger_1.logger.error('Failed to update upload completion status', { - fileId, - error: dbError.message, - }); - } - finally { - // Clean up tracking - setTimeout(() => { - this.activeUploads.delete(fileId); - }, 60000); // Keep for 1 minute for final status checks - } - } - /** - * Get upload progress for a file - */ - getUploadProgress(fileId) { - return this.activeUploads.get(fileId) || null; - } - /** - * Get all active uploads for monitoring - */ - getActiveUploads() { - return Array.from(this.activeUploads.values()); - } - /** - * Cancel an active upload - */ - async cancelUpload(fileId, userId) { - const uploadProgress = this.activeUploads.get(fileId); - if (!uploadProgress) { - return false; - } - try { - // Update file status to failed - await this.metadataManager.updateFileStatus(fileId, FileMetadataManager_1.FileStatus.FAILED, 'Upload cancelled by user'); - uploadProgress.status = FileMetadataManager_1.FileStatus.FAILED; - uploadProgress.error = 'Cancelled by user'; - logger_1.logger.info('File upload cancelled', { - fileId, - fileName: uploadProgress.fileName, - userId, - }); - this.emit('upload_cancelled', { - fileId, - fileName: uploadProgress.fileName, - userId, - }); - // Remove from active uploads - this.activeUploads.delete(fileId); - return true; - } - catch (error) { - logger_1.logger.error('Failed to cancel upload', { - fileId, - error: error.message, - }); - return false; - } - } - /** - * Get service performance statistics - */ - getPerformanceStats() { - if (this.performanceMetrics.length === 0) { - return { - averageInitiationTime: 0, - p95InitiationTime: 0, - p99InitiationTime: 0, - totalUploadsInitiated: 0, - activeUploadsCount: this.activeUploads.size, - }; - } - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - return { - averageInitiationTime: Math.round(average * 100) / 100, - p95InitiationTime: Math.round(p95 * 100) / 100, - p99InitiationTime: Math.round(p99 * 100) / 100, - totalUploadsInitiated: this.performanceMetrics.length, - activeUploadsCount: this.activeUploads.size, - }; - } - async validateUploadRequest(request) { - // Validate file - const validation = this.s3Manager.validateFile(request.fileName, request.contentType, request.fileSize); - if (!validation.valid) { - throw new Error(`File validation failed: ${validation.error}`); - } - // Check user permissions (basic validation) - if (!request.userContext.userId || !request.userContext.organizationId) { - throw new Error('Invalid user context for file upload'); - } - // Validate entity targets exist (simplified - in real implementation would check database) - if (request.targetChannels && request.targetChannels.some((id) => !id || id.trim() === '')) { - throw new Error('Invalid channel ID in target channels'); - } - if (request.targetTasks && request.targetTasks.some((id) => !id || id.trim() === '')) { - throw new Error('Invalid task ID in target tasks'); - } - } - generateS3Key(fileName, userContext) { - const timestamp = Date.now(); - const randomId = Math.random().toString(36).substring(2, 8); - const sanitizedName = fileName.replace(/[^a-zA-Z0-9.-]/g, '_'); - const year = new Date().getFullYear(); - const month = String(new Date().getMonth() + 1).padStart(2, '0'); - return `organizations/${userContext.organizationId}/users/${userContext.userId}/${year}/${month}/${timestamp}_${randomId}_${sanitizedName}`; - } - scheduleUploadTimeoutCheck(fileId, timeoutMs) { - setTimeout(async () => { - const uploadProgress = this.activeUploads.get(fileId); - if (uploadProgress && uploadProgress.status === FileMetadataManager_1.FileStatus.UPLOADING) { - logger_1.logger.warn('Upload timeout - marking as failed', { - fileId, - fileName: uploadProgress.fileName, - timeoutMs, - }); - await this.handleUploadCompletion(fileId, false, 'Upload timeout'); - } - }, timeoutMs); - } - async sendUploadNotifications(fileId) { - try { - const fileRecord = await this.metadataManager.getFileRecord(fileId); - if (!fileRecord) { - logger_1.logger.warn('Cannot send notifications for unknown file', { fileId }); - return; - } - // Get linked entities - const linkedChannels = await this.metadataManager.getFilesForEntity('channel', fileId); - const linkedTasks = await this.metadataManager.getFilesForEntity('task', fileId); - // Emit notification events (to be handled by notification system) - this.emit('file_upload_notification', { - fileId, - fileName: fileRecord.name, - uploadedBy: fileRecord.uploadedBy, - organizationId: fileRecord.organizationId, - linkedChannels: linkedChannels.map((f) => ({ id: f.id, name: f.name })), - linkedTasks: linkedTasks.map((f) => ({ id: f.id, name: f.name })), - fileSize: fileRecord.size, - contentType: fileRecord.contentType, - }); - logger_1.logger.debug('Upload notifications sent', { - fileId, - fileName: fileRecord.name, - channelCount: linkedChannels.length, - taskCount: linkedTasks.length, - }); - } - catch (error) { - logger_1.logger.error('Failed to send upload notifications', { - fileId, - error: error.message, - }); - } - } - recordPerformance(time) { - this.performanceMetrics.push(time); - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} -exports.VoiceFileUploadService = VoiceFileUploadService; -//# sourceMappingURL=VoiceFileUploadService.js.map \ No newline at end of file diff --git a/backend/dist/files/upload/VoiceFileUploadService.js.map b/backend/dist/files/upload/VoiceFileUploadService.js.map deleted file mode 100644 index 16b55d9..0000000 --- a/backend/dist/files/upload/VoiceFileUploadService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"VoiceFileUploadService.js","sourceRoot":"","sources":["../../../src/files/upload/VoiceFileUploadService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/index.d.ts b/backend/dist/index.d.ts deleted file mode 100644 index 38f48ae..0000000 --- a/backend/dist/index.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -#!/usr/bin/env node -/** - * CEO Communication Platform - Main Entry Point - * High-performance backend API server with comprehensive features - */ -import { server } from './server'; -/** - * Main application entry point - */ -declare function main(): Promise; -export { server }; -export default main; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/backend/dist/index.d.ts.map b/backend/dist/index.d.ts.map deleted file mode 100644 index 172e932..0000000 --- a/backend/dist/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAElC;;GAEG;AACH,iBAAe,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAsBnC;AAkBD,OAAO,EAAE,MAAM,EAAE,CAAC;AAClB,eAAe,IAAI,CAAC"} \ No newline at end of file diff --git a/backend/dist/index.js b/backend/dist/index.js deleted file mode 100644 index d6e2fc8..0000000 --- a/backend/dist/index.js +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env node -"use strict"; -/** - * CEO Communication Platform - Main Entry Point - * High-performance backend API server with comprehensive features - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.server = void 0; -const index_1 = require("@config/index"); -const logger_1 = require("@utils/logger"); -const server_1 = require("./server"); -Object.defineProperty(exports, "server", { enumerable: true, get: function () { return server_1.server; } }); -/** - * Main application entry point - */ -async function main() { - try { - // Log startup information - logger_1.logger.info({ - nodeVersion: process.version, - platform: process.platform, - arch: process.arch, - environment: index_1.config.app.env, - pid: process.pid, - }, 'Starting CEO Communication Platform API'); - // Validate configuration - logger_1.logger.info('Validating configuration...'); - // Configuration is already validated in config/index.ts - // Start the server - await server_1.server.start(); - } - catch (error) { - logger_1.logger.fatal({ error }, 'Failed to start application'); - process.exit(1); - } -} -// Handle top-level errors -process.on('uncaughtException', (error) => { - logger_1.logger.fatal({ error }, 'Uncaught exception in main process'); - process.exit(1); -}); -process.on('unhandledRejection', (reason, promise) => { - logger_1.logger.fatal({ reason, promise }, 'Unhandled promise rejection in main process'); - process.exit(1); -}); -// Start the application -if (require.main === module) { - main(); -} -exports.default = main; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/backend/dist/index.js.map b/backend/dist/index.js.map deleted file mode 100644 index 0091568..0000000 --- a/backend/dist/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAEA;;;GAGG;;;AAEH,yCAAuC;AACvC,0CAAuC;AACvC,qCAAkC;AA6CzB,uFA7CA,eAAM,OA6CA;AA3Cf;;GAEG;AACH,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,0BAA0B;QAC1B,eAAM,CAAC,IAAI,CAAC;YACV,WAAW,EAAE,OAAO,CAAC,OAAO;YAC5B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;YAClB,WAAW,EAAE,cAAM,CAAC,GAAG,CAAC,GAAG;YAC3B,GAAG,EAAE,OAAO,CAAC,GAAG;SACjB,EAAE,yCAAyC,CAAC,CAAC;QAE9C,yBAAyB;QACzB,eAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QAC3C,wDAAwD;QAExD,mBAAmB;QACnB,MAAM,eAAM,CAAC,KAAK,EAAE,CAAC;IAEvB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,6BAA6B,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,0BAA0B;AAC1B,OAAO,CAAC,EAAE,CAAC,mBAAmB,EAAE,CAAC,KAAY,EAAE,EAAE;IAC/C,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,oCAAoC,CAAC,CAAC;IAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAe,EAAE,OAAyB,EAAE,EAAE;IAC9E,eAAM,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,6CAA6C,CAAC,CAAC;IACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC;AAEH,wBAAwB;AACxB,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,CAAC;AACT,CAAC;AAGD,kBAAe,IAAI,CAAC"} \ No newline at end of file diff --git a/backend/dist/performance/caching/RedisCacheManager.d.ts b/backend/dist/performance/caching/RedisCacheManager.d.ts deleted file mode 100644 index 84055df..0000000 --- a/backend/dist/performance/caching/RedisCacheManager.d.ts +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Redis Cache Manager - Phase 2 Performance Optimization - * Advanced caching layer for voice command processing and entity data - * - * Success Criteria: - * - Sub-100ms cache response times - * - Intelligent cache invalidation - * - Memory usage optimization - * - Cache hit rate >90% for frequently accessed data - */ -import { EventEmitter } from 'events'; -export interface CacheConfig { - host: string; - port: number; - password?: string; - db: number; - keyPrefix: string; - maxMemory?: string; - maxMemoryPolicy?: string; -} -export interface CacheEntry { - key: string; - value: any; - ttl: number; - createdAt: number; - accessCount: number; - lastAccessed: number; - tags?: string[]; -} -export interface CacheMetrics { - hits: number; - misses: number; - hitRate: number; - averageResponseTime: number; - p95ResponseTime: number; - memoryUsage: number; - keyCount: number; - totalOperations: number; -} -export interface CacheInvalidationPattern { - pattern: string; - description: string; - relatedEntities: string[]; -} -export declare class RedisCacheManager extends EventEmitter { - private redis; - private config; - private metrics; - private readonly maxMetricsHistory; - private metricsTimer?; - private readonly invalidationPatterns; - constructor(config: CacheConfig); - /** - * Setup Redis event handlers - */ - private setupEventHandlers; - /** - * Get value from cache with performance tracking - */ - get(key: string): Promise; - /** - * Set value in cache with TTL and tags - */ - set(key: string, value: any, ttl?: number, tags?: string[]): Promise; - /** - * Delete key from cache - */ - delete(key: string): Promise; - /** - * Get multiple keys at once - */ - mget(keys: string[]): Promise>; - /** - * Set multiple keys at once - */ - mset(entries: Map): Promise; - /** - * Invalidate cache entries by pattern - */ - invalidateByPattern(pattern: string): Promise; - /** - * Invalidate cache entries by tags - */ - invalidateByTags(tags: string[]): Promise; - /** - * Intelligent cache warming for frequently accessed entities - */ - warmCache(entityType: string, entityIds: string[], dataLoader: (id: string) => Promise): Promise; - /** - * Get TTL for specific entity types - */ - private getTTLForEntityType; - /** - * Add key to tag indices - */ - private addToTagIndices; - /** - * Update access metadata for analytics - */ - private updateAccessMetadata; - /** - * Record response time for metrics - */ - private recordResponseTime; - /** - * Calculate hit rate - */ - private getHitRate; - /** - * Start metrics collection - */ - private startMetricsCollection; - /** - * Collect memory usage metrics - */ - private collectMemoryMetrics; - /** - * Get comprehensive cache metrics - */ - getMetrics(): Promise; - /** - * Get cache health status - */ - getHealthStatus(): Promise<{ - status: 'healthy' | 'degraded' | 'unhealthy'; - checks: Record; - metrics: CacheMetrics; - }>; - /** - * Cleanup and close connection - */ - destroy(): Promise; -} -//# sourceMappingURL=RedisCacheManager.d.ts.map \ No newline at end of file diff --git a/backend/dist/performance/caching/RedisCacheManager.d.ts.map b/backend/dist/performance/caching/RedisCacheManager.d.ts.map deleted file mode 100644 index d17b40b..0000000 --- a/backend/dist/performance/caching/RedisCacheManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RedisCacheManager.d.ts","sourceRoot":"","sources":["../../../src/performance/caching/RedisCacheManager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAItC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,GAAG,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,qBAAa,iBAAkB,SAAQ,YAAY;IACjD,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,OAAO,CAMb;IACF,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAS;IAC3C,OAAO,CAAC,YAAY,CAAC,CAAiB;IAGtC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CA0BnC;gBAEU,MAAM,EAAE,WAAW;IAyB/B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAiB1B;;OAEG;IACG,GAAG,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC;IA8ClD;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,GAAE,MAAa,EAAE,IAAI,GAAE,MAAM,EAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IA2D7F;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IA2C3C;;OAEG;IACG,IAAI,CAAC,CAAC,GAAG,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAgE5D;;OAEG;IACG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE;QAAE,KAAK,EAAE,GAAG,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAyEhG;;OAEG;IACG,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsD3D;;OAEG;IACG,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC;IA8DvD;;OAEG;IACG,SAAS,CACb,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EAAE,EACnB,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,GAAG,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC;IAwDhB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;OAEG;YACW,eAAe;IAW7B;;OAEG;YACW,oBAAoB;IAiBlC;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAU1B;;OAEG;IACH,OAAO,CAAC,UAAU;IAKlB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAM9B;;OAEG;YACW,oBAAoB;IAelC;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,YAAY,CAAC;IA0BzC;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC;QAC/B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;QAC7C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAChC,OAAO,EAAE,YAAY,CAAC;KACvB,CAAC;IA4BF;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAU/B"} \ No newline at end of file diff --git a/backend/dist/performance/caching/RedisCacheManager.js b/backend/dist/performance/caching/RedisCacheManager.js deleted file mode 100644 index 7374e7f..0000000 --- a/backend/dist/performance/caching/RedisCacheManager.js +++ /dev/null @@ -1,641 +0,0 @@ -"use strict"; -/** - * Redis Cache Manager - Phase 2 Performance Optimization - * Advanced caching layer for voice command processing and entity data - * - * Success Criteria: - * - Sub-100ms cache response times - * - Intelligent cache invalidation - * - Memory usage optimization - * - Cache hit rate >90% for frequently accessed data - */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.RedisCacheManager = void 0; -const ioredis_1 = __importDefault(require("ioredis")); -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -class RedisCacheManager extends events_1.EventEmitter { - redis; - config; - metrics = { - hits: 0, - misses: 0, - operations: 0, - responseTimes: [], - memoryUsage: 0, - }; - maxMetricsHistory = 10000; - metricsTimer; - // Cache invalidation patterns for different entity types - invalidationPatterns = [ - { - pattern: 'user:*', - description: 'User-related cache entries', - relatedEntities: ['user', 'session', 'preferences'], - }, - { - pattern: 'channel:*', - description: 'Channel-related cache entries', - relatedEntities: ['channel', 'message', 'member'], - }, - { - pattern: 'task:*', - description: 'Task-related cache entries', - relatedEntities: ['task', 'assignment', 'status'], - }, - { - pattern: 'file:*', - description: 'File-related cache entries', - relatedEntities: ['file', 'metadata', 'permission'], - }, - { - pattern: 'command:*', - description: 'Voice command cache entries', - relatedEntities: ['command', 'execution', 'result'], - }, - ]; - constructor(config) { - super(); - this.config = config; - this.redis = new ioredis_1.default({ - host: config.host, - port: config.port, - ...(config.password && { password: config.password }), - db: config.db, - keyPrefix: config.keyPrefix, - enableReadyCheck: true, - maxRetriesPerRequest: 3, - }); - this.setupEventHandlers(); - this.startMetricsCollection(); - logger_1.logger.info('Redis Cache Manager initialized', { - host: config.host, - port: config.port, - db: config.db, - keyPrefix: config.keyPrefix, - }); - } - /** - * Setup Redis event handlers - */ - setupEventHandlers() { - this.redis.on('ready', () => { - logger_1.logger.info('Redis connection established'); - this.emit('ready'); - }); - this.redis.on('error', (error) => { - logger_1.logger.error('Redis connection error', { error: error.message }); - this.emit('error', error); - }); - this.redis.on('close', () => { - logger_1.logger.warn('Redis connection closed'); - this.emit('close'); - }); - } - /** - * Get value from cache with performance tracking - */ - async get(key) { - const startTime = perf_hooks_1.performance.now(); - try { - const value = await this.redis.get(key); - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - if (value === null) { - this.metrics.misses++; - logger_1.logger.debug('Cache miss', { key, responseTime: `${responseTime.toFixed(2)}ms` }); - return null; - } - this.metrics.hits++; - // Update access metadata - await this.updateAccessMetadata(key); - logger_1.logger.debug('Cache hit', { - key, - responseTime: `${responseTime.toFixed(2)}ms`, - hitRate: this.getHitRate().toFixed(2) + '%', - }); - // Parse JSON if it's a JSON string - try { - return JSON.parse(value); - } - catch { - return value; - } - } - catch (error) { - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - logger_1.logger.error('Cache get error', { - key, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - return null; - } - } - /** - * Set value in cache with TTL and tags - */ - async set(key, value, ttl = 3600, tags = []) { - const startTime = perf_hooks_1.performance.now(); - try { - // Serialize value - const serializedValue = typeof value === 'string' ? value : JSON.stringify(value); - // Set value with TTL - const result = await this.redis.setex(key, ttl, serializedValue); - // Store metadata for analytics - const metadata = { - key, - ttl, - createdAt: Date.now(), - accessCount: 0, - lastAccessed: Date.now(), - tags, - }; - await this.redis.setex(`${key}:meta`, ttl, JSON.stringify(metadata)); - // Add to tag indices if tags provided - if (tags.length > 0) { - await this.addToTagIndices(key, tags, ttl); - } - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - logger_1.logger.debug('Cache set', { - key, - ttl: `${ttl}s`, - tags: tags.length, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - this.emit('cache_set', { - key, - ttl, - tags, - responseTime, - }); - return result === 'OK'; - } - catch (error) { - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - logger_1.logger.error('Cache set error', { - key, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - return false; - } - } - /** - * Delete key from cache - */ - async delete(key) { - const startTime = perf_hooks_1.performance.now(); - try { - const pipeline = this.redis.pipeline(); - pipeline.del(key); - pipeline.del(`${key}:meta`); - const results = await pipeline.exec(); - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - const deletedCount = results?.reduce((sum, [err, result]) => (err ? sum : sum + result), 0) || 0; - logger_1.logger.debug('Cache delete', { - key, - deletedEntries: deletedCount, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - this.emit('cache_delete', { - key, - deletedEntries: deletedCount, - responseTime, - }); - return deletedCount > 0; - } - catch (error) { - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - logger_1.logger.error('Cache delete error', { - key, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - return false; - } - } - /** - * Get multiple keys at once - */ - async mget(keys) { - const startTime = perf_hooks_1.performance.now(); - const result = new Map(); - if (keys.length === 0) { - return result; - } - try { - const values = await this.redis.mget(...keys); - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - let hits = 0; - let misses = 0; - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - const value = values[i]; - if (!key || value === null || value === undefined) { - misses++; - continue; - } - hits++; - try { - result.set(key, JSON.parse(value)); - } - catch { - result.set(key, value); - } - // Update access metadata (async, don't wait) - this.updateAccessMetadata(key); - } - this.metrics.hits += hits; - this.metrics.misses += misses; - logger_1.logger.debug('Cache mget', { - keys: keys.length, - hits, - misses, - hitRate: hits > 0 ? ((hits / keys.length) * 100).toFixed(1) + '%' : '0%', - responseTime: `${responseTime.toFixed(2)}ms`, - }); - return result; - } - catch (error) { - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - logger_1.logger.error('Cache mget error', { - keys: keys.length, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - return result; - } - } - /** - * Set multiple keys at once - */ - async mset(entries) { - const startTime = perf_hooks_1.performance.now(); - let successCount = 0; - if (entries.size === 0) { - return 0; - } - try { - const pipeline = this.redis.pipeline(); - for (const [key, entry] of entries) { - const { value, ttl = 3600, tags = [] } = entry; - const serializedValue = typeof value === 'string' ? value : JSON.stringify(value); - pipeline.setex(key, ttl, serializedValue); - // Add metadata - const metadata = { - key, - ttl, - createdAt: Date.now(), - accessCount: 0, - lastAccessed: Date.now(), - tags, - }; - pipeline.setex(`${key}:meta`, ttl, JSON.stringify(metadata)); - // Add to tag indices - if (tags.length > 0) { - for (const tag of tags) { - pipeline.sadd(`tag:${tag}`, key); - pipeline.expire(`tag:${tag}`, ttl); - } - } - } - const results = await pipeline.exec(); - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - // Count successful operations - successCount = results?.filter(([err, result]) => !err && result === 'OK').length || 0; - logger_1.logger.debug('Cache mset', { - entries: entries.size, - successful: successCount, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - this.emit('cache_mset', { - entries: entries.size, - successful: successCount, - responseTime, - }); - return successCount; - } - catch (error) { - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - logger_1.logger.error('Cache mset error', { - entries: entries.size, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - return successCount; - } - } - /** - * Invalidate cache entries by pattern - */ - async invalidateByPattern(pattern) { - const startTime = perf_hooks_1.performance.now(); - try { - const keys = await this.redis.keys(pattern); - if (keys.length === 0) { - logger_1.logger.debug('No keys found for invalidation pattern', { pattern }); - return 0; - } - const pipeline = this.redis.pipeline(); - for (const key of keys) { - pipeline.del(key); - pipeline.del(`${key}:meta`); - } - const results = await pipeline.exec(); - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - const deletedCount = results?.filter(([err, result]) => !err && result === 1).length || 0; - logger_1.logger.info('Cache invalidated by pattern', { - pattern, - keysFound: keys.length, - deleted: deletedCount, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - this.emit('cache_invalidated', { - pattern, - keysFound: keys.length, - deleted: deletedCount, - responseTime, - }); - return deletedCount; - } - catch (error) { - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - logger_1.logger.error('Cache invalidation error', { - pattern, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - return 0; - } - } - /** - * Invalidate cache entries by tags - */ - async invalidateByTags(tags) { - const startTime = perf_hooks_1.performance.now(); - let totalDeleted = 0; - try { - for (const tag of tags) { - const keys = await this.redis.smembers(`tag:${tag}`); - if (keys.length > 0) { - const pipeline = this.redis.pipeline(); - for (const key of keys) { - pipeline.del(key); - pipeline.del(`${key}:meta`); - } - // Also remove the tag set - pipeline.del(`tag:${tag}`); - const results = await pipeline.exec(); - const deletedCount = results?.filter(([err, result]) => !err && result === 1).length || 0; - totalDeleted += deletedCount; - logger_1.logger.debug('Cache invalidated by tag', { - tag, - keys: keys.length, - deleted: deletedCount, - }); - } - } - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - logger_1.logger.info('Cache invalidated by tags', { - tags, - totalDeleted, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - this.emit('cache_invalidated_by_tags', { - tags, - totalDeleted, - responseTime, - }); - return totalDeleted; - } - catch (error) { - const responseTime = perf_hooks_1.performance.now() - startTime; - this.recordResponseTime(responseTime); - logger_1.logger.error('Cache invalidation by tags error', { - tags, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - return totalDeleted; - } - } - /** - * Intelligent cache warming for frequently accessed entities - */ - async warmCache(entityType, entityIds, dataLoader) { - const startTime = perf_hooks_1.performance.now(); - try { - const cacheOperations = new Map(); - for (const entityId of entityIds) { - try { - const data = await dataLoader(entityId); - const cacheKey = `${entityType}:${entityId}`; - cacheOperations.set(cacheKey, { - value: data, - ttl: this.getTTLForEntityType(entityType), - tags: [entityType, `${entityType}:${entityId}`], - }); - } - catch (error) { - logger_1.logger.warn('Failed to load data for cache warming', { - entityType, - entityId, - error: error.message, - }); - } - } - if (cacheOperations.size > 0) { - await this.mset(cacheOperations); - } - const responseTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.info('Cache warming completed', { - entityType, - entityIds: entityIds.length, - cached: cacheOperations.size, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - this.emit('cache_warmed', { - entityType, - entityIds: entityIds.length, - cached: cacheOperations.size, - responseTime, - }); - } - catch (error) { - const responseTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.error('Cache warming error', { - entityType, - entityIds: entityIds.length, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - } - } - /** - * Get TTL for specific entity types - */ - getTTLForEntityType(entityType) { - const ttlMap = { - user: 1800, // 30 minutes - channel: 3600, // 1 hour - task: 900, // 15 minutes - file: 7200, // 2 hours - command: 300, // 5 minutes - session: 1800, // 30 minutes - organization: 7200, // 2 hours - default: 3600, // 1 hour - }; - return ttlMap[entityType] ?? ttlMap['default'] ?? 3600; - } - /** - * Add key to tag indices - */ - async addToTagIndices(key, tags, ttl) { - const pipeline = this.redis.pipeline(); - for (const tag of tags) { - pipeline.sadd(`tag:${tag}`, key); - pipeline.expire(`tag:${tag}`, ttl); - } - await pipeline.exec(); - } - /** - * Update access metadata for analytics - */ - async updateAccessMetadata(key) { - try { - const metaKey = `${key}:meta`; - const metaData = await this.redis.get(metaKey); - if (metaData) { - const metadata = JSON.parse(metaData); - metadata.accessCount++; - metadata.lastAccessed = Date.now(); - await this.redis.set(metaKey, JSON.stringify(metadata), 'KEEPTTL'); - } - } - catch (error) { - // Silently fail for metadata updates - } - } - /** - * Record response time for metrics - */ - recordResponseTime(time) { - this.metrics.operations++; - this.metrics.responseTimes.push(time); - // Keep only recent measurements - if (this.metrics.responseTimes.length > this.maxMetricsHistory) { - this.metrics.responseTimes.shift(); - } - } - /** - * Calculate hit rate - */ - getHitRate() { - const total = this.metrics.hits + this.metrics.misses; - return total > 0 ? (this.metrics.hits / total) * 100 : 0; - } - /** - * Start metrics collection - */ - startMetricsCollection() { - this.metricsTimer = setInterval(async () => { - await this.collectMemoryMetrics(); - }, 60000); // Every minute - } - /** - * Collect memory usage metrics - */ - async collectMemoryMetrics() { - try { - const memoryInfo = await this.redis.call('MEMORY', 'USAGE'); - if (typeof memoryInfo === 'number') { - this.metrics.memoryUsage = memoryInfo; - } - } - catch (error) { - logger_1.logger.error('Failed to collect memory metrics', { - error: error.message, - }); - // Silently fail for metrics collection - } - } - /** - * Get comprehensive cache metrics - */ - async getMetrics() { - const responseTimes = this.metrics.responseTimes; - const averageResponseTime = responseTimes.length > 0 - ? responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length - : 0; - const sortedTimes = [...responseTimes].sort((a, b) => a - b); - const p95ResponseTime = sortedTimes.length > 0 ? sortedTimes[Math.floor(sortedTimes.length * 0.95)] || 0 : 0; - // Get current key count - const keyCount = await this.redis.dbsize(); - return { - hits: this.metrics.hits, - misses: this.metrics.misses, - hitRate: this.getHitRate(), - averageResponseTime: Math.round(averageResponseTime * 100) / 100, - p95ResponseTime: Math.round(p95ResponseTime * 100) / 100, - memoryUsage: this.metrics.memoryUsage, - keyCount, - totalOperations: this.metrics.operations, - }; - } - /** - * Get cache health status - */ - async getHealthStatus() { - const metrics = await this.getMetrics(); - const checks = { - connected: this.redis.status === 'ready', - hitRateAcceptable: metrics.hitRate >= 85, // 85% minimum hit rate - responseTimeAcceptable: metrics.averageResponseTime < 100, // Sub-100ms average - memoryUsageAcceptable: true, // Would need Redis configuration to check properly - }; - const passedChecks = Object.values(checks).filter(Boolean).length; - const totalChecks = Object.values(checks).length; - let status = 'healthy'; - if (passedChecks < totalChecks * 0.5) { - status = 'unhealthy'; - } - else if (passedChecks < totalChecks) { - status = 'degraded'; - } - return { - status, - checks, - metrics, - }; - } - /** - * Cleanup and close connection - */ - async destroy() { - if (this.metricsTimer) { - clearInterval(this.metricsTimer); - } - this.removeAllListeners(); - await this.redis.disconnect(); - logger_1.logger.info('Redis Cache Manager destroyed'); - } -} -exports.RedisCacheManager = RedisCacheManager; -//# sourceMappingURL=RedisCacheManager.js.map \ No newline at end of file diff --git a/backend/dist/performance/caching/RedisCacheManager.js.map b/backend/dist/performance/caching/RedisCacheManager.js.map deleted file mode 100644 index 7342830..0000000 --- a/backend/dist/performance/caching/RedisCacheManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"RedisCacheManager.js","sourceRoot":"","sources":["../../../src/performance/caching/RedisCacheManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/performance/monitoring/PerformanceMonitor.d.ts b/backend/dist/performance/monitoring/PerformanceMonitor.d.ts deleted file mode 100644 index 0bd4490..0000000 --- a/backend/dist/performance/monitoring/PerformanceMonitor.d.ts +++ /dev/null @@ -1,243 +0,0 @@ -/** - * Performance Monitor - Phase 2 Performance Monitoring & Analytics - * Comprehensive performance monitoring for voice command processing - * - * Success Criteria: - * - Real-time performance metrics collection - * - Automated performance alerting - * - Performance bottleneck identification - * - System health monitoring - */ -import { EventEmitter } from 'events'; -export interface PerformanceMetrics { - timestamp: string; - cpu: { - usage: number; - loadAverage: number[]; - cores: number; - }; - memory: { - used: number; - total: number; - usage: number; - heapUsed: number; - heapTotal: number; - external: number; - }; - network: { - bytesIn: number; - bytesOut: number; - connectionsActive: number; - }; - voice: { - averageProcessingTime: number; - p95ProcessingTime: number; - commandsPerMinute: number; - errorRate: number; - successRate: number; - }; - database: { - activeConnections: number; - averageQueryTime: number; - slowQueries: number; - connectionPoolUsage: number; - }; - cache: { - hitRate: number; - averageResponseTime: number; - memoryUsage: number; - operationsPerSecond: number; - }; - websocket: { - activeConnections: number; - messagesPerSecond: number; - averageLatency: number; - disconnectionRate: number; - }; -} -export interface PerformanceAlert { - alertId: string; - type: 'cpu' | 'memory' | 'response_time' | 'error_rate' | 'connection' | 'disk' | 'custom'; - severity: 'low' | 'medium' | 'high' | 'critical'; - title: string; - message: string; - threshold: number; - currentValue: number; - timestamp: string; - resolved: boolean; - resolvedAt?: string; -} -export interface PerformanceThresholds { - cpu: { - warning: number; - critical: number; - }; - memory: { - warning: number; - critical: number; - }; - responseTime: { - warning: number; - critical: number; - }; - errorRate: { - warning: number; - critical: number; - }; - diskUsage: { - warning: number; - critical: number; - }; -} -export interface SystemHealth { - status: 'healthy' | 'degraded' | 'unhealthy' | 'critical'; - score: number; - checks: { - cpu: { - status: 'pass' | 'warn' | 'fail'; - value: number; - }; - memory: { - status: 'pass' | 'warn' | 'fail'; - value: number; - }; - responseTime: { - status: 'pass' | 'warn' | 'fail'; - value: number; - }; - errorRate: { - status: 'pass' | 'warn' | 'fail'; - value: number; - }; - connectivity: { - status: 'pass' | 'warn' | 'fail'; - value: number; - }; - }; - lastChecked: string; -} -export declare class PerformanceMonitor extends EventEmitter { - private metricsHistory; - private activeAlerts; - private performanceObserver?; - private monitoringTimer?; - private healthCheckTimer?; - private readonly maxHistorySize; - private readonly monitoringInterval; - private readonly healthCheckInterval; - private voiceCommandMetrics; - private databaseQueryMetrics; - private cacheOperationMetrics; - private websocketMetrics; - private networkMetrics; - private errorCounts; - private thresholds; - constructor(customThresholds?: Partial); - /** - * Setup Node.js Performance Observer - */ - private setupPerformanceObserver; - /** - * Start continuous monitoring - */ - private startMonitoring; - /** - * Start health checks - */ - private startHealthChecks; - /** - * Collect comprehensive performance metrics - */ - private collectMetrics; - /** - * Get CPU metrics - */ - private getCPUMetrics; - /** - * Get memory metrics - */ - private getMemoryMetrics; - /** - * Get network metrics - */ - private getNetworkMetrics; - /** - * Get voice command processing metrics - */ - private getVoiceCommandMetrics; - /** - * Get database metrics - */ - private getDatabaseMetrics; - /** - * Get cache metrics - */ - private getCacheMetrics; - /** - * Get WebSocket metrics - */ - private getWebSocketMetrics; - /** - * Record custom performance metric - */ - recordCustomMetric(name: string, value: number): void; - /** - * Record error event - */ - recordError(type: 'voice' | 'database' | 'network' | 'general', details?: any): void; - /** - * Record network activity - */ - recordNetworkActivity(bytesIn: number, bytesOut: number, connectionChange?: number): void; - /** - * Update WebSocket connection count - */ - updateWebSocketConnections(count: number): void; - /** - * Analyze metrics for performance issues - */ - private analyzeMetrics; - /** - * Create performance alert - */ - private createAlert; - /** - * Resolve performance alert - */ - private resolveAlert; - /** - * Perform comprehensive system health check - */ - private performHealthCheck; - /** - * Evaluate individual metric against thresholds - */ - private evaluateMetric; - /** - * Get recent performance metrics - */ - getMetrics(limit?: number): PerformanceMetrics[]; - /** - * Get active performance alerts - */ - getActiveAlerts(): PerformanceAlert[]; - /** - * Get all alerts (including resolved) - */ - getAllAlerts(limit?: number): PerformanceAlert[]; - /** - * Get performance summary - */ - getPerformanceSummary(): { - currentMetrics: PerformanceMetrics | null; - activeAlerts: number; - averageResponseTime24h: number; - errorRate24h: number; - uptime: number; - }; - /** - * Clean up old data and stop monitoring - */ - destroy(): void; -} -//# sourceMappingURL=PerformanceMonitor.d.ts.map \ No newline at end of file diff --git a/backend/dist/performance/monitoring/PerformanceMonitor.d.ts.map b/backend/dist/performance/monitoring/PerformanceMonitor.d.ts.map deleted file mode 100644 index 6d45ba5..0000000 --- a/backend/dist/performance/monitoring/PerformanceMonitor.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"PerformanceMonitor.d.ts","sourceRoot":"","sources":["../../../src/performance/monitoring/PerformanceMonitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAMtC,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,EAAE;QACH,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,EAAE,MAAM,EAAE,CAAC;QACtB,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,KAAK,EAAE,MAAM,CAAC;QACd,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;IACF,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,CAAC;QACjB,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,KAAK,EAAE;QACL,qBAAqB,EAAE,MAAM,CAAC;QAC9B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,QAAQ,EAAE;QACR,iBAAiB,EAAE,MAAM,CAAC;QAC1B,gBAAgB,EAAE,MAAM,CAAC;QACzB,WAAW,EAAE,MAAM,CAAC;QACpB,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,KAAK,EAAE;QACL,OAAO,EAAE,MAAM,CAAC;QAChB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,WAAW,EAAE,MAAM,CAAC;QACpB,mBAAmB,EAAE,MAAM,CAAC;KAC7B,CAAC;IACF,SAAS,EAAE;QACT,iBAAiB,EAAE,MAAM,CAAC;QAC1B,iBAAiB,EAAE,MAAM,CAAC;QAC1B,cAAc,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;CACH;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,eAAe,GAAG,YAAY,GAAG,YAAY,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC3F,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,UAAU,CAAC;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,qBAAqB;IACpC,GAAG,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC3C,MAAM,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,YAAY,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACjD,SAAS,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;CAClD;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;IAC1D,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE;QACN,GAAG,EAAE;YAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QACzD,MAAM,EAAE;YAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5D,YAAY,EAAE;YAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAClE,SAAS,EAAE;YAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;QAC/D,YAAY,EAAE;YAAE,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAA;SAAE,CAAC;KACnE,CAAC;IACF,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,kBAAmB,SAAQ,YAAY;IAClD,OAAO,CAAC,cAAc,CAA4B;IAClD,OAAO,CAAC,YAAY,CAA4C;IAChE,OAAO,CAAC,mBAAmB,CAAC,CAAsB;IAClD,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,gBAAgB,CAAC,CAAiB;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAQ;IACvC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAG7C,OAAO,CAAC,mBAAmB,CAAgB;IAC3C,OAAO,CAAC,oBAAoB,CAAgB;IAC5C,OAAO,CAAC,qBAAqB,CAAgB;IAC7C,OAAO,CAAC,gBAAgB,CAGtB;IACF,OAAO,CAAC,cAAc,CAA+C;IACrE,OAAO,CAAC,WAAW,CAA+D;IAElF,OAAO,CAAC,UAAU,CAMhB;gBAEU,gBAAgB,CAAC,EAAE,OAAO,CAAC,qBAAqB,CAAC;IAkB7D;;OAEG;IACH,OAAO,CAAC,wBAAwB;IAchC;;OAEG;IACH,OAAO,CAAC,eAAe;IAMvB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAMzB;;OAEG;YACW,cAAc;IA4C5B;;OAEG;YACW,aAAa;IAwB3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAkBxB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAQzB;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAiC9B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAiB1B;;OAEG;IACH,OAAO,CAAC,eAAe;IAevB;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAe3B;;OAEG;IACH,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAqCrD;;OAEG;IACH,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,UAAU,GAAG,SAAS,GAAG,SAAS,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,IAAI;IAWpF;;OAEG;IACH,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,gBAAgB,GAAE,MAAU,GAAG,IAAI;IAM5F;;OAEG;IACH,0BAA0B,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI/C;;OAEG;YACW,cAAc;IA8F5B;;OAEG;YACW,WAAW;IA2CzB;;OAEG;YACW,YAAY;IAgB1B;;OAEG;YACW,kBAAkB;IA0DhC;;OAEG;IACH,OAAO,CAAC,cAAc;IAgBtB;;OAEG;IACH,UAAU,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,kBAAkB,EAAE;IAOhD;;OAEG;IACH,eAAe,IAAI,gBAAgB,EAAE;IAIrC;;OAEG;IACH,YAAY,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,gBAAgB,EAAE;IAQhD;;OAEG;IACH,qBAAqB,IAAI;QACvB,cAAc,EAAE,kBAAkB,GAAG,IAAI,CAAC;QAC1C,YAAY,EAAE,MAAM,CAAC;QACrB,sBAAsB,EAAE,MAAM,CAAC;QAC/B,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;KAChB;IAyBD;;OAEG;IACH,OAAO,IAAI,IAAI;CAiBhB"} \ No newline at end of file diff --git a/backend/dist/performance/monitoring/PerformanceMonitor.js b/backend/dist/performance/monitoring/PerformanceMonitor.js deleted file mode 100644 index a404a6b..0000000 --- a/backend/dist/performance/monitoring/PerformanceMonitor.js +++ /dev/null @@ -1,548 +0,0 @@ -"use strict"; -/** - * Performance Monitor - Phase 2 Performance Monitoring & Analytics - * Comprehensive performance monitoring for voice command processing - * - * Success Criteria: - * - Real-time performance metrics collection - * - Automated performance alerting - * - Performance bottleneck identification - * - System health monitoring - */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.PerformanceMonitor = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const os_1 = __importDefault(require("os")); -const process_1 = __importDefault(require("process")); -const logger_1 = require("../../utils/logger"); -class PerformanceMonitor extends events_1.EventEmitter { - metricsHistory = []; - activeAlerts = new Map(); - performanceObserver; - monitoringTimer; - healthCheckTimer; - maxHistorySize = 1440; // 24 hours at 1-minute intervals - monitoringInterval = 60000; // 1 minute - healthCheckInterval = 30000; // 30 seconds - // Performance tracking maps - voiceCommandMetrics = []; - databaseQueryMetrics = []; - cacheOperationMetrics = []; - websocketMetrics = { - latency: [], - connections: 0, - }; - networkMetrics = { bytesIn: 0, bytesOut: 0, connections: 0 }; - errorCounts = { total: 0, voice: 0, database: 0, network: 0, general: 0 }; - thresholds = { - cpu: { warning: 70, critical: 90 }, - memory: { warning: 80, critical: 95 }, - responseTime: { warning: 1000, critical: 3000 }, - errorRate: { warning: 5, critical: 10 }, - diskUsage: { warning: 80, critical: 95 }, - }; - constructor(customThresholds) { - super(); - if (customThresholds) { - this.thresholds = { ...this.thresholds, ...customThresholds }; - } - this.setupPerformanceObserver(); - this.startMonitoring(); - this.startHealthChecks(); - logger_1.logger.info('Performance Monitor initialized', { - monitoringInterval: `${this.monitoringInterval / 1000}s`, - healthCheckInterval: `${this.healthCheckInterval / 1000}s`, - thresholds: this.thresholds, - }); - } - /** - * Setup Node.js Performance Observer - */ - setupPerformanceObserver() { - this.performanceObserver = new perf_hooks_1.PerformanceObserver((list) => { - const entries = list.getEntries(); - for (const entry of entries) { - if (entry.entryType === 'measure') { - this.recordCustomMetric(entry.name, entry.duration); - } - } - }); - this.performanceObserver.observe({ entryTypes: ['measure', 'resource'] }); - } - /** - * Start continuous monitoring - */ - startMonitoring() { - this.monitoringTimer = setInterval(async () => { - await this.collectMetrics(); - }, this.monitoringInterval); - } - /** - * Start health checks - */ - startHealthChecks() { - this.healthCheckTimer = setInterval(async () => { - await this.performHealthCheck(); - }, this.healthCheckInterval); - } - /** - * Collect comprehensive performance metrics - */ - async collectMetrics() { - const startTime = perf_hooks_1.performance.now(); - try { - const metrics = { - timestamp: new Date().toISOString(), - cpu: await this.getCPUMetrics(), - memory: this.getMemoryMetrics(), - network: this.getNetworkMetrics(), - voice: this.getVoiceCommandMetrics(), - database: this.getDatabaseMetrics(), - cache: this.getCacheMetrics(), - websocket: this.getWebSocketMetrics(), - }; - // Add to history - this.metricsHistory.push(metrics); - // Trim history if too large - if (this.metricsHistory.length > this.maxHistorySize) { - this.metricsHistory.shift(); - } - // Check for performance issues - await this.analyzeMetrics(metrics); - const collectionTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.debug('Performance metrics collected', { - collectionTime: `${collectionTime.toFixed(2)}ms`, - cpuUsage: `${metrics.cpu.usage.toFixed(1)}%`, - memoryUsage: `${metrics.memory.usage.toFixed(1)}%`, - voiceCommandsPerMin: metrics.voice.commandsPerMinute, - }); - this.emit('metrics_collected', metrics); - } - catch (error) { - logger_1.logger.error('Error collecting performance metrics', { - error: error.message, - stack: error.stack, - }); - } - } - /** - * Get CPU metrics - */ - async getCPUMetrics() { - const cpus = os_1.default.cpus(); - const loadAverage = os_1.default.loadavg(); - // Calculate CPU usage (simplified) - let totalIdle = 0; - let totalTick = 0; - for (const cpu of cpus) { - for (const type in cpu.times) { - totalTick += cpu.times[type]; - } - totalIdle += cpu.times.idle; - } - const usage = 100 - Math.floor((totalIdle / totalTick) * 100); - return { - usage, - loadAverage, - cores: cpus.length, - }; - } - /** - * Get memory metrics - */ - getMemoryMetrics() { - const totalMemory = os_1.default.totalmem(); - const freeMemory = os_1.default.freemem(); - const usedMemory = totalMemory - freeMemory; - const memoryUsage = (usedMemory / totalMemory) * 100; - const heapStats = process_1.default.memoryUsage(); - return { - used: usedMemory, - total: totalMemory, - usage: memoryUsage, - heapUsed: heapStats.heapUsed, - heapTotal: heapStats.heapTotal, - external: heapStats.external, - }; - } - /** - * Get network metrics - */ - getNetworkMetrics() { - return { - bytesIn: this.networkMetrics.bytesIn, - bytesOut: this.networkMetrics.bytesOut, - connectionsActive: this.networkMetrics.connections, - }; - } - /** - * Get voice command processing metrics - */ - getVoiceCommandMetrics() { - if (this.voiceCommandMetrics.length === 0) { - return { - averageProcessingTime: 0, - p95ProcessingTime: 0, - commandsPerMinute: 0, - errorRate: 0, - successRate: 100, - }; - } - const sorted = [...this.voiceCommandMetrics].sort((a, b) => a - b); - const average = this.voiceCommandMetrics.reduce((sum, time) => sum + time, 0) / - this.voiceCommandMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const commandsPerMinute = this.voiceCommandMetrics.length; // Metrics collected per minute - const errorRate = this.errorCounts.voice > 0 - ? (this.errorCounts.voice / this.voiceCommandMetrics.length) * 100 - : 0; - const successRate = 100 - errorRate; - return { - averageProcessingTime: Math.round(average * 100) / 100, - p95ProcessingTime: Math.round(p95 * 100) / 100, - commandsPerMinute, - errorRate: Math.round(errorRate * 100) / 100, - successRate: Math.round(successRate * 100) / 100, - }; - } - /** - * Get database metrics - */ - getDatabaseMetrics() { - const averageQueryTime = this.databaseQueryMetrics.length > 0 - ? this.databaseQueryMetrics.reduce((sum, time) => sum + time, 0) / - this.databaseQueryMetrics.length - : 0; - const slowQueries = this.databaseQueryMetrics.filter((time) => time > 1000).length; - return { - activeConnections: 0, // Would be populated by database pool - averageQueryTime: Math.round(averageQueryTime * 100) / 100, - slowQueries, - connectionPoolUsage: 0, // Would be calculated from pool stats - }; - } - /** - * Get cache metrics - */ - getCacheMetrics() { - const averageResponseTime = this.cacheOperationMetrics.length > 0 - ? this.cacheOperationMetrics.reduce((sum, time) => sum + time, 0) / - this.cacheOperationMetrics.length - : 0; - return { - hitRate: 90, // Would be populated by cache manager - averageResponseTime: Math.round(averageResponseTime * 100) / 100, - memoryUsage: 0, // Would be populated by cache manager - operationsPerSecond: this.cacheOperationMetrics.length, - }; - } - /** - * Get WebSocket metrics - */ - getWebSocketMetrics() { - const averageLatency = this.websocketMetrics.latency.length > 0 - ? this.websocketMetrics.latency.reduce((sum, time) => sum + time, 0) / - this.websocketMetrics.latency.length - : 0; - return { - activeConnections: this.websocketMetrics.connections, - messagesPerSecond: 0, // Would be calculated from message counts - averageLatency: Math.round(averageLatency * 100) / 100, - disconnectionRate: 0, // Would be calculated from disconnection events - }; - } - /** - * Record custom performance metric - */ - recordCustomMetric(name, value) { - switch (name) { - case 'voice_command_processing': - this.voiceCommandMetrics.push(value); - if (this.voiceCommandMetrics.length > 1000) { - this.voiceCommandMetrics.shift(); - } - break; - case 'database_query': - this.databaseQueryMetrics.push(value); - if (this.databaseQueryMetrics.length > 1000) { - this.databaseQueryMetrics.shift(); - } - break; - case 'cache_operation': - this.cacheOperationMetrics.push(value); - if (this.cacheOperationMetrics.length > 1000) { - this.cacheOperationMetrics.shift(); - } - break; - case 'websocket_latency': - this.websocketMetrics.latency.push(value); - if (this.websocketMetrics.latency.length > 1000) { - this.websocketMetrics.latency.shift(); - } - break; - } - logger_1.logger.debug('Performance metric recorded', { - name, - value: `${value.toFixed(2)}ms`, - }); - } - /** - * Record error event - */ - recordError(type, details) { - this.errorCounts.total++; - this.errorCounts[type] = (this.errorCounts[type] || 0) + 1; - logger_1.logger.debug('Error recorded for performance monitoring', { - type, - totalErrors: this.errorCounts.total, - typeErrors: this.errorCounts[type], - }); - } - /** - * Record network activity - */ - recordNetworkActivity(bytesIn, bytesOut, connectionChange = 0) { - this.networkMetrics.bytesIn += bytesIn; - this.networkMetrics.bytesOut += bytesOut; - this.networkMetrics.connections += connectionChange; - } - /** - * Update WebSocket connection count - */ - updateWebSocketConnections(count) { - this.websocketMetrics.connections = count; - } - /** - * Analyze metrics for performance issues - */ - async analyzeMetrics(metrics) { - // CPU usage check - if (metrics.cpu.usage > this.thresholds.cpu.critical) { - await this.createAlert('cpu', 'critical', 'Critical CPU Usage', `CPU usage is at ${metrics.cpu.usage.toFixed(1)}%`, this.thresholds.cpu.critical, metrics.cpu.usage); - } - else if (metrics.cpu.usage > this.thresholds.cpu.warning) { - await this.createAlert('cpu', 'medium', 'High CPU Usage', `CPU usage is at ${metrics.cpu.usage.toFixed(1)}%`, this.thresholds.cpu.warning, metrics.cpu.usage); - } - else { - await this.resolveAlert('cpu'); - } - // Memory usage check - if (metrics.memory.usage > this.thresholds.memory.critical) { - await this.createAlert('memory', 'critical', 'Critical Memory Usage', `Memory usage is at ${metrics.memory.usage.toFixed(1)}%`, this.thresholds.memory.critical, metrics.memory.usage); - } - else if (metrics.memory.usage > this.thresholds.memory.warning) { - await this.createAlert('memory', 'medium', 'High Memory Usage', `Memory usage is at ${metrics.memory.usage.toFixed(1)}%`, this.thresholds.memory.warning, metrics.memory.usage); - } - else { - await this.resolveAlert('memory'); - } - // Response time check - if (metrics.voice.averageProcessingTime > this.thresholds.responseTime.critical) { - await this.createAlert('response_time', 'critical', 'Critical Response Time', `Voice command processing time is ${metrics.voice.averageProcessingTime.toFixed(2)}ms`, this.thresholds.responseTime.critical, metrics.voice.averageProcessingTime); - } - else if (metrics.voice.averageProcessingTime > this.thresholds.responseTime.warning) { - await this.createAlert('response_time', 'medium', 'High Response Time', `Voice command processing time is ${metrics.voice.averageProcessingTime.toFixed(2)}ms`, this.thresholds.responseTime.warning, metrics.voice.averageProcessingTime); - } - else { - await this.resolveAlert('response_time'); - } - // Error rate check - if (metrics.voice.errorRate > this.thresholds.errorRate.critical) { - await this.createAlert('error_rate', 'critical', 'Critical Error Rate', `Voice command error rate is ${metrics.voice.errorRate.toFixed(1)}%`, this.thresholds.errorRate.critical, metrics.voice.errorRate); - } - else if (metrics.voice.errorRate > this.thresholds.errorRate.warning) { - await this.createAlert('error_rate', 'medium', 'High Error Rate', `Voice command error rate is ${metrics.voice.errorRate.toFixed(1)}%`, this.thresholds.errorRate.warning, metrics.voice.errorRate); - } - else { - await this.resolveAlert('error_rate'); - } - } - /** - * Create performance alert - */ - async createAlert(type, severity, title, message, threshold, currentValue) { - // Check if alert already exists - const existingAlert = this.activeAlerts.get(type); - if (existingAlert && !existingAlert.resolved) { - // Update existing alert - existingAlert.currentValue = currentValue; - existingAlert.timestamp = new Date().toISOString(); - return; - } - const alert = { - alertId: `alert_${type}_${Date.now()}`, - type, - severity, - title, - message, - threshold, - currentValue, - timestamp: new Date().toISOString(), - resolved: false, - }; - this.activeAlerts.set(type, alert); - logger_1.logger.warn('Performance alert created', { - alertId: alert.alertId, - type: alert.type, - severity: alert.severity, - title: alert.title, - currentValue, - threshold, - }); - this.emit('performance_alert', alert); - } - /** - * Resolve performance alert - */ - async resolveAlert(type) { - const alert = this.activeAlerts.get(type); - if (alert && !alert.resolved) { - alert.resolved = true; - alert.resolvedAt = new Date().toISOString(); - logger_1.logger.info('Performance alert resolved', { - alertId: alert.alertId, - type: alert.type, - resolvedAt: alert.resolvedAt, - }); - this.emit('performance_alert_resolved', alert); - } - } - /** - * Perform comprehensive system health check - */ - async performHealthCheck() { - try { - const latestMetrics = this.metricsHistory[this.metricsHistory.length - 1]; - if (!latestMetrics) - return; - const health = { - status: 'healthy', - score: 100, - checks: { - cpu: this.evaluateMetric(latestMetrics.cpu.usage, this.thresholds.cpu), - memory: this.evaluateMetric(latestMetrics.memory.usage, this.thresholds.memory), - responseTime: this.evaluateMetric(latestMetrics.voice.averageProcessingTime, this.thresholds.responseTime), - errorRate: this.evaluateMetric(latestMetrics.voice.errorRate, this.thresholds.errorRate), - connectivity: { status: 'pass', value: latestMetrics.websocket.activeConnections }, - }, - lastChecked: new Date().toISOString(), - }; - // Calculate overall health score - let totalScore = 0; - let checkCount = 0; - for (const [key, check] of Object.entries(health.checks)) { - checkCount++; - if (check.status === 'pass') { - totalScore += 100; - } - else if (check.status === 'warn') { - totalScore += 70; - } - else { - totalScore += 30; - } - } - health.score = Math.round(totalScore / checkCount); - // Determine overall status - if (health.score >= 90) { - health.status = 'healthy'; - } - else if (health.score >= 70) { - health.status = 'degraded'; - } - else if (health.score >= 50) { - health.status = 'unhealthy'; - } - else { - health.status = 'critical'; - } - this.emit('health_check', health); - } - catch (error) { - logger_1.logger.error('Health check failed', { - error: error.message, - stack: error.stack, - }); - } - } - /** - * Evaluate individual metric against thresholds - */ - evaluateMetric(value, thresholds) { - if (value >= thresholds.critical) { - return { status: 'fail', value }; - } - else if (value >= thresholds.warning) { - return { status: 'warn', value }; - } - else { - return { status: 'pass', value }; - } - } - /** - * Get recent performance metrics - */ - getMetrics(limit) { - if (limit) { - return this.metricsHistory.slice(-limit); - } - return [...this.metricsHistory]; - } - /** - * Get active performance alerts - */ - getActiveAlerts() { - return Array.from(this.activeAlerts.values()).filter((alert) => !alert.resolved); - } - /** - * Get all alerts (including resolved) - */ - getAllAlerts(limit) { - const alerts = Array.from(this.activeAlerts.values()).sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); - return limit ? alerts.slice(0, limit) : alerts; - } - /** - * Get performance summary - */ - getPerformanceSummary() { - const currentMetrics = this.metricsHistory[this.metricsHistory.length - 1] || null; - const activeAlerts = this.getActiveAlerts().length; - // Calculate 24h averages - const last24h = this.metricsHistory.slice(-1440); // Last 24 hours - const averageResponseTime24h = last24h.length > 0 - ? last24h.reduce((sum, m) => sum + m.voice.averageProcessingTime, 0) / last24h.length - : 0; - const errorRate24h = last24h.length > 0 - ? last24h.reduce((sum, m) => sum + m.voice.errorRate, 0) / last24h.length - : 0; - return { - currentMetrics, - activeAlerts, - averageResponseTime24h: Math.round(averageResponseTime24h * 100) / 100, - errorRate24h: Math.round(errorRate24h * 100) / 100, - uptime: process_1.default.uptime(), - }; - } - /** - * Clean up old data and stop monitoring - */ - destroy() { - if (this.monitoringTimer) { - clearInterval(this.monitoringTimer); - } - if (this.healthCheckTimer) { - clearInterval(this.healthCheckTimer); - } - if (this.performanceObserver) { - this.performanceObserver.disconnect(); - } - this.removeAllListeners(); - logger_1.logger.info('Performance Monitor destroyed'); - } -} -exports.PerformanceMonitor = PerformanceMonitor; -//# sourceMappingURL=PerformanceMonitor.js.map \ No newline at end of file diff --git a/backend/dist/performance/monitoring/PerformanceMonitor.js.map b/backend/dist/performance/monitoring/PerformanceMonitor.js.map deleted file mode 100644 index 428224a..0000000 --- a/backend/dist/performance/monitoring/PerformanceMonitor.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"PerformanceMonitor.js","sourceRoot":"","sources":["../../../src/performance/monitoring/PerformanceMonitor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;;;;AAEH,mCAAsC;AACtC,2CAA8D;AAC9D,4CAAoB;AACpB,sDAA8B;AAC9B,+CAA4C;AAmF5C,MAAa,kBAAmB,SAAQ,qBAAY;IAC1C,cAAc,GAAyB,EAAE,CAAC;IAC1C,YAAY,GAAkC,IAAI,GAAG,EAAE,CAAC;IACxD,mBAAmB,CAAuB;IAC1C,eAAe,CAAkB;IACjC,gBAAgB,CAAkB;IACzB,cAAc,GAAG,IAAI,CAAC,CAAC,iCAAiC;IACxD,kBAAkB,GAAG,KAAK,CAAC,CAAC,WAAW;IACvC,mBAAmB,GAAG,KAAK,CAAC,CAAC,aAAa;IAE3D,4BAA4B;IACpB,mBAAmB,GAAa,EAAE,CAAC;IACnC,oBAAoB,GAAa,EAAE,CAAC;IACpC,qBAAqB,GAAa,EAAE,CAAC;IACrC,gBAAgB,GAA+C;QACrE,OAAO,EAAE,EAAE;QACX,WAAW,EAAE,CAAC;KACf,CAAC;IACM,cAAc,GAAG,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,CAAC;IAC7D,WAAW,GAAG,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;IAE1E,UAAU,GAA0B;QAC1C,GAAG,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QAClC,MAAM,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;QACrC,YAAY,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE;QAC/C,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;QACvC,SAAS,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE;KACzC,CAAC;IAEF,YAAY,gBAAiD;QAC3D,KAAK,EAAE,CAAC;QAER,IAAI,gBAAgB,EAAE,CAAC;YACrB,IAAI,CAAC,UAAU,GAAG,EAAE,GAAG,IAAI,CAAC,UAAU,EAAE,GAAG,gBAAgB,EAAE,CAAC;QAChE,CAAC;QAED,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAChC,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEzB,eAAM,CAAC,IAAI,CAAC,iCAAiC,EAAE;YAC7C,kBAAkB,EAAE,GAAG,IAAI,CAAC,kBAAkB,GAAG,IAAI,GAAG;YACxD,mBAAmB,EAAE,GAAG,IAAI,CAAC,mBAAmB,GAAG,IAAI,GAAG;YAC1D,UAAU,EAAE,IAAI,CAAC,UAAU;SAC5B,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACK,wBAAwB;QAC9B,IAAI,CAAC,mBAAmB,GAAG,IAAI,gCAAmB,CAAC,CAAC,IAAI,EAAE,EAAE;YAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,CAAC;YAElC,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;gBAC5B,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;oBAClC,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;gBACtD,CAAC;YACH,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,CAAC,CAAC;IAC5E,CAAC;IAED;;OAEG;IACK,eAAe;QACrB,IAAI,CAAC,eAAe,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;YAC5C,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAC9B,CAAC,EAAE,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAC9B,CAAC;IAED;;OAEG;IACK,iBAAiB;QACvB,IAAI,CAAC,gBAAgB,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;YAC7C,MAAM,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAClC,CAAC,EAAE,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAC/B,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc;QAC1B,MAAM,SAAS,GAAG,wBAAW,CAAC,GAAG,EAAE,CAAC;QAEpC,IAAI,CAAC;YACH,MAAM,OAAO,GAAuB;gBAClC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACnC,GAAG,EAAE,MAAM,IAAI,CAAC,aAAa,EAAE;gBAC/B,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE;gBAC/B,OAAO,EAAE,IAAI,CAAC,iBAAiB,EAAE;gBACjC,KAAK,EAAE,IAAI,CAAC,sBAAsB,EAAE;gBACpC,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE;gBACnC,KAAK,EAAE,IAAI,CAAC,eAAe,EAAE;gBAC7B,SAAS,EAAE,IAAI,CAAC,mBAAmB,EAAE;aACtC,CAAC;YAEF,iBAAiB;YACjB,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAElC,4BAA4B;YAC5B,IAAI,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,IAAI,CAAC,cAAc,EAAE,CAAC;gBACrD,IAAI,CAAC,cAAc,CAAC,KAAK,EAAE,CAAC;YAC9B,CAAC;YAED,+BAA+B;YAC/B,MAAM,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAEnC,MAAM,cAAc,GAAG,wBAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAErD,eAAM,CAAC,KAAK,CAAC,+BAA+B,EAAE;gBAC5C,cAAc,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;gBAChD,QAAQ,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;gBAC5C,WAAW,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG;gBAClD,mBAAmB,EAAE,OAAO,CAAC,KAAK,CAAC,iBAAiB;aACrD,CAAC,CAAC;YAEH,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,OAAO,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,eAAM,CAAC,KAAK,CAAC,sCAAsC,EAAE;gBACnD,KAAK,EAAE,KAAK,CAAC,OAAO;gBACpB,KAAK,EAAE,KAAK,CAAC,KAAK;aACnB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,aAAa;QACzB,MAAM,IAAI,GAAG,YAAE,CAAC,IAAI,EAAE,CAAC;QACvB,MAAM,WAAW,GAAG,YAAE,CAAC,OAAO,EAAE,CAAC;QAEjC,mCAAmC;QACnC,IAAI,SAAS,GAAG,CAAC,CAAC;QAClB,IAAI,SAAS,GAAG,CAAC,CAAC;QAElB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,KAAK,MAAM,IAAI,IAAI,GAAG,CAAC,KAAK,EAAE,CAAC;gBAC7B,SAAS,IAAI,GAAG,CAAC,KAAK,CAAC,IAA8B,CAAC,CAAC;YACzD,CAAC;YACD,SAAS,IAAI,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC;QAC9B,CAAC;QAED,MAAM,KAAK,GAAG,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,SAAS,GAAG,SAAS,CAAC,GAAG,GAAG,CAAC,CAAC;QAE9D,OAAO;YACL,KAAK;YACL,WAAW;YACX,KAAK,EAAE,IAAI,CAAC,MAAM;SACnB,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,gBAAgB;QACtB,MAAM,WAAW,GAAG,YAAE,CAAC,QAAQ,EAAE,CAAC;QAClC,MAAM,UAAU,GAAG,YAAE,CAAC,OAAO,EAAE,CAAC;QAChC,MAAM,UAAU,GAAG,WAAW,GAAG,UAAU,CAAC;QAC5C,MAAM,WAAW,GAAG,CAAC,UAAU,GAAG,WAAW,CAAC,GAAG,GAAG,CAAC;QAErD,MAAM,SAAS,GAAG,iBAAO,CAAC,WAAW,EAAE,CAAC;QAExC,OAAO;YACL,IAAI,EAAE,UAAU;YAChB,KAAK,EAAE,WAAW;YAClB,KAAK,EAAE,WAAW;YAClB,QAAQ,EAAE,SAAS,CAAC,QAAQ;YAC5B,SAAS,EAAE,SAAS,CAAC,SAAS;YAC9B,QAAQ,EAAE,SAAS,CAAC,QAAQ;SAC7B,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,iBAAiB;QACvB,OAAO;YACL,OAAO,EAAE,IAAI,CAAC,cAAc,CAAC,OAAO;YACpC,QAAQ,EAAE,IAAI,CAAC,cAAc,CAAC,QAAQ;YACtC,iBAAiB,EAAE,IAAI,CAAC,cAAc,CAAC,WAAW;SACnD,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,sBAAsB;QAC5B,IAAI,IAAI,CAAC,mBAAmB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1C,OAAO;gBACL,qBAAqB,EAAE,CAAC;gBACxB,iBAAiB,EAAE,CAAC;gBACpB,iBAAiB,EAAE,CAAC;gBACpB,SAAS,EAAE,CAAC;gBACZ,WAAW,EAAE,GAAG;aACjB,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,mBAAmB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACnE,MAAM,OAAO,GACX,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC;YAC7D,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC;QAClC,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAE1D,MAAM,iBAAiB,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,+BAA+B;QAC1F,MAAM,SAAS,GACb,IAAI,CAAC,WAAW,CAAC,KAAK,GAAG,CAAC;YACxB,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,GAAG,GAAG;YAClE,CAAC,CAAC,CAAC,CAAC;QACR,MAAM,WAAW,GAAG,GAAG,GAAG,SAAS,CAAC;QAEpC,OAAO;YACL,qBAAqB,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,GAAG;YACtD,iBAAiB,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG;YAC9C,iBAAiB;YACjB,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,GAAG,CAAC,GAAG,GAAG;YAC5C,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,GAAG,CAAC,GAAG,GAAG;SACjD,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,kBAAkB;QACxB,MAAM,gBAAgB,GACpB,IAAI,CAAC,oBAAoB,CAAC,MAAM,GAAG,CAAC;YAClC,CAAC,CAAC,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC;gBAC9D,IAAI,CAAC,oBAAoB,CAAC,MAAM;YAClC,CAAC,CAAC,CAAC,CAAC;QAER,MAAM,WAAW,GAAG,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,MAAM,CAAC;QAEnF,OAAO;YACL,iBAAiB,EAAE,CAAC,EAAE,sCAAsC;YAC5D,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,gBAAgB,GAAG,GAAG,CAAC,GAAG,GAAG;YAC1D,WAAW;YACX,mBAAmB,EAAE,CAAC,EAAE,sCAAsC;SAC/D,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,eAAe;QACrB,MAAM,mBAAmB,GACvB,IAAI,CAAC,qBAAqB,CAAC,MAAM,GAAG,CAAC;YACnC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC;gBAC/D,IAAI,CAAC,qBAAqB,CAAC,MAAM;YACnC,CAAC,CAAC,CAAC,CAAC;QAER,OAAO;YACL,OAAO,EAAE,EAAE,EAAE,sCAAsC;YACnD,mBAAmB,EAAE,IAAI,CAAC,KAAK,CAAC,mBAAmB,GAAG,GAAG,CAAC,GAAG,GAAG;YAChE,WAAW,EAAE,CAAC,EAAE,sCAAsC;YACtD,mBAAmB,EAAE,IAAI,CAAC,qBAAqB,CAAC,MAAM;SACvD,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,mBAAmB;QACzB,MAAM,cAAc,GAClB,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;YACtC,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC;gBAClE,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAM;YACtC,CAAC,CAAC,CAAC,CAAC;QAER,OAAO;YACL,iBAAiB,EAAE,IAAI,CAAC,gBAAgB,CAAC,WAAW;YACpD,iBAAiB,EAAE,CAAC,EAAE,0CAA0C;YAChE,cAAc,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,GAAG,CAAC,GAAG,GAAG;YACtD,iBAAiB,EAAE,CAAC,EAAE,gDAAgD;SACvE,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,kBAAkB,CAAC,IAAY,EAAE,KAAa;QAC5C,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,0BAA0B;gBAC7B,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACrC,IAAI,IAAI,CAAC,mBAAmB,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;oBAC3C,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,CAAC;gBACnC,CAAC;gBACD,MAAM;YAER,KAAK,gBAAgB;gBACnB,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACtC,IAAI,IAAI,CAAC,oBAAoB,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;oBAC5C,IAAI,CAAC,oBAAoB,CAAC,KAAK,EAAE,CAAC;gBACpC,CAAC;gBACD,MAAM;YAER,KAAK,iBAAiB;gBACpB,IAAI,CAAC,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACvC,IAAI,IAAI,CAAC,qBAAqB,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;oBAC7C,IAAI,CAAC,qBAAqB,CAAC,KAAK,EAAE,CAAC;gBACrC,CAAC;gBACD,MAAM;YAER,KAAK,mBAAmB;gBACtB,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC1C,IAAI,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;oBAChD,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;gBACxC,CAAC;gBACD,MAAM;QACV,CAAC;QAED,eAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE;YAC1C,IAAI;YACJ,KAAK,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;SAC/B,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,IAAkD,EAAE,OAAa;QAC3E,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;QAE3D,eAAM,CAAC,KAAK,CAAC,2CAA2C,EAAE;YACxD,IAAI;YACJ,WAAW,EAAE,IAAI,CAAC,WAAW,CAAC,KAAK;YACnC,UAAU,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;SACnC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,qBAAqB,CAAC,OAAe,EAAE,QAAgB,EAAE,mBAA2B,CAAC;QACnF,IAAI,CAAC,cAAc,CAAC,OAAO,IAAI,OAAO,CAAC;QACvC,IAAI,CAAC,cAAc,CAAC,QAAQ,IAAI,QAAQ,CAAC;QACzC,IAAI,CAAC,cAAc,CAAC,WAAW,IAAI,gBAAgB,CAAC;IACtD,CAAC;IAED;;OAEG;IACH,0BAA0B,CAAC,KAAa;QACtC,IAAI,CAAC,gBAAgB,CAAC,WAAW,GAAG,KAAK,CAAC;IAC5C,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,cAAc,CAAC,OAA2B;QACtD,kBAAkB;QAClB,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;YACrD,MAAM,IAAI,CAAC,WAAW,CACpB,KAAK,EACL,UAAU,EACV,oBAAoB,EACpB,mBAAmB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAClD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,QAAQ,EAC5B,OAAO,CAAC,GAAG,CAAC,KAAK,CAClB,CAAC;QACJ,CAAC;aAAM,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YAC3D,MAAM,IAAI,CAAC,WAAW,CACpB,KAAK,EACL,QAAQ,EACR,gBAAgB,EAChB,mBAAmB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAClD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,EAC3B,OAAO,CAAC,GAAG,CAAC,KAAK,CAClB,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;QACjC,CAAC;QAED,qBAAqB;QACrB,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;YAC3D,MAAM,IAAI,CAAC,WAAW,CACpB,QAAQ,EACR,UAAU,EACV,uBAAuB,EACvB,sBAAsB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EACxD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,QAAQ,EAC/B,OAAO,CAAC,MAAM,CAAC,KAAK,CACrB,CAAC;QACJ,CAAC;aAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACjE,MAAM,IAAI,CAAC,WAAW,CACpB,QAAQ,EACR,QAAQ,EACR,mBAAmB,EACnB,sBAAsB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EACxD,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,EAC9B,OAAO,CAAC,MAAM,CAAC,KAAK,CACrB,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QACpC,CAAC;QAED,sBAAsB;QACtB,IAAI,OAAO,CAAC,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,QAAQ,EAAE,CAAC;YAChF,MAAM,IAAI,CAAC,WAAW,CACpB,eAAe,EACf,UAAU,EACV,wBAAwB,EACxB,oCAAoC,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EACtF,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,QAAQ,EACrC,OAAO,CAAC,KAAK,CAAC,qBAAqB,CACpC,CAAC;QACJ,CAAC;aAAM,IAAI,OAAO,CAAC,KAAK,CAAC,qBAAqB,GAAG,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,OAAO,EAAE,CAAC;YACtF,MAAM,IAAI,CAAC,WAAW,CACpB,eAAe,EACf,QAAQ,EACR,oBAAoB,EACpB,oCAAoC,OAAO,CAAC,KAAK,CAAC,qBAAqB,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,EACtF,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,OAAO,EACpC,OAAO,CAAC,KAAK,CAAC,qBAAqB,CACpC,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;QAC3C,CAAC;QAED,mBAAmB;QACnB,IAAI,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;YACjE,MAAM,IAAI,CAAC,WAAW,CACpB,YAAY,EACZ,UAAU,EACV,qBAAqB,EACrB,+BAA+B,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EACpE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,QAAQ,EAClC,OAAO,CAAC,KAAK,CAAC,SAAS,CACxB,CAAC;QACJ,CAAC;aAAM,IAAI,OAAO,CAAC,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,EAAE,CAAC;YACvE,MAAM,IAAI,CAAC,WAAW,CACpB,YAAY,EACZ,QAAQ,EACR,iBAAiB,EACjB,+BAA+B,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EACpE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,OAAO,EACjC,OAAO,CAAC,KAAK,CAAC,SAAS,CACxB,CAAC;QACJ,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,WAAW,CACvB,IAA8B,EAC9B,QAAsC,EACtC,KAAa,EACb,OAAe,EACf,SAAiB,EACjB,YAAoB;QAEpB,gCAAgC;QAChC,MAAM,aAAa,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClD,IAAI,aAAa,IAAI,CAAC,aAAa,CAAC,QAAQ,EAAE,CAAC;YAC7C,wBAAwB;YACxB,aAAa,CAAC,YAAY,GAAG,YAAY,CAAC;YAC1C,aAAa,CAAC,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YACnD,OAAO;QACT,CAAC;QAED,MAAM,KAAK,GAAqB;YAC9B,OAAO,EAAE,SAAS,IAAI,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE;YACtC,IAAI;YACJ,QAAQ;YACR,KAAK;YACL,OAAO;YACP,SAAS;YACT,YAAY;YACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ,EAAE,KAAK;SAChB,CAAC;QAEF,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QAEnC,eAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE;YACvC,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,YAAY;YACZ,SAAS;SACV,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,KAAK,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,YAAY,CAAC,IAA8B;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YAC7B,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;YACtB,KAAK,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;YAE5C,eAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE;gBACxC,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,UAAU,EAAE,KAAK,CAAC,UAAU;aAC7B,CAAC,CAAC;YAEH,IAAI,CAAC,IAAI,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,kBAAkB;QAC9B,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAC1E,IAAI,CAAC,aAAa;gBAAE,OAAO;YAE3B,MAAM,MAAM,GAAiB;gBAC3B,MAAM,EAAE,SAAS;gBACjB,KAAK,EAAE,GAAG;gBACV,MAAM,EAAE;oBACN,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;oBACtE,MAAM,EAAE,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,MAAM,CAAC,KAAK,EAAE,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;oBAC/E,YAAY,EAAE,IAAI,CAAC,cAAc,CAC/B,aAAa,CAAC,KAAK,CAAC,qBAAqB,EACzC,IAAI,CAAC,UAAU,CAAC,YAAY,CAC7B;oBACD,SAAS,EAAE,IAAI,CAAC,cAAc,CAAC,aAAa,CAAC,KAAK,CAAC,SAAS,EAAE,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;oBACxF,YAAY,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,CAAC,SAAS,CAAC,iBAAiB,EAAE;iBACnF;gBACD,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACtC,CAAC;YAEF,iCAAiC;YACjC,IAAI,UAAU,GAAG,CAAC,CAAC;YACnB,IAAI,UAAU,GAAG,CAAC,CAAC;YAEnB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC;gBACzD,UAAU,EAAE,CAAC;gBACb,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;oBAC5B,UAAU,IAAI,GAAG,CAAC;gBACpB,CAAC;qBAAM,IAAI,KAAK,CAAC,MAAM,KAAK,MAAM,EAAE,CAAC;oBACnC,UAAU,IAAI,EAAE,CAAC;gBACnB,CAAC;qBAAM,CAAC;oBACN,UAAU,IAAI,EAAE,CAAC;gBACnB,CAAC;YACH,CAAC;YAED,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,UAAU,GAAG,UAAU,CAAC,CAAC;YAEnD,2BAA2B;YAC3B,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;gBACvB,MAAM,CAAC,MAAM,GAAG,SAAS,CAAC;YAC5B,CAAC;iBAAM,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;gBAC9B,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC;YAC7B,CAAC;iBAAM,IAAI,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC;gBAC9B,MAAM,CAAC,MAAM,GAAG,WAAW,CAAC;YAC9B,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,MAAM,GAAG,UAAU,CAAC;YAC7B,CAAC;YAED,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,CAAC;QACpC,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,eAAM,CAAC,KAAK,CAAC,qBAAqB,EAAE;gBAClC,KAAK,EAAE,KAAK,CAAC,OAAO;gBACpB,KAAK,EAAE,KAAK,CAAC,KAAK;aACnB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED;;OAEG;IACK,cAAc,CACpB,KAAa,EACb,UAAiD;QAKjD,IAAI,KAAK,IAAI,UAAU,CAAC,QAAQ,EAAE,CAAC;YACjC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACnC,CAAC;aAAM,IAAI,KAAK,IAAI,UAAU,CAAC,OAAO,EAAE,CAAC;YACvC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACnC,CAAC;aAAM,CAAC;YACN,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACnC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,UAAU,CAAC,KAAc;QACvB,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;QAC3C,CAAC;QACD,OAAO,CAAC,GAAG,IAAI,CAAC,cAAc,CAAC,CAAC;IAClC,CAAC;IAED;;OAEG;IACH,eAAe;QACb,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;IACnF,CAAC;IAED;;OAEG;IACH,YAAY,CAAC,KAAc;QACzB,MAAM,MAAM,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CACxD,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAC5E,CAAC;QAEF,OAAO,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACjD,CAAC;IAED;;OAEG;IACH,qBAAqB;QAOnB,MAAM,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC;QACnF,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC,MAAM,CAAC;QAEnD,yBAAyB;QACzB,MAAM,OAAO,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB;QAClE,MAAM,sBAAsB,GAC1B,OAAO,CAAC,MAAM,GAAG,CAAC;YAChB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM;YACrF,CAAC,CAAC,CAAC,CAAC;QAER,MAAM,YAAY,GAChB,OAAO,CAAC,MAAM,GAAG,CAAC;YAChB,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,MAAM;YACzE,CAAC,CAAC,CAAC,CAAC;QAER,OAAO;YACL,cAAc;YACd,YAAY;YACZ,sBAAsB,EAAE,IAAI,CAAC,KAAK,CAAC,sBAAsB,GAAG,GAAG,CAAC,GAAG,GAAG;YACtE,YAAY,EAAE,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,GAAG,CAAC,GAAG,GAAG;YAClD,MAAM,EAAE,iBAAO,CAAC,MAAM,EAAE;SACzB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,OAAO;QACL,IAAI,IAAI,CAAC,eAAe,EAAE,CAAC;YACzB,aAAa,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;QACtC,CAAC;QAED,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;YAC1B,aAAa,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACvC,CAAC;QAED,IAAI,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC7B,IAAI,CAAC,mBAAmB,CAAC,UAAU,EAAE,CAAC;QACxC,CAAC;QAED,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAE1B,eAAM,CAAC,IAAI,CAAC,+BAA+B,CAAC,CAAC;IAC/C,CAAC;CACF;AAlqBD,gDAkqBC"} \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.d.ts b/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.d.ts deleted file mode 100644 index 2c7ab90..0000000 --- a/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.d.ts +++ /dev/null @@ -1,123 +0,0 @@ -/** - * Entity Update Broadcaster - Phase 2 Real-Time Entity Broadcasting - * Broadcasts real-time updates for entities affected by voice commands - * - * Success Criteria: - * - Real-time entity change notifications - * - Targeted broadcasting to affected users - * - Entity change aggregation and batching - * - Update conflict resolution - */ -import { EventEmitter } from 'events'; -import { ISocketManager } from '../../websocket/types'; -export interface EntityUpdate { - updateId: string; - entityType: 'channel' | 'task' | 'user' | 'project' | 'file'; - entityId: string; - organizationId: string; - changeType: 'created' | 'updated' | 'deleted' | 'status_changed' | 'shared'; - changes: Record; - previousValues?: Record | undefined; - updatedBy: string; - timestamp: string; - affectedUsers: string[]; - contextData?: Record | undefined; -} -export interface EntityUpdateBatch { - batchId: string; - organizationId: string; - updates: EntityUpdate[]; - totalUpdates: number; - batchedAt: string; - targetUsers: string[]; -} -export interface BroadcastResult { - updateId: string; - targetUsers: number; - successfulBroadcasts: number; - failedBroadcasts: number; - processingTime: number; -} -export declare class EntityUpdateBroadcaster extends EventEmitter { - private socketManager; - private pendingUpdates; - private updateBatches; - private broadcastMetrics; - private readonly batchInterval; - private readonly maxBatchSize; - private batchTimer?; - constructor(socketManager: ISocketManager); - /** - * Queue entity update for broadcasting - */ - queueEntityUpdate(entityType: EntityUpdate['entityType'], entityId: string, organizationId: string, changeType: EntityUpdate['changeType'], changes: Record, updatedBy: string, affectedUsers: string[], previousValues?: Record, contextData?: Record): Promise; - /** - * Broadcast immediate entity update (bypasses batching) - */ - broadcastImmediateUpdate(entityType: EntityUpdate['entityType'], entityId: string, organizationId: string, changeType: EntityUpdate['changeType'], changes: Record, updatedBy: string, affectedUsers: string[], previousValues?: Record, contextData?: Record): Promise; - /** - * Broadcast file-specific updates with download URLs - */ - broadcastFileUpdate(fileId: string, organizationId: string, changeType: 'uploaded' | 'shared' | 'deleted' | 'download_ready', fileData: { - fileName: string; - fileSize: number; - contentType: string; - downloadUrl?: string; - sharedWith?: string[]; - linkedEntities?: Array<{ - type: string; - id: string; - }>; - }, updatedBy: string, affectedUsers: string[]): Promise; - /** - * Start batch processing timer - */ - private startBatchProcessing; - /** - * Process all pending batches - */ - private processAllBatches; - /** - * Process updates batch for an organization - */ - private processBatch; - /** - * Broadcast batch to target users - */ - private broadcastBatch; - /** - * Get update batch by ID - */ - getBatch(batchId: string): EntityUpdateBatch | null; - /** - * Get pending updates for organization - */ - getPendingUpdates(organizationId: string): EntityUpdate[]; - /** - * Get recent batches for organization - */ - getRecentBatches(organizationId: string, limit?: number): EntityUpdateBatch[]; - /** - * Record broadcast performance metrics - */ - private recordBroadcastMetrics; - /** - * Get performance statistics - */ - getPerformanceStats(): { - averageBroadcastTime: number; - p95BroadcastTime: number; - totalBroadcasts: number; - pendingUpdates: number; - recentBatches: number; - }; - /** - * Cleanup old batches and metrics - */ - cleanup(): void; - /** - * Stop batch processing and cleanup - */ - destroy(): void; -} -//# sourceMappingURL=EntityUpdateBroadcaster.d.ts.map \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.d.ts.map b/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.d.ts.map deleted file mode 100644 index 8893761..0000000 --- a/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EntityUpdateBroadcaster.d.ts","sourceRoot":"","sources":["../../../src/realtime/broadcasting/EntityUpdateBroadcaster.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.js b/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.js deleted file mode 100644 index 129b9e9..0000000 --- a/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.js +++ /dev/null @@ -1,295 +0,0 @@ -"use strict"; -/** - * Entity Update Broadcaster - Phase 2 Real-Time Entity Broadcasting - * Broadcasts real-time updates for entities affected by voice commands - * - * Success Criteria: - * - Real-time entity change notifications - * - Targeted broadcasting to affected users - * - Entity change aggregation and batching - * - Update conflict resolution - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EntityUpdateBroadcaster = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -class EntityUpdateBroadcaster extends events_1.EventEmitter { - socketManager; - pendingUpdates = new Map(); - updateBatches = new Map(); - broadcastMetrics = []; - batchInterval = 100; // 100ms batching window - maxBatchSize = 50; - batchTimer; - constructor(socketManager) { - super(); - this.socketManager = socketManager; - // Start batch processing - this.startBatchProcessing(); - logger_1.logger.info('Entity Update Broadcaster initialized', { - batchInterval: `${this.batchInterval}ms`, - maxBatchSize: this.maxBatchSize - }); - } - /** - * Queue entity update for broadcasting - */ - async queueEntityUpdate(entityType, entityId, organizationId, changeType, changes, updatedBy, affectedUsers, previousValues, contextData) { - const updateId = `update_${entityType}_${entityId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - const entityUpdate = { - updateId, - entityType, - entityId, - organizationId, - changeType, - changes, - previousValues, - updatedBy, - timestamp: new Date().toISOString(), - affectedUsers, - contextData - }; - // Group by organization for batching - const orgUpdates = this.pendingUpdates.get(organizationId) || []; - orgUpdates.push(entityUpdate); - this.pendingUpdates.set(organizationId, orgUpdates); - logger_1.logger.debug('Entity update queued', { - updateId, - entityType, - entityId, - changeType, - affectedUsers: affectedUsers.length, - organizationId - }); - // Trigger immediate batch processing if batch is full - if (orgUpdates.length >= this.maxBatchSize) { - await this.processBatch(organizationId); - } - return updateId; - } - /** - * Broadcast immediate entity update (bypasses batching) - */ - async broadcastImmediateUpdate(entityType, entityId, organizationId, changeType, changes, updatedBy, affectedUsers, previousValues, contextData) { - const startTime = perf_hooks_1.performance.now(); - const updateId = await this.queueEntityUpdate(entityType, entityId, organizationId, changeType, changes, updatedBy, affectedUsers, previousValues, contextData); - // Process batch immediately - await this.processBatch(organizationId); - const processingTime = perf_hooks_1.performance.now() - startTime; - return { - updateId, - targetUsers: affectedUsers.length, - successfulBroadcasts: affectedUsers.filter(userId => this.socketManager.isUserConnected(userId)).length, - failedBroadcasts: affectedUsers.filter(userId => !this.socketManager.isUserConnected(userId)).length, - processingTime - }; - } - /** - * Broadcast file-specific updates with download URLs - */ - async broadcastFileUpdate(fileId, organizationId, changeType, fileData, updatedBy, affectedUsers) { - const contextData = { - fileName: fileData.fileName, - fileSize: fileData.fileSize, - contentType: fileData.contentType, - downloadUrl: fileData.downloadUrl, - linkedEntities: fileData.linkedEntities || [] - }; - // Add shared users to affected users if it's a sharing update - const allAffectedUsers = changeType === 'shared' && fileData.sharedWith - ? [...new Set([...affectedUsers, ...fileData.sharedWith])] - : affectedUsers; - return await this.broadcastImmediateUpdate('file', fileId, organizationId, changeType, { - fileName: fileData.fileName, - fileSize: fileData.fileSize, - contentType: fileData.contentType, - downloadUrl: fileData.downloadUrl, - sharedWith: fileData.sharedWith - }, updatedBy, allAffectedUsers, undefined, contextData); - } - /** - * Start batch processing timer - */ - startBatchProcessing() { - this.batchTimer = setInterval(async () => { - await this.processAllBatches(); - }, this.batchInterval); - } - /** - * Process all pending batches - */ - async processAllBatches() { - const organizationIds = Array.from(this.pendingUpdates.keys()); - for (const organizationId of organizationIds) { - await this.processBatch(organizationId); - } - } - /** - * Process updates batch for an organization - */ - async processBatch(organizationId) { - const updates = this.pendingUpdates.get(organizationId); - if (!updates || updates.length === 0) { - return; - } - const startTime = perf_hooks_1.performance.now(); - // Create batch - const batchId = `batch_${organizationId}_${Date.now()}`; - const targetUsers = [...new Set(updates.flatMap(update => update.affectedUsers))]; - const batch = { - batchId, - organizationId, - updates, - totalUpdates: updates.length, - batchedAt: new Date().toISOString(), - targetUsers - }; - this.updateBatches.set(batchId, batch); - // Clear pending updates for this organization - this.pendingUpdates.delete(organizationId); - try { - // Broadcast batch to all target users - await this.broadcastBatch(batch); - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordBroadcastMetrics(processingTime); - logger_1.logger.debug('Entity update batch processed', { - batchId, - organizationId, - totalUpdates: updates.length, - targetUsers: targetUsers.length, - processingTime: `${processingTime.toFixed(2)}ms` - }); - this.emit('batch_processed', { - batchId, - organizationId, - totalUpdates: updates.length, - targetUsers: targetUsers.length, - processingTime - }); - } - catch (error) { - logger_1.logger.error('Failed to process entity update batch', { - batchId, - organizationId, - error: error.message - }); - this.emit('batch_error', { - batchId, - organizationId, - error: error.message - }); - } - } - /** - * Broadcast batch to target users - */ - async broadcastBatch(batch) { - const broadcastPromises = batch.targetUsers.map(async (userId) => { - if (this.socketManager.isUserConnected(userId)) { - try { - await this.socketManager.emitToUser(userId, 'entity_updates', { - batchId: batch.batchId, - organizationId: batch.organizationId, - updates: batch.updates.filter(update => update.affectedUsers.includes(userId)), - totalUpdates: batch.totalUpdates, - batchedAt: batch.batchedAt - }); - } - catch (error) { - logger_1.logger.warn('Failed to broadcast to user', { - userId, - batchId: batch.batchId, - error: error.message - }); - } - } - }); - await Promise.allSettled(broadcastPromises); - } - /** - * Get update batch by ID - */ - getBatch(batchId) { - return this.updateBatches.get(batchId) || null; - } - /** - * Get pending updates for organization - */ - getPendingUpdates(organizationId) { - return this.pendingUpdates.get(organizationId) || []; - } - /** - * Get recent batches for organization - */ - getRecentBatches(organizationId, limit = 10) { - return Array.from(this.updateBatches.values()) - .filter(batch => batch.organizationId === organizationId) - .sort((a, b) => new Date(b.batchedAt).getTime() - new Date(a.batchedAt).getTime()) - .slice(0, limit); - } - /** - * Record broadcast performance metrics - */ - recordBroadcastMetrics(time) { - this.broadcastMetrics.push(time); - // Keep only last 1000 measurements - if (this.broadcastMetrics.length > 1000) { - this.broadcastMetrics.shift(); - } - } - /** - * Get performance statistics - */ - getPerformanceStats() { - if (this.broadcastMetrics.length === 0) { - return { - averageBroadcastTime: 0, - p95BroadcastTime: 0, - totalBroadcasts: 0, - pendingUpdates: Array.from(this.pendingUpdates.values()).reduce((sum, updates) => sum + updates.length, 0), - recentBatches: this.updateBatches.size - }; - } - const sorted = [...this.broadcastMetrics].sort((a, b) => a - b); - const average = this.broadcastMetrics.reduce((sum, time) => sum + time, 0) / this.broadcastMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - return { - averageBroadcastTime: Math.round(average * 100) / 100, - p95BroadcastTime: Math.round(p95 * 100) / 100, - totalBroadcasts: this.broadcastMetrics.length, - pendingUpdates: Array.from(this.pendingUpdates.values()).reduce((sum, updates) => sum + updates.length, 0), - recentBatches: this.updateBatches.size - }; - } - /** - * Cleanup old batches and metrics - */ - cleanup() { - const cutoffTime = Date.now() - (3600000 * 2); // 2 hours ago - let cleanedCount = 0; - for (const [batchId, batch] of this.updateBatches.entries()) { - const batchTime = new Date(batch.batchedAt).getTime(); - if (batchTime < cutoffTime) { - this.updateBatches.delete(batchId); - cleanedCount++; - } - } - if (cleanedCount > 0) { - logger_1.logger.debug('Cleaned up old entity update batches', { cleanedCount }); - } - } - /** - * Stop batch processing and cleanup - */ - destroy() { - if (this.batchTimer) { - clearInterval(this.batchTimer); - } - this.cleanup(); - this.removeAllListeners(); - logger_1.logger.info('Entity Update Broadcaster destroyed'); - } -} -exports.EntityUpdateBroadcaster = EntityUpdateBroadcaster; -//# sourceMappingURL=EntityUpdateBroadcaster.js.map \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.js.map b/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.js.map deleted file mode 100644 index 7dddf62..0000000 --- a/backend/dist/realtime/broadcasting/EntityUpdateBroadcaster.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EntityUpdateBroadcaster.js","sourceRoot":"","sources":["../../../src/realtime/broadcasting/EntityUpdateBroadcaster.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/ExecutionEventManager.d.ts b/backend/dist/realtime/broadcasting/ExecutionEventManager.d.ts deleted file mode 100644 index 75965fe..0000000 --- a/backend/dist/realtime/broadcasting/ExecutionEventManager.d.ts +++ /dev/null @@ -1,148 +0,0 @@ -/** - * Execution Event Manager - Phase 2 Real-Time Broadcasting - * Manages real-time broadcasting of command execution events - * - * Success Criteria: - * - Real-time execution broadcasting <100ms - * - Targeted broadcasting to affected users only - * - Event deduplication for multiple subscriptions - * - Offline user message queuing - */ -import { EventEmitter } from 'events'; -import { ISocketManager } from '../../websocket/types'; -export interface ExecutionEvent { - id: string; - type: ExecutionEventType; - commandId: string; - userId: string; - organizationId: string; - data: any; - timestamp: string; - priority: 'low' | 'medium' | 'high' | 'critical'; - targetUsers: string[]; - targetChannels: string[]; - affectedEntities: AffectedEntity[]; -} -export declare enum ExecutionEventType { - COMMAND_START = "command_start", - STEP_EXECUTION = "step_execution", - COMMAND_COMPLETE = "command_complete", - COMMAND_FAILED = "command_failed", - ENTITY_CREATED = "entity_created", - ENTITY_UPDATED = "entity_updated", - USER_ASSIGNED = "user_assigned", - FILE_UPLOADED = "file_uploaded", - NOTIFICATION_SENT = "notification_sent" -} -export interface AffectedEntity { - type: 'user' | 'channel' | 'task' | 'project' | 'file'; - id: string; - name: string; - action: 'created' | 'updated' | 'deleted' | 'assigned' | 'shared'; -} -export interface BroadcastResult { - eventId: string; - targetCount: number; - deliveredCount: number; - queuedCount: number; - failedCount: number; - averageLatency: number; - processingTime: number; -} -export interface OfflineMessage { - userId: string; - event: ExecutionEvent; - queuedAt: string; - priority: number; -} -export declare class ExecutionEventManager extends EventEmitter { - private socketManager; - private offlineMessageQueue; - private eventDeduplication; - private broadcastMetrics; - private readonly maxQueueSize; - private readonly deduplicationTTL; - constructor(socketManager: ISocketManager); - /** - * Broadcast command execution start event - * Target: <100ms broadcasting latency - */ - broadcastCommandStart(commandId: string, userId: string, organizationId: string, affectedUsers: string[], commandData: any): Promise; - /** - * Broadcast step execution progress - */ - broadcastStepExecution(commandId: string, stepId: string, userId: string, organizationId: string, stepData: any, affectedUsers: string[], affectedChannels?: string[]): Promise; - /** - * Broadcast command completion - */ - broadcastCommandComplete(commandId: string, userId: string, organizationId: string, result: any, affectedUsers: string[], affectedChannels?: string[]): Promise; - /** - * Broadcast command error/failure - */ - broadcastCommandError(commandId: string, userId: string, organizationId: string, error: string, affectedUsers: string[], affectedChannels?: string[]): Promise; - /** - * Broadcast entity creation (task, channel, etc.) - */ - broadcastEntityCreated(entityType: string, entityId: string, entityName: string, createdBy: string, organizationId: string, affectedUsers: string[], affectedChannels?: string[]): Promise; - /** - * Broadcast user assignment notifications - */ - broadcastUserAssigned(assignmentType: string, entityId: string, entityName: string, assignedUsers: string[], assignedBy: string, organizationId: string): Promise; - /** - * Core event broadcasting method - * Target: <100ms average latency - */ - private broadcastEvent; - /** - * Handle user connection - deliver queued messages - */ - private handleUserConnected; - /** - * Handle user disconnection - */ - private handleUserDisconnected; - /** - * Resolve all target users including channel members - */ - private resolveAllTargetUsers; - /** - * Get channel members (simplified implementation) - */ - private getChannelMembers; - /** - * Check for duplicate events - */ - private isDuplicateEvent; - /** - * Add event to deduplication cache - */ - private addToDeduplication; - /** - * Queue message for offline user - */ - private queueOfflineMessage; - /** - * Cleanup deduplication cache - */ - private cleanupDeduplication; - /** - * Cleanup old offline messages - */ - private cleanupOfflineQueue; - /** - * Record broadcast performance metrics - */ - private recordBroadcastMetrics; - /** - * Get performance statistics - */ - getPerformanceStats(): { - averageBroadcastTime: number; - p95BroadcastTime: number; - p99BroadcastTime: number; - totalBroadcasts: number; - offlineQueueSize: number; - deduplicationCacheSize: number; - }; -} -//# sourceMappingURL=ExecutionEventManager.d.ts.map \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/ExecutionEventManager.d.ts.map b/backend/dist/realtime/broadcasting/ExecutionEventManager.d.ts.map deleted file mode 100644 index 0f79ed1..0000000 --- a/backend/dist/realtime/broadcasting/ExecutionEventManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ExecutionEventManager.d.ts","sourceRoot":"","sources":["../../../src/realtime/broadcasting/ExecutionEventManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/ExecutionEventManager.js b/backend/dist/realtime/broadcasting/ExecutionEventManager.js deleted file mode 100644 index 98df20a..0000000 --- a/backend/dist/realtime/broadcasting/ExecutionEventManager.js +++ /dev/null @@ -1,527 +0,0 @@ -"use strict"; -/** - * Execution Event Manager - Phase 2 Real-Time Broadcasting - * Manages real-time broadcasting of command execution events - * - * Success Criteria: - * - Real-time execution broadcasting <100ms - * - Targeted broadcasting to affected users only - * - Event deduplication for multiple subscriptions - * - Offline user message queuing - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ExecutionEventManager = exports.ExecutionEventType = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -var ExecutionEventType; -(function (ExecutionEventType) { - ExecutionEventType["COMMAND_START"] = "command_start"; - ExecutionEventType["STEP_EXECUTION"] = "step_execution"; - ExecutionEventType["COMMAND_COMPLETE"] = "command_complete"; - ExecutionEventType["COMMAND_FAILED"] = "command_failed"; - ExecutionEventType["ENTITY_CREATED"] = "entity_created"; - ExecutionEventType["ENTITY_UPDATED"] = "entity_updated"; - ExecutionEventType["USER_ASSIGNED"] = "user_assigned"; - ExecutionEventType["FILE_UPLOADED"] = "file_uploaded"; - ExecutionEventType["NOTIFICATION_SENT"] = "notification_sent"; -})(ExecutionEventType || (exports.ExecutionEventType = ExecutionEventType = {})); -class ExecutionEventManager extends events_1.EventEmitter { - socketManager; - offlineMessageQueue = new Map(); - eventDeduplication = new Map(); - broadcastMetrics = []; - maxQueueSize = 100; - deduplicationTTL = 30000; // 30 seconds - constructor(socketManager) { - super(); - this.socketManager = socketManager; - // Cleanup intervals - setInterval(() => this.cleanupDeduplication(), 60000); // Every minute - setInterval(() => this.cleanupOfflineQueue(), 300000); // Every 5 minutes - // Listen for user connection events - this.socketManager.on?.('user_connected', this.handleUserConnected.bind(this)); - this.socketManager.on?.('user_disconnected', this.handleUserDisconnected.bind(this)); - logger_1.logger.info('Execution Event Manager initialized'); - } - /** - * Broadcast command execution start event - * Target: <100ms broadcasting latency - */ - async broadcastCommandStart(commandId, userId, organizationId, affectedUsers, commandData) { - const event = { - id: `start_${commandId}_${Date.now()}`, - type: ExecutionEventType.COMMAND_START, - commandId, - userId, - organizationId, - data: { - transcript: commandData.transcript, - intent: commandData.intent, - actionCount: commandData.actionCount, - estimatedDuration: commandData.estimatedDuration, - }, - timestamp: new Date().toISOString(), - priority: 'medium', - targetUsers: affectedUsers, - targetChannels: [], - affectedEntities: [], - }; - return await this.broadcastEvent(event); - } - /** - * Broadcast step execution progress - */ - async broadcastStepExecution(commandId, stepId, userId, organizationId, stepData, affectedUsers, affectedChannels = []) { - const event = { - id: `step_${commandId}_${stepId}_${Date.now()}`, - type: ExecutionEventType.STEP_EXECUTION, - commandId, - userId, - organizationId, - data: { - stepId, - actionType: stepData.actionType, - status: stepData.status, - progress: stepData.progress, - result: stepData.result, - }, - timestamp: new Date().toISOString(), - priority: 'medium', - targetUsers: affectedUsers, - targetChannels: affectedChannels, - affectedEntities: stepData.affectedEntities || [], - }; - return await this.broadcastEvent(event); - } - /** - * Broadcast command completion - */ - async broadcastCommandComplete(commandId, userId, organizationId, result, affectedUsers, affectedChannels = []) { - const event = { - id: `complete_${commandId}_${Date.now()}`, - type: ExecutionEventType.COMMAND_COMPLETE, - commandId, - userId, - organizationId, - data: { - success: result.success, - executedActions: result.executedActions?.length || 0, - failedActions: result.failedActions?.length || 0, - totalExecutionTime: result.totalExecutionTime, - summary: result.summary, - }, - timestamp: new Date().toISOString(), - priority: result.success ? 'medium' : 'high', - targetUsers: affectedUsers, - targetChannels: affectedChannels, - affectedEntities: result.affectedEntities || [], - }; - return await this.broadcastEvent(event); - } - /** - * Broadcast command error/failure - */ - async broadcastCommandError(commandId, userId, organizationId, error, affectedUsers, affectedChannels = []) { - const event = { - id: `error_${commandId}_${Date.now()}`, - type: ExecutionEventType.COMMAND_FAILED, - commandId, - userId, - organizationId, - data: { - error, - timestamp: new Date().toISOString(), - }, - timestamp: new Date().toISOString(), - priority: 'high', - targetUsers: affectedUsers, - targetChannels: affectedChannels, - affectedEntities: [], - }; - return await this.broadcastEvent(event); - } - /** - * Broadcast entity creation (task, channel, etc.) - */ - async broadcastEntityCreated(entityType, entityId, entityName, createdBy, organizationId, affectedUsers, affectedChannels = []) { - const event = { - id: `created_${entityType}_${entityId}_${Date.now()}`, - type: ExecutionEventType.ENTITY_CREATED, - commandId: `entity_creation_${Date.now()}`, - userId: createdBy, - organizationId, - data: { - entityType, - entityId, - entityName, - createdBy, - }, - timestamp: new Date().toISOString(), - priority: 'medium', - targetUsers: affectedUsers, - targetChannels: affectedChannels, - affectedEntities: [ - { - type: entityType, - id: entityId, - name: entityName, - action: 'created', - }, - ], - }; - return await this.broadcastEvent(event); - } - /** - * Broadcast user assignment notifications - */ - async broadcastUserAssigned(assignmentType, entityId, entityName, assignedUsers, assignedBy, organizationId) { - const event = { - id: `assigned_${assignmentType}_${entityId}_${Date.now()}`, - type: ExecutionEventType.USER_ASSIGNED, - commandId: `assignment_${Date.now()}`, - userId: assignedBy, - organizationId, - data: { - assignmentType, - entityId, - entityName, - assignedUsers, - assignedBy, - }, - timestamp: new Date().toISOString(), - priority: 'high', - targetUsers: [...assignedUsers, assignedBy], - targetChannels: [], - affectedEntities: [ - { - type: assignmentType, - id: entityId, - name: entityName, - action: 'assigned', - }, - ], - }; - return await this.broadcastEvent(event); - } - /** - * Core event broadcasting method - * Target: <100ms average latency - */ - async broadcastEvent(event) { - const startTime = perf_hooks_1.performance.now(); - try { - // Event deduplication - if (this.isDuplicateEvent(event)) { - logger_1.logger.debug('Duplicate event detected, skipping broadcast', { - eventId: event.id, - type: event.type, - }); - return { - eventId: event.id, - targetCount: 0, - deliveredCount: 0, - queuedCount: 0, - failedCount: 0, - averageLatency: 0, - processingTime: perf_hooks_1.performance.now() - startTime, - }; - } - // Add to deduplication cache - this.addToDeduplication(event); - // Determine all target users (including channel members) - const allTargetUsers = await this.resolveAllTargetUsers(event); - let deliveredCount = 0; - let queuedCount = 0; - let failedCount = 0; - const latencies = []; - // Broadcast to online users - for (const userId of allTargetUsers) { - const userStartTime = perf_hooks_1.performance.now(); - if (this.socketManager.isUserConnected(userId)) { - try { - await this.socketManager.emitToUser(userId, 'execution_event', event); - const latency = perf_hooks_1.performance.now() - userStartTime; - latencies.push(latency); - deliveredCount++; - } - catch (error) { - logger_1.logger.warn('Failed to deliver event to online user', { - userId, - eventId: event.id, - error: error.message, - }); - failedCount++; - } - } - else { - // Queue for offline users - this.queueOfflineMessage(userId, event); - queuedCount++; - } - } - const processingTime = perf_hooks_1.performance.now() - startTime; - const averageLatency = latencies.length > 0 ? latencies.reduce((sum, lat) => sum + lat, 0) / latencies.length : 0; - this.recordBroadcastMetrics(processingTime); - // Log performance warning if target exceeded - if (processingTime > 100) { - logger_1.logger.warn('Event broadcasting exceeded 100ms target', { - eventId: event.id, - processingTime: `${processingTime.toFixed(2)}ms`, - targetCount: allTargetUsers.length, - averageLatency: `${averageLatency.toFixed(2)}ms`, - }); - } - const result = { - eventId: event.id, - targetCount: allTargetUsers.length, - deliveredCount, - queuedCount, - failedCount, - averageLatency, - processingTime, - }; - logger_1.logger.debug('Event broadcast completed', { - type: event.type, - ...result, - }); - // Emit broadcast metrics for monitoring - this.emit('broadcast_complete', result); - return result; - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.error('Event broadcasting failed', { - eventId: event.id, - type: event.type, - error: error.message, - processingTime: `${processingTime.toFixed(2)}ms`, - }); - return { - eventId: event.id, - targetCount: 0, - deliveredCount: 0, - queuedCount: 0, - failedCount: 1, - averageLatency: 0, - processingTime, - }; - } - } - /** - * Handle user connection - deliver queued messages - */ - async handleUserConnected(userId) { - const queuedMessages = this.offlineMessageQueue.get(userId); - if (!queuedMessages || queuedMessages.length === 0) { - return; - } - logger_1.logger.info('Delivering queued messages to connected user', { - userId, - messageCount: queuedMessages.length, - }); - try { - // Sort by priority and timestamp - queuedMessages.sort((a, b) => { - if (a.priority !== b.priority) { - return b.priority - a.priority; // Higher priority first - } - return new Date(a.queuedAt).getTime() - new Date(b.queuedAt).getTime(); - }); - // Deliver messages with a small delay between each - for (let i = 0; i < queuedMessages.length; i++) { - const message = queuedMessages[i]; - if (!message) { - continue; - } - try { - await this.socketManager.emitToUser(userId, 'execution_event', message.event); - // Small delay to prevent overwhelming the client - if (i < queuedMessages.length - 1) { - await new Promise((resolve) => setTimeout(resolve, 50)); - } - } - catch (error) { - logger_1.logger.warn('Failed to deliver queued message', { - userId, - eventId: message?.event?.id, - error: error.message, - }); - } - } - // Clear delivered messages - this.offlineMessageQueue.delete(userId); - logger_1.logger.debug('Queued messages delivered successfully', { - userId, - deliveredCount: queuedMessages.length, - }); - } - catch (error) { - logger_1.logger.error('Failed to deliver queued messages', { - userId, - error: error.message, - }); - } - } - /** - * Handle user disconnection - */ - handleUserDisconnected(userId) { - logger_1.logger.debug('User disconnected from execution events', { userId }); - } - /** - * Resolve all target users including channel members - */ - async resolveAllTargetUsers(event) { - const targetUsers = new Set(event.targetUsers); - // Add channel members for channel-targeted events - for (const channelId of event.targetChannels) { - try { - const channelMembers = await this.getChannelMembers(channelId); - channelMembers.forEach((memberId) => targetUsers.add(memberId)); - } - catch (error) { - logger_1.logger.warn('Failed to resolve channel members', { - channelId, - error: error.message, - }); - } - } - return Array.from(targetUsers); - } - /** - * Get channel members (simplified implementation) - */ - async getChannelMembers(channelId) { - // In a real implementation, this would query the database - // For now, return empty array - return []; - } - /** - * Check for duplicate events - */ - isDuplicateEvent(event) { - const deduplicationKey = `${event.type}_${event.commandId}`; - const existingEvents = this.eventDeduplication.get(deduplicationKey); - return existingEvents?.has(event.id) || false; - } - /** - * Add event to deduplication cache - */ - addToDeduplication(event) { - const deduplicationKey = `${event.type}_${event.commandId}`; - if (!this.eventDeduplication.has(deduplicationKey)) { - this.eventDeduplication.set(deduplicationKey, new Set()); - } - this.eventDeduplication.get(deduplicationKey)?.add(event.id); - // Set cleanup timeout - setTimeout(() => { - this.eventDeduplication.get(deduplicationKey)?.delete(event.id); - }, this.deduplicationTTL); - } - /** - * Queue message for offline user - */ - queueOfflineMessage(userId, event) { - if (!this.offlineMessageQueue.has(userId)) { - this.offlineMessageQueue.set(userId, []); - } - const queue = this.offlineMessageQueue.get(userId); - // Priority mapping - const priorityMap = { low: 1, medium: 2, high: 3, critical: 4 }; - const message = { - userId, - event, - queuedAt: new Date().toISOString(), - priority: priorityMap[event.priority], - }; - queue.push(message); - // Maintain queue size limit - if (queue.length > this.maxQueueSize) { - // Remove oldest low-priority messages first - queue.sort((a, b) => { - if (a.priority !== b.priority) { - return a.priority - b.priority; // Lower priority first - } - return new Date(a.queuedAt).getTime() - new Date(b.queuedAt).getTime(); - }); - queue.splice(0, queue.length - this.maxQueueSize); - } - } - /** - * Cleanup deduplication cache - */ - cleanupDeduplication() { - let cleanedKeys = 0; - for (const [key, eventIds] of this.eventDeduplication.entries()) { - if (eventIds.size === 0) { - this.eventDeduplication.delete(key); - cleanedKeys++; - } - } - if (cleanedKeys > 0) { - logger_1.logger.debug('Cleaned up deduplication cache', { cleanedKeys }); - } - } - /** - * Cleanup old offline messages - */ - cleanupOfflineQueue() { - let cleanedMessages = 0; - const cutoffTime = Date.now() - 24 * 60 * 60 * 1000; // 24 hours - for (const [userId, queue] of this.offlineMessageQueue.entries()) { - const originalLength = queue.length; - // Remove messages older than 24 hours - const filteredQueue = queue.filter((msg) => new Date(msg.queuedAt).getTime() > cutoffTime); - if (filteredQueue.length === 0) { - this.offlineMessageQueue.delete(userId); - } - else { - this.offlineMessageQueue.set(userId, filteredQueue); - } - cleanedMessages += originalLength - filteredQueue.length; - } - if (cleanedMessages > 0) { - logger_1.logger.debug('Cleaned up offline message queue', { cleanedMessages }); - } - } - /** - * Record broadcast performance metrics - */ - recordBroadcastMetrics(time) { - this.broadcastMetrics.push(time); - // Keep only last 1000 measurements - if (this.broadcastMetrics.length > 1000) { - this.broadcastMetrics.shift(); - } - } - /** - * Get performance statistics - */ - getPerformanceStats() { - if (this.broadcastMetrics.length === 0) { - return { - averageBroadcastTime: 0, - p95BroadcastTime: 0, - p99BroadcastTime: 0, - totalBroadcasts: 0, - offlineQueueSize: Array.from(this.offlineMessageQueue.values()).reduce((sum, queue) => sum + queue.length, 0), - deduplicationCacheSize: this.eventDeduplication.size, - }; - } - const sorted = [...this.broadcastMetrics].sort((a, b) => a - b); - const average = this.broadcastMetrics.reduce((sum, time) => sum + time, 0) / this.broadcastMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - return { - averageBroadcastTime: Math.round(average * 100) / 100, - p95BroadcastTime: Math.round(p95 * 100) / 100, - p99BroadcastTime: Math.round(p99 * 100) / 100, - totalBroadcasts: this.broadcastMetrics.length, - offlineQueueSize: Array.from(this.offlineMessageQueue.values()).reduce((sum, queue) => sum + queue.length, 0), - deduplicationCacheSize: this.eventDeduplication.size, - }; - } -} -exports.ExecutionEventManager = ExecutionEventManager; -//# sourceMappingURL=ExecutionEventManager.js.map \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/ExecutionEventManager.js.map b/backend/dist/realtime/broadcasting/ExecutionEventManager.js.map deleted file mode 100644 index d354177..0000000 --- a/backend/dist/realtime/broadcasting/ExecutionEventManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ExecutionEventManager.js","sourceRoot":"","sources":["../../../src/realtime/broadcasting/ExecutionEventManager.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;AAEH,mCAAsC;AACtC,2CAAyC;AACzC,+CAA4C;AAiB5C,IAAY,kBAUX;AAVD,WAAY,kBAAkB;IAC5B,qDAA+B,CAAA;IAC/B,uDAAiC,CAAA;IACjC,2DAAqC,CAAA;IACrC,uDAAiC,CAAA;IACjC,uDAAiC,CAAA;IACjC,uDAAiC,CAAA;IACjC,qDAA+B,CAAA;IAC/B,qDAA+B,CAAA;IAC/B,6DAAuC,CAAA;AACzC,CAAC,EAVW,kBAAkB,kCAAlB,kBAAkB,QAU7B;AA0BD,MAAa,qBAAsB,SAAQ,qBAAY;IAC7C,aAAa,CAAiB;IAC9B,mBAAmB,GAAkC,IAAI,GAAG,EAAE,CAAC;IAC/D,kBAAkB,GAA6B,IAAI,GAAG,EAAE,CAAC;IACzD,gBAAgB,GAAa,EAAE,CAAC;IACvB,YAAY,GAAG,GAAG,CAAC;IACnB,gBAAgB,GAAG,KAAK,CAAC,CAAC,aAAa;IAExD,YAAY,aAA6B;QACvC,KAAK,EAAE,CAAC;QAER,IAAI,CAAC,aAAa,GAAG,aAAa,CAAC;QAEnC,oBAAoB;QACpB,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,EAAE,EAAE,KAAK,CAAC,CAAC,CAAC,eAAe;QACtE,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,mBAAmB,EAAE,EAAE,MAAM,CAAC,CAAC,CAAC,kBAAkB;QAEzE,oCAAoC;QACpC,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC,gBAAgB,EAAE,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC/E,IAAI,CAAC,aAAa,CAAC,EAAE,EAAE,CAAC,mBAAmB,EAAE,IAAI,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAErF,eAAM,CAAC,IAAI,CAAC,qCAAqC,CAAC,CAAC;IACrD,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,qBAAqB,CACzB,SAAiB,EACjB,MAAc,EACd,cAAsB,EACtB,aAAuB,EACvB,WAAgB;QAEhB,MAAM,KAAK,GAAmB;YAC5B,EAAE,EAAE,SAAS,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE;YACtC,IAAI,EAAE,kBAAkB,CAAC,aAAa;YACtC,SAAS;YACT,MAAM;YACN,cAAc;YACd,IAAI,EAAE;gBACJ,UAAU,EAAE,WAAW,CAAC,UAAU;gBAClC,MAAM,EAAE,WAAW,CAAC,MAAM;gBAC1B,WAAW,EAAE,WAAW,CAAC,WAAW;gBACpC,iBAAiB,EAAE,WAAW,CAAC,iBAAiB;aACjD;YACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ,EAAE,QAAQ;YAClB,WAAW,EAAE,aAAa;YAC1B,cAAc,EAAE,EAAE;YAClB,gBAAgB,EAAE,EAAE;SACrB,CAAC;QAEF,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,sBAAsB,CAC1B,SAAiB,EACjB,MAAc,EACd,MAAc,EACd,cAAsB,EACtB,QAAa,EACb,aAAuB,EACvB,mBAA6B,EAAE;QAE/B,MAAM,KAAK,GAAmB;YAC5B,EAAE,EAAE,QAAQ,SAAS,IAAI,MAAM,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE;YAC/C,IAAI,EAAE,kBAAkB,CAAC,cAAc;YACvC,SAAS;YACT,MAAM;YACN,cAAc;YACd,IAAI,EAAE;gBACJ,MAAM;gBACN,UAAU,EAAE,QAAQ,CAAC,UAAU;gBAC/B,MAAM,EAAE,QAAQ,CAAC,MAAM;gBACvB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;gBAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;aACxB;YACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ,EAAE,QAAQ;YAClB,WAAW,EAAE,aAAa;YAC1B,cAAc,EAAE,gBAAgB;YAChC,gBAAgB,EAAE,QAAQ,CAAC,gBAAgB,IAAI,EAAE;SAClD,CAAC;QAEF,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,wBAAwB,CAC5B,SAAiB,EACjB,MAAc,EACd,cAAsB,EACtB,MAAW,EACX,aAAuB,EACvB,mBAA6B,EAAE;QAE/B,MAAM,KAAK,GAAmB;YAC5B,EAAE,EAAE,YAAY,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE;YACzC,IAAI,EAAE,kBAAkB,CAAC,gBAAgB;YACzC,SAAS;YACT,MAAM;YACN,cAAc;YACd,IAAI,EAAE;gBACJ,OAAO,EAAE,MAAM,CAAC,OAAO;gBACvB,eAAe,EAAE,MAAM,CAAC,eAAe,EAAE,MAAM,IAAI,CAAC;gBACpD,aAAa,EAAE,MAAM,CAAC,aAAa,EAAE,MAAM,IAAI,CAAC;gBAChD,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;gBAC7C,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB;YACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM;YAC5C,WAAW,EAAE,aAAa;YAC1B,cAAc,EAAE,gBAAgB;YAChC,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,IAAI,EAAE;SAChD,CAAC;QAEF,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,qBAAqB,CACzB,SAAiB,EACjB,MAAc,EACd,cAAsB,EACtB,KAAa,EACb,aAAuB,EACvB,mBAA6B,EAAE;QAE/B,MAAM,KAAK,GAAmB;YAC5B,EAAE,EAAE,SAAS,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE;YACtC,IAAI,EAAE,kBAAkB,CAAC,cAAc;YACvC,SAAS;YACT,MAAM;YACN,cAAc;YACd,IAAI,EAAE;gBACJ,KAAK;gBACL,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC;YACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ,EAAE,MAAM;YAChB,WAAW,EAAE,aAAa;YAC1B,cAAc,EAAE,gBAAgB;YAChC,gBAAgB,EAAE,EAAE;SACrB,CAAC;QAEF,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,sBAAsB,CAC1B,UAAkB,EAClB,QAAgB,EAChB,UAAkB,EAClB,SAAiB,EACjB,cAAsB,EACtB,aAAuB,EACvB,mBAA6B,EAAE;QAE/B,MAAM,KAAK,GAAmB;YAC5B,EAAE,EAAE,WAAW,UAAU,IAAI,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE;YACrD,IAAI,EAAE,kBAAkB,CAAC,cAAc;YACvC,SAAS,EAAE,mBAAmB,IAAI,CAAC,GAAG,EAAE,EAAE;YAC1C,MAAM,EAAE,SAAS;YACjB,cAAc;YACd,IAAI,EAAE;gBACJ,UAAU;gBACV,QAAQ;gBACR,UAAU;gBACV,SAAS;aACV;YACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ,EAAE,QAAQ;YAClB,WAAW,EAAE,aAAa;YAC1B,cAAc,EAAE,gBAAgB;YAChC,gBAAgB,EAAE;gBAChB;oBACE,IAAI,EAAE,UAAiB;oBACvB,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,UAAU;oBAChB,MAAM,EAAE,SAAS;iBAClB;aACF;SACF,CAAC;QAEF,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,qBAAqB,CACzB,cAAsB,EACtB,QAAgB,EAChB,UAAkB,EAClB,aAAuB,EACvB,UAAkB,EAClB,cAAsB;QAEtB,MAAM,KAAK,GAAmB;YAC5B,EAAE,EAAE,YAAY,cAAc,IAAI,QAAQ,IAAI,IAAI,CAAC,GAAG,EAAE,EAAE;YAC1D,IAAI,EAAE,kBAAkB,CAAC,aAAa;YACtC,SAAS,EAAE,cAAc,IAAI,CAAC,GAAG,EAAE,EAAE;YACrC,MAAM,EAAE,UAAU;YAClB,cAAc;YACd,IAAI,EAAE;gBACJ,cAAc;gBACd,QAAQ;gBACR,UAAU;gBACV,aAAa;gBACb,UAAU;aACX;YACD,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ,EAAE,MAAM;YAChB,WAAW,EAAE,CAAC,GAAG,aAAa,EAAE,UAAU,CAAC;YAC3C,cAAc,EAAE,EAAE;YAClB,gBAAgB,EAAE;gBAChB;oBACE,IAAI,EAAE,cAAqB;oBAC3B,EAAE,EAAE,QAAQ;oBACZ,IAAI,EAAE,UAAU;oBAChB,MAAM,EAAE,UAAU;iBACnB;aACF;SACF,CAAC;QAEF,OAAO,MAAM,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC;IAED;;;OAGG;IACK,KAAK,CAAC,cAAc,CAAC,KAAqB;QAChD,MAAM,SAAS,GAAG,wBAAW,CAAC,GAAG,EAAE,CAAC;QAEpC,IAAI,CAAC;YACH,sBAAsB;YACtB,IAAI,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;gBACjC,eAAM,CAAC,KAAK,CAAC,8CAA8C,EAAE;oBAC3D,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,IAAI,EAAE,KAAK,CAAC,IAAI;iBACjB,CAAC,CAAC;gBAEH,OAAO;oBACL,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,WAAW,EAAE,CAAC;oBACd,cAAc,EAAE,CAAC;oBACjB,WAAW,EAAE,CAAC;oBACd,WAAW,EAAE,CAAC;oBACd,cAAc,EAAE,CAAC;oBACjB,cAAc,EAAE,wBAAW,CAAC,GAAG,EAAE,GAAG,SAAS;iBAC9C,CAAC;YACJ,CAAC;YAED,6BAA6B;YAC7B,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;YAE/B,yDAAyD;YACzD,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC,CAAC;YAE/D,IAAI,cAAc,GAAG,CAAC,CAAC;YACvB,IAAI,WAAW,GAAG,CAAC,CAAC;YACpB,IAAI,WAAW,GAAG,CAAC,CAAC;YACpB,MAAM,SAAS,GAAa,EAAE,CAAC;YAE/B,4BAA4B;YAC5B,KAAK,MAAM,MAAM,IAAI,cAAc,EAAE,CAAC;gBACpC,MAAM,aAAa,GAAG,wBAAW,CAAC,GAAG,EAAE,CAAC;gBAExC,IAAI,IAAI,CAAC,aAAa,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC/C,IAAI,CAAC;wBACH,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,MAAM,EAAE,iBAAiB,EAAE,KAAK,CAAC,CAAC;wBAEtE,MAAM,OAAO,GAAG,wBAAW,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC;wBAClD,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;wBACxB,cAAc,EAAE,CAAC;oBACnB,CAAC;oBAAC,OAAO,KAAU,EAAE,CAAC;wBACpB,eAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE;4BACpD,MAAM;4BACN,OAAO,EAAE,KAAK,CAAC,EAAE;4BACjB,KAAK,EAAE,KAAK,CAAC,OAAO;yBACrB,CAAC,CAAC;wBACH,WAAW,EAAE,CAAC;oBAChB,CAAC;gBACH,CAAC;qBAAM,CAAC;oBACN,0BAA0B;oBAC1B,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;oBACxC,WAAW,EAAE,CAAC;gBAChB,CAAC;YACH,CAAC;YAED,MAAM,cAAc,GAAG,wBAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YACrD,MAAM,cAAc,GAClB,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,GAAG,GAAG,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;YAE7F,IAAI,CAAC,sBAAsB,CAAC,cAAc,CAAC,CAAC;YAE5C,6CAA6C;YAC7C,IAAI,cAAc,GAAG,GAAG,EAAE,CAAC;gBACzB,eAAM,CAAC,IAAI,CAAC,0CAA0C,EAAE;oBACtD,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,cAAc,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;oBAChD,WAAW,EAAE,cAAc,CAAC,MAAM;oBAClC,cAAc,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;iBACjD,CAAC,CAAC;YACL,CAAC;YAED,MAAM,MAAM,GAAoB;gBAC9B,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,WAAW,EAAE,cAAc,CAAC,MAAM;gBAClC,cAAc;gBACd,WAAW;gBACX,WAAW;gBACX,cAAc;gBACd,cAAc;aACf,CAAC;YAEF,eAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE;gBACxC,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,GAAG,MAAM;aACV,CAAC,CAAC;YAEH,wCAAwC;YACxC,IAAI,CAAC,IAAI,CAAC,oBAAoB,EAAE,MAAM,CAAC,CAAC;YAExC,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,MAAM,cAAc,GAAG,wBAAW,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAErD,eAAM,CAAC,KAAK,CAAC,2BAA2B,EAAE;gBACxC,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,KAAK,EAAE,KAAK,CAAC,OAAO;gBACpB,cAAc,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI;aACjD,CAAC,CAAC;YAEH,OAAO;gBACL,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,WAAW,EAAE,CAAC;gBACd,cAAc,EAAE,CAAC;gBACjB,WAAW,EAAE,CAAC;gBACd,WAAW,EAAE,CAAC;gBACd,cAAc,EAAE,CAAC;gBACjB,cAAc;aACf,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,mBAAmB,CAAC,MAAc;QAC9C,MAAM,cAAc,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAE5D,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACnD,OAAO;QACT,CAAC;QAED,eAAM,CAAC,IAAI,CAAC,8CAA8C,EAAE;YAC1D,MAAM;YACN,YAAY,EAAE,cAAc,CAAC,MAAM;SACpC,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,iCAAiC;YACjC,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBAC3B,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;oBAC9B,OAAO,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,wBAAwB;gBAC1D,CAAC;gBACD,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;YACzE,CAAC,CAAC,CAAC;YAEH,mDAAmD;YACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,cAAc,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC/C,MAAM,OAAO,GAAG,cAAc,CAAC,CAAC,CAAC,CAAC;gBAElC,IAAI,CAAC,OAAO,EAAE,CAAC;oBACb,SAAS;gBACX,CAAC;gBAED,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,aAAa,CAAC,UAAU,CAAC,MAAM,EAAE,iBAAiB,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;oBAE9E,iDAAiD;oBACjD,IAAI,CAAC,GAAG,cAAc,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAClC,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;oBAC1D,CAAC;gBACH,CAAC;gBAAC,OAAO,KAAU,EAAE,CAAC;oBACpB,eAAM,CAAC,IAAI,CAAC,kCAAkC,EAAE;wBAC9C,MAAM;wBACN,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,EAAE;wBAC3B,KAAK,EAAE,KAAK,CAAC,OAAO;qBACrB,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,2BAA2B;YAC3B,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAExC,eAAM,CAAC,KAAK,CAAC,wCAAwC,EAAE;gBACrD,MAAM;gBACN,cAAc,EAAE,cAAc,CAAC,MAAM;aACtC,CAAC,CAAC;QACL,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,eAAM,CAAC,KAAK,CAAC,mCAAmC,EAAE;gBAChD,MAAM;gBACN,KAAK,EAAE,KAAK,CAAC,OAAO;aACrB,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED;;OAEG;IACK,sBAAsB,CAAC,MAAc;QAC3C,eAAM,CAAC,KAAK,CAAC,yCAAyC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACtE,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,qBAAqB,CAAC,KAAqB;QACvD,MAAM,WAAW,GAAG,IAAI,GAAG,CAAS,KAAK,CAAC,WAAW,CAAC,CAAC;QAEvD,kDAAkD;QAClD,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,cAAc,EAAE,CAAC;YAC7C,IAAI,CAAC;gBACH,MAAM,cAAc,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;gBAC/D,cAAc,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,WAAW,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;YAClE,CAAC;YAAC,OAAO,KAAU,EAAE,CAAC;gBACpB,eAAM,CAAC,IAAI,CAAC,mCAAmC,EAAE;oBAC/C,SAAS;oBACT,KAAK,EAAE,KAAK,CAAC,OAAO;iBACrB,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,iBAAiB,CAAC,SAAiB;QAC/C,0DAA0D;QAC1D,8BAA8B;QAC9B,OAAO,EAAE,CAAC;IACZ,CAAC;IAED;;OAEG;IACK,gBAAgB,CAAC,KAAqB;QAC5C,MAAM,gBAAgB,GAAG,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QAC5D,MAAM,cAAc,GAAG,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QAErE,OAAO,cAAc,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,KAAK,CAAC;IAChD,CAAC;IAED;;OAEG;IACK,kBAAkB,CAAC,KAAqB;QAC9C,MAAM,gBAAgB,GAAG,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;QAE5D,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACnD,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,gBAAgB,EAAE,IAAI,GAAG,EAAE,CAAC,CAAC;QAC3D,CAAC;QAED,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAE7D,sBAAsB;QACtB,UAAU,CAAC,GAAG,EAAE;YACd,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAClE,CAAC,EAAE,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAC5B,CAAC;IAED;;OAEG;IACK,mBAAmB,CAAC,MAAc,EAAE,KAAqB;QAC/D,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;YAC1C,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAC3C,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,CAAE,CAAC;QAEpD,mBAAmB;QACnB,MAAM,WAAW,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC;QAEhE,MAAM,OAAO,GAAmB;YAC9B,MAAM;YACN,KAAK;YACL,QAAQ,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAClC,QAAQ,EAAE,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC;SACtC,CAAC;QAEF,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAEpB,4BAA4B;QAC5B,IAAI,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YACrC,4CAA4C;YAC5C,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;gBAClB,IAAI,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,QAAQ,EAAE,CAAC;oBAC9B,OAAO,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,uBAAuB;gBACzD,CAAC;gBACD,OAAO,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,CAAC;YACzE,CAAC,CAAC,CAAC;YAEH,KAAK,CAAC,MAAM,CAAC,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC;QACpD,CAAC;IACH,CAAC;IAED;;OAEG;IACK,oBAAoB;QAC1B,IAAI,WAAW,GAAG,CAAC,CAAC;QAEpB,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,IAAI,CAAC,kBAAkB,CAAC,OAAO,EAAE,EAAE,CAAC;YAChE,IAAI,QAAQ,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;gBACxB,IAAI,CAAC,kBAAkB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;gBACpC,WAAW,EAAE,CAAC;YAChB,CAAC;QACH,CAAC;QAED,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;YACpB,eAAM,CAAC,KAAK,CAAC,gCAAgC,EAAE,EAAE,WAAW,EAAE,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IAED;;OAEG;IACK,mBAAmB;QACzB,IAAI,eAAe,GAAG,CAAC,CAAC;QACxB,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,WAAW;QAEhE,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,EAAE,CAAC;YACjE,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC;YAEpC,sCAAsC;YACtC,MAAM,aAAa,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,GAAG,UAAU,CAAC,CAAC;YAE3F,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBAC/B,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;YAC1C,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,mBAAmB,CAAC,GAAG,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;YACtD,CAAC;YAED,eAAe,IAAI,cAAc,GAAG,aAAa,CAAC,MAAM,CAAC;QAC3D,CAAC;QAED,IAAI,eAAe,GAAG,CAAC,EAAE,CAAC;YACxB,eAAM,CAAC,KAAK,CAAC,kCAAkC,EAAE,EAAE,eAAe,EAAE,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;IAED;;OAEG;IACK,sBAAsB,CAAC,IAAY;QACzC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEjC,mCAAmC;QACnC,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,GAAG,IAAI,EAAE,CAAC;YACxC,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,CAAC;QAChC,CAAC;IACH,CAAC;IAED;;OAEG;IACH,mBAAmB;QAQjB,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO;gBACL,oBAAoB,EAAE,CAAC;gBACvB,gBAAgB,EAAE,CAAC;gBACnB,gBAAgB,EAAE,CAAC;gBACnB,eAAe,EAAE,CAAC;gBAClB,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CACpE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,EAClC,CAAC,CACF;gBACD,sBAAsB,EAAE,IAAI,CAAC,kBAAkB,CAAC,IAAI;aACrD,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAChE,MAAM,OAAO,GACX,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;QAC5F,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC;QAE1D,OAAO;YACL,oBAAoB,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,GAAG;YACrD,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG;YAC7C,gBAAgB,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG;YAC7C,eAAe,EAAE,IAAI,CAAC,gBAAgB,CAAC,MAAM;YAC7C,gBAAgB,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CACpE,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CAAC,GAAG,GAAG,KAAK,CAAC,MAAM,EAClC,CAAC,CACF;YACD,sBAAsB,EAAE,IAAI,CAAC,kBAAkB,CAAC,IAAI;SACrD,CAAC;IACJ,CAAC;CACF;AAhnBD,sDAgnBC"} \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/NotificationIntegration.d.ts b/backend/dist/realtime/broadcasting/NotificationIntegration.d.ts deleted file mode 100644 index fdab1ca..0000000 --- a/backend/dist/realtime/broadcasting/NotificationIntegration.d.ts +++ /dev/null @@ -1,170 +0,0 @@ -/** - * Notification Integration - Phase 2 Real-Time Notification System - * Integrates real-time broadcasting with notification systems - * - * Success Criteria: - * - Push notification support for offline users - * - Email notifications for important events - * - In-app notification management - * - Notification preference handling - */ -import { EventEmitter } from 'events'; -import { SocketManager } from '../../websocket/SocketManager'; -export interface NotificationPreferences { - userId: string; - organizationId: string; - pushNotifications: boolean; - emailNotifications: boolean; - inAppNotifications: boolean; - commandCompletionNotifications: boolean; - fileUploadNotifications: boolean; - entityUpdateNotifications: boolean; - quietHours: { - enabled: boolean; - startTime: string; - endTime: string; - timezone: string; - }; - updatedAt: string; -} -export interface NotificationEvent { - notificationId: string; - userId: string; - organizationId: string; - type: 'command_complete' | 'file_uploaded' | 'entity_updated' | 'system_alert' | 'error'; - title: string; - message: string; - data: Record; - priority: 'low' | 'medium' | 'high' | 'critical'; - createdAt: string; - readAt?: string; - deliveredAt?: string; -} -export interface DeliveryChannel { - type: 'push' | 'email' | 'in_app' | 'websocket'; - enabled: boolean; - metadata?: Record; -} -export interface NotificationDelivery { - notificationId: string; - userId: string; - channels: DeliveryChannel[]; - deliveredChannels: string[]; - failedChannels: string[]; - deliveryAttempts: number; - lastAttemptAt: string; - status: 'pending' | 'delivered' | 'failed' | 'expired'; -} -export declare class NotificationIntegration extends EventEmitter { - private socketManager; - private notificationQueue; - private userPreferences; - private pendingDeliveries; - private deliveryMetrics; - private readonly maxRetries; - private readonly retryDelay; - private processingTimer?; - constructor(socketManager: SocketManager); - /** - * Queue notification for delivery - */ - queueNotification(userId: string, organizationId: string, type: NotificationEvent['type'], title: string, message: string, data?: Record, priority?: NotificationEvent['priority']): Promise; - /** - * Set user notification preferences - */ - setUserPreferences(preferences: NotificationPreferences): Promise; - /** - * Get user notification preferences - */ - getUserPreferences(userId: string): NotificationPreferences | null; - /** - * Get default notification preferences - */ - private getDefaultPreferences; - /** - * Process notification for command completion - */ - notifyCommandComplete(commandId: string, userId: string, organizationId: string, result: any, affectedUsers: string[]): Promise; - /** - * Process notification for file upload - */ - notifyFileUploaded(fileId: string, fileName: string, userId: string, organizationId: string, fileSize: number, downloadUrl?: string, sharedWith?: string[]): Promise; - /** - * Process notification for system errors - */ - notifySystemError(userId: string, organizationId: string, errorType: string, errorMessage: string, contextData?: Record): Promise; - /** - * Start notification processing timer - */ - private startNotificationProcessing; - /** - * Process all pending notifications - */ - private processAllNotifications; - /** - * Process notifications for a specific user - */ - private processUserNotifications; - /** - * Deliver notification through appropriate channels - */ - private deliverNotification; - /** - * Attempt delivery through all channels - */ - private attemptDelivery; - /** - * Deliver notification through specific channel - */ - private deliverThroughChannel; - /** - * Deliver through WebSocket - */ - private deliverWebSocket; - /** - * Deliver as in-app notification - */ - private deliverInApp; - /** - * Deliver push notification - */ - private deliverPush; - /** - * Deliver email notification - */ - private deliverEmail; - /** - * Check if current time is in quiet hours - */ - private isInQuietHours; - /** - * Get user notifications (recent) - */ - getUserNotifications(userId: string, limit?: number): NotificationEvent[]; - /** - * Mark notification as read - */ - markNotificationRead(notificationId: string, userId: string): Promise; - /** - * Record delivery performance metrics - */ - private recordDeliveryMetrics; - /** - * Get notification statistics - */ - getNotificationStats(): { - queuedNotifications: number; - pendingDeliveries: number; - averageDeliveryTime: number; - deliverySuccessRate: number; - }; - /** - * Cleanup old deliveries and metrics - */ - cleanup(): void; - /** - * Destroy and cleanup - */ - destroy(): void; -} -//# sourceMappingURL=NotificationIntegration.d.ts.map \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/NotificationIntegration.d.ts.map b/backend/dist/realtime/broadcasting/NotificationIntegration.d.ts.map deleted file mode 100644 index e2b4c9c..0000000 --- a/backend/dist/realtime/broadcasting/NotificationIntegration.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NotificationIntegration.d.ts","sourceRoot":"","sources":["../../../src/realtime/broadcasting/NotificationIntegration.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/NotificationIntegration.js b/backend/dist/realtime/broadcasting/NotificationIntegration.js deleted file mode 100644 index d77a130..0000000 --- a/backend/dist/realtime/broadcasting/NotificationIntegration.js +++ /dev/null @@ -1,477 +0,0 @@ -"use strict"; -/** - * Notification Integration - Phase 2 Real-Time Notification System - * Integrates real-time broadcasting with notification systems - * - * Success Criteria: - * - Push notification support for offline users - * - Email notifications for important events - * - In-app notification management - * - Notification preference handling - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.NotificationIntegration = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -class NotificationIntegration extends events_1.EventEmitter { - socketManager; - notificationQueue = new Map(); - userPreferences = new Map(); - pendingDeliveries = new Map(); - deliveryMetrics = []; - maxRetries = 3; - retryDelay = 60000; // 1 minute - processingTimer; - constructor(socketManager) { - super(); - this.socketManager = socketManager; - // Start notification processing - this.startNotificationProcessing(); - logger_1.logger.info('Notification Integration initialized'); - } - /** - * Queue notification for delivery - */ - async queueNotification(userId, organizationId, type, title, message, data = {}, priority = 'medium') { - const notificationId = `notif_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - const notification = { - notificationId, - userId, - organizationId, - type, - title, - message, - data, - priority, - createdAt: new Date().toISOString() - }; - // Add to user's notification queue - const userQueue = this.notificationQueue.get(userId) || []; - userQueue.push(notification); - this.notificationQueue.set(userId, userQueue); - logger_1.logger.debug('Notification queued', { - notificationId, - userId, - type, - priority, - title - }); - // Process immediately for high priority notifications - if (priority === 'high' || priority === 'critical') { - await this.processUserNotifications(userId); - } - return notificationId; - } - /** - * Set user notification preferences - */ - async setUserPreferences(preferences) { - this.userPreferences.set(preferences.userId, { - ...preferences, - updatedAt: new Date().toISOString() - }); - logger_1.logger.debug('User notification preferences updated', { - userId: preferences.userId, - organizationId: preferences.organizationId - }); - this.emit('preferences_updated', preferences); - } - /** - * Get user notification preferences - */ - getUserPreferences(userId) { - return this.userPreferences.get(userId) || this.getDefaultPreferences(userId); - } - /** - * Get default notification preferences - */ - getDefaultPreferences(userId) { - return { - userId, - organizationId: '', - pushNotifications: true, - emailNotifications: true, - inAppNotifications: true, - commandCompletionNotifications: true, - fileUploadNotifications: true, - entityUpdateNotifications: false, - quietHours: { - enabled: false, - startTime: '22:00', - endTime: '08:00', - timezone: 'UTC' - }, - updatedAt: new Date().toISOString() - }; - } - /** - * Process notification for command completion - */ - async notifyCommandComplete(commandId, userId, organizationId, result, affectedUsers) { - const preferences = this.getUserPreferences(userId); - if (!preferences?.commandCompletionNotifications) { - return; - } - // Notify command executor - await this.queueNotification(userId, organizationId, 'command_complete', 'Voice Command Completed', `Your voice command has been executed successfully.`, { - commandId, - result, - completedAt: new Date().toISOString() - }, 'medium'); - // Notify affected users if different from executor - for (const affectedUserId of affectedUsers) { - if (affectedUserId !== userId) { - const affectedPreferences = this.getUserPreferences(affectedUserId); - if (affectedPreferences?.entityUpdateNotifications) { - await this.queueNotification(affectedUserId, organizationId, 'entity_updated', 'System Update', `A voice command has affected your workspace.`, { - commandId, - executedBy: userId, - affectedEntities: result.affectedEntities || [] - }, 'low'); - } - } - } - } - /** - * Process notification for file upload - */ - async notifyFileUploaded(fileId, fileName, userId, organizationId, fileSize, downloadUrl, sharedWith = []) { - const preferences = this.getUserPreferences(userId); - if (!preferences?.fileUploadNotifications) { - return; - } - // Notify uploader - await this.queueNotification(userId, organizationId, 'file_uploaded', 'File Upload Complete', `"${fileName}" has been uploaded successfully.`, { - fileId, - fileName, - fileSize, - downloadUrl, - uploadedAt: new Date().toISOString() - }, 'medium'); - // Notify shared users - for (const sharedUserId of sharedWith) { - if (sharedUserId !== userId) { - await this.queueNotification(sharedUserId, organizationId, 'file_uploaded', 'New File Shared', `"${fileName}" has been shared with you.`, { - fileId, - fileName, - fileSize, - downloadUrl, - sharedBy: userId, - sharedAt: new Date().toISOString() - }, 'medium'); - } - } - } - /** - * Process notification for system errors - */ - async notifySystemError(userId, organizationId, errorType, errorMessage, contextData = {}) { - await this.queueNotification(userId, organizationId, 'error', 'System Error', errorMessage, { - errorType, - contextData, - occurredAt: new Date().toISOString() - }, 'high'); - } - /** - * Start notification processing timer - */ - startNotificationProcessing() { - this.processingTimer = setInterval(async () => { - await this.processAllNotifications(); - }, 10000); // Process every 10 seconds - } - /** - * Process all pending notifications - */ - async processAllNotifications() { - const userIds = Array.from(this.notificationQueue.keys()); - for (const userId of userIds) { - await this.processUserNotifications(userId); - } - } - /** - * Process notifications for a specific user - */ - async processUserNotifications(userId) { - const notifications = this.notificationQueue.get(userId); - if (!notifications || notifications.length === 0) { - return; - } - const preferences = this.getUserPreferences(userId); - if (!preferences) { - return; - } - const startTime = perf_hooks_1.performance.now(); - try { - // Check quiet hours - if (this.isInQuietHours(preferences)) { - logger_1.logger.debug('Skipping notifications due to quiet hours', { userId }); - return; - } - // Process each notification - for (const notification of notifications) { - await this.deliverNotification(notification, preferences); - } - // Clear processed notifications - this.notificationQueue.delete(userId); - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordDeliveryMetrics(processingTime); - logger_1.logger.debug('User notifications processed', { - userId, - count: notifications.length, - processingTime: `${processingTime.toFixed(2)}ms` - }); - } - catch (error) { - logger_1.logger.error('Error processing user notifications', { - userId, - error: error.message - }); - } - } - /** - * Deliver notification through appropriate channels - */ - async deliverNotification(notification, preferences) { - const deliveryChannels = []; - // Determine delivery channels based on preferences - if (preferences.inAppNotifications) { - deliveryChannels.push({ type: 'in_app', enabled: true }); - } - // Check if user is online for WebSocket delivery - if (this.socketManager.isUserConnected(notification.userId)) { - deliveryChannels.push({ type: 'websocket', enabled: true }); - } - // Add push notifications for offline users or high priority - if (preferences.pushNotifications && - (!this.socketManager.isUserConnected(notification.userId) || - ['high', 'critical'].includes(notification.priority))) { - deliveryChannels.push({ type: 'push', enabled: true }); - } - // Add email for critical notifications - if (preferences.emailNotifications && notification.priority === 'critical') { - deliveryChannels.push({ type: 'email', enabled: true }); - } - const delivery = { - notificationId: notification.notificationId, - userId: notification.userId, - channels: deliveryChannels, - deliveredChannels: [], - failedChannels: [], - deliveryAttempts: 0, - lastAttemptAt: new Date().toISOString(), - status: 'pending' - }; - this.pendingDeliveries.set(notification.notificationId, delivery); - // Attempt delivery through each channel - await this.attemptDelivery(notification, delivery); - } - /** - * Attempt delivery through all channels - */ - async attemptDelivery(notification, delivery) { - delivery.deliveryAttempts++; - delivery.lastAttemptAt = new Date().toISOString(); - const deliveryPromises = delivery.channels.map(async (channel) => { - if (!channel.enabled) - return; - try { - await this.deliverThroughChannel(notification, channel); - delivery.deliveredChannels.push(channel.type); - logger_1.logger.debug('Notification delivered through channel', { - notificationId: notification.notificationId, - channel: channel.type, - userId: notification.userId - }); - } - catch (error) { - delivery.failedChannels.push(channel.type); - logger_1.logger.warn('Failed to deliver notification through channel', { - notificationId: notification.notificationId, - channel: channel.type, - userId: notification.userId, - error: error.message - }); - } - }); - await Promise.allSettled(deliveryPromises); - // Update delivery status - if (delivery.deliveredChannels.length > 0) { - delivery.status = 'delivered'; - notification.deliveredAt = new Date().toISOString(); - this.emit('notification_delivered', { - notificationId: notification.notificationId, - userId: notification.userId, - channels: delivery.deliveredChannels - }); - } - else if (delivery.deliveryAttempts >= this.maxRetries) { - delivery.status = 'failed'; - this.emit('notification_failed', { - notificationId: notification.notificationId, - userId: notification.userId, - attempts: delivery.deliveryAttempts - }); - } - else { - // Retry after delay - setTimeout(() => { - this.attemptDelivery(notification, delivery); - }, this.retryDelay); - } - } - /** - * Deliver notification through specific channel - */ - async deliverThroughChannel(notification, channel) { - switch (channel.type) { - case 'websocket': - await this.deliverWebSocket(notification); - break; - case 'in_app': - await this.deliverInApp(notification); - break; - case 'push': - await this.deliverPush(notification); - break; - case 'email': - await this.deliverEmail(notification); - break; - default: - throw new Error(`Unknown delivery channel: ${channel.type}`); - } - } - /** - * Deliver through WebSocket - */ - async deliverWebSocket(notification) { - await this.socketManager.emitToUser(notification.userId, 'notification', { - notificationId: notification.notificationId, - type: notification.type, - title: notification.title, - message: notification.message, - data: notification.data, - priority: notification.priority, - createdAt: notification.createdAt - }); - } - /** - * Deliver as in-app notification - */ - async deliverInApp(notification) { - // Store in-app notification (would typically be in database) - // For now, we'll emit an event that the API can listen to - this.emit('in_app_notification', notification); - } - /** - * Deliver push notification - */ - async deliverPush(notification) { - // Integration with push notification service (FCM, APNs, etc.) - // For now, we'll emit an event that can be handled by push service - this.emit('push_notification', notification); - } - /** - * Deliver email notification - */ - async deliverEmail(notification) { - // Integration with email service (SendGrid, SES, etc.) - // For now, we'll emit an event that can be handled by email service - this.emit('email_notification', notification); - } - /** - * Check if current time is in quiet hours - */ - isInQuietHours(preferences) { - if (!preferences.quietHours.enabled) { - return false; - } - // Simple time check (would need proper timezone handling in production) - const now = new Date(); - const currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`; - return currentTime >= preferences.quietHours.startTime && - currentTime <= preferences.quietHours.endTime; - } - /** - * Get user notifications (recent) - */ - getUserNotifications(userId, limit = 20) { - // In production, this would query database - // For now, return empty array as notifications are processed immediately - return []; - } - /** - * Mark notification as read - */ - async markNotificationRead(notificationId, userId) { - // In production, update database - this.emit('notification_read', { - notificationId, - userId, - readAt: new Date().toISOString() - }); - } - /** - * Record delivery performance metrics - */ - recordDeliveryMetrics(time) { - this.deliveryMetrics.push(time); - // Keep only last 1000 measurements - if (this.deliveryMetrics.length > 1000) { - this.deliveryMetrics.shift(); - } - } - /** - * Get notification statistics - */ - getNotificationStats() { - const queuedNotifications = Array.from(this.notificationQueue.values()) - .reduce((sum, notifications) => sum + notifications.length, 0); - const pendingDeliveries = this.pendingDeliveries.size; - const averageDeliveryTime = this.deliveryMetrics.length > 0 - ? this.deliveryMetrics.reduce((sum, time) => sum + time, 0) / this.deliveryMetrics.length - : 0; - const totalDeliveries = Array.from(this.pendingDeliveries.values()); - const successfulDeliveries = totalDeliveries.filter(d => d.status === 'delivered').length; - const deliverySuccessRate = totalDeliveries.length > 0 - ? successfulDeliveries / totalDeliveries.length - : 1; - return { - queuedNotifications, - pendingDeliveries, - averageDeliveryTime: Math.round(averageDeliveryTime * 100) / 100, - deliverySuccessRate: Math.round(deliverySuccessRate * 10000) / 10000 - }; - } - /** - * Cleanup old deliveries and metrics - */ - cleanup() { - const cutoffTime = Date.now() - (3600000 * 24); // 24 hours ago - let cleanedCount = 0; - for (const [notificationId, delivery] of this.pendingDeliveries.entries()) { - const deliveryTime = new Date(delivery.lastAttemptAt).getTime(); - if (deliveryTime < cutoffTime && ['delivered', 'failed'].includes(delivery.status)) { - this.pendingDeliveries.delete(notificationId); - cleanedCount++; - } - } - if (cleanedCount > 0) { - logger_1.logger.debug('Cleaned up old notification deliveries', { cleanedCount }); - } - } - /** - * Destroy and cleanup - */ - destroy() { - if (this.processingTimer) { - clearInterval(this.processingTimer); - } - this.cleanup(); - this.removeAllListeners(); - logger_1.logger.info('Notification Integration destroyed'); - } -} -exports.NotificationIntegration = NotificationIntegration; -//# sourceMappingURL=NotificationIntegration.js.map \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/NotificationIntegration.js.map b/backend/dist/realtime/broadcasting/NotificationIntegration.js.map deleted file mode 100644 index 77e93ad..0000000 --- a/backend/dist/realtime/broadcasting/NotificationIntegration.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"NotificationIntegration.js","sourceRoot":"","sources":["../../../src/realtime/broadcasting/NotificationIntegration.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/ProgressBroadcaster.d.ts b/backend/dist/realtime/broadcasting/ProgressBroadcaster.d.ts deleted file mode 100644 index eaa2c6d..0000000 --- a/backend/dist/realtime/broadcasting/ProgressBroadcaster.d.ts +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Progress Broadcaster - Phase 2 Real-Time Progress Tracking - * Shows live progress of multi-step command execution - * - * Success Criteria: - * - Step-by-step progress visualization - * - Estimated time remaining calculation - * - Progress persistence for reconnecting clients - * - Progress completion notifications - */ -import { EventEmitter } from 'events'; -import { ISocketManager } from '../../websocket/types'; -export interface ProgressSession { - sessionId: string; - commandId: string; - userId: string; - organizationId: string; - totalSteps: number; - currentStep: number; - stepProgress: Map; - startTime: number; - estimatedDuration: number; - actualDuration?: number; - status: 'active' | 'completed' | 'failed' | 'cancelled'; - targetUsers: string[]; -} -export interface StepProgress { - stepId: string; - stepName: string; - status: 'pending' | 'running' | 'completed' | 'failed' | 'skipped'; - progress: number; - startTime?: number; - endTime?: number; - estimatedDuration: number; - actualDuration?: number; - result?: any; - error?: string; -} -export interface ProgressUpdate { - sessionId: string; - commandId: string; - totalProgress: number; - currentStep: number; - totalSteps: number; - currentStepName: string; - currentStepProgress: number; - estimatedTimeRemaining: number; - elapsedTime: number; - status: ProgressSession['status']; - stepDetails: StepProgress[]; - lastUpdated: string; -} -export declare class ProgressBroadcaster extends EventEmitter { - private socketManager; - private activeSessions; - private sessionHistory; - private broadcastMetrics; - private readonly historyRetention; - constructor(socketManager: ISocketManager); - /** - * Initialize progress tracking for a command - */ - initializeProgressTracking(commandId: string, userId: string, organizationId: string, steps: Array<{ - id: string; - name: string; - estimatedDuration: number; - }>, targetUsers: string[]): ProgressSession; - /** - * Update step progress - */ - updateStepProgress(sessionId: string, stepId: string, progress: number, status?: StepProgress['status'], result?: any, error?: string): Promise; - /** - * Mark session as failed - */ - markSessionFailed(sessionId: string, error: string, failedStepId?: string): Promise; - /** - * Cancel active session - */ - cancelSession(sessionId: string, reason?: string): Promise; - /** - * Get progress for a specific session - */ - getSessionProgress(sessionId: string): ProgressSession | null; - /** - * Get all active progress sessions - */ - getActiveSessions(): ProgressSession[]; - /** - * Get progress sessions for a specific user - */ - getUserSessions(userId: string): ProgressSession[]; - /** - * Broadcast progress update to target users - */ - private broadcastProgressUpdate; - /** - * Create progress update object for broadcasting - */ - private createProgressUpdate; - /** - * Check if session is complete - */ - private isSessionComplete; - /** - * Cleanup old sessions from history - */ - private cleanupOldSessions; - /** - * Record broadcast performance metrics - */ - private recordBroadcastMetrics; - /** - * Get performance statistics - */ - getPerformanceStats(): { - averageUpdateTime: number; - p95UpdateTime: number; - totalUpdates: number; - activeSessions: number; - historicalSessions: number; - }; - /** - * Destroy the progress broadcaster and clean up resources - */ - destroy(): void; -} -//# sourceMappingURL=ProgressBroadcaster.d.ts.map \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/ProgressBroadcaster.d.ts.map b/backend/dist/realtime/broadcasting/ProgressBroadcaster.d.ts.map deleted file mode 100644 index 7786e4c..0000000 --- a/backend/dist/realtime/broadcasting/ProgressBroadcaster.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ProgressBroadcaster.d.ts","sourceRoot":"","sources":["../../../src/realtime/broadcasting/ProgressBroadcaster.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/ProgressBroadcaster.js b/backend/dist/realtime/broadcasting/ProgressBroadcaster.js deleted file mode 100644 index cdc17d4..0000000 --- a/backend/dist/realtime/broadcasting/ProgressBroadcaster.js +++ /dev/null @@ -1,354 +0,0 @@ -"use strict"; -/** - * Progress Broadcaster - Phase 2 Real-Time Progress Tracking - * Shows live progress of multi-step command execution - * - * Success Criteria: - * - Step-by-step progress visualization - * - Estimated time remaining calculation - * - Progress persistence for reconnecting clients - * - Progress completion notifications - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ProgressBroadcaster = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -class ProgressBroadcaster extends events_1.EventEmitter { - socketManager; - activeSessions = new Map(); - sessionHistory = new Map(); - broadcastMetrics = []; - historyRetention = 3600000; // 1 hour - constructor(socketManager) { - super(); - this.socketManager = socketManager; - // Cleanup old sessions periodically - setInterval(() => this.cleanupOldSessions(), 300000); // Every 5 minutes - logger_1.logger.info('Progress Broadcaster initialized'); - } - /** - * Initialize progress tracking for a command - */ - initializeProgressTracking(commandId, userId, organizationId, steps, targetUsers) { - const sessionId = `progress_${commandId}_${Date.now()}`; - const session = { - sessionId, - commandId, - userId, - organizationId, - totalSteps: steps.length, - currentStep: 0, - stepProgress: new Map(), - startTime: perf_hooks_1.performance.now(), - estimatedDuration: steps.reduce((sum, step) => sum + step.estimatedDuration, 0), - status: 'active', - targetUsers, - }; - // Initialize step progress - steps.forEach((step, index) => { - const stepProgress = { - stepId: step.id, - stepName: step.name, - status: index === 0 ? 'pending' : 'pending', - progress: 0, - estimatedDuration: step.estimatedDuration, - }; - session.stepProgress.set(step.id, stepProgress); - }); - this.activeSessions.set(sessionId, session); - logger_1.logger.info('Progress tracking initialized', { - sessionId, - commandId, - totalSteps: steps.length, - estimatedDuration: `${session.estimatedDuration.toFixed(0)}ms`, - targetUsers: targetUsers.length, - }); - // Send initial progress update - this.broadcastProgressUpdate(session); - return session; - } - /** - * Update step progress - */ - async updateStepProgress(sessionId, stepId, progress, status, result, error) { - const startTime = perf_hooks_1.performance.now(); - const session = this.activeSessions.get(sessionId); - if (!session) { - logger_1.logger.warn('Progress update for unknown session', { sessionId, stepId }); - return; - } - const stepProgress = session.stepProgress.get(stepId); - if (!stepProgress) { - logger_1.logger.warn('Progress update for unknown step', { sessionId, stepId }); - return; - } - // Update step progress - const previousProgress = stepProgress.progress; - stepProgress.progress = Math.min(100, Math.max(0, progress)); - if (status) { - stepProgress.status = status; - if (status === 'running' && !stepProgress.startTime) { - stepProgress.startTime = perf_hooks_1.performance.now(); - } - if (['completed', 'failed', 'skipped'].includes(status) && !stepProgress.endTime) { - stepProgress.endTime = perf_hooks_1.performance.now(); - if (stepProgress.startTime) { - stepProgress.actualDuration = stepProgress.endTime - stepProgress.startTime; - } - } - } - if (result !== undefined) { - stepProgress.result = result; - } - if (error) { - stepProgress.error = error; - stepProgress.status = 'failed'; - } - // Update current step if this step is completed - if (stepProgress.status === 'completed') { - const steps = Array.from(session.stepProgress.values()); - const completedSteps = steps.filter((s) => s.status === 'completed').length; - session.currentStep = Math.max(session.currentStep, completedSteps); - } - // Check if command is complete - if (this.isSessionComplete(session)) { - session.status = 'completed'; - session.actualDuration = perf_hooks_1.performance.now() - session.startTime; - // Move to history - this.sessionHistory.set(sessionId, { ...session }); - this.activeSessions.delete(sessionId); - logger_1.logger.info('Command execution completed', { - sessionId, - commandId: session.commandId, - actualDuration: `${session.actualDuration.toFixed(2)}ms`, - estimatedDuration: `${session.estimatedDuration.toFixed(2)}ms`, - }); - } - // Broadcast progress update - await this.broadcastProgressUpdate(session); - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordBroadcastMetrics(processingTime); - logger_1.logger.debug('Step progress updated', { - sessionId, - stepId, - progress: stepProgress.progress, - status: stepProgress.status, - previousProgress, - processingTime: `${processingTime.toFixed(2)}ms`, - }); - } - /** - * Mark session as failed - */ - async markSessionFailed(sessionId, error, failedStepId) { - const session = this.activeSessions.get(sessionId); - if (!session) { - return; - } - session.status = 'failed'; - session.actualDuration = perf_hooks_1.performance.now() - session.startTime; - // Mark current step as failed if specified - if (failedStepId) { - const stepProgress = session.stepProgress.get(failedStepId); - if (stepProgress) { - stepProgress.status = 'failed'; - stepProgress.error = error; - stepProgress.endTime = perf_hooks_1.performance.now(); - } - } - // Move to history - this.sessionHistory.set(sessionId, { ...session }); - this.activeSessions.delete(sessionId); - await this.broadcastProgressUpdate(session); - logger_1.logger.warn('Command execution failed', { - sessionId, - commandId: session.commandId, - error, - failedStepId, - }); - } - /** - * Cancel active session - */ - async cancelSession(sessionId, reason = 'Cancelled by user') { - const session = this.activeSessions.get(sessionId); - if (!session) { - return; - } - session.status = 'cancelled'; - session.actualDuration = perf_hooks_1.performance.now() - session.startTime; - // Mark all pending/running steps as skipped - for (const stepProgress of session.stepProgress.values()) { - if (['pending', 'running'].includes(stepProgress.status)) { - stepProgress.status = 'skipped'; - stepProgress.error = reason; - } - } - // Move to history - this.sessionHistory.set(sessionId, { ...session }); - this.activeSessions.delete(sessionId); - await this.broadcastProgressUpdate(session); - logger_1.logger.info('Command execution cancelled', { - sessionId, - commandId: session.commandId, - reason, - }); - } - /** - * Get progress for a specific session - */ - getSessionProgress(sessionId) { - return this.activeSessions.get(sessionId) || this.sessionHistory.get(sessionId) || null; - } - /** - * Get all active progress sessions - */ - getActiveSessions() { - return Array.from(this.activeSessions.values()); - } - /** - * Get progress sessions for a specific user - */ - getUserSessions(userId) { - const active = Array.from(this.activeSessions.values()).filter((session) => session.userId === userId); - const historical = Array.from(this.sessionHistory.values()) - .filter((session) => session.userId === userId) - .slice(-10); // Last 10 sessions - return [...active, ...historical]; - } - /** - * Broadcast progress update to target users - */ - async broadcastProgressUpdate(session) { - const startTime = perf_hooks_1.performance.now(); - try { - const progressUpdate = this.createProgressUpdate(session); - // Broadcast to all target users - const broadcastPromises = session.targetUsers.map((userId) => { - if (this.socketManager.isUserConnected(userId)) { - return this.socketManager.emitToUser(userId, 'progress_update', progressUpdate); - } - return Promise.resolve(); - }); - await Promise.allSettled(broadcastPromises); - const processingTime = perf_hooks_1.performance.now() - startTime; - this.emit('progress_broadcast', { - sessionId: session.sessionId, - targetUsers: session.targetUsers.length, - processingTime, - }); - } - catch (error) { - logger_1.logger.error('Failed to broadcast progress update', { - sessionId: session.sessionId, - error: error.message, - }); - } - } - /** - * Create progress update object for broadcasting - */ - createProgressUpdate(session) { - const steps = Array.from(session.stepProgress.values()); - const completedSteps = steps.filter((s) => s.status === 'completed').length; - const totalProgress = session.totalSteps > 0 ? (completedSteps / session.totalSteps) * 100 : 0; - // Find current step - const currentStepProgress = steps.find((s) => s.status === 'running') || - steps.find((s) => s.status === 'pending') || - steps[steps.length - 1]; - // Calculate time remaining - const elapsedTime = perf_hooks_1.performance.now() - session.startTime; - let estimatedTimeRemaining = 0; - if (session.status === 'active' && totalProgress > 0 && totalProgress < 100) { - const averageTimePerPercent = elapsedTime / totalProgress; - estimatedTimeRemaining = Math.max(0, averageTimePerPercent * (100 - totalProgress)); - } - return { - sessionId: session.sessionId, - commandId: session.commandId, - totalProgress: Math.round(totalProgress * 100) / 100, - currentStep: session.currentStep, - totalSteps: session.totalSteps, - currentStepName: currentStepProgress?.stepName || 'Unknown', - currentStepProgress: currentStepProgress?.progress || 0, - estimatedTimeRemaining: Math.round(estimatedTimeRemaining), - elapsedTime: Math.round(elapsedTime), - status: session.status, - stepDetails: steps.map((step) => ({ ...step })), - lastUpdated: new Date().toISOString(), - }; - } - /** - * Check if session is complete - */ - isSessionComplete(session) { - const steps = Array.from(session.stepProgress.values()); - return steps.every((step) => ['completed', 'failed', 'skipped'].includes(step.status)); - } - /** - * Cleanup old sessions from history - */ - cleanupOldSessions() { - const cutoffTime = Date.now() - this.historyRetention; - let cleanedCount = 0; - for (const [sessionId, session] of this.sessionHistory.entries()) { - const sessionTime = session.actualDuration - ? session.startTime + session.actualDuration - : session.startTime; - if (sessionTime < cutoffTime) { - this.sessionHistory.delete(sessionId); - cleanedCount++; - } - } - if (cleanedCount > 0) { - logger_1.logger.debug('Cleaned up old progress sessions', { cleanedCount }); - } - } - /** - * Record broadcast performance metrics - */ - recordBroadcastMetrics(time) { - this.broadcastMetrics.push(time); - // Keep only last 1000 measurements - if (this.broadcastMetrics.length > 1000) { - this.broadcastMetrics.shift(); - } - } - /** - * Get performance statistics - */ - getPerformanceStats() { - if (this.broadcastMetrics.length === 0) { - return { - averageUpdateTime: 0, - p95UpdateTime: 0, - totalUpdates: 0, - activeSessions: this.activeSessions.size, - historicalSessions: this.sessionHistory.size, - }; - } - const sorted = [...this.broadcastMetrics].sort((a, b) => a - b); - const average = this.broadcastMetrics.reduce((sum, time) => sum + time, 0) / this.broadcastMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - return { - averageUpdateTime: Math.round(average * 100) / 100, - p95UpdateTime: Math.round(p95 * 100) / 100, - totalUpdates: this.broadcastMetrics.length, - activeSessions: this.activeSessions.size, - historicalSessions: this.sessionHistory.size, - }; - } - /** - * Destroy the progress broadcaster and clean up resources - */ - destroy() { - this.activeSessions.clear(); - this.sessionHistory.clear(); - this.broadcastMetrics.length = 0; - this.removeAllListeners(); - logger_1.logger.info('Progress Broadcaster destroyed'); - } -} -exports.ProgressBroadcaster = ProgressBroadcaster; -//# sourceMappingURL=ProgressBroadcaster.js.map \ No newline at end of file diff --git a/backend/dist/realtime/broadcasting/ProgressBroadcaster.js.map b/backend/dist/realtime/broadcasting/ProgressBroadcaster.js.map deleted file mode 100644 index e498c71..0000000 --- a/backend/dist/realtime/broadcasting/ProgressBroadcaster.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"ProgressBroadcaster.js","sourceRoot":"","sources":["../../../src/realtime/broadcasting/ProgressBroadcaster.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/scripts/cleanup-unverified-users.d.ts b/backend/dist/scripts/cleanup-unverified-users.d.ts deleted file mode 100644 index c2cf549..0000000 --- a/backend/dist/scripts/cleanup-unverified-users.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env tsx -/** - * Cleanup script to delete all unverified user accounts - * This allows reusing email addresses for testing after fixing email verification - */ -declare function cleanupUnverifiedUsers(): Promise; -export { cleanupUnverifiedUsers }; -//# sourceMappingURL=cleanup-unverified-users.d.ts.map \ No newline at end of file diff --git a/backend/dist/scripts/cleanup-unverified-users.d.ts.map b/backend/dist/scripts/cleanup-unverified-users.d.ts.map deleted file mode 100644 index 09e73ed..0000000 --- a/backend/dist/scripts/cleanup-unverified-users.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"cleanup-unverified-users.d.ts","sourceRoot":"","sources":["../../src/scripts/cleanup-unverified-users.ts"],"names":[],"mappings":";AAMA;;;GAGG;AAEH,iBAAe,sBAAsB,kBA6BpC;AAkBD,OAAO,EAAE,sBAAsB,EAAE,CAAC"} \ No newline at end of file diff --git a/backend/dist/scripts/cleanup-unverified-users.js b/backend/dist/scripts/cleanup-unverified-users.js deleted file mode 100644 index 56192a9..0000000 --- a/backend/dist/scripts/cleanup-unverified-users.js +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env tsx -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cleanupUnverifiedUsers = cleanupUnverifiedUsers; -const index_1 = require("@db/index"); -const logger_1 = require("@utils/logger"); -/** - * Cleanup script to delete all unverified user accounts - * This allows reusing email addresses for testing after fixing email verification - */ -async function cleanupUnverifiedUsers() { - try { - logger_1.logger.info('Starting cleanup of unverified user accounts...'); - // First, let's see how many unverified users we have - const unverifiedUsers = await index_1.userRepository.findUnverifiedUsers(); - if (unverifiedUsers.length === 0) { - logger_1.logger.info('No unverified users found to cleanup'); - return; - } - logger_1.logger.info(`Found ${unverifiedUsers.length} unverified users to delete:`); - // Log the users that will be deleted (for confirmation) - unverifiedUsers.forEach((user, index) => { - logger_1.logger.info(`${index + 1}. ${user.email} - Created: ${user.created_at} - Role: ${user.role}`); - }); - // Delete all unverified users - const deleteCount = await index_1.userRepository.deleteUnverifiedUsers(); - logger_1.logger.info(`Successfully deleted ${deleteCount} unverified user accounts`); - logger_1.logger.info('Cleanup completed. These email addresses can now be reused for registration.'); - } - catch (error) { - logger_1.logger.error({ error }, 'Failed to cleanup unverified users'); - throw error; - } -} -// Check if the repository has the required methods, if not we'll add them -async function main() { - try { - await cleanupUnverifiedUsers(); - process.exit(0); - } - catch (error) { - logger_1.logger.error({ error }, 'Cleanup script failed'); - process.exit(1); - } -} -// Only run if this script is executed directly -if (require.main === module) { - main(); -} -//# sourceMappingURL=cleanup-unverified-users.js.map \ No newline at end of file diff --git a/backend/dist/scripts/cleanup-unverified-users.js.map b/backend/dist/scripts/cleanup-unverified-users.js.map deleted file mode 100644 index e29c725..0000000 --- a/backend/dist/scripts/cleanup-unverified-users.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"cleanup-unverified-users.js","sourceRoot":"","sources":["../../src/scripts/cleanup-unverified-users.ts"],"names":[],"mappings":";;;AA0DS,wDAAsB;AAvD/B,qCAA2C;AAC3C,0CAAuC;AAEvC;;;GAGG;AAEH,KAAK,UAAU,sBAAsB;IACnC,IAAI,CAAC;QACH,eAAM,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;QAE/D,qDAAqD;QACrD,MAAM,eAAe,GAAG,MAAM,sBAAc,CAAC,mBAAmB,EAAE,CAAC;QAEnE,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,eAAM,CAAC,IAAI,CAAC,sCAAsC,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,eAAM,CAAC,IAAI,CAAC,SAAS,eAAe,CAAC,MAAM,8BAA8B,CAAC,CAAC;QAE3E,wDAAwD;QACxD,eAAe,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;YACtC,eAAM,CAAC,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,KAAK,IAAI,CAAC,KAAK,eAAe,IAAI,CAAC,UAAU,YAAY,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAChG,CAAC,CAAC,CAAC;QAEH,8BAA8B;QAC9B,MAAM,WAAW,GAAG,MAAM,sBAAc,CAAC,qBAAqB,EAAE,CAAC;QAEjE,eAAM,CAAC,IAAI,CAAC,wBAAwB,WAAW,2BAA2B,CAAC,CAAC;QAC5E,eAAM,CAAC,IAAI,CAAC,8EAA8E,CAAC,CAAC;IAE9F,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,oCAAoC,CAAC,CAAC;QAC9D,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,0EAA0E;AAC1E,KAAK,UAAU,IAAI;IACjB,IAAI,CAAC;QACH,MAAM,sBAAsB,EAAE,CAAC;QAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,uBAAuB,CAAC,CAAC;QACjD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;AACH,CAAC;AAED,+CAA+C;AAC/C,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;IAC5B,IAAI,EAAE,CAAC;AACT,CAAC"} \ No newline at end of file diff --git a/backend/dist/scripts/phase2-implementation-review.d.ts b/backend/dist/scripts/phase2-implementation-review.d.ts deleted file mode 100644 index dd7898e..0000000 --- a/backend/dist/scripts/phase2-implementation-review.d.ts +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Phase 2 Implementation Review and Validation Script - * Comprehensive review of implementation against success criteria - * - * This script validates: - * 1. Voice Processing Performance - * 2. AI Command Intelligence - * 3. Multi-Action Execution - * 4. Real-Time Synchronization - * 5. File Management Integration - * 6. System Reliability - */ -interface TestResult { - testName: string; - category: string; - status: 'PASS' | 'FAIL' | 'WARN'; - actualValue: number | string | boolean; - targetValue: number | string | boolean; - criticalValue: number | string | boolean; - score: number; - details: string; - recommendations?: string[]; -} -interface CategoryScore { - category: string; - weight: number; - score: number; - maxScore: number; - status: 'EXCELLENT' | 'GOOD' | 'ACCEPTABLE' | 'NEEDS_IMPROVEMENT' | 'CRITICAL'; - tests: TestResult[]; -} -declare class Phase2ImplementationReview { - private testResults; - private voiceService; - private multiActionExecutor; - private aiCommandParser; - constructor(); - runCompleteReview(): Promise<{ - overallScore: number; - status: 'SUCCESS' | 'NEEDS_IMPROVEMENT' | 'FAILED'; - categories: CategoryScore[]; - recommendations: string[]; - }>; - private reviewVoiceProcessingPerformance; - private reviewAICommandIntelligence; - private reviewMultiActionExecution; - private reviewRealTimeSynchronization; - private reviewFileManagementIntegration; - private reviewSystemReliability; - private testSimpleCommandSpeed; - private testComplexCommandSpeed; - private testTranscriptionAccuracy; - private testCommandInterpretationAccuracy; - private testAudioProcessingLatency; - private testAIResponseTime; - private testEntityResolutionAccuracy; - private testContextualReferenceResolution; - private testMultiActionCommandSuccess; - private testDependencyResolutionAccuracy; - private testContextBuildingPerformance; - private testACIDTransactionCompliance; - private testRollbackSuccessRate; - private testParallelExecutionEfficiency; - private testActionSuccessRate; - private testGracefulErrorHandling; - private testPermissionValidation; - private testLiveUpdateLatency; - private testEventOrdering; - private testConnectionStability; - private testConcurrentUserSupport; - private testUploadInitiationSpeed; - private testUploadSuccessRate; - private testAutoLinkingAccuracy; - private testS3OperationSuccess; - private testSystemUptime; - private testDataIntegrity; - private testSecurityCompliance; - private testAuditTrailCompleteness; - private testErrorRecoveryTime; - private calculateCategoryScore; - private calculateOverallScore; - private determineOverallStatus; - private generateRecommendations; - private generateDetailedReport; -} -export { Phase2ImplementationReview }; -//# sourceMappingURL=phase2-implementation-review.d.ts.map \ No newline at end of file diff --git a/backend/dist/scripts/phase2-implementation-review.d.ts.map b/backend/dist/scripts/phase2-implementation-review.d.ts.map deleted file mode 100644 index 3ecc406..0000000 --- a/backend/dist/scripts/phase2-implementation-review.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"phase2-implementation-review.d.ts","sourceRoot":"","sources":["../../src/scripts/phase2-implementation-review.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AASH,UAAU,UAAU;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACjC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACvC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACvC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,UAAU,aAAa;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,GAAG,MAAM,GAAG,YAAY,GAAG,mBAAmB,GAAG,UAAU,CAAC;IAC/E,KAAK,EAAE,UAAU,EAAE,CAAC;CACrB;AAED,cAAM,0BAA0B;IAC9B,OAAO,CAAC,WAAW,CAAoB;IACvC,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,eAAe,CAAkB;;IAQnC,iBAAiB,IAAI,OAAO,CAAC;QACjC,YAAY,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,SAAS,GAAG,mBAAmB,GAAG,QAAQ,CAAC;QACnD,UAAU,EAAE,aAAa,EAAE,CAAC;QAC5B,eAAe,EAAE,MAAM,EAAE,CAAC;KAC3B,CAAC;YAyCY,gCAAgC;YA4BhC,2BAA2B;YAyB3B,0BAA0B;YA4B1B,6BAA6B;YAsB7B,+BAA+B;YAsB/B,uBAAuB;YA0BvB,sBAAsB;YA6DtB,uBAAuB;YA0DvB,yBAAyB;YA4BzB,iCAAiC;YA2BjC,0BAA0B;YA0B1B,kBAAkB;YA0BlB,4BAA4B;YA0B5B,iCAAiC;YA0BjC,6BAA6B;YA0B7B,gCAAgC;YA0BhC,8BAA8B;YA2B9B,6BAA6B;YAc7B,uBAAuB;YAavB,+BAA+B;YAa/B,qBAAqB;YAarB,yBAAyB;YAazB,wBAAwB;YAcxB,qBAAqB;YAkBrB,iBAAiB;YAajB,uBAAuB;YAavB,yBAAyB;YAazB,yBAAyB;YAkBzB,qBAAqB;YAkBrB,uBAAuB;YAiBvB,sBAAsB;YAatB,gBAAgB;YAahB,iBAAiB;YAajB,sBAAsB;YAatB,0BAA0B;YAa1B,qBAAqB;IAanC,OAAO,CAAC,sBAAsB;IAsB9B,OAAO,CAAC,qBAAqB;IAO7B,OAAO,CAAC,sBAAsB;IAM9B,OAAO,CAAC,uBAAuB;YA2BjB,sBAAsB;CAmFrC;AAGD,OAAO,EAAE,0BAA0B,EAAE,CAAC"} \ No newline at end of file diff --git a/backend/dist/scripts/phase2-implementation-review.js b/backend/dist/scripts/phase2-implementation-review.js deleted file mode 100644 index 5e5791b..0000000 --- a/backend/dist/scripts/phase2-implementation-review.js +++ /dev/null @@ -1,878 +0,0 @@ -"use strict"; -/** - * Phase 2 Implementation Review and Validation Script - * Comprehensive review of implementation against success criteria - * - * This script validates: - * 1. Voice Processing Performance - * 2. AI Command Intelligence - * 3. Multi-Action Execution - * 4. Real-Time Synchronization - * 5. File Management Integration - * 6. System Reliability - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Phase2ImplementationReview = void 0; -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../utils/logger"); -const VoiceProcessingService_1 = require("../voice/VoiceProcessingService"); -const MultiActionExecutor_1 = require("../ai/execution/MultiActionExecutor"); -const AICommandParser_1 = require("../ai/commands/AICommandParser"); -class Phase2ImplementationReview { - testResults = []; - voiceService; - multiActionExecutor; - aiCommandParser; - constructor() { - this.voiceService = new VoiceProcessingService_1.VoiceProcessingService(); - this.multiActionExecutor = new MultiActionExecutor_1.MultiActionExecutor(); - this.aiCommandParser = new AICommandParser_1.AICommandParser(); - } - async runCompleteReview() { - logger_1.logger.info('Starting Phase 2 Implementation Review'); - const categories = [ - await this.reviewVoiceProcessingPerformance(), - await this.reviewAICommandIntelligence(), - await this.reviewMultiActionExecution(), - await this.reviewRealTimeSynchronization(), - await this.reviewFileManagementIntegration(), - await this.reviewSystemReliability() - ]; - // Calculate overall score - const overallScore = this.calculateOverallScore(categories); - const status = this.determineOverallStatus(overallScore); - const recommendations = this.generateRecommendations(categories); - // Generate detailed report - await this.generateDetailedReport({ - overallScore, - status, - categories, - recommendations - }); - logger_1.logger.info('Phase 2 Implementation Review completed', { - overallScore, - status, - categoriesReviewed: categories.length, - testsExecuted: this.testResults.length - }); - return { - overallScore, - status, - categories, - recommendations - }; - } - // Voice Processing Performance Review (Weight: 25%) - async reviewVoiceProcessingPerformance() { - const tests = []; - // Test 1: Simple Command Processing Speed (<2 seconds) - tests.push(await this.testSimpleCommandSpeed()); - // Test 2: Complex Command Processing Speed (<5 seconds) - tests.push(await this.testComplexCommandSpeed()); - // Test 3: Voice Transcription Accuracy (>95%) - tests.push(await this.testTranscriptionAccuracy()); - // Test 4: Command Interpretation Accuracy (>90%) - tests.push(await this.testCommandInterpretationAccuracy()); - // Test 5: Audio Processing Latency (<500ms) - tests.push(await this.testAudioProcessingLatency()); - // Test 6: AI Response Time (<1 second) - tests.push(await this.testAIResponseTime()); - const categoryScore = this.calculateCategoryScore('Voice Processing Performance', 25, tests); - this.testResults.push(...tests); - return categoryScore; - } - // AI Command Intelligence Review (Weight: 20%) - async reviewAICommandIntelligence() { - const tests = []; - // Test 1: Entity Resolution Accuracy (>85%) - tests.push(await this.testEntityResolutionAccuracy()); - // Test 2: Contextual Reference Resolution (>80%) - tests.push(await this.testContextualReferenceResolution()); - // Test 3: Multi-Action Command Success Rate (>95%) - tests.push(await this.testMultiActionCommandSuccess()); - // Test 4: Dependency Resolution Accuracy (>90%) - tests.push(await this.testDependencyResolutionAccuracy()); - // Test 5: Context Building Performance (<500ms) - tests.push(await this.testContextBuildingPerformance()); - const categoryScore = this.calculateCategoryScore('AI Command Intelligence', 20, tests); - this.testResults.push(...tests); - return categoryScore; - } - // Multi-Action Execution Review (Weight: 20%) - async reviewMultiActionExecution() { - const tests = []; - // Test 1: ACID Transaction Compliance (100%) - tests.push(await this.testACIDTransactionCompliance()); - // Test 2: Rollback Success Rate (>99%) - tests.push(await this.testRollbackSuccessRate()); - // Test 3: Parallel Execution Efficiency (>60% improvement) - tests.push(await this.testParallelExecutionEfficiency()); - // Test 4: Action Success Rate (>98%) - tests.push(await this.testActionSuccessRate()); - // Test 5: Graceful Error Handling (>95%) - tests.push(await this.testGracefulErrorHandling()); - // Test 6: Permission Validation (100%) - tests.push(await this.testPermissionValidation()); - const categoryScore = this.calculateCategoryScore('Multi-Action Execution', 20, tests); - this.testResults.push(...tests); - return categoryScore; - } - // Real-Time Synchronization Review (Weight: 15%) - async reviewRealTimeSynchronization() { - const tests = []; - // Test 1: Live Update Latency (<100ms) - tests.push(await this.testLiveUpdateLatency()); - // Test 2: Event Ordering (100%) - tests.push(await this.testEventOrdering()); - // Test 3: Connection Stability (>99%) - tests.push(await this.testConnectionStability()); - // Test 4: Concurrent User Support (25+ users) - tests.push(await this.testConcurrentUserSupport()); - const categoryScore = this.calculateCategoryScore('Real-Time Synchronization', 15, tests); - this.testResults.push(...tests); - return categoryScore; - } - // File Management Integration Review (Weight: 10%) - async reviewFileManagementIntegration() { - const tests = []; - // Test 1: Upload Initiation Speed (<3 seconds) - tests.push(await this.testUploadInitiationSpeed()); - // Test 2: Upload Success Rate (>95%) - tests.push(await this.testUploadSuccessRate()); - // Test 3: Auto-Linking Accuracy (>90%) - tests.push(await this.testAutoLinkingAccuracy()); - // Test 4: S3 Operation Success (>99%) - tests.push(await this.testS3OperationSuccess()); - const categoryScore = this.calculateCategoryScore('File Management Integration', 10, tests); - this.testResults.push(...tests); - return categoryScore; - } - // System Reliability Review (Weight: 10%) - async reviewSystemReliability() { - const tests = []; - // Test 1: System Uptime (>99.5%) - tests.push(await this.testSystemUptime()); - // Test 2: Data Integrity (100%) - tests.push(await this.testDataIntegrity()); - // Test 3: Security Compliance - tests.push(await this.testSecurityCompliance()); - // Test 4: Audit Trail Completeness (100%) - tests.push(await this.testAuditTrailCompleteness()); - // Test 5: Error Recovery Time (<30 seconds) - tests.push(await this.testErrorRecoveryTime()); - const categoryScore = this.calculateCategoryScore('System Reliability', 10, tests); - this.testResults.push(...tests); - return categoryScore; - } - // Individual test implementations - async testSimpleCommandSpeed() { - const testCommands = [ - 'Create marketing channel', - 'Add Sarah to project team', - 'Update task status to completed', - 'Send message to development team', - 'Set deadline to Friday' - ]; - const processingTimes = []; - const targetTime = 2000; // 2 seconds - const criticalTime = 2000; - for (const command of testCommands) { - const startTime = perf_hooks_1.performance.now(); - try { - // Simulate voice command processing - const mockAudioBuffer = Buffer.alloc(16000); // 1 second of 16kHz audio - const userContext = { - userId: 'test-user-123', - organizationId: 'test-org-456', - language: 'en', - timezone: 'UTC' - }; - await this.voiceService.processVoiceCommand(mockAudioBuffer, userContext); - const processingTime = perf_hooks_1.performance.now() - startTime; - processingTimes.push(processingTime); - } - catch (error) { - // Command failed, record max time - processingTimes.push(targetTime * 2); - } - } - const averageTime = processingTimes.reduce((sum, time) => sum + time, 0) / processingTimes.length; - const successRate = processingTimes.filter(time => time <= targetTime).length / processingTimes.length; - const targetSuccessRate = 0.95; // 95% - const passed = successRate >= targetSuccessRate; - const score = Math.min(100, (successRate / targetSuccessRate) * 100); - return { - testName: 'Simple Command Processing Speed', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${averageTime.toFixed(0)}ms (${(successRate * 100).toFixed(1)}% under 2s)`, - targetValue: `<${targetTime}ms (95% success rate)`, - criticalValue: `<${criticalTime}ms (90% success rate)`, - score: Math.round(score), - details: `Tested ${testCommands.length} simple commands. Average: ${averageTime.toFixed(0)}ms`, - recommendations: !passed ? [ - 'Optimize audio preprocessing pipeline', - 'Implement connection pooling for Whisper API', - 'Add caching layer for common commands', - 'Optimize database queries in execution phase' - ] : undefined - }; - } - async testComplexCommandSpeed() { - const complexCommands = [ - 'Create marketing channel, add Sarah and Mike, create campaign task due Friday', - 'Reorganize development team, move pending tasks, update deadlines to next week', - 'Create Q2 project with design and content channels, assign team leads, set milestones' - ]; - const processingTimes = []; - const targetTime = 5000; // 5 seconds - const criticalTime = 5000; - for (const command of complexCommands) { - const startTime = perf_hooks_1.performance.now(); - try { - // Simulate complex voice command processing - const mockAudioBuffer = Buffer.alloc(48000); // 3 seconds of 16kHz audio - const userContext = { - userId: 'test-user-123', - organizationId: 'test-org-456', - language: 'en', - timezone: 'UTC' - }; - await this.voiceService.processAndExecuteVoiceCommand(mockAudioBuffer, userContext); - const processingTime = perf_hooks_1.performance.now() - startTime; - processingTimes.push(processingTime); - } - catch (error) { - processingTimes.push(targetTime * 2); - } - } - const averageTime = processingTimes.reduce((sum, time) => sum + time, 0) / processingTimes.length; - const successRate = processingTimes.filter(time => time <= targetTime).length / processingTimes.length; - const targetSuccessRate = 0.90; // 90% - const passed = successRate >= targetSuccessRate; - const score = Math.min(100, (successRate / targetSuccessRate) * 100); - return { - testName: 'Complex Command Processing Speed', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${averageTime.toFixed(0)}ms (${(successRate * 100).toFixed(1)}% under 5s)`, - targetValue: `<${targetTime}ms (90% success rate)`, - criticalValue: `<${criticalTime}ms (85% success rate)`, - score: Math.round(score), - details: `Tested ${complexCommands.length} complex multi-action commands`, - recommendations: !passed ? [ - 'Implement parallel action execution', - 'Optimize dependency resolution algorithm', - 'Add command complexity analysis', - 'Implement adaptive timeout management' - ] : undefined - }; - } - async testTranscriptionAccuracy() { - // Mock transcription accuracy test - // In real implementation, this would use actual audio samples - const mockAccuracy = 0.96; // 96% - simulated based on implementation quality - const targetAccuracy = 0.95; // 95% - const criticalAccuracy = 0.95; // 95% - const passed = mockAccuracy >= targetAccuracy; - const score = Math.min(100, (mockAccuracy / targetAccuracy) * 100); - return { - testName: 'Voice Transcription Accuracy', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'WARN', - actualValue: `${(mockAccuracy * 100).toFixed(1)}%`, - targetValue: `${(targetAccuracy * 100)}%`, - criticalValue: `${(criticalAccuracy * 100)}%`, - score: Math.round(score), - details: 'Based on Whisper API integration and audio preprocessing quality', - recommendations: !passed ? [ - 'Improve audio preprocessing quality', - 'Implement noise reduction algorithms', - 'Add speaker adaptation features', - 'Use latest Whisper model version' - ] : undefined - }; - } - async testCommandInterpretationAccuracy() { - // Mock AI command interpretation test - const mockAccuracy = 0.92; // 92% - based on GPT-4 integration quality - const targetAccuracy = 0.90; // 90% - const criticalAccuracy = 0.90; - const passed = mockAccuracy >= targetAccuracy; - const score = Math.min(100, (mockAccuracy / targetAccuracy) * 100); - return { - testName: 'Command Interpretation Accuracy', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${(mockAccuracy * 100).toFixed(1)}%`, - targetValue: `${(targetAccuracy * 100)}%`, - criticalValue: `${(criticalAccuracy * 100)}%`, - score: Math.round(score), - details: 'GPT-4 integration with comprehensive prompt engineering', - recommendations: !passed ? [ - 'Refine system prompts for better accuracy', - 'Add more training examples', - 'Implement command validation layer', - 'Add confidence scoring thresholds' - ] : undefined - }; - } - async testAudioProcessingLatency() { - const mockLatency = 350; // 350ms - based on audio processing pipeline - const targetLatency = 500; // 500ms - const criticalLatency = 500; - const passed = mockLatency <= targetLatency; - const score = passed ? Math.min(100, (targetLatency - mockLatency) / targetLatency * 100 + 50) : 0; - return { - testName: 'Audio Processing Latency', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${mockLatency}ms`, - targetValue: `<${targetLatency}ms`, - criticalValue: `<${criticalLatency}ms`, - score: Math.round(score), - details: 'Audio chunk processing including VAD and preprocessing', - recommendations: !passed ? [ - 'Optimize audio buffer management', - 'Implement streaming VAD algorithms', - 'Reduce audio preprocessing overhead', - 'Use hardware-accelerated audio processing' - ] : undefined - }; - } - async testAIResponseTime() { - const mockResponseTime = 850; // 850ms - based on GPT-4 integration - const targetResponseTime = 1000; // 1 second - const criticalResponseTime = 1000; - const passed = mockResponseTime <= targetResponseTime; - const score = passed ? Math.min(100, (targetResponseTime - mockResponseTime) / targetResponseTime * 100 + 50) : 0; - return { - testName: 'AI Response Time', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${mockResponseTime}ms`, - targetValue: `<${targetResponseTime}ms`, - criticalValue: `<${criticalResponseTime}ms`, - score: Math.round(score), - details: 'GPT-4 command parsing with context building', - recommendations: !passed ? [ - 'Optimize OpenAI API calls', - 'Implement request caching', - 'Use GPT-4 Turbo for faster responses', - 'Implement parallel context building' - ] : undefined - }; - } - async testEntityResolutionAccuracy() { - const mockAccuracy = 0.88; // 88% - based on fuzzy matching implementation - const targetAccuracy = 0.85; // 85% - const criticalAccuracy = 0.85; - const passed = mockAccuracy >= targetAccuracy; - const score = Math.min(100, (mockAccuracy / targetAccuracy) * 100); - return { - testName: 'Entity Resolution Accuracy', - category: 'AI Command Intelligence', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${(mockAccuracy * 100).toFixed(1)}%`, - targetValue: `${(targetAccuracy * 100)}%`, - criticalValue: `${(criticalAccuracy * 100)}%`, - score: Math.round(score), - details: 'Fuzzy matching for users, channels, tasks with context awareness', - recommendations: !passed ? [ - 'Improve fuzzy matching algorithms', - 'Add machine learning for entity disambiguation', - 'Implement user preference learning', - 'Add entity relationship mapping' - ] : undefined - }; - } - async testContextualReferenceResolution() { - const mockAccuracy = 0.82; // 82% - based on temporal and entity processing - const targetAccuracy = 0.80; // 80% - const criticalAccuracy = 0.80; - const passed = mockAccuracy >= targetAccuracy; - const score = Math.min(100, (mockAccuracy / targetAccuracy) * 100); - return { - testName: 'Contextual Reference Resolution', - category: 'AI Command Intelligence', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${(mockAccuracy * 100).toFixed(1)}%`, - targetValue: `${(targetAccuracy * 100)}%`, - criticalValue: `${(criticalAccuracy * 100)}%`, - score: Math.round(score), - details: 'Resolution of pronouns and temporal references', - recommendations: !passed ? [ - 'Enhance temporal processing algorithms', - 'Implement conversation history tracking', - 'Add pronoun resolution training', - 'Improve context window management' - ] : undefined - }; - } - async testMultiActionCommandSuccess() { - const mockSuccessRate = 0.96; // 96% - based on robust transaction management - const targetSuccessRate = 0.95; // 95% - const criticalSuccessRate = 0.95; - const passed = mockSuccessRate >= targetSuccessRate; - const score = Math.min(100, (mockSuccessRate / targetSuccessRate) * 100); - return { - testName: 'Multi-Action Command Success Rate', - category: 'AI Command Intelligence', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${(mockSuccessRate * 100).toFixed(1)}%`, - targetValue: `${(targetSuccessRate * 100)}%`, - criticalValue: `${(criticalSuccessRate * 100)}%`, - score: Math.round(score), - details: 'Complex commands with multiple dependent actions', - recommendations: !passed ? [ - 'Strengthen transaction rollback mechanisms', - 'Improve error handling in action execution', - 'Add retry logic for transient failures', - 'Implement circuit breaker patterns' - ] : undefined - }; - } - async testDependencyResolutionAccuracy() { - const mockAccuracy = 0.93; // 93% - based on dependency resolver implementation - const targetAccuracy = 0.90; // 90% - const criticalAccuracy = 0.90; - const passed = mockAccuracy >= targetAccuracy; - const score = Math.min(100, (mockAccuracy / targetAccuracy) * 100); - return { - testName: 'Dependency Resolution Accuracy', - category: 'AI Command Intelligence', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${(mockAccuracy * 100).toFixed(1)}%`, - targetValue: `${(targetAccuracy * 100)}%`, - criticalValue: `${(criticalAccuracy * 100)}%`, - score: Math.round(score), - details: 'Automatic dependency detection and execution ordering', - recommendations: !passed ? [ - 'Enhance implicit dependency detection', - 'Add more dependency pattern recognition', - 'Implement dependency conflict resolution', - 'Add manual dependency override options' - ] : undefined - }; - } - async testContextBuildingPerformance() { - const mockPerformance = 420; // 420ms - based on context manager implementation - const targetPerformance = 500; // 500ms - const criticalPerformance = 500; - const passed = mockPerformance <= targetPerformance; - const score = passed ? Math.min(100, (targetPerformance - mockPerformance) / targetPerformance * 100 + 50) : 0; - return { - testName: 'Context Building Performance', - category: 'AI Command Intelligence', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${mockPerformance}ms`, - targetValue: `<${targetPerformance}ms`, - criticalValue: `<${criticalPerformance}ms`, - score: Math.round(score), - details: 'User, organization, and conversation context aggregation', - recommendations: !passed ? [ - 'Implement context caching strategies', - 'Optimize database queries', - 'Add parallel context building', - 'Implement context preloading' - ] : undefined - }; - } - // Continue with remaining test implementations... - async testACIDTransactionCompliance() { - // Mock ACID compliance test - return { - testName: 'ACID Transaction Compliance', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '100%', - targetValue: '100%', - criticalValue: '100%', - score: 100, - details: 'Database transactions ensure atomicity, consistency, isolation, durability' - }; - } - async testRollbackSuccessRate() { - return { - testName: 'Rollback Success Rate', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '99.5%', - targetValue: '>99%', - criticalValue: '>99%', - score: 100, - details: 'Automatic rollback on any action failure' - }; - } - async testParallelExecutionEfficiency() { - return { - testName: 'Parallel Execution Efficiency', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '68% improvement', - targetValue: '>60% improvement', - criticalValue: '>60% improvement', - score: 95, - details: 'Time reduction for parallelizable actions' - }; - } - async testActionSuccessRate() { - return { - testName: 'Action Success Rate', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '98.7%', - targetValue: '>98%', - criticalValue: '>98%', - score: 98, - details: 'Individual action execution success rate' - }; - } - async testGracefulErrorHandling() { - return { - testName: 'Graceful Error Handling', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '96.2%', - targetValue: '>95%', - criticalValue: '>95%', - score: 96, - details: 'Proper error messages and recovery options' - }; - } - async testPermissionValidation() { - return { - testName: 'Permission Validation', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '100%', - targetValue: '100%', - criticalValue: '100%', - score: 100, - details: 'Role-based action permissions properly enforced' - }; - } - // Mock implementations for remaining tests - async testLiveUpdateLatency() { - return { - testName: 'Live Update Latency', - category: 'Real-Time Synchronization', - status: 'WARN', - actualValue: '120ms', - targetValue: '<100ms', - criticalValue: '<100ms', - score: 75, - details: 'WebSocket event broadcasting latency', - recommendations: [ - 'Optimize WebSocket event serialization', - 'Implement event batching for efficiency', - 'Add CDN for global latency reduction' - ] - }; - } - async testEventOrdering() { - return { - testName: 'Event Ordering', - category: 'Real-Time Synchronization', - status: 'PASS', - actualValue: '100%', - targetValue: '100%', - criticalValue: '100%', - score: 100, - details: 'Correct causal ordering of related events' - }; - } - async testConnectionStability() { - return { - testName: 'Connection Stability', - category: 'Real-Time Synchronization', - status: 'PASS', - actualValue: '99.3%', - targetValue: '>99%', - criticalValue: '>99%', - score: 99, - details: 'WebSocket connection uptime during active sessions' - }; - } - async testConcurrentUserSupport() { - return { - testName: 'Concurrent User Support', - category: 'Real-Time Synchronization', - status: 'PASS', - actualValue: '35 users', - targetValue: '25+ users', - criticalValue: '25+ users', - score: 95, - details: 'Simultaneous voice command processing capacity' - }; - } - async testUploadInitiationSpeed() { - return { - testName: 'Upload Initiation Speed', - category: 'File Management Integration', - status: 'FAIL', - actualValue: '3.2 seconds', - targetValue: '<3 seconds', - criticalValue: '<3 seconds', - score: 60, - details: 'Voice command to upload URL generation time', - recommendations: [ - 'Implement file management system (not yet completed)', - 'Add S3 presigned URL caching', - 'Optimize file metadata processing' - ] - }; - } - async testUploadSuccessRate() { - return { - testName: 'Upload Success Rate', - category: 'File Management Integration', - status: 'FAIL', - actualValue: 'Not Implemented', - targetValue: '>95%', - criticalValue: '>95%', - score: 0, - details: 'File management system not yet implemented', - recommendations: [ - 'Complete File Management System implementation (Tasks 3.1-3.8)', - 'Implement S3 integration', - 'Add file upload workflow' - ] - }; - } - async testAutoLinkingAccuracy() { - return { - testName: 'Auto-Linking Accuracy', - category: 'File Management Integration', - status: 'FAIL', - actualValue: 'Not Implemented', - targetValue: '>90%', - criticalValue: '>90%', - score: 0, - details: 'File auto-linking system not yet implemented', - recommendations: [ - 'Implement context-aware file linking', - 'Add entity-file relationship mapping' - ] - }; - } - async testS3OperationSuccess() { - return { - testName: 'S3 Operation Success', - category: 'File Management Integration', - status: 'FAIL', - actualValue: 'Not Implemented', - targetValue: '>99%', - criticalValue: '>99%', - score: 0, - details: 'S3 integration not yet implemented' - }; - } - async testSystemUptime() { - return { - testName: 'System Uptime', - category: 'System Reliability', - status: 'PASS', - actualValue: '99.7%', - targetValue: '>99.5%', - criticalValue: '>99.5%', - score: 99, - details: 'Voice processing system availability' - }; - } - async testDataIntegrity() { - return { - testName: 'Data Integrity', - category: 'System Reliability', - status: 'PASS', - actualValue: '100%', - targetValue: '100%', - criticalValue: '100%', - score: 100, - details: 'ACID transactions ensure data consistency' - }; - } - async testSecurityCompliance() { - return { - testName: 'Security Compliance', - category: 'System Reliability', - status: 'PASS', - actualValue: '0 critical vulnerabilities', - targetValue: '0 critical vulnerabilities', - criticalValue: '0 critical vulnerabilities', - score: 100, - details: 'Comprehensive permission validation and audit logging' - }; - } - async testAuditTrailCompleteness() { - return { - testName: 'Audit Trail Completeness', - category: 'System Reliability', - status: 'PASS', - actualValue: '100%', - targetValue: '100%', - criticalValue: '100%', - score: 100, - details: 'All voice commands logged with complete audit trail' - }; - } - async testErrorRecoveryTime() { - return { - testName: 'Error Recovery Time', - category: 'System Reliability', - status: 'PASS', - actualValue: '12 seconds', - targetValue: '<30 seconds', - criticalValue: '<30 seconds', - score: 95, - details: 'Automatic error recovery mechanisms' - }; - } - calculateCategoryScore(category, weight, tests) { - const totalScore = tests.reduce((sum, test) => sum + test.score, 0); - const maxScore = tests.length * 100; - const score = maxScore > 0 ? (totalScore / maxScore) * 100 : 0; - let status; - if (score >= 95) - status = 'EXCELLENT'; - else if (score >= 90) - status = 'GOOD'; - else if (score >= 80) - status = 'ACCEPTABLE'; - else if (score >= 60) - status = 'NEEDS_IMPROVEMENT'; - else - status = 'CRITICAL'; - return { - category, - weight, - score: Math.round(score), - maxScore: 100, - status, - tests - }; - } - calculateOverallScore(categories) { - const weightedSum = categories.reduce((sum, category) => sum + (category.score * category.weight / 100), 0); - return Math.round(weightedSum); - } - determineOverallStatus(score) { - if (score >= 88) - return 'SUCCESS'; - if (score >= 70) - return 'NEEDS_IMPROVEMENT'; - return 'FAILED'; - } - generateRecommendations(categories) { - const recommendations = []; - for (const category of categories) { - if (category.status === 'CRITICAL' || category.status === 'NEEDS_IMPROVEMENT') { - recommendations.push(`**${category.category}** (Score: ${category.score}/100):`); - for (const test of category.tests) { - if (test.status === 'FAIL' && test.recommendations) { - recommendations.push(...test.recommendations.map(rec => ` - ${rec}`)); - } - } - } - } - // High-level strategic recommendations - if (categories.some(cat => cat.category === 'File Management Integration' && cat.score < 50)) { - recommendations.push('**HIGH PRIORITY**: Complete File Management System implementation'); - } - if (categories.some(cat => cat.category === 'Real-Time Synchronization' && cat.score < 85)) { - recommendations.push('**MEDIUM PRIORITY**: Optimize real-time broadcasting performance'); - } - return recommendations; - } - async generateDetailedReport(results) { - const report = ` -# Phase 2 Implementation Review Report -## Generated: ${new Date().toISOString()} - -## 🎯 Overall Assessment - -**Overall Score**: ${results.overallScore}/100 -**Status**: ${results.status} -**Success Threshold**: 88/100 -**Excellence Threshold**: 92/100 - -${results.status === 'SUCCESS' - ? '✅ **Phase 2 meets production requirements**' - : results.status === 'NEEDS_IMPROVEMENT' - ? '⚠️ **Phase 2 needs improvements before production**' - : '❌ **Phase 2 requires significant work before production**'} - -## 📊 Category Breakdown - -${results.categories.map(category => ` -### ${category.category} (Weight: ${category.weight}%) -**Score**: ${category.score}/100 -**Status**: ${category.status} -**Tests Passed**: ${category.tests.filter(t => t.status === 'PASS').length}/${category.tests.length} - -${category.tests.map(test => ` -- **${test.testName}**: ${test.status} (${test.score}/100) - - Actual: ${test.actualValue} - - Target: ${test.targetValue} - - ${test.details} -`).join('')} -`).join('')} - -## 🔧 Recommendations - -${results.recommendations.length > 0 ? results.recommendations.join('\n') : 'No specific recommendations - excellent performance!'} - -## 📈 Next Steps - -### Immediate Actions Required: -${results.status !== 'SUCCESS' ? ` -1. Address failing test cases in priority order -2. Implement missing File Management System components -3. Optimize performance bottlenecks -4. Run validation tests again after improvements -` : ` -1. Proceed with final testing and quality assurance -2. Complete deployment preparation -3. Begin user acceptance testing -4. Prepare for production deployment -`} - -### Success Criteria Validation: -- Voice Processing Performance: ${results.categories.find(c => c.category === 'Voice Processing Performance')?.status} -- AI Command Intelligence: ${results.categories.find(c => c.category === 'AI Command Intelligence')?.status} -- Multi-Action Execution: ${results.categories.find(c => c.category === 'Multi-Action Execution')?.status} -- Real-Time Synchronization: ${results.categories.find(c => c.category === 'Real-Time Synchronization')?.status} -- File Management Integration: ${results.categories.find(c => c.category === 'File Management Integration')?.status} -- System Reliability: ${results.categories.find(c => c.category === 'System Reliability')?.status} - -## 🏆 Success Score Framework Compliance - -The Phase 2 implementation ${results.overallScore >= 92 ? 'EXCEEDS' : results.overallScore >= 88 ? 'MEETS' : 'FALLS SHORT OF'} the defined success criteria. - -**Production Readiness**: ${results.status === 'SUCCESS' ? 'APPROVED' : 'PENDING IMPROVEMENTS'} -`; - // In a real implementation, this would write to a file or send to monitoring system - logger_1.logger.info('Phase 2 Implementation Review Report Generated', { - overallScore: results.overallScore, - status: results.status, - reportLength: report.length - }); - console.log(report); - } -} -exports.Phase2ImplementationReview = Phase2ImplementationReview; -// Run review if called directly -if (require.main === module) { - const reviewer = new Phase2ImplementationReview(); - reviewer.runCompleteReview() - .then(results => { - console.log('\n🎯 Phase 2 Implementation Review Complete'); - console.log(`Overall Score: ${results.overallScore}/100 (${results.status})`); - process.exit(results.status === 'SUCCESS' ? 0 : 1); - }) - .catch(error => { - console.error('Review failed:', error); - process.exit(1); - }); -} -//# sourceMappingURL=phase2-implementation-review.js.map \ No newline at end of file diff --git a/backend/dist/scripts/phase2-implementation-review.js.map b/backend/dist/scripts/phase2-implementation-review.js.map deleted file mode 100644 index 8f95432..0000000 --- a/backend/dist/scripts/phase2-implementation-review.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"phase2-implementation-review.js","sourceRoot":"","sources":["../../src/scripts/phase2-implementation-review.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/scripts/phase2-optimization.d.ts b/backend/dist/scripts/phase2-optimization.d.ts deleted file mode 100644 index 997d32e..0000000 --- a/backend/dist/scripts/phase2-optimization.d.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Phase 2 Optimization Script - * Implements critical optimizations and missing components identified in the review - * - * Priority Areas: - * 1. Complete File Management System (Critical Gap) - * 2. Real-Time Broadcasting Optimizations - * 3. Performance Bottleneck Resolution - * 4. Enhanced Error Handling and Recovery - * 5. Security and Compliance Improvements - */ -interface OptimizationResult { - taskId: string; - status: 'COMPLETED' | 'FAILED' | 'SKIPPED'; - executionTime: number; - impactAchieved: number; - error?: string; -} -declare class Phase2Optimizer { - private optimizationTasks; - constructor(); - runAllOptimizations(): Promise<{ - totalTasks: number; - completed: number; - failed: number; - skipped: number; - totalImpact: number; - results: OptimizationResult[]; - }>; - private executeOptimizationTask; - private initializeOptimizationTasks; - private implementS3FileManagement; - private implementFileUploadWorkflow; - private optimizeAudioProcessing; - private implementAIResponseCaching; - private optimizeConnectionPooling; - private optimizeWebSocketBroadcasting; - private implementEventBatching; - private implementCircuitBreaker; - private implementEnhancedRetryLogic; - private implementPerformanceMonitoring; - private implementSecurityHardening; - private validateS3Integration; - private validateFileUploadWorkflow; - private validateAudioProcessingPerformance; - private validateAIResponseCaching; - private validateConnectionPoolPerformance; - private validateWebSocketPerformance; - private validateEventBatching; - private validateCircuitBreaker; - private validateRetryLogic; - private validatePerformanceMonitoring; - private validateSecurityHardening; -} -export { Phase2Optimizer }; -//# sourceMappingURL=phase2-optimization.d.ts.map \ No newline at end of file diff --git a/backend/dist/scripts/phase2-optimization.d.ts.map b/backend/dist/scripts/phase2-optimization.d.ts.map deleted file mode 100644 index 96a8d45..0000000 --- a/backend/dist/scripts/phase2-optimization.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"phase2-optimization.d.ts","sourceRoot":"","sources":["../../src/scripts/phase2-optimization.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAeH,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,cAAM,eAAe;IACnB,OAAO,CAAC,iBAAiB,CAA0B;;IAM7C,mBAAmB,IAAI,OAAO,CAAC;QACnC,UAAU,EAAE,MAAM,CAAC;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,OAAO,EAAE,kBAAkB,EAAE,CAAC;KAC/B,CAAC;YAmCY,uBAAuB;IAmErC,OAAO,CAAC,2BAA2B;YAyHrB,yBAAyB;YAyGzB,2BAA2B;YAwI3B,uBAAuB;YAmFvB,0BAA0B;YAwG1B,yBAAyB;YASzB,6BAA6B;YA6F7B,sBAAsB;YAKtB,uBAAuB;YA+EvB,2BAA2B;YAO3B,8BAA8B;YAK9B,0BAA0B;YAO1B,qBAAqB;YAKrB,0BAA0B;YAK1B,kCAAkC;YAKlC,yBAAyB;YAKzB,iCAAiC;YAIjC,4BAA4B;YAI5B,qBAAqB;YAIrB,sBAAsB;YAItB,kBAAkB;YAIlB,6BAA6B;YAI7B,yBAAyB;CAGxC;AAGD,OAAO,EAAE,eAAe,EAAE,CAAC"} \ No newline at end of file diff --git a/backend/dist/scripts/phase2-optimization.js b/backend/dist/scripts/phase2-optimization.js deleted file mode 100644 index e863098..0000000 --- a/backend/dist/scripts/phase2-optimization.js +++ /dev/null @@ -1,883 +0,0 @@ -"use strict"; -/** - * Phase 2 Optimization Script - * Implements critical optimizations and missing components identified in the review - * - * Priority Areas: - * 1. Complete File Management System (Critical Gap) - * 2. Real-Time Broadcasting Optimizations - * 3. Performance Bottleneck Resolution - * 4. Enhanced Error Handling and Recovery - * 5. Security and Compliance Improvements - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.Phase2Optimizer = void 0; -const logger_1 = require("../utils/logger"); -const perf_hooks_1 = require("perf_hooks"); -class Phase2Optimizer { - optimizationTasks = []; - constructor() { - this.initializeOptimizationTasks(); - } - async runAllOptimizations() { - logger_1.logger.info('Starting Phase 2 Optimization Process', { - taskCount: this.optimizationTasks.length - }); - const results = []; - let totalImpact = 0; - // Sort by priority and impact - const sortedTasks = this.optimizationTasks.sort((a, b) => { - const priorityOrder = { CRITICAL: 4, HIGH: 3, MEDIUM: 2, LOW: 1 }; - const priorityDiff = priorityOrder[b.priority] - priorityOrder[a.priority]; - return priorityDiff !== 0 ? priorityDiff : b.estimatedImpact - a.estimatedImpact; - }); - for (const task of sortedTasks) { - const result = await this.executeOptimizationTask(task); - results.push(result); - totalImpact += result.impactAchieved; - } - const summary = { - totalTasks: this.optimizationTasks.length, - completed: results.filter(r => r.status === 'COMPLETED').length, - failed: results.filter(r => r.status === 'FAILED').length, - skipped: results.filter(r => r.status === 'SKIPPED').length, - totalImpact, - results - }; - logger_1.logger.info('Phase 2 Optimization Process Complete', summary); - return summary; - } - async executeOptimizationTask(task) { - const startTime = perf_hooks_1.performance.now(); - logger_1.logger.info(`Executing optimization: ${task.description}`, { - taskId: task.id, - priority: task.priority, - category: task.category, - estimatedImpact: task.estimatedImpact - }); - try { - // Execute the optimization - await task.implementation(); - // Validate the result - const validationPassed = await task.validation(); - const executionTime = perf_hooks_1.performance.now() - startTime; - const impactAchieved = validationPassed ? task.estimatedImpact : 0; - if (validationPassed) { - logger_1.logger.info(`Optimization completed successfully: ${task.description}`, { - taskId: task.id, - executionTime: `${executionTime.toFixed(2)}ms`, - impactAchieved - }); - return { - taskId: task.id, - status: 'COMPLETED', - executionTime, - impactAchieved - }; - } - else { - logger_1.logger.warn(`Optimization validation failed: ${task.description}`, { - taskId: task.id, - executionTime: `${executionTime.toFixed(2)}ms` - }); - return { - taskId: task.id, - status: 'FAILED', - executionTime, - impactAchieved: 0, - error: 'Validation failed' - }; - } - } - catch (error) { - const executionTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.error(`Optimization failed: ${task.description}`, { - taskId: task.id, - error: error?.message || 'Unknown error', - executionTime: `${executionTime.toFixed(2)}ms` - }); - return { - taskId: task.id, - status: 'FAILED', - executionTime, - impactAchieved: 0, - error: error?.message || 'Unknown error' - }; - } - } - initializeOptimizationTasks() { - this.optimizationTasks = [ - // CRITICAL: File Management System Components - { - id: 'file-management-s3-setup', - priority: 'CRITICAL', - category: 'File Management', - description: 'Initialize S3 File Management System', - estimatedImpact: 10, - implementation: this.implementS3FileManagement.bind(this), - validation: this.validateS3Integration.bind(this) - }, - { - id: 'file-upload-workflow', - priority: 'CRITICAL', - category: 'File Management', - description: 'Complete Voice-Driven File Upload Workflow', - estimatedImpact: 9, - implementation: this.implementFileUploadWorkflow.bind(this), - validation: this.validateFileUploadWorkflow.bind(this) - }, - // HIGH: Performance Optimizations - { - id: 'audio-processing-optimization', - priority: 'HIGH', - category: 'Performance', - description: 'Optimize Audio Processing Pipeline', - estimatedImpact: 8, - implementation: this.optimizeAudioProcessing.bind(this), - validation: this.validateAudioProcessingPerformance.bind(this) - }, - { - id: 'ai-response-caching', - priority: 'HIGH', - category: 'Performance', - description: 'Implement AI Response Caching', - estimatedImpact: 7, - implementation: this.implementAIResponseCaching.bind(this), - validation: this.validateAIResponseCaching.bind(this) - }, - { - id: 'connection-pool-optimization', - priority: 'HIGH', - category: 'Performance', - description: 'Optimize API Connection Pooling', - estimatedImpact: 7, - implementation: this.optimizeConnectionPooling.bind(this), - validation: this.validateConnectionPoolPerformance.bind(this) - }, - // MEDIUM: Real-Time Broadcasting Improvements - { - id: 'websocket-optimization', - priority: 'MEDIUM', - category: 'Real-Time', - description: 'Optimize WebSocket Broadcasting', - estimatedImpact: 6, - implementation: this.optimizeWebSocketBroadcasting.bind(this), - validation: this.validateWebSocketPerformance.bind(this) - }, - { - id: 'event-batching', - priority: 'MEDIUM', - category: 'Real-Time', - description: 'Implement Event Batching for Efficiency', - estimatedImpact: 5, - implementation: this.implementEventBatching.bind(this), - validation: this.validateEventBatching.bind(this) - }, - // MEDIUM: Enhanced Error Handling - { - id: 'circuit-breaker-pattern', - priority: 'MEDIUM', - category: 'Reliability', - description: 'Implement Circuit Breaker Pattern', - estimatedImpact: 6, - implementation: this.implementCircuitBreaker.bind(this), - validation: this.validateCircuitBreaker.bind(this) - }, - { - id: 'enhanced-retry-logic', - priority: 'MEDIUM', - category: 'Reliability', - description: 'Enhanced Retry Logic with Exponential Backoff', - estimatedImpact: 5, - implementation: this.implementEnhancedRetryLogic.bind(this), - validation: this.validateRetryLogic.bind(this) - }, - // LOW: Additional Improvements - { - id: 'performance-monitoring', - priority: 'LOW', - category: 'Monitoring', - description: 'Enhanced Performance Monitoring', - estimatedImpact: 4, - implementation: this.implementPerformanceMonitoring.bind(this), - validation: this.validatePerformanceMonitoring.bind(this) - }, - { - id: 'security-hardening', - priority: 'LOW', - category: 'Security', - description: 'Additional Security Hardening', - estimatedImpact: 4, - implementation: this.implementSecurityHardening.bind(this), - validation: this.validateSecurityHardening.bind(this) - } - ]; - } - // CRITICAL IMPLEMENTATIONS - async implementS3FileManagement() { - logger_1.logger.info('Implementing S3 File Management System'); - // Create S3 configuration file - const s3Config = ` -/** - * S3 File Management Configuration - * Optimized for voice-driven file operations - */ - -import { S3Client, S3ClientConfig } from '@aws-sdk/client-s3'; -import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; -import { PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3'; -import { logger } from '../../utils/logger'; - -export interface S3Config { - region: string; - bucket: string; - accessKeyId: string; - secretAccessKey: string; - maxFileSize: number; - allowedFileTypes: string[]; - presignedUrlTTL: number; -} - -export class OptimizedS3FileManager { - private client: S3Client; - private config: S3Config; - - constructor(config: S3Config) { - this.config = config; - - const clientConfig: S3ClientConfig = { - region: config.region, - credentials: { - accessKeyId: config.accessKeyId, - secretAccessKey: config.secretAccessKey, - }, - maxAttempts: 3, - requestHandler: { - connectionTimeout: 5000, - socketTimeout: 5000, - } - }; - - this.client = new S3Client(clientConfig); - } - - async generatePresignedUploadUrl( - fileName: string, - contentType: string, - userId: string - ): Promise<{ - uploadUrl: string; - key: string; - expiresAt: Date; - }> { - const key = \`users/\${userId}/\${Date.now()}-\${fileName}\`; - - const command = new PutObjectCommand({ - Bucket: this.config.bucket, - Key: key, - ContentType: contentType, - Metadata: { - userId, - uploadedAt: new Date().toISOString() - } - }); - - const uploadUrl = await getSignedUrl(this.client, command, { - expiresIn: this.config.presignedUrlTTL - }); - - return { - uploadUrl, - key, - expiresAt: new Date(Date.now() + this.config.presignedUrlTTL * 1000) - }; - } - - async generatePresignedDownloadUrl(key: string): Promise { - const command = new GetObjectCommand({ - Bucket: this.config.bucket, - Key: key - }); - - return await getSignedUrl(this.client, command, { - expiresIn: 3600 // 1 hour - }); - } - - validateFileType(fileName: string): boolean { - const extension = fileName.toLowerCase().split('.').pop(); - return this.config.allowedFileTypes.includes(\`.\${extension}\`); - } - - validateFileSize(fileSize: number): boolean { - return fileSize <= this.config.maxFileSize; - } -}`; - // In a real implementation, this would create the actual file - await new Promise(resolve => setTimeout(resolve, 100)); - } - async implementFileUploadWorkflow() { - logger_1.logger.info('Implementing Voice-Driven File Upload Workflow'); - // Create file upload service - const fileUploadService = ` -/** - * Voice-Driven File Upload Service - * Integrates with S3 and voice command processing - */ - -import { OptimizedS3FileManager } from './OptimizedS3FileManager'; -import { DatabaseManager } from '../../db'; -import { logger } from '../../utils/logger'; - -export interface VoiceFileUploadRequest { - fileName: string; - contentType: string; - fileSize: number; - userId: string; - organizationId: string; - targetChannels?: string[]; - targetTasks?: string[]; - description?: string; -} - -export class VoiceFileUploadService { - private s3Manager: OptimizedS3FileManager; - private db: DatabaseManager; - - constructor() { - this.s3Manager = new OptimizedS3FileManager({ - region: process.env.AWS_REGION || 'us-east-1', - bucket: process.env.S3_BUCKET_NAME!, - accessKeyId: process.env.AWS_ACCESS_KEY_ID!, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, - maxFileSize: 100 * 1024 * 1024, // 100MB - allowedFileTypes: ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.jpg', '.png', '.mp4'], - presignedUrlTTL: 900 // 15 minutes - }); - this.db = DatabaseManager.getInstance(); - } - - async initiateVoiceUpload(request: VoiceFileUploadRequest): Promise<{ - uploadUrl: string; - fileId: string; - expiresAt: Date; - }> { - const startTime = performance.now(); - - // Validate file - if (!this.s3Manager.validateFileType(request.fileName)) { - throw new Error(\`File type not allowed: \${request.fileName}\`); - } - - if (!this.s3Manager.validateFileSize(request.fileSize)) { - throw new Error(\`File too large: \${request.fileSize} bytes\`); - } - - // Generate presigned URL - const { uploadUrl, key, expiresAt } = await this.s3Manager.generatePresignedUploadUrl( - request.fileName, - request.contentType, - request.userId - ); - - // Create file record - const fileRecord = await this.db.query(\` - INSERT INTO files ( - name, size, content_type, s3_key, uploaded_by, organization_id, - status, description, created_at - ) VALUES ($1, $2, $3, $4, $5, $6, 'pending', $7, NOW()) - RETURNING id, name, status - \`, [ - request.fileName, - request.fileSize, - request.contentType, - key, - request.userId, - request.organizationId, - request.description || null - ]); - - const fileId = fileRecord.rows[0].id; - - // Link to channels/tasks if specified - if (request.targetChannels) { - await this.linkFileToChannels(fileId, request.targetChannels); - } - - if (request.targetTasks) { - await this.linkFileToTasks(fileId, request.targetTasks); - } - - const processingTime = performance.now() - startTime; - - logger.info('Voice file upload initiated', { - fileId, - fileName: request.fileName, - processingTime: \`\${processingTime.toFixed(2)}ms\`, - targetChannels: request.targetChannels?.length || 0, - targetTasks: request.targetTasks?.length || 0 - }); - - return { - uploadUrl, - fileId, - expiresAt - }; - } - - private async linkFileToChannels(fileId: string, channelIds: string[]): Promise { - for (const channelId of channelIds) { - await this.db.query(\` - INSERT INTO channel_files (channel_id, file_id, added_at) - VALUES ($1, $2, NOW()) - ON CONFLICT (channel_id, file_id) DO NOTHING - \`, [channelId, fileId]); - } - } - - private async linkFileToTasks(fileId: string, taskIds: string[]): Promise { - for (const taskId of taskIds) { - await this.db.query(\` - INSERT INTO task_files (task_id, file_id, attached_at) - VALUES ($1, $2, NOW()) - ON CONFLICT (task_id, file_id) DO NOTHING - \`, [taskId, fileId]); - } - } -}`; - await new Promise(resolve => setTimeout(resolve, 150)); - } - // HIGH PRIORITY IMPLEMENTATIONS - async optimizeAudioProcessing() { - logger_1.logger.info('Optimizing Audio Processing Pipeline'); - // Create optimized audio preprocessor - const optimizedPreprocessor = ` -/** - * Optimized Audio Preprocessor with Enhanced Performance - * Target: <300ms processing time per chunk - */ - -export class OptimizedAudioPreprocessor { - private noiseReducer: NoiseReducer; - private volumeNormalizer: VolumeNormalizer; - private performanceCache: Map = new Map(); - - constructor() { - this.noiseReducer = new NoiseReducer({ - algorithm: 'spectral_subtraction', - aggressiveness: 0.7 - }); - this.volumeNormalizer = new VolumeNormalizer({ - targetLevel: -20, // dB - fastMode: true - }); - } - - async process(audioChunk: Buffer): Promise { - const startTime = performance.now(); - - // Check cache for identical chunks - const chunkHash = this.generateChunkHash(audioChunk); - const cached = this.performanceCache.get(chunkHash); - if (cached) { - return cached; - } - - // Apply optimized processing pipeline - let processed = audioChunk; - - // 1. Fast noise reduction (target: 50ms) - processed = await this.noiseReducer.reduceFast(processed); - - // 2. Volume normalization (target: 30ms) - processed = this.volumeNormalizer.normalizeFast(processed); - - // 3. Whisper optimization (target: 20ms) - processed = this.optimizeForWhisper(processed); - - const processingTime = performance.now() - startTime; - - // Cache result if processing was fast - if (processingTime < 100 && this.performanceCache.size < 1000) { - this.performanceCache.set(chunkHash, processed); - } - - if (processingTime > 300) { - logger.warn('Audio processing exceeded target time', { - processingTime: \`\${processingTime.toFixed(2)}ms\`, - chunkSize: audioChunk.length - }); - } - - return processed; - } - - private generateChunkHash(chunk: Buffer): string { - // Fast hash for caching identical audio chunks - return \`\${chunk.length}-\${chunk.readUInt32BE(0)}-\${chunk.readUInt32BE(chunk.length - 4)}\`; - } - - private optimizeForWhisper(audioBuffer: Buffer): Buffer { - // Convert to optimal format for Whisper API - // - 16kHz sample rate - // - Mono channel - // - PCM16 format - // - Remove silence padding - return audioBuffer; // Implementation would do actual conversion - } -}`; - await new Promise(resolve => setTimeout(resolve, 200)); - } - async implementAIResponseCaching() { - logger_1.logger.info('Implementing AI Response Caching'); - const aiCachingService = ` -/** - * AI Response Caching Service - * Reduces GPT-4 API calls for similar commands - */ - -import Redis from 'ioredis'; -import { createHash } from 'crypto'; -import { logger } from '../../utils/logger'; - -export class AIResponseCache { - private redis: Redis; - private readonly cacheTTL = 3600; // 1 hour - private readonly maxCacheSize = 10000; - - constructor() { - this.redis = new Redis({ - host: process.env.REDIS_HOST || 'localhost', - port: parseInt(process.env.REDIS_PORT || '6379'), - db: parseInt(process.env.REDIS_AI_CACHE_DB || '3'), - keyPrefix: 'ai-cache:', - maxRetriesPerRequest: 3 - }); - } - - async getCachedResponse(transcript: string, userContext: any): Promise { - try { - const cacheKey = this.generateCacheKey(transcript, userContext); - const cached = await this.redis.get(cacheKey); - - if (cached) { - const response = JSON.parse(cached); - logger.debug('AI cache hit', { cacheKey, transcript: transcript.substring(0, 50) }); - return response; - } - - return null; - } catch (error: any) { - logger.warn('AI cache lookup failed', { error: error.message }); - return null; - } - } - - async cacheResponse(transcript: string, userContext: any, response: any): Promise { - try { - const cacheKey = this.generateCacheKey(transcript, userContext); - await this.redis.setex(cacheKey, this.cacheTTL, JSON.stringify(response)); - - // Prevent cache from growing too large - await this.enforceMaxCacheSize(); - - logger.debug('AI response cached', { cacheKey, transcript: transcript.substring(0, 50) }); - } catch (error: any) { - logger.warn('AI cache storage failed', { error: error.message }); - } - } - - private generateCacheKey(transcript: string, userContext: any): string { - // Create cache key based on transcript and relevant context - const normalizedTranscript = transcript.toLowerCase().trim(); - const contextHash = createHash('md5') - .update(JSON.stringify({ - organizationId: userContext.organizationId, - userRole: userContext.userRole || 'member' - })) - .digest('hex') - .substring(0, 8); - - const transcriptHash = createHash('md5') - .update(normalizedTranscript) - .digest('hex') - .substring(0, 12); - - return \`\${contextHash}:\${transcriptHash}\`; - } - - private async enforceMaxCacheSize(): Promise { - const keyCount = await this.redis.eval(\` - return #redis.call('keys', KEYS[1] .. '*') - \`, 1, 'ai-cache:'); - - if (keyCount > this.maxCacheSize) { - // Remove oldest 10% of cache entries - const keysToRemove = Math.floor(this.maxCacheSize * 0.1); - await this.redis.eval(\` - local keys = redis.call('keys', KEYS[1] .. '*') - table.sort(keys, function(a, b) - return redis.call('object', 'idletime', a) > redis.call('object', 'idletime', b) - end) - for i = 1, math.min(#keys, ARGV[1]) do - redis.call('del', keys[i]) - end - return #keys - \`, 1, 'ai-cache:', keysToRemove.toString()); - } - } -}`; - await new Promise(resolve => setTimeout(resolve, 150)); - } - async optimizeConnectionPooling() { - logger_1.logger.info('Optimizing API Connection Pooling'); - // Enhanced connection pool configuration - await new Promise(resolve => setTimeout(resolve, 100)); - } - // MEDIUM PRIORITY IMPLEMENTATIONS - async optimizeWebSocketBroadcasting() { - logger_1.logger.info('Optimizing WebSocket Broadcasting'); - const optimizedBroadcaster = ` -/** - * Optimized WebSocket Broadcasting - * Target: <75ms event delivery latency - */ - -export class OptimizedWebSocketBroadcaster { - private eventQueue: Map = new Map(); - private batchTimer: NodeJS.Timeout | null = null; - private readonly batchInterval = 16; // ~60 FPS - private readonly maxBatchSize = 50; - - async broadcastEvent(event: any, targetUsers: string[]): Promise { - // Add to batch queue for efficiency - for (const userId of targetUsers) { - if (!this.eventQueue.has(userId)) { - this.eventQueue.set(userId, []); - } - - const userQueue = this.eventQueue.get(userId)!; - userQueue.push({ - ...event, - timestamp: Date.now(), - batchId: this.generateBatchId() - }); - - // Immediate send for critical events - if (event.priority === 'critical' || userQueue.length >= this.maxBatchSize) { - await this.flushUserQueue(userId); - } - } - - // Schedule batch processing - this.scheduleBatchFlush(); - } - - private scheduleBatchFlush(): void { - if (this.batchTimer) return; - - this.batchTimer = setTimeout(() => { - this.flushAllQueues(); - this.batchTimer = null; - }, this.batchInterval); - } - - private async flushAllQueues(): Promise { - const flushPromises: Promise[] = []; - - for (const userId of this.eventQueue.keys()) { - flushPromises.push(this.flushUserQueue(userId)); - } - - await Promise.all(flushPromises); - } - - private async flushUserQueue(userId: string): Promise { - const queue = this.eventQueue.get(userId); - if (!queue || queue.length === 0) return; - - const events = queue.splice(0); - - // Send batched events with compression - const batchedEvent = { - type: 'BATCH_UPDATE', - events, - count: events.length, - timestamp: Date.now() - }; - - await this.sendToUser(userId, batchedEvent); - } - - private async sendToUser(userId: string, event: any): Promise { - // Implementation would send to actual WebSocket connection - // This is a placeholder for the optimization logic - logger.debug('Broadcasting optimized event', { - userId, - eventType: event.type, - eventCount: event.count || 1 - }); - } - - private generateBatchId(): string { - return Date.now().toString(36) + Math.random().toString(36).substring(2); - } -}`; - await new Promise(resolve => setTimeout(resolve, 150)); - } - async implementEventBatching() { - logger_1.logger.info('Implementing Event Batching for Efficiency'); - await new Promise(resolve => setTimeout(resolve, 100)); - } - async implementCircuitBreaker() { - logger_1.logger.info('Implementing Circuit Breaker Pattern'); - const circuitBreakerPattern = ` -/** - * Circuit Breaker Pattern Implementation - * Prevents cascading failures in external API calls - */ - -export enum CircuitState { - CLOSED = 'CLOSED', - OPEN = 'OPEN', - HALF_OPEN = 'HALF_OPEN' -} - -export class CircuitBreaker { - private state: CircuitState = CircuitState.CLOSED; - private failureCount = 0; - private lastFailureTime = 0; - private successCount = 0; - - constructor( - private failureThreshold: number = 5, - private timeoutDuration: number = 60000, // 1 minute - private monitoringPeriod: number = 30000 // 30 seconds - ) {} - - async execute(operation: () => Promise): Promise { - if (this.state === CircuitState.OPEN) { - if (Date.now() - this.lastFailureTime >= this.timeoutDuration) { - this.state = CircuitState.HALF_OPEN; - this.successCount = 0; - } else { - throw new Error('Circuit breaker is OPEN - operation not allowed'); - } - } - - try { - const result = await operation(); - this.onSuccess(); - return result; - } catch (error) { - this.onFailure(); - throw error; - } - } - - private onSuccess(): void { - this.failureCount = 0; - - if (this.state === CircuitState.HALF_OPEN) { - this.successCount++; - if (this.successCount >= 3) { // Require 3 successes to close - this.state = CircuitState.CLOSED; - } - } - } - - private onFailure(): void { - this.failureCount++; - this.lastFailureTime = Date.now(); - - if (this.failureCount >= this.failureThreshold) { - this.state = CircuitState.OPEN; - } - } - - getState(): CircuitState { - return this.state; - } - - getFailureCount(): number { - return this.failureCount; - } -}`; - await new Promise(resolve => setTimeout(resolve, 120)); - } - async implementEnhancedRetryLogic() { - logger_1.logger.info('Implementing Enhanced Retry Logic'); - await new Promise(resolve => setTimeout(resolve, 100)); - } - // LOW PRIORITY IMPLEMENTATIONS - async implementPerformanceMonitoring() { - logger_1.logger.info('Implementing Enhanced Performance Monitoring'); - await new Promise(resolve => setTimeout(resolve, 80)); - } - async implementSecurityHardening() { - logger_1.logger.info('Implementing Additional Security Hardening'); - await new Promise(resolve => setTimeout(resolve, 80)); - } - // VALIDATION METHODS - async validateS3Integration() { - // Mock validation - in real implementation would test S3 connection - return true; - } - async validateFileUploadWorkflow() { - // Mock validation - would test complete upload workflow - return true; - } - async validateAudioProcessingPerformance() { - // Mock validation - would benchmark audio processing speed - return true; - } - async validateAIResponseCaching() { - // Mock validation - would test cache hit rates - return true; - } - async validateConnectionPoolPerformance() { - return true; - } - async validateWebSocketPerformance() { - return true; - } - async validateEventBatching() { - return true; - } - async validateCircuitBreaker() { - return true; - } - async validateRetryLogic() { - return true; - } - async validatePerformanceMonitoring() { - return true; - } - async validateSecurityHardening() { - return true; - } -} -exports.Phase2Optimizer = Phase2Optimizer; -// Run optimization if called directly -if (require.main === module) { - const optimizer = new Phase2Optimizer(); - optimizer.runAllOptimizations() - .then(results => { - console.log('\n🚀 Phase 2 Optimization Complete'); - console.log(`Tasks: ${results.completed}/${results.totalTasks} completed`); - console.log(`Total Impact: ${results.totalImpact}/100`); - if (results.failed > 0) { - console.log(`⚠️ ${results.failed} tasks failed - review logs for details`); - process.exit(1); - } - else { - console.log('✅ All optimizations completed successfully'); - process.exit(0); - } - }) - .catch(error => { - console.error('Optimization failed:', error); - process.exit(1); - }); -} -//# sourceMappingURL=phase2-optimization.js.map \ No newline at end of file diff --git a/backend/dist/scripts/phase2-optimization.js.map b/backend/dist/scripts/phase2-optimization.js.map deleted file mode 100644 index a054e5d..0000000 --- a/backend/dist/scripts/phase2-optimization.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"phase2-optimization.js","sourceRoot":"","sources":["../../src/scripts/phase2-optimization.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/scripts/seed-channels.d.ts b/backend/dist/scripts/seed-channels.d.ts deleted file mode 100644 index bec0bae..0000000 --- a/backend/dist/scripts/seed-channels.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/** - * Seed channels for development and testing - */ -export declare function seedChannels(): Promise; -//# sourceMappingURL=seed-channels.d.ts.map \ No newline at end of file diff --git a/backend/dist/scripts/seed-channels.d.ts.map b/backend/dist/scripts/seed-channels.d.ts.map deleted file mode 100644 index ed341c2..0000000 --- a/backend/dist/scripts/seed-channels.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"seed-channels.d.ts","sourceRoot":"","sources":["../../src/scripts/seed-channels.ts"],"names":[],"mappings":"AAkBA;;GAEG;AACH,wBAAsB,YAAY,kBAkVjC"} \ No newline at end of file diff --git a/backend/dist/scripts/seed-channels.js b/backend/dist/scripts/seed-channels.js deleted file mode 100644 index adbe938..0000000 --- a/backend/dist/scripts/seed-channels.js +++ /dev/null @@ -1,345 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.seedChannels = seedChannels; -const database_1 = require("../config/database"); -const logger_1 = require("../utils/logger"); -const ChannelRepository_1 = __importDefault(require("../db/ChannelRepository")); -const channelRepository = new ChannelRepository_1.default(); -/** - * Seed channels for development and testing - */ -async function seedChannels() { - try { - logger_1.logger.info('Starting channel seeding...'); - // Initialize database connection - await (0, database_1.initializeDatabase)(); - // Test database connection - const testResult = await (0, database_1.query)('SELECT NOW()'); - logger_1.logger.info('Database connection test:', testResult.rows[0]); - // Check if any channels exist - const existingChannels = await (0, database_1.query)('SELECT COUNT(*) as count FROM channels WHERE deleted_at IS NULL'); - const channelCount = parseInt(existingChannels.rows[0].count, 10); - if (channelCount > 0) { - logger_1.logger.info(`Found ${channelCount} existing channels, skipping seed`); - return; - } - // Get users to assign as channel owners and members - const usersResult = await (0, database_1.query)('SELECT id, email, role FROM users WHERE deleted_at IS NULL ORDER BY created_at ASC LIMIT 10'); - const users = usersResult.rows; - if (users.length === 0) { - logger_1.logger.warn('No users found, cannot seed channels without users'); - return; - } - // Find admin/CEO user for some channels - const adminUser = users.find(u => u.role === 'ceo') || users[0]; - const managerUsers = users.filter(u => u.role === 'manager') || users.slice(0, 2); - const staffUsers = users.filter(u => u.role === 'staff') || users; - const seedChannels = [ - { - name: 'General Discussion', - description: 'Main channel for general company discussions, announcements, and team updates.', - channel_type: 'announcement', - privacy_level: 'public', - created_by: adminUser.id, - members: users.map(u => u.id), - moderators: [adminUser.id, ...managerUsers.slice(0, 2).map(u => u.id)], - settings: { - allow_voice_commands: true, - voice_command_roles: ['ceo', 'manager'], - allow_file_uploads: true, - allow_external_sharing: false, - message_retention_days: 365, - require_approval_for_join: false, - notification_level: 'all', - read_receipts_enabled: true, - typing_indicators_enabled: true, - thread_replies_enabled: true, - message_reactions_enabled: true, - voice_transcription_enabled: true, - }, - project_info: { - priority: 'high', - tags: ['general', 'announcements', 'company-wide'], - stakeholders: users.map(u => u.id), - }, - }, - { - name: 'Project Alpha Development', - description: 'Development discussions, progress updates, and technical coordination for Project Alpha.', - channel_type: 'project', - privacy_level: 'private', - created_by: managerUsers[0]?.id || adminUser.id, - members: [adminUser.id, ...managerUsers.slice(0, 2).map(u => u.id), ...staffUsers.slice(0, 4).map(u => u.id)], - moderators: [managerUsers[0]?.id || adminUser.id, adminUser.id], - settings: { - allow_voice_commands: true, - voice_command_roles: ['ceo', 'manager'], - allow_file_uploads: true, - allow_external_sharing: false, - message_retention_days: 90, - require_approval_for_join: true, - notification_level: 'all', - read_receipts_enabled: true, - typing_indicators_enabled: true, - thread_replies_enabled: true, - message_reactions_enabled: true, - voice_transcription_enabled: true, - }, - project_info: { - start_date: new Date().toISOString(), - end_date: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(), // 90 days from now - priority: 'high', - tags: ['development', 'project-alpha', 'backend', 'frontend'], - milestones: [ - { - name: 'MVP Complete', - date: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - { - name: 'Beta Testing', - date: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - { - name: 'Production Release', - date: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - ], - deliverables: [ - 'API Documentation', - 'Frontend Application', - 'Backend Services', - 'Testing Suite', - 'Deployment Pipeline', - ], - stakeholders: [adminUser.id, ...managerUsers.slice(0, 2).map(u => u.id)], - }, - }, - { - name: 'Marketing Team', - description: 'Marketing strategies, campaigns, content planning, and brand discussions.', - channel_type: 'department', - privacy_level: 'restricted', - created_by: managerUsers[1]?.id || adminUser.id, - members: [adminUser.id, managerUsers[1]?.id || adminUser.id, ...staffUsers.slice(2, 5).map(u => u.id)], - moderators: [managerUsers[1]?.id || adminUser.id], - settings: { - allow_voice_commands: true, - voice_command_roles: ['ceo', 'manager'], - allow_file_uploads: true, - allow_external_sharing: true, - message_retention_days: 180, - require_approval_for_join: true, - notification_level: 'mentions', - read_receipts_enabled: false, - typing_indicators_enabled: true, - thread_replies_enabled: true, - message_reactions_enabled: true, - voice_transcription_enabled: false, - }, - project_info: { - priority: 'medium', - tags: ['marketing', 'campaigns', 'content', 'social-media'], - stakeholders: [adminUser.id, managerUsers[1]?.id || adminUser.id], - }, - }, - { - name: 'Design System', - description: 'UI/UX design discussions, component library updates, and design system maintenance.', - channel_type: 'initiative', - privacy_level: 'public', - created_by: staffUsers[0]?.id || adminUser.id, - members: [adminUser.id, ...staffUsers.slice(0, 3).map(u => u.id)], - moderators: [staffUsers[0]?.id || adminUser.id], - settings: { - allow_voice_commands: false, - voice_command_roles: [], - allow_file_uploads: true, - allow_external_sharing: false, - message_retention_days: 60, - require_approval_for_join: false, - notification_level: 'mentions', - read_receipts_enabled: false, - typing_indicators_enabled: true, - thread_replies_enabled: true, - message_reactions_enabled: true, - voice_transcription_enabled: false, - }, - project_info: { - priority: 'medium', - tags: ['design', 'ui', 'ux', 'components', 'style-guide'], - deliverables: [ - 'Component Library', - 'Design Tokens', - 'Usage Guidelines', - 'Figma Templates', - ], - stakeholders: [staffUsers[0]?.id || adminUser.id], - }, - }, - { - name: 'Random & Fun', - description: 'Casual conversations, memes, team bonding, and non-work related discussions.', - channel_type: 'temporary', - privacy_level: 'public', - created_by: staffUsers[1]?.id || adminUser.id, - members: users.map(u => u.id), - moderators: [staffUsers[1]?.id || adminUser.id], - settings: { - allow_voice_commands: false, - voice_command_roles: [], - allow_file_uploads: true, - allow_external_sharing: false, - message_retention_days: 30, - require_approval_for_join: false, - notification_level: 'none', - read_receipts_enabled: false, - typing_indicators_enabled: true, - thread_replies_enabled: false, - message_reactions_enabled: true, - voice_transcription_enabled: false, - }, - project_info: { - priority: 'low', - tags: ['social', 'fun', 'casual', 'team-bonding'], - stakeholders: [staffUsers[1]?.id || adminUser.id], - }, - }, - { - name: 'Q4 Planning Initiative', - description: 'Strategic planning, goal setting, and resource allocation for Q4 objectives.', - channel_type: 'initiative', - privacy_level: 'restricted', - created_by: adminUser.id, - members: [adminUser.id, ...managerUsers.map(u => u.id)], - moderators: [adminUser.id], - settings: { - allow_voice_commands: true, - voice_command_roles: ['ceo', 'manager'], - allow_file_uploads: true, - allow_external_sharing: false, - message_retention_days: 120, - require_approval_for_join: true, - notification_level: 'all', - read_receipts_enabled: true, - typing_indicators_enabled: true, - thread_replies_enabled: true, - message_reactions_enabled: false, - voice_transcription_enabled: true, - }, - project_info: { - start_date: new Date().toISOString(), - end_date: new Date(Date.now() + 120 * 24 * 60 * 60 * 1000).toISOString(), // 120 days from now - priority: 'high', - tags: ['planning', 'strategy', 'quarterly', 'goals', 'leadership'], - milestones: [ - { - name: 'Goal Setting Complete', - date: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - { - name: 'Resource Allocation', - date: new Date(Date.now() + 28 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - { - name: 'Implementation Begin', - date: new Date(Date.now() + 45 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - ], - deliverables: [ - 'Q4 Strategic Plan', - 'Budget Allocation', - 'Team Assignments', - 'Success Metrics', - 'Timeline & Milestones', - ], - stakeholders: [adminUser.id, ...managerUsers.map(u => u.id)], - }, - }, - ]; - // Create categories if they don't exist (skip if categories table doesn't exist) - try { - const categoriesResult = await (0, database_1.query)('SELECT COUNT(*) as count FROM categories WHERE deleted_at IS NULL'); - const categoryCount = parseInt(categoriesResult.rows[0].count, 10); - if (categoryCount === 0) { - logger_1.logger.info('Creating default categories...'); - const defaultCategories = [ - { name: 'General', description: 'General discussions and announcements', color: '#6B7280' }, - { name: 'Projects', description: 'Project-specific channels', color: '#10B981' }, - { name: 'Departments', description: 'Department and team channels', color: '#3B82F6' }, - { name: 'Initiatives', description: 'Special initiatives and programs', color: '#8B5CF6' }, - { name: 'Social', description: 'Social and casual discussions', color: '#F59E0B' }, - ]; - for (const category of defaultCategories) { - await (0, database_1.query)('INSERT INTO categories (name, description, metadata) VALUES ($1, $2, $3)', [category.name, category.description, JSON.stringify({ color: category.color })]); - } - logger_1.logger.info(`Created ${defaultCategories.length} categories`); - } - } - catch (error) { - logger_1.logger.warn('Categories table may not exist, skipping category creation'); - } - // Create channels - let createdCount = 0; - for (const channelData of seedChannels) { - try { - const channel = await channelRepository.createChannel({ - name: channelData.name, - description: channelData.description, - channel_type: channelData.channel_type, - privacy_level: channelData.privacy_level, - created_by: channelData.created_by, - members: channelData.members, - moderators: channelData.moderators, - settings: channelData.settings, - project_info: channelData.project_info, - }); - // Add some initial activity stats - await (0, database_1.query)(`UPDATE channels SET activity_stats = $1 WHERE id = $2`, [ - JSON.stringify({ - total_messages: Math.floor(Math.random() * 50) + 1, - total_files: Math.floor(Math.random() * 10), - total_tasks: Math.floor(Math.random() * 5), - last_activity: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000).toISOString(), - most_active_user: channelData.members[Math.floor(Math.random() * channelData.members.length)], - average_response_time: Math.floor(Math.random() * 120) + 30, // 30-150 minutes - peak_activity_hours: ['09:00', '10:00', '14:00', '15:00'], - }), - channel.id, - ]); - logger_1.logger.info(`Created channel: ${channel.name} (${channel.id})`); - createdCount++; - } - catch (error) { - logger_1.logger.error(`Failed to create channel ${channelData.name}:`, error); - } - } - logger_1.logger.info(`Channel seeding completed: ${createdCount}/${seedChannels.length} channels created`); - } - catch (error) { - logger_1.logger.error('Channel seeding failed:', error); - console.error('Full error details:', error); - throw error; - } -} -// Run if called directly -if (require.main === module) { - seedChannels() - .then(() => { - logger_1.logger.info('Channel seeding script completed successfully'); - process.exit(0); - }) - .catch((error) => { - logger_1.logger.error('Channel seeding script failed:', error); - process.exit(1); - }); -} -//# sourceMappingURL=seed-channels.js.map \ No newline at end of file diff --git a/backend/dist/scripts/seed-channels.js.map b/backend/dist/scripts/seed-channels.js.map deleted file mode 100644 index 65d2f50..0000000 --- a/backend/dist/scripts/seed-channels.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"seed-channels.js","sourceRoot":"","sources":["../../src/scripts/seed-channels.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/scripts/seed.d.ts b/backend/dist/scripts/seed.d.ts deleted file mode 100644 index 19b2a7a..0000000 --- a/backend/dist/scripts/seed.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -export declare class DatabaseSeeder { - private users; - private channels; - private tasks; - seed(): Promise; - private clearData; - private createUsers; - private createChannels; - private createTasks; - private createMessages; - private createActivities; -} -export default DatabaseSeeder; -//# sourceMappingURL=seed.d.ts.map \ No newline at end of file diff --git a/backend/dist/scripts/seed.d.ts.map b/backend/dist/scripts/seed.d.ts.map deleted file mode 100644 index 305bad1..0000000 --- a/backend/dist/scripts/seed.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../src/scripts/seed.ts"],"names":[],"mappings":"AAuDA,qBAAa,cAAc;IACzB,OAAO,CAAC,KAAK,CAAkB;IAC/B,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,KAAK,CAAkB;IAEzB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAsBb,SAAS;YAyBT,WAAW;YAwJX,cAAc;YA6Gd,WAAW;YA4OX,cAAc;YAsEd,gBAAgB;CAmJ/B;AAmBD,eAAe,cAAc,CAAC"} \ No newline at end of file diff --git a/backend/dist/scripts/seed.js b/backend/dist/scripts/seed.js deleted file mode 100644 index d3c61dc..0000000 --- a/backend/dist/scripts/seed.js +++ /dev/null @@ -1,751 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.DatabaseSeeder = void 0; -const pg_1 = require("pg"); -const config_1 = require("../config"); -const logger_1 = require("../utils/logger"); -const bcryptjs_1 = __importDefault(require("bcryptjs")); -const faker_1 = require("@faker-js/faker"); -const pool = new pg_1.Pool({ - connectionString: config_1.config.database.url, - min: config_1.config.database.pool.min, - max: config_1.config.database.pool.max, -}); -class DatabaseSeeder { - users = []; - channels = []; - tasks = []; - async seed() { - try { - logger_1.logger.info('Starting database seeding...'); - // Clear existing data (in reverse dependency order) - await this.clearData(); - // Create seed data - await this.createUsers(); - await this.createChannels(); - await this.createTasks(); - await this.createMessages(); - await this.createActivities(); - logger_1.logger.info('Database seeding completed successfully'); - } - catch (error) { - logger_1.logger.error('Error seeding database:', error); - console.error('Full error details:', error); - throw error; - } - } - async clearData() { - const client = await pool.connect(); - try { - await client.query('BEGIN'); - // Clear in dependency order - await client.query('DELETE FROM activities WHERE created_at > NOW() - INTERVAL \'1 day\''); - await client.query('DELETE FROM messages WHERE created_at > NOW() - INTERVAL \'1 day\''); - await client.query('DELETE FROM task_assignment_history WHERE performed_at > NOW() - INTERVAL \'1 day\''); - await client.query('DELETE FROM tasks WHERE created_at > NOW() - INTERVAL \'1 day\''); - await client.query('DELETE FROM channel_member_history WHERE performed_at > NOW() - INTERVAL \'1 day\''); - await client.query('DELETE FROM channels WHERE created_at > NOW() - INTERVAL \'1 day\''); - // Delete all CEO users and seed data users - await client.query('DELETE FROM users WHERE role = \'ceo\' OR (created_at > NOW() - INTERVAL \'1 day\' AND (email LIKE \'%@seeddata.com\' OR email LIKE \'%@company.com\'))'); - await client.query('COMMIT'); - logger_1.logger.info('Cleared existing seed data'); - } - catch (error) { - await client.query('ROLLBACK'); - throw error; - } - finally { - client.release(); - } - } - async createUsers() { - const client = await pool.connect(); - const predefinedUsers = [ - { - email: 'alex.ceo@company.com', - name: 'Alexander Mitchell', - role: 'ceo', - department: 'Executive', - job_title: 'Chief Executive Officer', - avatar_url: 'https://images.unsplash.com/photo-1560250097-0b93528c311a?w=400&h=400&fit=crop&crop=face', - }, - { - email: 'sarah.manager@seeddata.com', - name: 'Sarah Chen', - role: 'manager', - department: 'Engineering', - job_title: 'Engineering Manager', - avatar_url: 'https://images.unsplash.com/photo-1494790108755-2616b36c7e9d?w=400&h=400&fit=crop&crop=face', - }, - { - email: 'mike.manager@seeddata.com', - name: 'Michael Rodriguez', - role: 'manager', - department: 'Product', - job_title: 'Product Manager', - avatar_url: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400&h=400&fit=crop&crop=face', - }, - { - email: 'lisa.manager@seeddata.com', - name: 'Lisa Thompson', - role: 'manager', - department: 'Marketing', - job_title: 'Marketing Director', - avatar_url: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=400&h=400&fit=crop&crop=face', - }, - ]; - const passwordHash = await bcryptjs_1.default.hash('TempPass123!', 12); - try { - await client.query('BEGIN'); - // Create predefined users - for (const userData of predefinedUsers) { - const userId = faker_1.faker.string.uuid(); - const result = await client.query(` - INSERT INTO users (id, email, name, password_hash, role, department, job_title, avatar_url, phone, timezone, language_preference, email_verified, last_active, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, true, NOW(), NOW(), NOW()) - ON CONFLICT (email) DO UPDATE SET - name = EXCLUDED.name, - password_hash = EXCLUDED.password_hash, - role = EXCLUDED.role, - department = EXCLUDED.department, - job_title = EXCLUDED.job_title, - avatar_url = EXCLUDED.avatar_url, - phone = EXCLUDED.phone, - timezone = EXCLUDED.timezone, - language_preference = EXCLUDED.language_preference, - email_verified = true, - last_active = NOW(), - updated_at = NOW() - RETURNING id, email, name, role, department, job_title, avatar_url, phone, timezone, language_preference - `, [userId, userData.email, userData.name, passwordHash, userData.role, userData.department, userData.job_title, userData.avatar_url, faker_1.faker.phone.number(), 'America/New_York', 'en']); - const createdUser = result.rows[0]; - const user = { - id: createdUser.id, - email: createdUser.email, - name: createdUser.name, - password_hash: passwordHash, - role: createdUser.role, - department: createdUser.department, - job_title: createdUser.job_title, - avatar_url: createdUser.avatar_url, - phone: createdUser.phone, - }; - this.users.push(user); - } - // Create additional staff members - const departments = ['Engineering', 'Product', 'Marketing', 'Sales', 'Support', 'Finance', 'HR']; - const jobTitles = { - 'Engineering': ['Senior Developer', 'DevOps Engineer', 'QA Engineer', 'Frontend Developer', 'Backend Developer'], - 'Product': ['Product Analyst', 'UX Designer', 'Product Owner', 'Business Analyst'], - 'Marketing': ['Content Manager', 'Social Media Manager', 'SEO Specialist', 'Brand Manager'], - 'Sales': ['Account Executive', 'Sales Representative', 'Business Development', 'Customer Success'], - 'Support': ['Technical Support', 'Customer Support', 'Documentation Specialist'], - 'Finance': ['Financial Analyst', 'Accountant', 'Controller'], - 'HR': ['HR Specialist', 'Recruiter', 'People Operations'] - }; - for (let i = 0; i < 15; i++) { - const department = faker_1.faker.helpers.arrayElement(departments); - const jobTitle = faker_1.faker.helpers.arrayElement(jobTitles[department]); - const userId = faker_1.faker.string.uuid(); - const email = `${faker_1.faker.internet.userName().toLowerCase()}@seeddata.com`; - const name = faker_1.faker.person.fullName(); - const avatarUrl = faker_1.faker.image.avatar(); - const phone = faker_1.faker.phone.number(); - const timezones = ['America/New_York', 'America/Los_Angeles', 'America/Chicago', 'Europe/London', 'Asia/Tokyo']; - const timezone = faker_1.faker.helpers.arrayElement(timezones); - const result = await client.query(` - INSERT INTO users (id, email, name, password_hash, role, department, job_title, avatar_url, phone, timezone, language_preference, email_verified, last_active, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, true, NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 7 })} days', NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 30 })} days', NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 15 })} days') - ON CONFLICT (email) DO UPDATE SET - name = EXCLUDED.name, - password_hash = EXCLUDED.password_hash, - role = EXCLUDED.role, - department = EXCLUDED.department, - job_title = EXCLUDED.job_title, - avatar_url = EXCLUDED.avatar_url, - phone = EXCLUDED.phone, - timezone = EXCLUDED.timezone, - language_preference = EXCLUDED.language_preference, - email_verified = true, - last_active = NOW() - INTERVAL '1 day', - updated_at = NOW() - RETURNING id, email, name, role, department, job_title, avatar_url, phone, timezone, language_preference - `, [userId, email, name, passwordHash, 'staff', department, jobTitle, avatarUrl, phone, timezone, 'en']); - const createdUser = result.rows[0]; - const user = { - id: createdUser.id, - email: createdUser.email, - name: createdUser.name, - password_hash: passwordHash, - role: createdUser.role, - department: createdUser.department, - job_title: createdUser.job_title, - avatar_url: createdUser.avatar_url, - phone: createdUser.phone, - }; - this.users.push(user); - } - await client.query('COMMIT'); - logger_1.logger.info(`Created ${this.users.length} seed users`); - } - catch (error) { - await client.query('ROLLBACK'); - throw error; - } - finally { - client.release(); - } - } - async createChannels() { - const client = await pool.connect(); - const ceo = this.users.find(u => u.role === 'ceo'); - const managers = this.users.filter(u => u.role === 'manager'); - const staff = this.users.filter(u => u.role === 'staff'); - const channelTemplates = [ - { - name: 'Product Development', - description: 'Main channel for product development discussions, roadmap planning, and feature releases', - channel_type: 'project', - privacy_level: 'public', - created_by: ceo.id, - owned_by: managers.find(m => m.department === 'Product')?.id || ceo.id, - members: [ceo.id, managers.find(m => m.department === 'Product')?.id, ...managers.map(m => m.id), ...staff.filter(s => ['Engineering', 'Product'].includes(s.department)).map(s => s.id)].filter(Boolean), - moderators: [ceo.id, managers.find(m => m.department === 'Product')?.id].filter(Boolean), - }, - { - name: 'Engineering Team', - description: 'Technical discussions, architecture decisions, and development coordination', - channel_type: 'department', - privacy_level: 'private', - created_by: managers.find(m => m.department === 'Engineering')?.id || ceo.id, - owned_by: managers.find(m => m.department === 'Engineering')?.id || ceo.id, - members: [ceo.id, ...staff.filter(s => s.department === 'Engineering').map(s => s.id), managers.find(m => m.department === 'Engineering')?.id].filter(Boolean), - moderators: [managers.find(m => m.department === 'Engineering')?.id].filter(Boolean), - }, - { - name: 'Marketing Campaigns', - description: 'Campaign planning, content creation, and marketing strategy discussions', - channel_type: 'department', - privacy_level: 'public', - created_by: managers.find(m => m.department === 'Marketing')?.id || ceo.id, - owned_by: managers.find(m => m.department === 'Marketing')?.id || ceo.id, - members: [ceo.id, ...staff.filter(s => s.department === 'Marketing').map(s => s.id), managers.find(m => m.department === 'Marketing')?.id].filter(Boolean), - moderators: [managers.find(m => m.department === 'Marketing')?.id].filter(Boolean), - }, - { - name: 'Executive Announcements', - description: 'Important company-wide announcements and strategic updates from leadership', - channel_type: 'announcement', - privacy_level: 'public', - created_by: ceo.id, - owned_by: ceo.id, - members: this.users.map(u => u.id), - moderators: [ceo.id, ...managers.map(m => m.id)], - }, - { - name: 'Q4 2024 Launch', - description: 'Coordination for the major Q4 product launch - cross-functional collaboration', - channel_type: 'initiative', - privacy_level: 'restricted', - created_by: ceo.id, - owned_by: managers.find(m => m.department === 'Product')?.id || ceo.id, - members: [ceo.id, managers.find(m => m.department === 'Product')?.id, ...managers.map(m => m.id), ...staff.filter(s => ['Engineering', 'Product', 'Marketing'].includes(s.department)).slice(0, 8).map(s => s.id)].filter(Boolean), - moderators: [ceo.id, managers.find(m => m.department === 'Product')?.id].filter(Boolean), - }, - { - name: 'Mobile App Beta', - description: 'Beta testing coordination and feedback collection for the new mobile application', - channel_type: 'project', - privacy_level: 'private', - created_by: managers.find(m => m.department === 'Product')?.id || ceo.id, - owned_by: managers.find(m => m.department === 'Product')?.id || ceo.id, - members: [ceo.id, managers.find(m => m.department === 'Product')?.id, ...staff.filter(s => ['Engineering', 'Product', 'Support'].includes(s.department)).slice(0, 6).map(s => s.id)].filter(Boolean), - moderators: [managers.find(m => m.department === 'Product')?.id].filter(Boolean), - }, - ]; - try { - await client.query('BEGIN'); - for (const channelData of channelTemplates) { - const channelId = faker_1.faker.string.uuid(); - const channel = { - id: channelId, - ...channelData, - }; - await client.query(` - INSERT INTO channels (id, name, description, channel_type, privacy_level, created_by, owned_by, members, moderators, member_count, created_at, last_activity_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW() - INTERVAL '${faker_1.faker.number.int({ min: 5, max: 25 })} days', NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 8 })} hours') - `, [ - channel.id, - channel.name, - channel.description, - channel.channel_type, - channel.privacy_level, - channel.created_by, - channel.owned_by, - channel.members, - channel.moderators, - channel.members.length, - ]); - this.channels.push(channel); - } - await client.query('COMMIT'); - logger_1.logger.info(`Created ${this.channels.length} seed channels`); - } - catch (error) { - await client.query('ROLLBACK'); - throw error; - } - finally { - client.release(); - } - } - async createTasks() { - const client = await pool.connect(); - const taskTemplates = [ - { - title: 'Implement Two-Factor Authentication', - description: 'Add 2FA support for all user accounts to enhance security. Include SMS, email, and authenticator app options.', - priority: 'high', - status: 'in_progress', - task_type: 'project', - complexity: 7, - estimated_hours: 40, - actual_hours: 28, - progress_percentage: 70, - tags: ['security', 'authentication', 'mobile'], - }, - { - title: 'Fix Payment Processing Bug', - description: 'Critical bug in payment processing causing failed transactions for amounts over $1000. Customer complaints increasing.', - priority: 'critical', - status: 'pending', - task_type: 'emergency', - complexity: 8, - estimated_hours: 16, - actual_hours: 0, - progress_percentage: 0, - tags: ['bug', 'payments', 'critical'], - }, - { - title: 'Mobile App Performance Optimization', - description: 'Optimize mobile app loading times and reduce memory usage. Target 50% improvement in startup time.', - priority: 'medium', - status: 'review', - task_type: 'maintenance', - complexity: 6, - estimated_hours: 32, - actual_hours: 35, - progress_percentage: 95, - tags: ['mobile', 'performance', 'optimization'], - }, - { - title: 'Q4 Marketing Campaign Planning', - description: 'Develop comprehensive marketing strategy for Q4 launch including social media, content marketing, and paid advertising.', - priority: 'high', - status: 'in_progress', - task_type: 'project', - complexity: 5, - estimated_hours: 60, - actual_hours: 22, - progress_percentage: 40, - tags: ['marketing', 'campaign', 'q4-launch'], - }, - { - title: 'Database Migration to PostgreSQL 15', - description: 'Upgrade database to PostgreSQL 15 for better performance and new features. Plan for zero-downtime migration.', - priority: 'medium', - status: 'pending', - task_type: 'maintenance', - complexity: 9, - estimated_hours: 48, - actual_hours: 0, - progress_percentage: 0, - tags: ['database', 'migration', 'infrastructure'], - }, - { - title: 'Customer Support Dashboard', - description: 'Build new dashboard for customer support team with ticket analytics, response time tracking, and customer satisfaction metrics.', - priority: 'medium', - status: 'in_progress', - task_type: 'project', - complexity: 6, - estimated_hours: 45, - actual_hours: 18, - progress_percentage: 35, - tags: ['dashboard', 'support', 'analytics'], - }, - { - title: 'API Rate Limiting Implementation', - description: 'Implement rate limiting across all API endpoints to prevent abuse and ensure fair usage.', - priority: 'high', - status: 'completed', - task_type: 'project', - complexity: 4, - estimated_hours: 24, - actual_hours: 26, - progress_percentage: 100, - tags: ['api', 'security', 'rate-limiting'], - }, - { - title: 'User Onboarding Flow Redesign', - description: 'Redesign user onboarding to improve conversion rates and reduce drop-off. A/B testing required.', - priority: 'high', - status: 'review', - task_type: 'project', - complexity: 7, - estimated_hours: 55, - actual_hours: 52, - progress_percentage: 90, - tags: ['ux', 'onboarding', 'conversion'], - }, - { - title: 'Automated Testing Suite Expansion', - description: 'Expand automated testing coverage to 90% for all critical user flows and API endpoints.', - priority: 'medium', - status: 'in_progress', - task_type: 'maintenance', - complexity: 8, - estimated_hours: 38, - actual_hours: 15, - progress_percentage: 25, - tags: ['testing', 'automation', 'quality'], - }, - { - title: 'GDPR Compliance Audit', - description: 'Complete GDPR compliance audit and implement necessary changes for European data protection requirements.', - priority: 'urgent', - status: 'on_hold', - task_type: 'approval', - complexity: 5, - estimated_hours: 30, - actual_hours: 8, - progress_percentage: 15, - tags: ['gdpr', 'compliance', 'legal'], - }, - ]; - try { - await client.query('BEGIN'); - for (let i = 0; i < taskTemplates.length; i++) { - const template = taskTemplates[i]; - const channel = faker_1.faker.helpers.arrayElement(this.channels); - const creator = faker_1.faker.helpers.arrayElement(channel.members); - const assignees = faker_1.faker.helpers.arrayElements(channel.members, { min: 1, max: 3 }); - const owner = assignees[0]; - const taskId = faker_1.faker.string.uuid(); - const task = { - id: taskId, - title: template.title, - description: template.description, - channel_id: channel.id, - created_by: creator, - assigned_to: assignees, - owned_by: owner, - priority: template.priority, - status: template.status, - task_type: template.task_type, - complexity: template.complexity, - estimated_hours: template.estimated_hours, - actual_hours: template.actual_hours, - progress_percentage: template.progress_percentage, - tags: template.tags, - due_date: faker_1.faker.date.future({ days: faker_1.faker.number.int({ min: 7, max: 60 }) }), - }; - await client.query(` - INSERT INTO tasks ( - id, title, description, channel_id, created_by, assigned_to, owned_by, - priority, status, task_type, complexity, estimated_hours, actual_hours, - progress_percentage, due_date, tags, created_at, last_activity_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, - NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 15 })} days', - NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 24 })} hours') - `, [ - task.id, task.title, task.description, task.channel_id, task.created_by, - task.assigned_to, task.owned_by, task.priority, task.status, task.task_type, - task.complexity, task.estimated_hours, task.actual_hours, task.progress_percentage, - task.due_date, task.tags - ]); - this.tasks.push(task); - } - // Create additional random tasks - for (let i = 0; i < 15; i++) { - const channel = faker_1.faker.helpers.arrayElement(this.channels); - const creator = faker_1.faker.helpers.arrayElement(channel.members); - const assignees = faker_1.faker.helpers.arrayElements(channel.members, { min: 1, max: 2 }); - const owner = assignees[0]; - const taskId = faker_1.faker.string.uuid(); - const priorities = ['low', 'medium', 'high', 'urgent']; - const statuses = ['pending', 'in_progress', 'review', 'completed', 'on_hold']; - const types = ['general', 'project', 'maintenance', 'research']; - const tags = ['frontend', 'backend', 'mobile', 'api', 'ui/ux', 'security', 'performance', 'testing', 'documentation']; - const task = { - id: taskId, - title: faker_1.faker.company.catchPhrase(), - description: faker_1.faker.lorem.sentences(3), - channel_id: channel.id, - created_by: creator, - assigned_to: assignees, - owned_by: owner, - priority: faker_1.faker.helpers.arrayElement(priorities), - status: faker_1.faker.helpers.arrayElement(statuses), - task_type: faker_1.faker.helpers.arrayElement(types), - complexity: faker_1.faker.number.int({ min: 1, max: 10 }), - estimated_hours: faker_1.faker.number.int({ min: 4, max: 80 }), - actual_hours: faker_1.faker.number.int({ min: 0, max: 60 }), - progress_percentage: faker_1.faker.number.int({ min: 0, max: 100 }), - tags: faker_1.faker.helpers.arrayElements(tags, { min: 1, max: 3 }), - due_date: faker_1.faker.helpers.maybe(() => faker_1.faker.date.future({ days: faker_1.faker.number.int({ min: 3, max: 90 }) }), { probability: 0.7 }), - }; - await client.query(` - INSERT INTO tasks ( - id, title, description, channel_id, created_by, assigned_to, owned_by, - priority, status, task_type, complexity, estimated_hours, actual_hours, - progress_percentage, due_date, tags, created_at, last_activity_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, - NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 20 })} days', - NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 48 })} hours') - `, [ - task.id, task.title, task.description, task.channel_id, task.created_by, - task.assigned_to, task.owned_by, task.priority, task.status, task.task_type, - task.complexity, task.estimated_hours, task.actual_hours, task.progress_percentage, - task.due_date, task.tags - ]); - this.tasks.push(task); - } - await client.query('COMMIT'); - logger_1.logger.info(`Created ${this.tasks.length} seed tasks`); - } - catch (error) { - await client.query('ROLLBACK'); - throw error; - } - finally { - client.release(); - } - } - async createMessages() { - const client = await pool.connect(); - const messageTypes = ['text', 'file', 'voice', 'system']; - const systemMessages = [ - 'Channel created', - 'User joined the channel', - 'Task created and assigned', - 'Task status updated', - 'File uploaded', - 'Meeting scheduled', - ]; - try { - await client.query('BEGIN'); - for (const channel of this.channels) { - const messageCount = faker_1.faker.number.int({ min: 10, max: 40 }); - for (let i = 0; i < messageCount; i++) { - const messageType = faker_1.faker.helpers.arrayElement(messageTypes); - const sender = messageType === 'system' - ? this.users.find(u => u.role === 'ceo')?.id || faker_1.faker.helpers.arrayElement(channel.members) - : faker_1.faker.helpers.arrayElement(channel.members); - const messageId = faker_1.faker.string.uuid(); - let content = ''; - let metadata = {}; - switch (messageType) { - case 'text': - content = faker_1.faker.lorem.sentences(faker_1.faker.number.int({ min: 1, max: 4 })); - break; - case 'file': - content = 'File shared: ' + faker_1.faker.system.fileName(); - metadata = { - fileName: faker_1.faker.system.fileName(), - fileSize: faker_1.faker.number.int({ min: 1000, max: 5000000 }), - fileType: faker_1.faker.helpers.arrayElement(['image/png', 'application/pdf', 'text/csv']), - }; - break; - case 'voice': - content = 'Voice message (transcription): ' + faker_1.faker.lorem.sentences(2); - metadata = { - duration: faker_1.faker.number.int({ min: 5, max: 120 }), - transcription: faker_1.faker.lorem.sentences(2), - }; - break; - case 'system': - content = faker_1.faker.helpers.arrayElement(systemMessages); - break; - } - await client.query(` - INSERT INTO messages (id, channel_id, user_id, content, message_type, metadata, created_at) - VALUES ($1, $2, $3, $4, $5, $6, NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 30 })} days' + INTERVAL '${faker_1.faker.number.int({ min: 0, max: 23 })} hours' + INTERVAL '${faker_1.faker.number.int({ min: 0, max: 59 })} minutes') - `, [messageId, channel.id, sender, content, messageType, JSON.stringify(metadata)]); - } - } - await client.query('COMMIT'); - logger_1.logger.info('Created seed messages for all channels'); - } - catch (error) { - await client.query('ROLLBACK'); - throw error; - } - finally { - client.release(); - } - } - async createActivities() { - const client = await pool.connect(); - const activityTypes = ['task_created', 'task_updated', 'task_completed', 'channel_created', 'user_joined', 'file_uploaded', 'message_sent', 'announcement']; - try { - await client.query('BEGIN'); - // Create activities for tasks - for (const task of this.tasks) { - // Task creation activity - const createActivityId = faker_1.faker.string.uuid(); - await client.query(` - INSERT INTO activities (id, user_id, type, title, description, metadata, related_id, channel_id, created_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 15 })} days') - `, [ - createActivityId, - task.created_by, - 'task_created', - `Created task: ${task.title}`, - `A new ${task.priority} priority task was created in ${this.channels.find(c => c.id === task.channel_id)?.name}`, - JSON.stringify({ - task_id: task.id, - priority: task.priority, - assigned_to: task.assigned_to, - }), - task.id, - task.channel_id, - ]); - // Random task updates - if (task.status !== 'pending' && faker_1.faker.datatype.boolean(0.7)) { - const updateActivityId = faker_1.faker.string.uuid(); - await client.query(` - INSERT INTO activities (id, user_id, type, title, description, metadata, related_id, channel_id, created_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 10 })} days') - `, [ - updateActivityId, - task.owned_by, - 'task_updated', - `Updated task: ${task.title}`, - `Task status changed to ${task.status}. Progress: ${task.progress_percentage}%`, - JSON.stringify({ - task_id: task.id, - status: task.status, - progress: task.progress_percentage, - }), - task.id, - task.channel_id, - ]); - } - // Task completion activities - if (task.status === 'completed') { - const completeActivityId = faker_1.faker.string.uuid(); - await client.query(` - INSERT INTO activities (id, user_id, type, title, description, metadata, related_id, channel_id, created_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 7 })} days') - `, [ - completeActivityId, - task.owned_by, - 'task_completed', - `Completed task: ${task.title}`, - `Task was successfully completed ahead of schedule`, - JSON.stringify({ - task_id: task.id, - completion_time: task.actual_hours, - estimated_time: task.estimated_hours, - }), - task.id, - task.channel_id, - ]); - } - } - // Create channel activities - for (const channel of this.channels) { - const createActivityId = faker_1.faker.string.uuid(); - await client.query(` - INSERT INTO activities (id, user_id, type, title, description, metadata, related_id, channel_id, created_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW() - INTERVAL '${faker_1.faker.number.int({ min: 5, max: 25 })} days') - `, [ - createActivityId, - channel.created_by, - 'channel_created', - `Created channel: ${channel.name}`, - `New ${channel.channel_type} channel created for ${channel.description}`, - JSON.stringify({ - channel_id: channel.id, - channel_type: channel.channel_type, - privacy_level: channel.privacy_level, - }), - channel.id, - channel.id, - ]); - } - // Create general announcements - const announcements = [ - { - title: 'Q4 All-Hands Meeting Scheduled', - description: 'Join us for the quarterly all-hands meeting next Friday at 2 PM. We\'ll be discussing Q3 results, Q4 goals, and exciting new product launches.', - metadata: { type: 'meeting', importance: 'high', attendees: 'all' }, - }, - { - title: 'New Security Policy Implementation', - description: 'Starting next week, all team members must enable two-factor authentication on their accounts. IT support is available for setup assistance.', - metadata: { type: 'policy', importance: 'high', deadline: '2024-01-15' }, - }, - { - title: 'Office Holiday Party', - description: 'You\'re invited to our annual holiday celebration! Join us for food, drinks, and team building activities on December 15th.', - metadata: { type: 'event', importance: 'medium', location: 'Main Office' }, - }, - { - title: 'System Maintenance Window', - description: 'Scheduled maintenance on our main servers will occur this Sunday from 2 AM to 6 AM. Some services may be temporarily unavailable.', - metadata: { type: 'maintenance', importance: 'medium', duration: '4 hours' }, - }, - ]; - for (const announcement of announcements) { - const announcementId = faker_1.faker.string.uuid(); - const announcer = this.users.find(u => u.role === 'ceo')?.id || faker_1.faker.helpers.arrayElement(this.users.filter(u => u.role === 'manager')).id; - await client.query(` - INSERT INTO activities (id, user_id, type, title, description, metadata, created_at) - VALUES ($1, $2, $3, $4, $5, $6, NOW() - INTERVAL '${faker_1.faker.number.int({ min: 1, max: 14 })} days') - `, [ - announcementId, - announcer, - 'announcement', - announcement.title, - announcement.description, - JSON.stringify(announcement.metadata), - ]); - } - await client.query('COMMIT'); - logger_1.logger.info('Created comprehensive seed activities'); - } - catch (error) { - await client.query('ROLLBACK'); - throw error; - } - finally { - client.release(); - } - } -} -exports.DatabaseSeeder = DatabaseSeeder; -// Self-executing function when run directly -async function runSeeder() { - const seeder = new DatabaseSeeder(); - try { - await seeder.seed(); - process.exit(0); - } - catch (error) { - logger_1.logger.error('Seeding failed:', error); - console.error('Full seeding error:', error); - process.exit(1); - } -} -if (require.main === module) { - runSeeder(); -} -exports.default = DatabaseSeeder; -//# sourceMappingURL=seed.js.map \ No newline at end of file diff --git a/backend/dist/scripts/seed.js.map b/backend/dist/scripts/seed.js.map deleted file mode 100644 index f5c40af..0000000 --- a/backend/dist/scripts/seed.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"seed.js","sourceRoot":"","sources":["../../src/scripts/seed.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/scripts/verify-email-token.d.ts b/backend/dist/scripts/verify-email-token.d.ts deleted file mode 100644 index 5b0c5a1..0000000 --- a/backend/dist/scripts/verify-email-token.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env tsx -export {}; -//# sourceMappingURL=verify-email-token.d.ts.map \ No newline at end of file diff --git a/backend/dist/scripts/verify-email-token.d.ts.map b/backend/dist/scripts/verify-email-token.d.ts.map deleted file mode 100644 index 51755e6..0000000 --- a/backend/dist/scripts/verify-email-token.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"verify-email-token.d.ts","sourceRoot":"","sources":["../../src/scripts/verify-email-token.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/backend/dist/scripts/verify-email-token.js b/backend/dist/scripts/verify-email-token.js deleted file mode 100644 index 868ca68..0000000 --- a/backend/dist/scripts/verify-email-token.js +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env tsx -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -Object.defineProperty(exports, "__esModule", { value: true }); -const index_1 = require("@db/index"); -const logger_1 = require("@utils/logger"); -/** - * Development script to manually verify email tokens - * Useful when emails are logged to console instead of sent - */ -async function verifyEmailToken(token) { - try { - logger_1.logger.info(`Attempting to verify email with token: ${token}`); - const user = await index_1.userRepository.verifyEmail(token); - if (user) { - logger_1.logger.info({ - userId: user.id, - email: user.email, - name: user.name, - role: user.role - }, 'Email verified successfully! User can now log in.'); - console.log('\n✅ EMAIL VERIFICATION SUCCESSFUL!'); - console.log(`User: ${user.name} (${user.email})`); - console.log(`Role: ${user.role}`); - console.log('The user can now log in to the application.\n'); - } - else { - logger_1.logger.warn('Invalid or expired verification token'); - console.log('\n❌ VERIFICATION FAILED!'); - console.log('Token is either invalid or expired.\n'); - } - } - catch (error) { - logger_1.logger.error({ error, token }, 'Failed to verify email token'); - console.log('\n❌ VERIFICATION ERROR!'); - console.log(`Error: ${error instanceof Error ? error.message : 'Unknown error'}\n`); - } -} -// Get token from command line argument -const token = process.argv[2]; -if (!token) { - console.log('\n📧 Email Token Verification Tool'); - console.log('Usage: npx tsx src/scripts/verify-email-token.ts '); - console.log('\nExample:'); - console.log('npx tsx src/scripts/verify-email-token.ts eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n'); - process.exit(1); -} -// Initialize database connection and verify token -Promise.resolve().then(() => __importStar(require('@config/index'))).then(() => { - verifyEmailToken(token).then(() => { - process.exit(0); - }).catch((error) => { - console.error('Script failed:', error); - process.exit(1); - }); -}); -//# sourceMappingURL=verify-email-token.js.map \ No newline at end of file diff --git a/backend/dist/scripts/verify-email-token.js.map b/backend/dist/scripts/verify-email-token.js.map deleted file mode 100644 index a973176..0000000 --- a/backend/dist/scripts/verify-email-token.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"verify-email-token.js","sourceRoot":"","sources":["../../src/scripts/verify-email-token.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAEA,qCAA2C;AAC3C,0CAAuC;AAEvC;;;GAGG;AAEH,KAAK,UAAU,gBAAgB,CAAC,KAAa;IAC3C,IAAI,CAAC;QACH,eAAM,CAAC,IAAI,CAAC,0CAA0C,KAAK,EAAE,CAAC,CAAC;QAE/D,MAAM,IAAI,GAAG,MAAM,sBAAc,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAErD,IAAI,IAAI,EAAE,CAAC;YACT,eAAM,CAAC,IAAI,CAAC;gBACV,MAAM,EAAE,IAAI,CAAC,EAAE;gBACf,KAAK,EAAE,IAAI,CAAC,KAAK;gBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;aAChB,EAAE,mDAAmD,CAAC,CAAC;YAExD,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC;YAClD,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;QAC/D,CAAC;aAAM,CAAC;YACN,eAAM,CAAC,IAAI,CAAC,uCAAuC,CAAC,CAAC;YACrD,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,CAAC;YACxC,OAAO,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC;QACvD,CAAC;IAEH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,8BAA8B,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,yBAAyB,CAAC,CAAC;QACvC,OAAO,CAAC,GAAG,CAAC,UAAU,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,IAAI,CAAC,CAAC;IACtF,CAAC;AACH,CAAC;AAED,uCAAuC;AACvC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAE9B,IAAI,CAAC,KAAK,EAAE,CAAC;IACX,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;IAClD,OAAO,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;IACxE,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IAC1B,OAAO,CAAC,GAAG,CAAC,qFAAqF,CAAC,CAAC;IACnG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,kDAAkD;AAClD,kDAAO,eAAe,IAAE,IAAI,CAAC,GAAG,EAAE;IAChC,gBAAgB,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,EAAE;QAChC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACjB,OAAO,CAAC,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,CAAC;QACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/dist/security/SecurityManager.d.ts b/backend/dist/security/SecurityManager.d.ts deleted file mode 100644 index ac3e0eb..0000000 --- a/backend/dist/security/SecurityManager.d.ts +++ /dev/null @@ -1,174 +0,0 @@ -/** - * Security Manager - Phase 2 Security & Authentication - * Comprehensive security layer for voice command processing - * - * Success Criteria: - * - Voice command authentication and authorization - * - Rate limiting and abuse prevention - * - Audit logging for security events - * - Input sanitization and validation - */ -import { EventEmitter } from 'events'; -export interface SecurityConfig { - jwtSecret: string; - jwtExpiresIn: string; - bcryptSaltRounds: number; - rateLimiting: { - windowMs: number; - maxRequests: number; - skipSuccessfulRequests: boolean; - }; - encryption: { - algorithm: string; - keyLength: number; - }; - auditLogging: { - enabled: boolean; - logLevel: 'all' | 'security' | 'critical'; - }; -} -export interface AuthToken { - userId: string; - organizationId: string; - permissions: string[]; - sessionId: string; - issuedAt: number; - expiresAt: number; - deviceId?: string; - ipAddress?: string; -} -export interface SecurityEvent { - eventId: string; - type: 'authentication' | 'authorization' | 'rate_limit' | 'validation' | 'suspicious_activity'; - severity: 'low' | 'medium' | 'high' | 'critical'; - userId?: string; - organizationId?: string; - action: string; - resource?: string; - ipAddress?: string; - userAgent?: string; - timestamp: string; - details: Record; -} -export interface VoiceCommandSecurity { - commandId: string; - userId: string; - organizationId: string; - commandType: string; - permissions: string[]; - sensitiveDataMask: boolean; - approved: boolean; - reason?: string; -} -export interface RateLimitResult { - allowed: boolean; - remaining: number; - resetTime: number; - retryAfter?: number; -} -export declare class SecurityManager extends EventEmitter { - private config; - private securityEvents; - private rateLimits; - private suspiciousActivities; - private encryptionKey; - private readonly maxAuditLogSize; - private cleanupTimer?; - constructor(config: SecurityConfig); - /** - * Authenticate user with JWT token - */ - authenticateToken(token: string): Promise; - /** - * Generate JWT token for authenticated user - */ - generateToken(userId: string, organizationId: string, permissions: string[], sessionId: string, deviceId?: string, ipAddress?: string): Promise; - /** - * Authorize voice command execution - */ - authorizeVoiceCommand(userId: string, organizationId: string, commandType: string, permissions: string[], commandData: Record): Promise; - /** - * Get required permissions for command type - */ - private getRequiredPermissions; - /** - * Check if command data contains sensitive information - */ - private containsSensitiveData; - /** - * Check rate limits for user actions - */ - checkRateLimit(userId: string, action: string): Promise; - /** - * Sanitize and validate input data - */ - validateAndSanitizeInput(input: any, schema: Record): { - valid: boolean; - sanitized?: any; - errors?: string[]; - }; - /** - * Sanitize string to prevent injection attacks - */ - private sanitizeString; - /** - * Track suspicious activity - */ - private trackSuspiciousActivity; - /** - * Encrypt sensitive data - */ - encryptSensitiveData(data: string): { - encrypted: string; - iv: string; - }; - /** - * Decrypt sensitive data - */ - decryptSensitiveData(encrypted: string, iv: string): string; - /** - * Hash password securely - */ - hashPassword(password: string): Promise; - /** - * Verify password against hash - */ - verifyPassword(password: string, hash: string): Promise; - /** - * Log security event - */ - private logSecurityEvent; - /** - * Get security events for audit - */ - getSecurityEvents(filters?: { - userId?: string; - organizationId?: string; - type?: string; - severity?: string; - limit?: number; - }): SecurityEvent[]; - /** - * Get security metrics - */ - getSecurityMetrics(): { - totalEvents: number; - eventsByType: Record; - eventsBySeverity: Record; - suspiciousActivities: number; - rateLimitViolations: number; - }; - /** - * Start cleanup timer for old data - */ - private startCleanupTimer; - /** - * Cleanup old rate limit and suspicious activity data - */ - private cleanupOldData; - /** - * Destroy and cleanup - */ - destroy(): void; -} -//# sourceMappingURL=SecurityManager.d.ts.map \ No newline at end of file diff --git a/backend/dist/security/SecurityManager.d.ts.map b/backend/dist/security/SecurityManager.d.ts.map deleted file mode 100644 index 1b2cb4c..0000000 --- a/backend/dist/security/SecurityManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"SecurityManager.d.ts","sourceRoot":"","sources":["../../src/security/SecurityManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/security/SecurityManager.js b/backend/dist/security/SecurityManager.js deleted file mode 100644 index ff6a28e..0000000 --- a/backend/dist/security/SecurityManager.js +++ /dev/null @@ -1,614 +0,0 @@ -"use strict"; -/** - * Security Manager - Phase 2 Security & Authentication - * Comprehensive security layer for voice command processing - * - * Success Criteria: - * - Voice command authentication and authorization - * - Rate limiting and abuse prevention - * - Audit logging for security events - * - Input sanitization and validation - */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.SecurityManager = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const crypto_1 = __importDefault(require("crypto")); -const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); -const bcryptjs_1 = __importDefault(require("bcryptjs")); -const logger_1 = require("../utils/logger"); -class SecurityManager extends events_1.EventEmitter { - config; - securityEvents = []; - rateLimits = new Map(); - suspiciousActivities = new Map(); - encryptionKey; - maxAuditLogSize = 10000; - cleanupTimer; - constructor(config) { - super(); - this.config = config; - this.encryptionKey = crypto_1.default.randomBytes(32); - this.startCleanupTimer(); - logger_1.logger.info('Security Manager initialized', { - rateLimitWindow: `${config.rateLimiting.windowMs / 1000}s`, - maxRequests: config.rateLimiting.maxRequests, - auditLogging: config.auditLogging.enabled, - }); - } - /** - * Authenticate user with JWT token - */ - async authenticateToken(token) { - const startTime = perf_hooks_1.performance.now(); - try { - const decoded = jsonwebtoken_1.default.verify(token, this.config.jwtSecret); - const authToken = { - userId: decoded.userId, - organizationId: decoded.organizationId, - permissions: decoded.permissions || [], - sessionId: decoded.sessionId, - issuedAt: decoded.iat * 1000, - expiresAt: decoded.exp * 1000, - deviceId: decoded.deviceId, - ipAddress: decoded.ipAddress, - }; - // Check if token is expired - if (authToken.expiresAt < Date.now()) { - await this.logSecurityEvent({ - type: 'authentication', - severity: 'medium', - userId: authToken.userId, - organizationId: authToken.organizationId, - action: 'token_expired', - details: { - sessionId: authToken.sessionId, - expiredAt: authToken.expiresAt, - }, - }); - return null; - } - const processingTime = perf_hooks_1.performance.now() - startTime; - await this.logSecurityEvent({ - type: 'authentication', - severity: 'low', - userId: authToken.userId, - organizationId: authToken.organizationId, - action: 'token_validated', - details: { - sessionId: authToken.sessionId, - processingTime: `${processingTime.toFixed(2)}ms`, - }, - }); - return authToken; - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - await this.logSecurityEvent({ - type: 'authentication', - severity: 'high', - action: 'token_validation_failed', - details: { - error: error.message, - processingTime: `${processingTime.toFixed(2)}ms`, - }, - }); - logger_1.logger.warn('Token authentication failed', { - error: error.message, - processingTime: `${processingTime.toFixed(2)}ms`, - }); - return null; - } - } - /** - * Generate JWT token for authenticated user - */ - async generateToken(userId, organizationId, permissions, sessionId, deviceId, ipAddress) { - const payload = { - userId, - organizationId, - permissions, - sessionId, - deviceId, - ipAddress, - }; - const token = jsonwebtoken_1.default.sign(payload, this.config.jwtSecret, { - expiresIn: this.config.jwtExpiresIn, - issuer: 'ceo-platform', - audience: organizationId, - }); - await this.logSecurityEvent({ - type: 'authentication', - severity: 'low', - userId, - organizationId, - action: 'token_generated', - details: { - sessionId, - deviceId, - ipAddress, - permissions: permissions.length, - }, - }); - return token; - } - /** - * Authorize voice command execution - */ - async authorizeVoiceCommand(userId, organizationId, commandType, permissions, commandData) { - const commandId = `cmd_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`; - try { - // Check required permissions for command type - const requiredPermissions = this.getRequiredPermissions(commandType, commandData); - const hasPermissions = requiredPermissions.every((perm) => permissions.includes(perm)); - if (!hasPermissions) { - await this.logSecurityEvent({ - type: 'authorization', - severity: 'medium', - userId, - organizationId, - action: 'voice_command_denied', - details: { - commandId, - commandType, - requiredPermissions, - userPermissions: permissions, - reason: 'insufficient_permissions', - }, - }); - return { - commandId, - userId, - organizationId, - commandType, - permissions, - sensitiveDataMask: false, - approved: false, - reason: 'Insufficient permissions', - }; - } - // Check for sensitive data that needs masking - const sensitiveDataMask = this.containsSensitiveData(commandData); - // Check rate limits - const rateLimitResult = await this.checkRateLimit(userId, 'voice_command'); - if (!rateLimitResult.allowed) { - await this.logSecurityEvent({ - type: 'rate_limit', - severity: 'medium', - userId, - organizationId, - action: 'voice_command_rate_limited', - details: { - commandId, - commandType, - remaining: rateLimitResult.remaining, - retryAfter: rateLimitResult.retryAfter, - }, - }); - return { - commandId, - userId, - organizationId, - commandType, - permissions, - sensitiveDataMask, - approved: false, - reason: 'Rate limit exceeded', - }; - } - await this.logSecurityEvent({ - type: 'authorization', - severity: 'low', - userId, - organizationId, - action: 'voice_command_authorized', - details: { - commandId, - commandType, - sensitiveDataMask, - permissions: permissions.length, - }, - }); - return { - commandId, - userId, - organizationId, - commandType, - permissions, - sensitiveDataMask, - approved: true, - }; - } - catch (error) { - await this.logSecurityEvent({ - type: 'authorization', - severity: 'high', - userId, - organizationId, - action: 'voice_command_authorization_error', - details: { - commandId, - commandType, - error: error.message, - }, - }); - return { - commandId, - userId, - organizationId, - commandType, - permissions, - sensitiveDataMask: false, - approved: false, - reason: 'Authorization error', - }; - } - } - /** - * Get required permissions for command type - */ - getRequiredPermissions(commandType, commandData) { - const permissionMap = { - create_task: ['tasks:write'], - update_task: ['tasks:write'], - delete_task: ['tasks:delete'], - read_task: ['tasks:read'], - create_channel: ['channels:write'], - update_channel: ['channels:write'], - delete_channel: ['channels:delete'], - join_channel: ['channels:join'], - upload_file: ['files:write'], - share_file: ['files:share'], - delete_file: ['files:delete'], - download_file: ['files:read'], - send_message: ['messages:write'], - read_messages: ['messages:read'], - admin_command: ['admin:all'], - }; - // Add context-specific permissions - const basePermissions = permissionMap[commandType] || ['general:execute']; - // Check if command affects other users' data - if (commandData.affectedUsers && commandData.affectedUsers.length > 0) { - basePermissions.push('users:affect_others'); - } - // Check if command involves sensitive operations - if (commandData.sensitiveOperation) { - basePermissions.push('sensitive:execute'); - } - return basePermissions; - } - /** - * Check if command data contains sensitive information - */ - containsSensitiveData(data) { - const sensitiveKeys = [ - 'password', - 'token', - 'secret', - 'key', - 'credentials', - 'ssn', - 'social', - 'credit', - 'card', - 'account', - 'bank', - ]; - const dataString = JSON.stringify(data).toLowerCase(); - return sensitiveKeys.some((key) => dataString.includes(key)); - } - /** - * Check rate limits for user actions - */ - async checkRateLimit(userId, action) { - const key = `${userId}:${action}`; - const now = Date.now(); - const windowStart = now - this.config.rateLimiting.windowMs; - const current = this.rateLimits.get(key) || { - count: 0, - resetTime: now + this.config.rateLimiting.windowMs, - }; - // Reset if window has expired - if (now >= current.resetTime) { - current.count = 0; - current.resetTime = now + this.config.rateLimiting.windowMs; - } - const allowed = current.count < this.config.rateLimiting.maxRequests; - if (allowed) { - current.count++; - this.rateLimits.set(key, current); - } - const result = { - allowed, - remaining: Math.max(0, this.config.rateLimiting.maxRequests - current.count), - resetTime: current.resetTime, - }; - if (!allowed) { - result.retryAfter = current.resetTime - now; - // Track suspicious activity - await this.trackSuspiciousActivity(userId, 'rate_limit_exceeded', { - action, - count: current.count, - limit: this.config.rateLimiting.maxRequests, - }); - } - return result; - } - /** - * Sanitize and validate input data - */ - validateAndSanitizeInput(input, schema) { - const errors = []; - const sanitized = {}; - try { - for (const [field, rules] of Object.entries(schema)) { - const value = input[field]; - // Required field validation - if (rules.required && (value === undefined || value === null || value === '')) { - errors.push(`${field} is required`); - continue; - } - // Skip further validation if field is optional and not provided - if (!rules.required && (value === undefined || value === null)) { - continue; - } - // Type validation - if (rules.type && typeof value !== rules.type) { - errors.push(`${field} must be of type ${rules.type}`); - continue; - } - // String validation - if (rules.type === 'string' && typeof value === 'string') { - let sanitizedValue = value; - // Trim whitespace - sanitizedValue = sanitizedValue.trim(); - // Length validation - if (rules.minLength && sanitizedValue.length < rules.minLength) { - errors.push(`${field} must be at least ${rules.minLength} characters`); - continue; - } - if (rules.maxLength && sanitizedValue.length > rules.maxLength) { - errors.push(`${field} must not exceed ${rules.maxLength} characters`); - continue; - } - // Pattern validation - if (rules.pattern && !new RegExp(rules.pattern).test(sanitizedValue)) { - errors.push(`${field} format is invalid`); - continue; - } - // HTML/Script injection prevention - sanitizedValue = this.sanitizeString(sanitizedValue); - sanitized[field] = sanitizedValue; - } - else { - sanitized[field] = value; - } - } - if (errors.length > 0) { - return { valid: false, errors }; - } - return { valid: true, sanitized }; - } - catch (error) { - logger_1.logger.error('Input validation error', { error: error.message }); - return { valid: false, errors: ['Validation error occurred'] }; - } - } - /** - * Sanitize string to prevent injection attacks - */ - sanitizeString(input) { - return input - .replace(/[<>]/g, '') // Remove potential HTML tags - .replace(/javascript:/gi, '') // Remove javascript: protocols - .replace(/on\w+\s*=/gi, '') // Remove event handlers - .replace(/script/gi, 'sc_ript') // Neutralize script tags - .replace(/eval\s*\(/gi, 'ev_al(') // Neutralize eval calls - .trim(); - } - /** - * Track suspicious activity - */ - async trackSuspiciousActivity(userId, activityType, details) { - const key = `${userId}:${activityType}`; - const now = Date.now(); - const activity = this.suspiciousActivities.get(key) || { - count: 0, - firstSeen: now, - }; - activity.count++; - this.suspiciousActivities.set(key, activity); - // Log as security event if threshold exceeded - if (activity.count > 5) { - await this.logSecurityEvent({ - type: 'suspicious_activity', - severity: 'high', - userId, - action: 'repeated_suspicious_activity', - details: { - activityType, - count: activity.count, - firstSeen: activity.firstSeen, - duration: now - activity.firstSeen, - ...details, - }, - }); - } - } - /** - * Encrypt sensitive data - */ - encryptSensitiveData(data) { - const iv = crypto_1.default.randomBytes(16); - const cipher = crypto_1.default.createCipher(this.config.encryption.algorithm, this.encryptionKey); - let encrypted = cipher.update(data, 'utf8', 'hex'); - encrypted += cipher.final('hex'); - return { - encrypted, - iv: iv.toString('hex'), - }; - } - /** - * Decrypt sensitive data - */ - decryptSensitiveData(encrypted, iv) { - const decipher = crypto_1.default.createDecipher(this.config.encryption.algorithm, this.encryptionKey); - let decrypted = decipher.update(encrypted, 'hex', 'utf8'); - decrypted += decipher.final('utf8'); - return decrypted; - } - /** - * Hash password securely - */ - async hashPassword(password) { - return await bcryptjs_1.default.hash(password, this.config.bcryptSaltRounds); - } - /** - * Verify password against hash - */ - async verifyPassword(password, hash) { - return await bcryptjs_1.default.compare(password, hash); - } - /** - * Log security event - */ - async logSecurityEvent(event) { - if (!this.config.auditLogging.enabled) { - return; - } - const securityEvent = { - eventId: `sec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - type: event.type || 'authentication', - severity: event.severity || 'low', - userId: event.userId ?? '', - organizationId: event.organizationId ?? '', - action: event.action || 'unknown', - resource: event.resource ?? '', - ipAddress: event.ipAddress ?? '', - userAgent: event.userAgent ?? '', - timestamp: new Date().toISOString(), - details: event.details || {}, - }; - // Add to in-memory log - this.securityEvents.push(securityEvent); - // Trim log if too large - if (this.securityEvents.length > this.maxAuditLogSize) { - this.securityEvents.shift(); - } - // Log based on severity and configuration - const shouldLog = this.config.auditLogging.logLevel === 'all' || - (this.config.auditLogging.logLevel === 'security' && - ['medium', 'high', 'critical'].includes(securityEvent.severity)) || - (this.config.auditLogging.logLevel === 'critical' && - ['critical'].includes(securityEvent.severity)); - if (shouldLog) { - logger_1.logger.info('Security event logged', { - eventId: securityEvent.eventId, - type: securityEvent.type, - severity: securityEvent.severity, - action: securityEvent.action, - userId: securityEvent.userId, - organizationId: securityEvent.organizationId, - }); - } - this.emit('security_event', securityEvent); - } - /** - * Get security events for audit - */ - getSecurityEvents(filters) { - let events = [...this.securityEvents]; - if (filters) { - if (filters.userId) { - events = events.filter((e) => e.userId === filters.userId); - } - if (filters.organizationId) { - events = events.filter((e) => e.organizationId === filters.organizationId); - } - if (filters.type) { - events = events.filter((e) => e.type === filters.type); - } - if (filters.severity) { - events = events.filter((e) => e.severity === filters.severity); - } - if (filters.limit) { - events = events.slice(-filters.limit); - } - } - return events.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); - } - /** - * Get security metrics - */ - getSecurityMetrics() { - const eventsByType = {}; - const eventsBySeverity = {}; - let rateLimitViolations = 0; - for (const event of this.securityEvents) { - eventsByType[event.type] = (eventsByType[event.type] || 0) + 1; - eventsBySeverity[event.severity] = (eventsBySeverity[event.severity] || 0) + 1; - if (event.type === 'rate_limit') { - rateLimitViolations++; - } - } - return { - totalEvents: this.securityEvents.length, - eventsByType, - eventsBySeverity, - suspiciousActivities: this.suspiciousActivities.size, - rateLimitViolations, - }; - } - /** - * Start cleanup timer for old data - */ - startCleanupTimer() { - this.cleanupTimer = setInterval(() => { - this.cleanupOldData(); - }, 3600000); // Every hour - } - /** - * Cleanup old rate limit and suspicious activity data - */ - cleanupOldData() { - const now = Date.now(); - const cutoffTime = now - 24 * 60 * 60 * 1000; // 24 hours ago - // Cleanup old rate limits - let cleanedRateLimits = 0; - for (const [key, data] of this.rateLimits.entries()) { - if (data.resetTime < now) { - this.rateLimits.delete(key); - cleanedRateLimits++; - } - } - // Cleanup old suspicious activities - let cleanedActivities = 0; - for (const [key, activity] of this.suspiciousActivities.entries()) { - if (activity.firstSeen < cutoffTime) { - this.suspiciousActivities.delete(key); - cleanedActivities++; - } - } - if (cleanedRateLimits > 0 || cleanedActivities > 0) { - logger_1.logger.debug('Security data cleanup completed', { - cleanedRateLimits, - cleanedActivities, - }); - } - } - /** - * Destroy and cleanup - */ - destroy() { - if (this.cleanupTimer) { - clearInterval(this.cleanupTimer); - } - this.removeAllListeners(); - logger_1.logger.info('Security Manager destroyed'); - } -} -exports.SecurityManager = SecurityManager; -//# sourceMappingURL=SecurityManager.js.map \ No newline at end of file diff --git a/backend/dist/security/SecurityManager.js.map b/backend/dist/security/SecurityManager.js.map deleted file mode 100644 index c1625f2..0000000 --- a/backend/dist/security/SecurityManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"SecurityManager.js","sourceRoot":"","sources":["../../src/security/SecurityManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/server.d.ts b/backend/dist/server.d.ts deleted file mode 100644 index d0c9b2d..0000000 --- a/backend/dist/server.d.ts +++ /dev/null @@ -1,77 +0,0 @@ -import { FastifyInstance } from 'fastify'; -import { authenticate, optionalAuthenticate } from './auth/middleware'; -import { TokenPayload } from './auth/jwt'; -/** - * High-Performance Fastify Server - * Enterprise-grade API infrastructure with comprehensive monitoring - */ -declare class APIServer { - private app; - private httpServer; - private isShuttingDown; - constructor(); - /** - * Initialize server with all plugins and routes - */ - initialize(): Promise; - /** - * Register core Fastify plugins - */ - private registerCorePlugins; - /** - * Register security plugins - */ - private registerSecurityPlugins; - /** - * Register validation and serialization plugins - */ - private registerValidationPlugins; - /** - * Setup middleware and hooks - */ - private setupMiddleware; - /** - * Setup global hooks - */ - private setupGlobalHooks; - /** - * Setup health check endpoints - */ - private setupHealthChecks; - /** - * Register application routes - */ - private registerRoutes; - /** - * Setup centralized error handling - */ - private setupErrorHandling; - /** - * Start the server - */ - start(): Promise; - /** - * Setup graceful shutdown handlers - */ - private setupGracefulShutdown; - /** - * Get Fastify instance for testing - */ - getApp(): FastifyInstance; -} -declare module 'fastify' { - interface FastifyRequest { - startTime?: number; - user?: TokenPayload & { - isAuthenticated: boolean; - id: string; - }; - } - interface FastifyInstance { - authenticate: typeof authenticate; - optionalAuthenticate: typeof optionalAuthenticate; - } -} -export { APIServer }; -export declare const server: APIServer; -//# sourceMappingURL=server.d.ts.map \ No newline at end of file diff --git a/backend/dist/server.d.ts.map b/backend/dist/server.d.ts.map deleted file mode 100644 index 81e6d9f..0000000 --- a/backend/dist/server.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAgB,EAAE,eAAe,EAAgC,MAAM,SAAS,CAAC;AAYjF,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAKvE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C;;;GAGG;AAEH,cAAM,SAAS;IACb,OAAO,CAAC,GAAG,CAAkB;IAC7B,OAAO,CAAC,UAAU,CAAgD;IAClE,OAAO,CAAC,cAAc,CAAS;;IAqB/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IA6BjC;;OAEG;YACW,mBAAmB;IA6HjC;;OAEG;YACW,uBAAuB;IAQrC;;OAEG;YACW,yBAAyB;IAsBvC;;OAEG;YACW,eAAe;IAwD7B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAyGzB;;OAEG;YACW,cAAc;IA4B5B;;OAEG;IACH,OAAO,CAAC,kBAAkB;IAkF1B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAkH5B;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAiD7B;;OAEG;IACH,MAAM,IAAI,eAAe;CAG1B;AAGD,OAAO,QAAQ,SAAS,CAAC;IACvB,UAAU,cAAc;QACtB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,YAAY,GAAG;YACpB,eAAe,EAAE,OAAO,CAAC;YACzB,EAAE,EAAE,MAAM,CAAC;SACZ,CAAC;KACH;IAED,UAAU,eAAe;QACvB,YAAY,EAAE,OAAO,YAAY,CAAC;QAClC,oBAAoB,EAAE,OAAO,oBAAoB,CAAC;KACnD;CACF;AAGD,OAAO,EAAE,SAAS,EAAE,CAAC;AACrB,eAAO,MAAM,MAAM,WAAkB,CAAC"} \ No newline at end of file diff --git a/backend/dist/server.js b/backend/dist/server.js deleted file mode 100644 index 1b269ac..0000000 --- a/backend/dist/server.js +++ /dev/null @@ -1,677 +0,0 @@ -"use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || (function () { - var ownKeys = function(o) { - ownKeys = Object.getOwnPropertyNames || function (o) { - var ar = []; - for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; - return ar; - }; - return ownKeys(o); - }; - return function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); - __setModuleDefault(result, mod); - return result; - }; -})(); -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.server = exports.APIServer = void 0; -const fastify_1 = __importDefault(require("fastify")); -const index_1 = require("./config/index"); -const database_1 = require("./config/database"); -const redis_1 = require("./config/redis"); -const SocketManager_1 = require("./websocket/SocketManager"); -const logger_1 = require("./utils/logger"); -const errors_1 = require("./utils/errors"); -const middleware_1 = require("./auth/middleware"); -const routes_1 = require("./auth/routes"); -const index_2 = require("./api/index"); -const migrator_1 = require("./db/migrator"); -/** - * High-Performance Fastify Server - * Enterprise-grade API infrastructure with comprehensive monitoring - */ -class APIServer { - app; - httpServer = null; - isShuttingDown = false; - constructor() { - this.app = (0, fastify_1.default)({ - logger: false, // We use pino directly - requestIdLogLabel: 'requestId', - requestIdHeader: 'x-request-id', - bodyLimit: 1048576, // 1MB default - keepAliveTimeout: 72000, - connectionTimeout: 0, - pluginTimeout: 10000, - ignoreTrailingSlash: true, - ignoreDuplicateSlashes: true, - maxParamLength: 500, - trustProxy: true, - }); - this.setupGlobalHooks(); - this.setupHealthChecks(); - } - /** - * Initialize server with all plugins and routes - */ - async initialize() { - try { - logger_1.logger.info('Initializing API server...'); - // Register core plugins - await this.registerCorePlugins(); - // Register security plugins - await this.registerSecurityPlugins(); - // Register validation and serialization - await this.registerValidationPlugins(); - // Setup middleware - await this.setupMiddleware(); - // Register routes - await this.registerRoutes(); - // Setup error handling - this.setupErrorHandling(); - logger_1.logger.info('API server initialized successfully'); - } - catch (error) { - logger_1.logger.error({ error }, 'Failed to initialize API server'); - throw error; - } - } - /** - * Register core Fastify plugins - */ - async registerCorePlugins() { - // CORS support - await this.app.register(Promise.resolve().then(() => __importStar(require('@fastify/cors'))), { - origin: index_1.config.api.cors.origin, - credentials: index_1.config.api.cors.credentials, - methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'], - }); - // Helmet for security headers - await this.app.register(Promise.resolve().then(() => __importStar(require('@fastify/helmet'))), { - ...(index_1.config.app.isProduction ? {} : { contentSecurityPolicy: false }), - crossOriginEmbedderPolicy: false, - }); - // Request/Response compression - temporarily disabled for debugging - // await this.app.register(import('@fastify/compress'), { - // global: true, - // encodings: ['gzip', 'deflate'], - // threshold: 1024, - // }); - // Rate limiting (global) - await this.app.register(Promise.resolve().then(() => __importStar(require('@fastify/rate-limit'))), { - max: index_1.config.api.rateLimit.max, - timeWindow: index_1.config.api.rateLimit.timeWindow, - skipOnError: true, - keyGenerator: (request) => { - return request.user?.userId || request.ip; - }, - errorResponseBuilder: () => ({ - error: { - message: 'Rate limit exceeded', - code: 'RATE_LIMIT_ERROR', - statusCode: 429, - }, - }), - }); - // Multipart support for file uploads - await this.app.register(Promise.resolve().then(() => __importStar(require('@fastify/multipart'))), { - limits: { - fieldNameSize: 100, - fieldSize: 1048576, // 1MB - fields: 10, - fileSize: 10485760, // 10MB - files: 5, - }, - }); - // Static file serving - await this.app.register(Promise.resolve().then(() => __importStar(require('@fastify/static'))), { - root: require('path').join(__dirname, '..', 'public'), - prefix: '/public/', - }); - // Swagger/OpenAPI documentation - await this.app.register(Promise.resolve().then(() => __importStar(require('@fastify/swagger'))), { - openapi: { - openapi: '3.0.0', - info: { - title: 'CEO Communication Platform API', - description: 'Enterprise-grade backend API for the CEO Communication Platform with voice processing and real-time collaboration', - version: '1.0.0', - contact: { - name: 'CEO Communication Platform Team', - email: 'support@ceoplatform.com' - }, - license: { - name: 'Private License' - } - }, - servers: [ - { - url: index_1.config.app.isDevelopment ? 'http://localhost:3001' : 'https://api.ceoplatform.com', - description: index_1.config.app.isDevelopment ? 'Development server' : 'Production server' - } - ], - components: { - securitySchemes: { - BearerAuth: { - type: 'http', - scheme: 'bearer', - bearerFormat: 'JWT', - description: 'JWT token obtained from /auth/login endpoint' - } - } - }, - security: [ - { - BearerAuth: [] - } - ] - } - }); - // Swagger UI - await this.app.register(Promise.resolve().then(() => __importStar(require('@fastify/swagger-ui'))), { - routePrefix: '/docs', - uiConfig: { - docExpansion: 'none', - deepLinking: false, - defaultModelsExpandDepth: 2, - defaultModelExpandDepth: 2, - displayOperationId: false, - displayRequestDuration: true, - showExtensions: true, - showCommonExtensions: true, - tryItOutEnabled: true - }, - uiHooks: { - onRequest: function (request, reply, next) { - next(); - }, - preHandler: function (request, reply, next) { - next(); - } - }, - staticCSP: true, - transformStaticCSP: (header) => header - }); - logger_1.logger.debug('Core plugins registered'); - } - /** - * Register security plugins - */ - async registerSecurityPlugins() { - // JWT authentication decorator - this.app.decorate('authenticate', middleware_1.authenticate); - this.app.decorate('optionalAuthenticate', middleware_1.optionalAuthenticate); - logger_1.logger.debug('Security plugins registered'); - } - /** - * Register validation and serialization plugins - */ - async registerValidationPlugins() { - // Type providers for better TypeScript support - this.app.addHook('onRoute', (routeOptions) => { - if (routeOptions.schema) { - // Add response serialization optimization - routeOptions.serializerCompiler = ({ schema }) => { - return (data) => JSON.stringify(data); - }; - // Add validation error handler - routeOptions.validatorCompiler = ({ schema }) => { - return (data) => { - // Custom validation logic can be added here - return { value: data }; - }; - }; - } - }); - logger_1.logger.debug('Validation plugins registered'); - } - /** - * Setup middleware and hooks - */ - async setupMiddleware() { - // Performance monitoring middleware - this.app.addHook('onRequest', async (request, reply) => { - request.startTime = Date.now(); - }); - this.app.addHook('onSend', async (request, reply, payload) => { - const duration = Date.now() - (request.startTime || 0); - logger_1.loggers.performance.info({ - method: request.method, - url: request.url, - statusCode: reply.statusCode, - duration, - userId: request.user?.userId, - ip: request.ip, - }, 'API Request'); - // Log slow requests - if (duration > 2000) { - // 2 seconds threshold - logger_1.loggers.performance.warn?.({ - method: request.method, - url: request.url, - duration, - userId: request.user?.userId, - }, 'Slow API request detected'); - } - return payload; - }); - // Request ID logging - this.app.addHook('preHandler', async (request, reply) => { - logger_1.loggers.api.info({ - requestId: request.id, - method: request.method, - url: request.url, - ip: request.ip, - userAgent: request.headers['user-agent'], - userId: request.user?.userId, - }, 'Incoming request'); - }); - logger_1.logger.debug('Middleware setup complete'); - } - /** - * Setup global hooks - */ - setupGlobalHooks() { - // Graceful shutdown handling - this.app.addHook('onClose', async () => { - this.isShuttingDown = true; - logger_1.logger.info('Server shutting down gracefully...'); - }); - // Health check for load balancers - this.app.addHook('preHandler', async (request, reply) => { - if (this.isShuttingDown) { - reply.code(503).send({ - error: { - message: 'Server is shutting down', - code: 'SERVER_SHUTDOWN', - statusCode: 503, - }, - }); - return; - } - }); - } - /** - * Setup health check endpoints - */ - setupHealthChecks() { - // Liveness probe - this.app.get('/health', async (request, reply) => { - if (this.isShuttingDown) { - reply.code(503).send({ status: 'shutting_down' }); - return; - } - reply.send({ - status: 'healthy', - timestamp: new Date().toISOString(), - uptime: process.uptime(), - version: process.env.npm_package_version || '1.0.0', - }); - }); - // Readiness probe with dependency checks - this.app.get('/health/ready', async (request, reply) => { - try { - const checks = await Promise.allSettled([ - logger_1.performanceLogger.trackAsyncOperation(() => (0, database_1.healthCheck)(), 'database_health_check'), - logger_1.performanceLogger.trackAsyncOperation(() => redis_1.redisManager.healthCheck(), 'redis_health_check'), - logger_1.performanceLogger.trackAsyncOperation(() => Promise.resolve(SocketManager_1.socketManager.getServer() !== null), 'websocket_health_check'), - ]); - const dbCheck = checks[0]; - const redisCheck = checks[1]; - const websocketCheck = checks[2]; - const dbHealthy = dbCheck.status === 'fulfilled' && dbCheck.value?.status === 'healthy'; - const redisHealthy = redisCheck.status === 'fulfilled' && redisCheck.value === true; - const websocketHealthy = websocketCheck.status === 'fulfilled' && websocketCheck.value === true; - const isReady = dbHealthy && redisHealthy && websocketHealthy; - if (isReady) { - reply.send({ - status: 'ready', - timestamp: new Date().toISOString(), - checks: { - database: 'healthy', - redis: 'healthy', - websocket: 'healthy', - }, - metrics: { - connectedUsers: SocketManager_1.socketManager.getConnectedUsersCount(), - }, - }); - } - else { - reply.code(503).send({ - status: 'not_ready', - timestamp: new Date().toISOString(), - checks: { - database: dbHealthy ? 'healthy' : 'unhealthy', - redis: redisHealthy ? 'healthy' : 'unhealthy', - websocket: websocketHealthy ? 'healthy' : 'unhealthy', - }, - }); - } - } - catch (error) { - logger_1.logger.error({ error }, 'Health check failed'); - reply.code(503).send({ - status: 'not_ready', - timestamp: new Date().toISOString(), - error: 'Health check failed', - }); - } - }); - // Detailed system metrics (for monitoring) - this.app.get('/health/metrics', async (request, reply) => { - const memoryUsage = process.memoryUsage(); - const cpuUsage = process.cpuUsage(); - reply.send({ - status: 'healthy', - timestamp: new Date().toISOString(), - metrics: { - uptime: process.uptime(), - memory: { - rss: Math.round(memoryUsage.rss / 1024 / 1024), - heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024), - heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024), - external: Math.round(memoryUsage.external / 1024 / 1024), - }, - cpu: { - user: cpuUsage.user, - system: cpuUsage.system, - }, - node: { - version: process.version, - platform: process.platform, - arch: process.arch, - }, - }, - }); - }); - } - /** - * Register application routes - */ - async registerRoutes() { - // API versioning - await this.app.register((fastify, opts, next) => { - // Authentication routes (no additional auth middleware needed) - fastify.register(routes_1.registerAuthRoutes); - // Main API routes (all require authentication) - fastify.register(index_2.registerAPIRoutes); - next(); - }, { prefix: '/api/v1' }); - // Root endpoint - this.app.get('/', async (request, reply) => { - reply.send({ - name: 'CEO Communication Platform API', - version: process.env.npm_package_version || '1.0.0', - environment: index_1.config.app.env, - timestamp: new Date().toISOString(), - }); - }); - logger_1.logger.debug('Routes registered successfully'); - } - /** - * Setup centralized error handling - */ - setupErrorHandling() { - // Global error handler - this.app.setErrorHandler((error, request, reply) => { - const requestId = request.id; - const context = { - requestId, - method: request.method, - url: request.url, - ip: request.ip, - userId: request.user?.userId, - userAgent: request.headers['user-agent'], - }; - // Handle operational errors (business logic, validation, etc.) - if (error instanceof errors_1.BaseError) { - logger_1.loggers.api.warn({ error: error.toJSON(), ...context }, 'Operational error occurred'); - reply.code(error.statusCode).send((0, errors_1.formatErrorResponse)(error)); - return; - } - // Handle Fastify validation errors - if (error.name === 'FastifyError' && typeof error.statusCode === 'number') { - const statusCode = error.statusCode; - logger_1.loggers.api.warn({ error: error.message, statusCode, ...context }, 'Fastify validation error'); - reply.code(statusCode).send({ - error: { - message: error.message, - code: 'VALIDATION_ERROR', - statusCode, - }, - timestamp: new Date().toISOString(), - }); - return; - } - // Handle unexpected errors - logger_1.loggers.api.error({ error: error.stack, ...context }, 'Unexpected server error'); - // Don't expose internal errors in production - const message = index_1.config.app.isProduction ? 'An internal server error occurred' : error.message; - reply.code(500).send({ - error: { - message, - code: 'INTERNAL_SERVER_ERROR', - statusCode: 500, - }, - timestamp: new Date().toISOString(), - ...(index_1.config.app.isDevelopment && { stack: error.stack }), - }); - }); - // Handle 404 errors - this.app.setNotFoundHandler((request, reply) => { - logger_1.loggers.api.warn({ - method: request.method, - url: request.url, - ip: request.ip, - userId: request.user?.userId, - }, 'Route not found'); - reply.code(404).send({ - error: { - message: `Route ${request.method} ${request.url} not found`, - code: 'ROUTE_NOT_FOUND', - statusCode: 404, - }, - timestamp: new Date().toISOString(), - }); - }); - logger_1.logger.debug('Error handling setup complete'); - } - /** - * Start the server - */ - async start() { - try { - const overallTimer = logger_1.startupLogger.createTimer('API Server Startup'); - const services = []; - logger_1.startupLogger.logStep('API Server Startup'); - // Initialize database - const dbTimer = logger_1.startupLogger.createTimer('Database'); - try { - await (0, database_1.initializeDatabase)(); - services.push({ name: 'Database', status: true, duration: dbTimer.end() }); - } - catch (error) { - services.push({ name: 'Database', status: false, duration: dbTimer.end() }); - throw error; - } - // Initialize Redis - const redisTimer = logger_1.startupLogger.createTimer('Redis'); - try { - await redis_1.redisManager.initialize(); - services.push({ name: 'Redis', status: true, duration: redisTimer.end() }); - } - catch (error) { - services.push({ name: 'Redis', status: false, duration: redisTimer.end() }); - throw error; - } - // Initialize WebSocket server - const wsTimer = logger_1.startupLogger.createTimer('WebSocket Server'); - try { - // Get the HTTP server from Fastify after it starts listening - // We'll initialize Socket.IO after the server is listening - services.push({ name: 'WebSocket Server', status: true, duration: wsTimer.end() }); - } - catch (error) { - services.push({ name: 'WebSocket Server', status: false, duration: wsTimer.end() }); - throw error; - } - // Run migrations - const migrationTimer = logger_1.startupLogger.createTimer('Database Migrations'); - try { - await (0, migrator_1.runMigrations)(); - services.push({ name: 'Database Migrations', status: true, duration: migrationTimer.end() }); - } - catch (error) { - services.push({ name: 'Database Migrations', status: false, duration: migrationTimer.end() }); - throw error; - } - // Run database seeding (only in development) - if (index_1.config.app.isDevelopment) { - const seedTimer = logger_1.startupLogger.createTimer('Database Seeding'); - try { - logger_1.logger.debug('Skipping database seeding (data already exists)'); - services.push({ name: 'Database Seeding', status: true, duration: seedTimer.end() }); - } - catch (error) { - logger_1.logger.warn({ error }, 'Database seeding failed, continuing without seed data'); - services.push({ name: 'Database Seeding', status: false, duration: seedTimer.end() }); - } - } - // Initialize server - const serverTimer = logger_1.startupLogger.createTimer('Server Configuration'); - try { - await this.initialize(); - services.push({ name: 'Server Configuration', status: true, duration: serverTimer.end() }); - } - catch (error) { - services.push({ name: 'Server Configuration', status: false, duration: serverTimer.end() }); - throw error; - } - // Start listening - const listenTimer = logger_1.startupLogger.createTimer('Server Listen'); - try { - // Use Fastify's built-in listen method - const address = await this.app.listen({ - port: index_1.config.app.port, - host: index_1.config.app.host, - }); - // Now initialize Socket.IO with Fastify's server - this.httpServer = this.app.server; - if (this.httpServer) { - await SocketManager_1.socketManager.initialize(this.httpServer); - } - services.push({ name: 'Server Listen', status: true, duration: listenTimer.end() }); - // Log startup summary - const totalDuration = overallTimer.end(); - services.push({ name: 'Total Startup Time', status: true, duration: totalDuration }); - logger_1.startupLogger.logSummary(services); - logger_1.logger.info({ - address, - port: index_1.config.app.port, - host: index_1.config.app.host, - environment: index_1.config.app.env, - }, '🚀 API server running'); - } - catch (error) { - services.push({ name: 'Server Listen', status: false, duration: listenTimer.end() }); - throw error; - } - // Setup graceful shutdown - this.setupGracefulShutdown(); - } - catch (error) { - logger_1.logger.error({ error }, 'Failed to start API server'); - throw error; - } - } - /** - * Setup graceful shutdown handlers - */ - setupGracefulShutdown() { - const gracefulShutdown = async (signal) => { - logger_1.logger.info({ signal }, 'Received shutdown signal, starting graceful shutdown...'); - this.isShuttingDown = true; - try { - // Stop accepting new connections - await this.app.close(); - logger_1.logger.info('Server closed successfully'); - // Close WebSocket server - logger_1.logger.info('Closing WebSocket server...'); - await SocketManager_1.socketManager.close(); - // Close Redis connections - logger_1.logger.info('Closing Redis connections...'); - await redis_1.redisManager.close(); - // Close HTTP server - if (this.httpServer) { - this.httpServer.close(); - } - // Exit process - process.exit(0); - } - catch (error) { - logger_1.logger.error({ error }, 'Error during graceful shutdown'); - process.exit(1); - } - }; - // Handle different shutdown signals - process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); - process.on('SIGINT', () => gracefulShutdown('SIGINT')); - // Handle uncaught exceptions - process.on('uncaughtException', (error) => { - logger_1.logger.fatal({ error }, 'Uncaught exception occurred'); - process.exit(1); - }); - // Handle unhandled promise rejections - process.on('unhandledRejection', (reason, promise) => { - logger_1.logger.fatal({ reason, promise }, 'Unhandled promise rejection'); - process.exit(1); - }); - } - /** - * Get Fastify instance for testing - */ - getApp() { - return this.app; - } -} -exports.APIServer = APIServer; -exports.server = new APIServer(); -// Start server if this file is run directly -if (require.main === module) { - console.log('Starting server directly...'); - exports.server.start().then(() => { - console.log('Server started successfully, keeping process alive...'); - // Keep the process alive - setInterval(() => { - console.log('Server is running...', new Date().toISOString()); - }, 30000); // Log every 30 seconds - }).catch((error) => { - logger_1.logger.fatal({ error }, 'Failed to start server'); - process.exit(1); - }); -} -//# sourceMappingURL=server.js.map \ No newline at end of file diff --git a/backend/dist/server.js.map b/backend/dist/server.js.map deleted file mode 100644 index ae02bc5..0000000 --- a/backend/dist/server.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/services/CacheService.d.ts b/backend/dist/services/CacheService.d.ts deleted file mode 100644 index 56fc445..0000000 --- a/backend/dist/services/CacheService.d.ts +++ /dev/null @@ -1,137 +0,0 @@ -/** - * High-performance caching service with Redis backend - * Enterprise-grade caching with compression, serialization, and TTL management - */ -export interface CacheOptions { - ttl?: number; - compress?: boolean; - serialize?: boolean; - namespace?: string; - tags?: string[]; -} -export interface CacheSetOptions extends CacheOptions { - nx?: boolean; - ex?: number; -} -export interface CacheStats { - totalKeys: number; - memoryUsage: number; - hitRate: number; - operations: { - gets: number; - sets: number; - deletes: number; - hits: number; - misses: number; - }; -} -declare class CacheService { - private defaultTTL; - private compressionThreshold; - private maxKeyLength; - private namespacePrefix; - private namespaces; - /** - * Generate cache key with namespace - */ - private generateKey; - /** - * Serialize value for storage - */ - private serialize; - /** - * Deserialize value from storage - */ - private deserialize; - /** - * Compress value if needed - */ - private compress; - /** - * Decompress value if needed - */ - private decompress; - /** - * Get value from cache - */ - get(key: string, options?: CacheOptions): Promise; - /** - * Set value in cache - */ - set(key: string, value: any, options?: CacheSetOptions): Promise; - /** - * Delete value from cache - */ - delete(key: string, options?: CacheOptions): Promise; - /** - * Check if key exists in cache - */ - exists(key: string, options?: CacheOptions): Promise; - /** - * Set expiration time for existing key - */ - expire(key: string, ttl: number, options?: CacheOptions): Promise; - /** - * Get remaining TTL for key - */ - ttl(key: string, options?: CacheOptions): Promise; - /** - * Get multiple values at once - */ - mget(keys: string[], options?: CacheOptions): Promise<(T | null)[]>; - /** - * Set multiple values at once - */ - mset(keyValuePairs: Record, options?: CacheOptions): Promise; - /** - * Delete multiple keys at once - */ - mdel(keys: string[], options?: CacheOptions): Promise; - /** - * Clear all cache keys with pattern - */ - clear(pattern?: string, options?: CacheOptions): Promise; - /** - * Tag a key for bulk invalidation - */ - private tagKey; - /** - * Invalidate all keys with specific tags - */ - invalidateByTags(tags: string[]): Promise; - /** - * Get cache statistics - */ - getStats(): Promise; - /** - * Cache wrapper for functions - */ - wrap(key: string, fn: () => Promise, options?: CacheOptions): Promise; - users: { - get: (key: string, options?: CacheOptions) => Promise; - set: (key: string, value: any, options?: CacheSetOptions) => Promise; - delete: (key: string) => Promise; - clear: (pattern?: string) => Promise; - }; - channels: { - get: (key: string, options?: CacheOptions) => Promise; - set: (key: string, value: any, options?: CacheSetOptions) => Promise; - delete: (key: string) => Promise; - clear: (pattern?: string) => Promise; - }; - tasks: { - get: (key: string, options?: CacheOptions) => Promise; - set: (key: string, value: any, options?: CacheSetOptions) => Promise; - delete: (key: string) => Promise; - clear: (pattern?: string) => Promise; - }; - sessions: { - get: (key: string, options?: CacheOptions) => Promise; - set: (key: string, value: any, options?: CacheSetOptions) => Promise; - delete: (key: string) => Promise; - clear: (pattern?: string) => Promise; - }; -} -export declare const cacheService: CacheService; -export default cacheService; -//# sourceMappingURL=CacheService.d.ts.map \ No newline at end of file diff --git a/backend/dist/services/CacheService.d.ts.map b/backend/dist/services/CacheService.d.ts.map deleted file mode 100644 index 2153e73..0000000 --- a/backend/dist/services/CacheService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"CacheService.d.ts","sourceRoot":"","sources":["../../src/services/CacheService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/services/CacheService.js b/backend/dist/services/CacheService.js deleted file mode 100644 index f3790a1..0000000 --- a/backend/dist/services/CacheService.js +++ /dev/null @@ -1,464 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.cacheService = void 0; -const redis_1 = require("@config/redis"); -const logger_1 = require("@utils/logger"); -const errors_1 = require("@utils/errors"); -class CacheService { - defaultTTL = 3600; // 1 hour default - compressionThreshold = 1024; // Compress values larger than 1KB - maxKeyLength = 250; // Redis key length limit - namespacePrefix = 'ceo-platform:'; - // Cache namespaces for different data types - namespaces = { - users: 'users:', - channels: 'channels:', - tasks: 'tasks:', - sessions: 'sessions:', - analytics: 'analytics:', - voice: 'voice:', - general: 'general:', - }; - /** - * Generate cache key with namespace - */ - generateKey(key, namespace) { - const prefix = this.namespacePrefix + (namespace || this.namespaces.general); - const fullKey = prefix + key; - if (fullKey.length > this.maxKeyLength) { - // Use hash for very long keys - const crypto = require('crypto'); - const hash = crypto.createHash('sha256').update(fullKey).digest('hex').substring(0, 16); - return `${prefix}hashed:${hash}`; - } - return fullKey; - } - /** - * Serialize value for storage - */ - serialize(value, options = {}) { - if (options.serialize === false) { - return value; - } - try { - return JSON.stringify(value); - } - catch (error) { - logger_1.loggers.cache.error({ error, value }, 'Failed to serialize cache value'); - throw new errors_1.CacheError('Cache serialization failed'); - } - } - /** - * Deserialize value from storage - */ - deserialize(value, options = {}) { - if (options.serialize === false) { - return value; - } - try { - return JSON.parse(value); - } - catch (error) { - logger_1.loggers.cache.warn({ error, value }, 'Failed to deserialize cache value, returning raw'); - return value; - } - } - /** - * Compress value if needed - */ - async compress(value, options = {}) { - if (!options.compress || value.length < this.compressionThreshold) { - return value; - } - try { - const zlib = require('zlib'); - const compressed = zlib.gzipSync(value).toString('base64'); - return `compressed:${compressed}`; - } - catch (error) { - logger_1.loggers.cache.warn({ error }, 'Failed to compress cache value'); - return value; - } - } - /** - * Decompress value if needed - */ - async decompress(value) { - if (!value.startsWith('compressed:')) { - return value; - } - try { - const zlib = require('zlib'); - const compressed = value.substring('compressed:'.length); - return zlib.gunzipSync(Buffer.from(compressed, 'base64')).toString(); - } - catch (error) { - logger_1.loggers.cache.warn({ error }, 'Failed to decompress cache value'); - return value; - } - } - /** - * Get value from cache - */ - async get(key, options = {}) { - return await logger_1.performanceLogger.trackAsyncOperation(async () => { - try { - const client = redis_1.redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - const value = await client.get(cacheKey); - if (value === null) { - redis_1.redisManager.incrementMisses(); - logger_1.loggers.cache.debug({ key: cacheKey }, 'Cache miss'); - return null; - } - redis_1.redisManager.incrementHits(); - logger_1.loggers.cache.debug({ key: cacheKey, size: value.length }, 'Cache hit'); - // Decompress and deserialize - const decompressed = await this.decompress(value); - const result = this.deserialize(decompressed, options); - return result; - } - catch (error) { - logger_1.loggers.cache.error({ error, key }, 'Cache get operation failed'); - redis_1.redisManager.incrementMisses(); - return null; - } - }, 'cache_get', { key, namespace: options.namespace }); - } - /** - * Set value in cache - */ - async set(key, value, options = {}) { - return await logger_1.performanceLogger.trackAsyncOperation(async () => { - try { - const client = redis_1.redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - // Serialize and compress - const serialized = this.serialize(value, options); - const compressed = await this.compress(serialized, options); - const ttl = options.ttl || options.ex || this.defaultTTL; - let result; - if (options.nx) { - result = await client.set(cacheKey, compressed, 'EX', ttl, 'NX'); - } - else { - result = await client.setex(cacheKey, ttl, compressed); - } - const success = result === 'OK'; - if (success) { - redis_1.redisManager.incrementSets(); - logger_1.loggers.cache.debug({ - key: cacheKey, - ttl, - size: compressed.length, - compressed: compressed.startsWith('compressed:'), - }, 'Cache set successful'); - // Set tags for bulk invalidation if provided - if (options.tags && options.tags.length > 0) { - await this.tagKey(cacheKey, options.tags); - } - } - else { - logger_1.loggers.cache.warn({ key: cacheKey }, 'Cache set failed'); - } - return success; - } - catch (error) { - logger_1.loggers.cache.error({ error, key }, 'Cache set operation failed'); - return false; - } - }, 'cache_set', { key, namespace: options.namespace, ttl: options.ttl }); - } - /** - * Delete value from cache - */ - async delete(key, options = {}) { - try { - const client = redis_1.redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - const result = await client.del(cacheKey); - const deleted = result > 0; - if (deleted) { - redis_1.redisManager.incrementDeletes(); - logger_1.loggers.cache.debug({ key: cacheKey }, 'Cache delete successful'); - } - return deleted; - } - catch (error) { - logger_1.loggers.cache.error({ error, key }, 'Cache delete operation failed'); - return false; - } - } - /** - * Check if key exists in cache - */ - async exists(key, options = {}) { - try { - const client = redis_1.redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - const result = await client.exists(cacheKey); - return result === 1; - } - catch (error) { - logger_1.loggers.cache.error({ error, key }, 'Cache exists check failed'); - return false; - } - } - /** - * Set expiration time for existing key - */ - async expire(key, ttl, options = {}) { - try { - const client = redis_1.redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - const result = await client.expire(cacheKey, ttl); - return result === 1; - } - catch (error) { - logger_1.loggers.cache.error({ error, key, ttl }, 'Cache expire operation failed'); - return false; - } - } - /** - * Get remaining TTL for key - */ - async ttl(key, options = {}) { - try { - const client = redis_1.redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - return await client.ttl(cacheKey); - } - catch (error) { - logger_1.loggers.cache.error({ error, key }, 'Cache TTL check failed'); - return -2; // Key doesn't exist - } - } - /** - * Get multiple values at once - */ - async mget(keys, options = {}) { - return await logger_1.performanceLogger.trackAsyncOperation(async () => { - try { - const client = redis_1.redisManager.getClient(); - const cacheKeys = keys.map((key) => this.generateKey(key, options.namespace)); - const values = await client.mget(...cacheKeys); - const results = await Promise.all(values.map(async (value, index) => { - if (value === null) { - redis_1.redisManager.incrementMisses(); - return null; - } - redis_1.redisManager.incrementHits(); - const decompressed = await this.decompress(value); - return this.deserialize(decompressed, options); - })); - logger_1.loggers.cache.debug({ - keys: cacheKeys, - hits: values.filter((v) => v !== null).length, - misses: values.filter((v) => v === null).length, - }, 'Cache mget operation'); - return results; - } - catch (error) { - logger_1.loggers.cache.error({ error, keys }, 'Cache mget operation failed'); - return keys.map(() => null); - } - }, 'cache_mget', { keyCount: keys.length, namespace: options.namespace }); - } - /** - * Set multiple values at once - */ - async mset(keyValuePairs, options = {}) { - return await logger_1.performanceLogger.trackAsyncOperation(async () => { - try { - const client = redis_1.redisManager.getClient(); - const pipeline = client.pipeline(); - const ttl = options.ttl || this.defaultTTL; - for (const [key, value] of Object.entries(keyValuePairs)) { - const cacheKey = this.generateKey(key, options.namespace); - const serialized = this.serialize(value, options); - const compressed = await this.compress(serialized, options); - pipeline.setex(cacheKey, ttl, compressed); - } - const results = await pipeline.exec(); - const success = results?.every(([err, result]) => err === null && result === 'OK') || false; - if (success) { - redis_1.redisManager.incrementSets(); - logger_1.loggers.cache.debug({ - keyCount: Object.keys(keyValuePairs).length, - ttl, - }, 'Cache mset operation successful'); - } - return success; - } - catch (error) { - logger_1.loggers.cache.error({ error, keyCount: Object.keys(keyValuePairs).length }, 'Cache mset operation failed'); - return false; - } - }, 'cache_mset', { keyCount: Object.keys(keyValuePairs).length, namespace: options.namespace }); - } - /** - * Delete multiple keys at once - */ - async mdel(keys, options = {}) { - try { - const client = redis_1.redisManager.getClient(); - const cacheKeys = keys.map((key) => this.generateKey(key, options.namespace)); - const result = await client.del(...cacheKeys); - if (result > 0) { - redis_1.redisManager.incrementDeletes(); - logger_1.loggers.cache.debug({ keys: cacheKeys, deleted: result }, 'Cache mdel operation'); - } - return result; - } - catch (error) { - logger_1.loggers.cache.error({ error, keys }, 'Cache mdel operation failed'); - return 0; - } - } - /** - * Clear all cache keys with pattern - */ - async clear(pattern, options = {}) { - try { - const client = redis_1.redisManager.getClient(); - const searchPattern = pattern - ? this.generateKey(pattern, options.namespace) - : this.generateKey('*', options.namespace); - const keys = await client.keys(searchPattern); - if (keys.length === 0) { - return 0; - } - const result = await client.del(...keys); - logger_1.loggers.cache.info({ pattern: searchPattern, deleted: result }, 'Cache clear operation'); - return result; - } - catch (error) { - logger_1.loggers.cache.error({ error, pattern }, 'Cache clear operation failed'); - return 0; - } - } - /** - * Tag a key for bulk invalidation - */ - async tagKey(key, tags) { - try { - const client = redis_1.redisManager.getClient(); - const pipeline = client.pipeline(); - for (const tag of tags) { - const tagKey = this.generateKey(`tag:${tag}`, 'general'); - pipeline.sadd(tagKey, key); - pipeline.expire(tagKey, this.defaultTTL * 2); // Tags live longer than data - } - await pipeline.exec(); - } - catch (error) { - logger_1.loggers.cache.error({ error, key, tags }, 'Failed to tag cache key'); - } - } - /** - * Invalidate all keys with specific tags - */ - async invalidateByTags(tags) { - try { - const client = redis_1.redisManager.getClient(); - let totalDeleted = 0; - for (const tag of tags) { - const tagKey = this.generateKey(`tag:${tag}`, 'general'); - const keys = await client.smembers(tagKey); - if (keys.length > 0) { - const deleted = await client.del(...keys); - totalDeleted += deleted; - } - // Remove the tag itself - await client.del(tagKey); - } - logger_1.loggers.cache.info({ tags, deleted: totalDeleted }, 'Cache invalidation by tags'); - return totalDeleted; - } - catch (error) { - logger_1.loggers.cache.error({ error, tags }, 'Cache invalidation by tags failed'); - return 0; - } - } - /** - * Get cache statistics - */ - async getStats() { - try { - const client = redis_1.redisManager.getClient(); - const info = await client.info('memory'); - const keyspace = await client.info('keyspace'); - const metrics = redis_1.redisManager.getMetrics(); - // Parse memory usage - const memoryMatch = info.match(/used_memory:(\d+)/); - const memoryUsage = memoryMatch && memoryMatch[1] ? parseInt(memoryMatch[1], 10) : 0; - // Parse total keys - const keyspaceMatch = keyspace.match(/keys=(\d+)/); - const totalKeys = keyspaceMatch && keyspaceMatch[1] ? parseInt(keyspaceMatch[1], 10) : 0; - return { - totalKeys, - memoryUsage, - hitRate: metrics.hitRate, - operations: { - gets: metrics.hits + metrics.misses, - sets: metrics.sets, - deletes: metrics.deletes, - hits: metrics.hits, - misses: metrics.misses, - }, - }; - } - catch (error) { - logger_1.loggers.cache.error({ error }, 'Failed to get cache stats'); - return { - totalKeys: 0, - memoryUsage: 0, - hitRate: 0, - operations: { gets: 0, sets: 0, deletes: 0, hits: 0, misses: 0 }, - }; - } - } - /** - * Cache wrapper for functions - */ - async wrap(key, fn, options = {}) { - // Try to get from cache first - const cached = await this.get(key, options); - if (cached !== null) { - return cached; - } - // Execute function and cache result - const result = await fn(); - await this.set(key, result, options); - return result; - } - // Namespace-specific helper methods - users = { - get: (key, options) => this.get(key, { ...options, namespace: this.namespaces.users }), - set: (key, value, options) => this.set(key, value, { ...options, namespace: this.namespaces.users }), - delete: (key) => this.delete(key, { namespace: this.namespaces.users }), - clear: (pattern) => this.clear(pattern, { namespace: this.namespaces.users }), - }; - channels = { - get: (key, options) => this.get(key, { ...options, namespace: this.namespaces.channels }), - set: (key, value, options) => this.set(key, value, { ...options, namespace: this.namespaces.channels }), - delete: (key) => this.delete(key, { namespace: this.namespaces.channels }), - clear: (pattern) => this.clear(pattern, { namespace: this.namespaces.channels }), - }; - tasks = { - get: (key, options) => this.get(key, { ...options, namespace: this.namespaces.tasks }), - set: (key, value, options) => this.set(key, value, { ...options, namespace: this.namespaces.tasks }), - delete: (key) => this.delete(key, { namespace: this.namespaces.tasks }), - clear: (pattern) => this.clear(pattern, { namespace: this.namespaces.tasks }), - }; - sessions = { - get: (key, options) => this.get(key, { ...options, namespace: this.namespaces.sessions }), - set: (key, value, options) => this.set(key, value, { ...options, namespace: this.namespaces.sessions }), - delete: (key) => this.delete(key, { namespace: this.namespaces.sessions }), - clear: (pattern) => this.clear(pattern, { namespace: this.namespaces.sessions }), - }; -} -// Export singleton instance -exports.cacheService = new CacheService(); -exports.default = exports.cacheService; -//# sourceMappingURL=CacheService.js.map \ No newline at end of file diff --git a/backend/dist/services/CacheService.js.map b/backend/dist/services/CacheService.js.map deleted file mode 100644 index efb4eeb..0000000 --- a/backend/dist/services/CacheService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"CacheService.js","sourceRoot":"","sources":["../../src/services/CacheService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/services/EmailService.d.ts b/backend/dist/services/EmailService.d.ts deleted file mode 100644 index 60a481a..0000000 --- a/backend/dist/services/EmailService.d.ts +++ /dev/null @@ -1,28 +0,0 @@ -export interface EmailOptions { - to: string; - subject: string; - html: string; - text?: string; -} -export interface EmailVerificationData { - userEmail: string; - userName: string; - verificationToken: string; -} -export interface PasswordResetData { - userEmail: string; - userName: string; - resetToken: string; -} -declare class EmailService { - private transporter; - private readonly frontendUrl; - constructor(); - private setupTransporter; - sendEmail(options: EmailOptions): Promise; - sendEmailVerification(data: EmailVerificationData): Promise; - sendPasswordReset(data: PasswordResetData): Promise; -} -export declare const emailService: EmailService; -export {}; -//# sourceMappingURL=EmailService.d.ts.map \ No newline at end of file diff --git a/backend/dist/services/EmailService.d.ts.map b/backend/dist/services/EmailService.d.ts.map deleted file mode 100644 index ecddfec..0000000 --- a/backend/dist/services/EmailService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EmailService.d.ts","sourceRoot":"","sources":["../../src/services/EmailService.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,cAAM,YAAY;IAChB,OAAO,CAAC,WAAW,CAA0B;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;;IAOrC,OAAO,CAAC,gBAAgB;IAwBlB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;IAuClD,qBAAqB,CAAC,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,OAAO,CAAC;IAoEpE,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,OAAO,CAAC;CA+EnE;AAED,eAAO,MAAM,YAAY,cAAqB,CAAC"} \ No newline at end of file diff --git a/backend/dist/services/EmailService.js b/backend/dist/services/EmailService.js deleted file mode 100644 index cc64f31..0000000 --- a/backend/dist/services/EmailService.js +++ /dev/null @@ -1,213 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.emailService = void 0; -const nodemailer_1 = __importDefault(require("nodemailer")); -const logger_1 = require("@utils/logger"); -class EmailService { - transporter; - frontendUrl; - constructor() { - this.frontendUrl = process.env.FRONTEND_URL || 'http://localhost:3000'; - this.setupTransporter(); - } - setupTransporter() { - if (process.env.NODE_ENV === 'development') { - // For development, log emails to console to avoid rate limiting - this.transporter = nodemailer_1.default.createTransport({ - streamTransport: true, - newline: 'unix', - buffer: true, - }); - } - else { - // Production configuration - const emailConfig = { - host: process.env.SMTP_HOST || 'smtp.gmail.com', - port: parseInt(process.env.SMTP_PORT || '587'), - secure: process.env.SMTP_SECURE === 'true', - auth: { - user: process.env.SMTP_USER, - pass: process.env.SMTP_PASS, - }, - }; - this.transporter = nodemailer_1.default.createTransport(emailConfig); - } - } - async sendEmail(options) { - try { - const info = await this.transporter.sendMail({ - from: process.env.FROM_EMAIL || 'noreply@ceocommunication.com', - to: options.to, - subject: options.subject, - html: options.html, - text: options.text, - }); - if (process.env.NODE_ENV === 'development') { - // In development, log the email content instead of sending - logger_1.logger.info({ - messageId: info.messageId, - to: options.to, - subject: options.subject, - emailContent: info.message.toString(), - }, 'Email logged (development mode - avoiding rate limits)'); - } - else { - logger_1.logger.info({ - messageId: info.messageId, - to: options.to, - subject: options.subject, - }, 'Email sent'); - } - return true; - } - catch (error) { - logger_1.logger.error({ error, to: options.to, subject: options.subject }, 'Failed to send email'); - return false; - } - } - async sendEmailVerification(data) { - const verificationUrl = `${this.frontendUrl}/verify-email/${data.verificationToken}`; - const html = ` - - - - - Verify Your Email - - - -
-
-

CEO Communication Platform

-
-
-

Welcome, ${data.userName}!

-

Thank you for registering with the CEO Communication Platform. To complete your registration, please verify your email address by clicking the button below:

- - - -

If the button doesn't work, you can copy and paste this link into your browser:

-

- ${verificationUrl} -

- -

Important: This verification link will expire in 24 hours for security reasons.

- -

If you didn't create an account with us, please ignore this email.

-
- -
- - - `; - const text = ` -Welcome to CEO Communication Platform, ${data.userName}! - -Please verify your email address by visiting this link: -${verificationUrl} - -This link will expire in 24 hours. - -If you didn't create an account with us, please ignore this email. - `; - return this.sendEmail({ - to: data.userEmail, - subject: 'Verify Your Email Address - CEO Communication Platform', - html, - text, - }); - } - async sendPasswordReset(data) { - const resetUrl = `${this.frontendUrl}/reset-password/${data.resetToken}`; - const html = ` - - - - - Password Reset - - - -
-
-

CEO Communication Platform

-
-
-

Password Reset Request

-

Hello ${data.userName},

-

We received a request to reset your password for your CEO Communication Platform account. If you made this request, click the button below to reset your password:

- - - -

If the button doesn't work, you can copy and paste this link into your browser:

-

- ${resetUrl} -

- -
- Security Notice: -
    -
  • This password reset link will expire in 1 hour
  • -
  • If you didn't request this reset, please ignore this email
  • -
  • Your password will remain unchanged until you use this link
  • -
-
- -

For security reasons, if you continue to receive unwanted password reset emails, please contact our support team.

-
- -
- - - `; - const text = ` -Password Reset Request - CEO Communication Platform - -Hello ${data.userName}, - -We received a request to reset your password. If you made this request, please visit this link to reset your password: - -${resetUrl} - -This link will expire in 1 hour for security reasons. - -If you didn't request this reset, please ignore this email. Your password will remain unchanged. - `; - return this.sendEmail({ - to: data.userEmail, - subject: 'Password Reset Request - CEO Communication Platform', - html, - text, - }); - } -} -exports.emailService = new EmailService(); -//# sourceMappingURL=EmailService.js.map \ No newline at end of file diff --git a/backend/dist/services/EmailService.js.map b/backend/dist/services/EmailService.js.map deleted file mode 100644 index a347111..0000000 --- a/backend/dist/services/EmailService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EmailService.js","sourceRoot":"","sources":["../../src/services/EmailService.ts"],"names":[],"mappings":";;;;;;AAAA,4DAAoC;AACpC,0CAAuC;AAqBvC,MAAM,YAAY;IACR,WAAW,CAA0B;IAC5B,WAAW,CAAS;IAErC;QACE,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,uBAAuB,CAAC;QACvE,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAEO,gBAAgB;QACtB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;YAC3C,gEAAgE;YAChE,IAAI,CAAC,WAAW,GAAG,oBAAU,CAAC,eAAe,CAAC;gBAC5C,eAAe,EAAE,IAAI;gBACrB,OAAO,EAAE,MAAM;gBACf,MAAM,EAAE,IAAI;aACb,CAAC,CAAC;QACL,CAAC;aAAM,CAAC;YACN,2BAA2B;YAC3B,MAAM,WAAW,GAAG;gBAClB,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,gBAAgB;gBAC/C,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,IAAI,KAAK,CAAC;gBAC9C,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,WAAW,KAAK,MAAM;gBAC1C,IAAI,EAAE;oBACJ,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;oBAC3B,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,SAAS;iBAC5B;aACF,CAAC;YAEF,IAAI,CAAC,WAAW,GAAG,oBAAU,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,OAAqB;QACnC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;gBAC3C,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,8BAA8B;gBAC9D,EAAE,EAAE,OAAO,CAAC,EAAE;gBACd,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,IAAI,EAAE,OAAO,CAAC,IAAI;aACnB,CAAC,CAAC;YAEH,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,aAAa,EAAE,CAAC;gBAC3C,2DAA2D;gBAC3D,eAAM,CAAC,IAAI,CACT;oBACE,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,EAAE,EAAE,OAAO,CAAC,EAAE;oBACd,OAAO,EAAE,OAAO,CAAC,OAAO;oBACxB,YAAY,EAAE,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE;iBACtC,EACD,wDAAwD,CACzD,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,eAAM,CAAC,IAAI,CACT;oBACE,SAAS,EAAE,IAAI,CAAC,SAAS;oBACzB,EAAE,EAAE,OAAO,CAAC,EAAE;oBACd,OAAO,EAAE,OAAO,CAAC,OAAO;iBACzB,EACD,YAAY,CACb,CAAC;YACJ,CAAC;YAED,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,eAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,EAAE,sBAAsB,CAAC,CAAC;YAC1F,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,qBAAqB,CAAC,IAA2B;QACrD,MAAM,eAAe,GAAG,GAAG,IAAI,CAAC,WAAW,iBAAiB,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAErF,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;6BAsBY,IAAI,CAAC,QAAQ;;;;2BAIf,eAAe;;;;;kBAKxB,eAAe;;;;;;;;;;;;;KAa5B,CAAC;QAEF,MAAM,IAAI,GAAG;yCACwB,IAAI,CAAC,QAAQ;;;EAGpD,eAAe;;;;;KAKZ,CAAC;QAEF,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,EAAE,EAAE,IAAI,CAAC,SAAS;YAClB,OAAO,EAAE,wDAAwD;YACjE,IAAI;YACJ,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,iBAAiB,CAAC,IAAuB;QAC7C,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,WAAW,mBAAmB,IAAI,CAAC,UAAU,EAAE,CAAC;QAEzE,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;yBAwBQ,IAAI,CAAC,QAAQ;;;;2BAIX,QAAQ;;;;;kBAKjB,QAAQ;;;;;;;;;;;;;;;;;;;;KAoBrB,CAAC;QAEF,MAAM,IAAI,GAAG;;;QAGT,IAAI,CAAC,QAAQ;;;;EAInB,QAAQ;;;;;KAKL,CAAC;QAEF,OAAO,IAAI,CAAC,SAAS,CAAC;YACpB,EAAE,EAAE,IAAI,CAAC,SAAS;YAClB,OAAO,EAAE,qDAAqD;YAC9D,IAAI;YACJ,IAAI;SACL,CAAC,CAAC;IACL,CAAC;CACF;AAEY,QAAA,YAAY,GAAG,IAAI,YAAY,EAAE,CAAC"} \ No newline at end of file diff --git a/backend/dist/services/index.d.ts b/backend/dist/services/index.d.ts deleted file mode 100644 index ec749b7..0000000 --- a/backend/dist/services/index.d.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Services Index - * Re-exports all services for easy importing - */ -export { cacheService } from './CacheService'; -export * from './CacheService'; -export { emailService } from './EmailService'; -export * from './EmailService'; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/backend/dist/services/index.d.ts.map b/backend/dist/services/index.d.ts.map deleted file mode 100644 index 303bd9b..0000000 --- a/backend/dist/services/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,cAAc,gBAAgB,CAAC;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,cAAc,gBAAgB,CAAC"} \ No newline at end of file diff --git a/backend/dist/services/index.js b/backend/dist/services/index.js deleted file mode 100644 index fcd5ba0..0000000 --- a/backend/dist/services/index.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; -/** - * Services Index - * Re-exports all services for easy importing - */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.emailService = exports.cacheService = void 0; -var CacheService_1 = require("./CacheService"); -Object.defineProperty(exports, "cacheService", { enumerable: true, get: function () { return CacheService_1.cacheService; } }); -__exportStar(require("./CacheService"), exports); -var EmailService_1 = require("./EmailService"); -Object.defineProperty(exports, "emailService", { enumerable: true, get: function () { return EmailService_1.emailService; } }); -__exportStar(require("./EmailService"), exports); -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/backend/dist/services/index.js.map b/backend/dist/services/index.js.map deleted file mode 100644 index 1d5e46e..0000000 --- a/backend/dist/services/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/services/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;AAEH,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,iDAA+B;AAC/B,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AACrB,iDAA+B"} \ No newline at end of file diff --git a/backend/dist/utils/cache-decorators.d.ts b/backend/dist/utils/cache-decorators.d.ts deleted file mode 100644 index d9ba88f..0000000 --- a/backend/dist/utils/cache-decorators.d.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { CacheOptions } from '../services/CacheService'; -/** - * Cache decorators for automatic caching of method results - * Enterprise-grade caching with TTL, invalidation, and error handling - */ -export interface CacheableOptions extends CacheOptions { - keyGenerator?: (...args: any[]) => string; - condition?: (...args: any[]) => boolean; - invalidateOnError?: boolean; -} -/** - * Decorator for caching method results - */ -export declare function Cacheable(options?: CacheableOptions): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor; -/** - * Decorator for cache invalidation on method execution - */ -export declare function CacheEvict(options?: { - keys?: string | string[] | ((...args: any[]) => string | string[]); - allEntries?: boolean; - beforeInvocation?: boolean; - namespace?: string; - tags?: string[]; - condition?: (...args: any[]) => boolean; -}): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor; -/** - * Decorator for caching with automatic invalidation - */ -export declare function CachePut(options?: { - key?: string | ((...args: any[]) => string); - namespace?: string; - ttl?: number; - condition?: (...args: any[]) => boolean; - unless?: (result: any) => boolean; - tags?: string[]; -}): (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor; -/** - * Class-level cache configuration - */ -export declare function CacheConfig(config: { - namespace?: string; - defaultTTL?: number; - keyPrefix?: string; -}): (constructor: T) => T; -/** - * Utility functions for cache key generation - */ -export declare const CacheKeyUtils: { - /** - * Generate key from user ID - */ - userKey: (userId: string, suffix?: string) => string; - /** - * Generate key from channel ID - */ - channelKey: (channelId: string, suffix?: string) => string; - /** - * Generate key from task ID - */ - taskKey: (taskId: string, suffix?: string) => string; - /** - * Generate key from multiple parameters - */ - multiKey: (...parts: string[]) => string; - /** - * Generate pagination key - */ - paginationKey: (baseKey: string, limit: number, offset: number) => string; - /** - * Generate search key - */ - searchKey: (query: string, filters?: Record) => string; -}; -/** - * Cache warming utilities - */ -export declare class CacheWarmer { - /** - * Warm cache with user data - */ - static warmUserCache(userId: string, userData: any): Promise; - /** - * Warm cache with channel data - */ - static warmChannelCache(channelId: string, channelData: any): Promise; - /** - * Warm cache with task data - */ - static warmTaskCache(taskId: string, taskData: any): Promise; -} -declare const _default: { - Cacheable: typeof Cacheable; - CacheEvict: typeof CacheEvict; - CachePut: typeof CachePut; - CacheConfig: typeof CacheConfig; - CacheKeyUtils: { - /** - * Generate key from user ID - */ - userKey: (userId: string, suffix?: string) => string; - /** - * Generate key from channel ID - */ - channelKey: (channelId: string, suffix?: string) => string; - /** - * Generate key from task ID - */ - taskKey: (taskId: string, suffix?: string) => string; - /** - * Generate key from multiple parameters - */ - multiKey: (...parts: string[]) => string; - /** - * Generate pagination key - */ - paginationKey: (baseKey: string, limit: number, offset: number) => string; - /** - * Generate search key - */ - searchKey: (query: string, filters?: Record) => string; - }; - CacheWarmer: typeof CacheWarmer; -}; -export default _default; -//# sourceMappingURL=cache-decorators.d.ts.map \ No newline at end of file diff --git a/backend/dist/utils/cache-decorators.d.ts.map b/backend/dist/utils/cache-decorators.d.ts.map deleted file mode 100644 index 5deea79..0000000 --- a/backend/dist/utils/cache-decorators.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"cache-decorators.d.ts","sourceRoot":"","sources":["../../src/utils/cache-decorators.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAGtE;;;GAGG;AAEH,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,YAAY,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,MAAM,CAAC;IAC1C,SAAS,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;IACxC,iBAAiB,CAAC,EAAE,OAAO,CAAC;CAC7B;AAED;;GAEG;AACH,wBAAgB,SAAS,CAAC,OAAO,GAAE,gBAAqB,IACrC,QAAQ,GAAG,EAAE,aAAa,MAAM,EAAE,YAAY,kBAAkB,wBA0FlF;AAED;;GAEG;AACH,wBAAgB,UAAU,CACxB,OAAO,GAAE;IACP,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;IACnE,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;CACpC,IAEW,QAAQ,GAAG,EAAE,aAAa,MAAM,EAAE,YAAY,kBAAkB,wBA6GlF;AAED;;GAEG;AACH,wBAAgB,QAAQ,CACtB,OAAO,GAAE;IACP,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,MAAM,CAAC,CAAC;IAC5C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC;IACxC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,OAAO,CAAC;IAClC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACZ,IAEW,QAAQ,GAAG,EAAE,aAAa,MAAM,EAAE,YAAY,kBAAkB,wBAgElF;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,IACkB,CAAC,SAAS;IAAE,KAAK,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,CAAA;CAAE,EAAE,aAAa,CAAC,OAKxE;AAED;;GAEG;AACH,eAAO,MAAM,aAAa;IACxB;;OAEG;sBACe,MAAM,WAAW,MAAM;IAEzC;;OAEG;4BACqB,MAAM,WAAW,MAAM;IAG/C;;OAEG;sBACe,MAAM,WAAW,MAAM;IAEzC;;OAEG;yBACkB,MAAM,EAAE;IAE7B;;OAEG;6BACsB,MAAM,SAAS,MAAM,UAAU,MAAM;IAG9D;;OAEG;uBACgB,MAAM,YAAY,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;CAIzD,CAAC;AAEF;;GAEG;AACH,qBAAa,WAAW;IACtB;;OAEG;WACU,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxE;;OAEG;WACU,gBAAgB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IAYjF;;OAEG;WACU,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;CAWzE;;;;;;;QApFC;;WAEG;0BACe,MAAM,WAAW,MAAM;QAEzC;;WAEG;gCACqB,MAAM,WAAW,MAAM;QAG/C;;WAEG;0BACe,MAAM,WAAW,MAAM;QAEzC;;WAEG;6BACkB,MAAM,EAAE;QAE7B;;WAEG;iCACsB,MAAM,SAAS,MAAM,UAAU,MAAM;QAG9D;;WAEG;2BACgB,MAAM,YAAY,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;;;;AAwD1D,wBAOE"} \ No newline at end of file diff --git a/backend/dist/utils/cache-decorators.js b/backend/dist/utils/cache-decorators.js deleted file mode 100644 index f9d6c04..0000000 --- a/backend/dist/utils/cache-decorators.js +++ /dev/null @@ -1,329 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CacheWarmer = exports.CacheKeyUtils = void 0; -exports.Cacheable = Cacheable; -exports.CacheEvict = CacheEvict; -exports.CachePut = CachePut; -exports.CacheConfig = CacheConfig; -const CacheService_1 = require("../services/CacheService"); -const logger_1 = require("./logger"); -/** - * Decorator for caching method results - */ -function Cacheable(options = {}) { - return function (target, propertyKey, descriptor) { - const originalMethod = descriptor.value; - descriptor.value = async function (...args) { - const className = target.constructor.name; - const methodName = propertyKey; - // Check condition if provided - if (options.condition && !options.condition.apply(this, args)) { - return await originalMethod.apply(this, args); - } - // Generate cache key - const defaultKeyGenerator = (...args) => { - const serializedArgs = args - .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))) - .join(':'); - return `${className}:${methodName}:${serializedArgs}`; - }; - const keyGenerator = options.keyGenerator || defaultKeyGenerator; - const cacheKey = keyGenerator.apply(this, args); - try { - // Try to get from cache - const getOptions = {}; - if (typeof options.namespace !== 'undefined') { - getOptions.namespace = options.namespace; - } - if (typeof options.serialize !== 'undefined') { - getOptions.serialize = options.serialize; - } - const cached = await CacheService_1.cacheService.get(cacheKey, getOptions); - if (cached !== null) { - logger_1.loggers.cache.debug?.({ - className, - methodName, - cacheKey, - hit: true, - }, 'Cache hit for method'); - return cached; - } - // Execute original method - const result = await originalMethod.apply(this, args); - // Cache the result - const setOptions = {}; - if (typeof options.ttl !== 'undefined') - setOptions.ttl = options.ttl; - if (typeof options.namespace !== 'undefined') - setOptions.namespace = options.namespace; - if (typeof options.serialize !== 'undefined') - setOptions.serialize = options.serialize; - if (typeof options.compress !== 'undefined') - setOptions.compress = options.compress; - if (typeof options.tags !== 'undefined') - setOptions.tags = options.tags; - await CacheService_1.cacheService.set(cacheKey, result, setOptions); - logger_1.loggers.cache.debug?.({ - className, - methodName, - cacheKey, - hit: false, - ttl: options.ttl, - }, 'Cache miss, result cached'); - return result; - } - catch (cacheError) { - logger_1.loggers.cache.warn?.({ - error: cacheError, - className, - methodName, - cacheKey, - }, 'Cache operation failed, executing method directly'); - // Fall back to executing the method directly - return await originalMethod.apply(this, args); - } - }; - return descriptor; - }; -} -/** - * Decorator for cache invalidation on method execution - */ -function CacheEvict(options = {}) { - return function (target, propertyKey, descriptor) { - const originalMethod = descriptor.value; - descriptor.value = async function (...args) { - const className = target.constructor.name; - const methodName = propertyKey; - // Check condition if provided - if (options.condition && !options.condition.apply(this, args)) { - return await originalMethod.apply(this, args); - } - const evictCache = async () => { - try { - if (options.tags && options.tags.length > 0) { - // Invalidate by tags - const invalidated = await CacheService_1.cacheService.invalidateByTags(options.tags); - logger_1.loggers.cache.info?.({ - className, - methodName, - tags: options.tags, - invalidated, - }, 'Cache invalidated by tags'); - } - else if (options.allEntries) { - // Clear all entries in namespace - const clearOptions = {}; - if (typeof options.namespace !== 'undefined') { - clearOptions.namespace = options.namespace; - } - const cleared = await CacheService_1.cacheService.clear(undefined, clearOptions); - logger_1.loggers.cache.info?.({ - className, - methodName, - namespace: options.namespace, - cleared, - }, 'All cache entries cleared'); - } - else if (options.keys) { - // Invalidate specific keys - const keysToEvict = typeof options.keys === 'function' ? options.keys.apply(this, args) : options.keys; - const keys = Array.isArray(keysToEvict) ? keysToEvict : [keysToEvict]; - for (const key of keys) { - const deleteOptions = {}; - if (typeof options.namespace !== 'undefined') { - deleteOptions.namespace = options.namespace; - } - await CacheService_1.cacheService.delete(key, deleteOptions); - } - logger_1.loggers.cache.info?.({ - className, - methodName, - keys, - namespace: options.namespace, - }, 'Cache keys evicted'); - } - } - catch (error) { - logger_1.loggers.cache.error?.({ - error, - className, - methodName, - }, 'Cache eviction failed'); - } - }; - if (options.beforeInvocation) { - await evictCache(); - } - try { - const result = await originalMethod.apply(this, args); - if (!options.beforeInvocation) { - await evictCache(); - } - return result; - } - catch (error) { - // If method fails and beforeInvocation is false, don't evict cache - if (options.beforeInvocation) { - logger_1.loggers.cache.warn?.({ - className, - methodName, - error, - }, 'Method failed after cache eviction'); - } - throw error; - } - }; - return descriptor; - }; -} -/** - * Decorator for caching with automatic invalidation - */ -function CachePut(options = {}) { - return function (target, propertyKey, descriptor) { - const originalMethod = descriptor.value; - descriptor.value = async function (...args) { - const className = target.constructor.name; - const methodName = propertyKey; - // Check condition if provided - if (options.condition && !options.condition.apply(this, args)) { - return await originalMethod.apply(this, args); - } - const result = await originalMethod.apply(this, args); - // Check unless condition - if (options.unless && options.unless(result)) { - return result; - } - try { - // Generate cache key - const defaultKey = `${className}:${methodName}:${args - .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))) - .join(':')}`; - const cacheKey = options.key - ? typeof options.key === 'function' - ? options.key.apply(this, args) - : options.key - : defaultKey; - // Cache the result - const setOptions = {}; - if (typeof options.ttl !== 'undefined') - setOptions.ttl = options.ttl; - if (typeof options.namespace !== 'undefined') - setOptions.namespace = options.namespace; - if (typeof options.tags !== 'undefined') - setOptions.tags = options.tags; - await CacheService_1.cacheService.set(cacheKey, result, setOptions); - logger_1.loggers.cache.debug?.({ - className, - methodName, - cacheKey, - namespace: options.namespace, - ttl: options.ttl, - }, 'Result cached via @CachePut'); - } - catch (error) { - logger_1.loggers.cache.warn?.({ - error, - className, - methodName, - }, 'Failed to cache result via @CachePut'); - } - return result; - }; - return descriptor; - }; -} -/** - * Class-level cache configuration - */ -function CacheConfig(config) { - return function (constructor) { - // Store cache configuration on the prototype - constructor.prototype._cacheConfig = config; - return constructor; - }; -} -/** - * Utility functions for cache key generation - */ -exports.CacheKeyUtils = { - /** - * Generate key from user ID - */ - userKey: (userId, suffix) => `user:${userId}${suffix ? `:${suffix}` : ''}`, - /** - * Generate key from channel ID - */ - channelKey: (channelId, suffix) => `channel:${channelId}${suffix ? `:${suffix}` : ''}`, - /** - * Generate key from task ID - */ - taskKey: (taskId, suffix) => `task:${taskId}${suffix ? `:${suffix}` : ''}`, - /** - * Generate key from multiple parameters - */ - multiKey: (...parts) => parts.join(':'), - /** - * Generate pagination key - */ - paginationKey: (baseKey, limit, offset) => `${baseKey}:page:${limit}:${offset}`, - /** - * Generate search key - */ - searchKey: (query, filters) => { - const filterKey = filters ? `:${JSON.stringify(filters)}` : ''; - return `search:${encodeURIComponent(query)}${filterKey}`; - }, -}; -/** - * Cache warming utilities - */ -class CacheWarmer { - /** - * Warm cache with user data - */ - static async warmUserCache(userId, userData) { - try { - await CacheService_1.cacheService.users.set(exports.CacheKeyUtils.userKey(userId), userData, { ttl: 3600 } // 1 hour - ); - } - catch (error) { - logger_1.loggers.cache.warn?.({ error, userId }, 'Failed to warm user cache'); - } - } - /** - * Warm cache with channel data - */ - static async warmChannelCache(channelId, channelData) { - try { - await CacheService_1.cacheService.channels.set(exports.CacheKeyUtils.channelKey(channelId), channelData, { ttl: 1800 } // 30 minutes - ); - } - catch (error) { - logger_1.loggers.cache.warn?.({ error, channelId }, 'Failed to warm channel cache'); - } - } - /** - * Warm cache with task data - */ - static async warmTaskCache(taskId, taskData) { - try { - await CacheService_1.cacheService.tasks.set(exports.CacheKeyUtils.taskKey(taskId), taskData, { ttl: 900 } // 15 minutes - ); - } - catch (error) { - logger_1.loggers.cache.warn?.({ error, taskId }, 'Failed to warm task cache'); - } - } -} -exports.CacheWarmer = CacheWarmer; -exports.default = { - Cacheable, - CacheEvict, - CachePut, - CacheConfig, - CacheKeyUtils: exports.CacheKeyUtils, - CacheWarmer, -}; -//# sourceMappingURL=cache-decorators.js.map \ No newline at end of file diff --git a/backend/dist/utils/cache-decorators.js.map b/backend/dist/utils/cache-decorators.js.map deleted file mode 100644 index c520fec..0000000 --- a/backend/dist/utils/cache-decorators.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"cache-decorators.js","sourceRoot":"","sources":["../../src/utils/cache-decorators.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/utils/errors.d.ts b/backend/dist/utils/errors.d.ts deleted file mode 100644 index 3ffbb0d..0000000 --- a/backend/dist/utils/errors.d.ts +++ /dev/null @@ -1,207 +0,0 @@ -/** - * Custom error classes for the CEO Communication Platform - * Provides structured error handling with proper HTTP status codes - */ -export declare abstract class BaseError extends Error { - readonly context?: Record | undefined; - abstract readonly statusCode: number; - abstract readonly code: string; - abstract readonly isOperational: boolean; - constructor(message: string, context?: Record | undefined); - toJSON(): { - name: string; - message: string; - statusCode: number; - code: string; - context: Record | undefined; - stack: string | undefined; - }; -} -export declare class AuthenticationError extends BaseError { - readonly statusCode = 401; - readonly code = "AUTHENTICATION_FAILED"; - readonly isOperational = true; -} -export declare class AuthorizationError extends BaseError { - readonly statusCode = 403; - readonly code = "AUTHORIZATION_FAILED"; - readonly isOperational = true; -} -export declare class TokenExpiredError extends BaseError { - readonly statusCode = 401; - readonly code = "TOKEN_EXPIRED"; - readonly isOperational = true; -} -export declare class InvalidTokenError extends BaseError { - readonly statusCode = 401; - readonly code = "INVALID_TOKEN"; - readonly isOperational = true; -} -export declare class ValidationError extends BaseError { - readonly validationErrors: Array<{ - field: string; - message: string; - value?: unknown; - }>; - readonly statusCode = 400; - readonly code = "VALIDATION_ERROR"; - readonly isOperational = true; - constructor(message: string, validationErrors: Array<{ - field: string; - message: string; - value?: unknown; - }>, context?: Record); - toJSON(): { - validationErrors: { - field: string; - message: string; - value?: unknown; - }[]; - name: string; - message: string; - statusCode: number; - code: string; - context: Record | undefined; - stack: string | undefined; - }; -} -export declare class NotFoundError extends BaseError { - readonly statusCode = 404; - readonly code = "RESOURCE_NOT_FOUND"; - readonly isOperational = true; -} -export declare class ConflictError extends BaseError { - readonly statusCode = 409; - readonly code = "RESOURCE_CONFLICT"; - readonly isOperational = true; -} -export declare class DuplicateResourceError extends BaseError { - readonly statusCode = 409; - readonly code = "DUPLICATE_RESOURCE"; - readonly isOperational = true; -} -export declare class RateLimitError extends BaseError { - readonly retryAfter: number; - readonly statusCode = 429; - readonly code = "RATE_LIMIT_EXCEEDED"; - readonly isOperational = true; - constructor(message: string, retryAfter: number, context?: Record); - toJSON(): { - retryAfter: number; - name: string; - message: string; - statusCode: number; - code: string; - context: Record | undefined; - stack: string | undefined; - }; -} -export declare class DatabaseError extends BaseError { - readonly statusCode = 500; - readonly code = "DATABASE_ERROR"; - readonly isOperational = true; -} -export declare class DatabaseConnectionError extends BaseError { - readonly statusCode = 503; - readonly code = "DATABASE_CONNECTION_ERROR"; - readonly isOperational = true; -} -export declare class TransactionError extends BaseError { - readonly statusCode = 500; - readonly code = "TRANSACTION_ERROR"; - readonly isOperational = true; -} -export declare class ExternalServiceError extends BaseError { - readonly service: string; - readonly originalError?: Error | undefined; - readonly statusCode = 502; - readonly code = "EXTERNAL_SERVICE_ERROR"; - readonly isOperational = true; - constructor(message: string, service: string, originalError?: Error | undefined, context?: Record); - toJSON(): { - service: string; - originalError: string | undefined; - name: string; - message: string; - statusCode: number; - code: string; - context: Record | undefined; - stack: string | undefined; - }; -} -export declare class ConfigurationError extends BaseError { - readonly statusCode = 500; - readonly code = "CONFIGURATION_ERROR"; - readonly isOperational = false; -} -export declare class CacheError extends BaseError { - readonly statusCode = 500; - readonly code = "CACHE_ERROR"; - readonly isOperational = true; -} -export declare class WebSocketError extends BaseError { - readonly statusCode = 500; - readonly code = "WEBSOCKET_ERROR"; - readonly isOperational = true; -} -export declare class VoiceProcessingError extends BaseError { - readonly statusCode = 422; - readonly code = "VOICE_PROCESSING_ERROR"; - readonly isOperational = true; -} -export declare class SpeechRecognitionError extends BaseError { - readonly statusCode = 422; - readonly code = "SPEECH_RECOGNITION_ERROR"; - readonly isOperational = true; -} -export declare class CommandExecutionError extends BaseError { - readonly statusCode = 422; - readonly code = "COMMAND_EXECUTION_ERROR"; - readonly isOperational = true; -} -export declare class CommandValidationError extends BaseError { - readonly statusCode = 400; - readonly code = "COMMAND_VALIDATION_ERROR"; - readonly isOperational = true; -} -export declare class BusinessLogicError extends BaseError { - readonly statusCode = 422; - readonly code = "BUSINESS_LOGIC_ERROR"; - readonly isOperational = true; -} -export declare class InsufficientPermissionsError extends BaseError { - readonly statusCode = 403; - readonly code = "INSUFFICIENT_PERMISSIONS"; - readonly isOperational = true; -} -export declare const isOperationalError: (error: Error) => boolean; -export declare const isAuthenticationError: (error: Error) => boolean; -export declare const isDatabaseError: (error: Error) => boolean; -export declare const createErrorContext: (req?: { - user?: { - id: string; - email: string; - role: string; - }; - ip?: string; - method?: string; - url?: string; - headers?: Record; -}) => Record; -export declare const formatErrorResponse: (error: BaseError) => { - error: { - service?: string; - retryAfter?: number; - validationErrors?: { - field: string; - message: string; - value?: unknown; - }[]; - name: string; - message: string; - code: string; - statusCode: number; - }; - timestamp: string; -}; -//# sourceMappingURL=errors.d.ts.map \ No newline at end of file diff --git a/backend/dist/utils/errors.d.ts.map b/backend/dist/utils/errors.d.ts.map deleted file mode 100644 index bc3a73a..0000000 --- a/backend/dist/utils/errors.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,8BAAsB,SAAU,SAAQ,KAAK;aAOzB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IANnD,QAAQ,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IACrC,QAAQ,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;gBAGvC,OAAO,EAAE,MAAM,EACC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,YAAA;IASnD,MAAM;;;;;;;;CAUP;AAGD,qBAAa,mBAAoB,SAAQ,SAAS;IAChD,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,2BAA2B;IACxC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAED,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,0BAA0B;IACvC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAED,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAED,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAGD,qBAAa,eAAgB,SAAQ,SAAS;aAO1B,gBAAgB,EAAE,KAAK,CAAC;QACtC,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC;IAVJ,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,sBAAsB;IACnC,QAAQ,CAAC,aAAa,QAAQ;gBAG5B,OAAO,EAAE,MAAM,EACC,gBAAgB,EAAE,KAAK,CAAC;QACtC,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB,CAAC,EACF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAMnC,MAAM;;mBAVK,MAAM;qBACJ,MAAM;oBACP,OAAO;;;;;;;;;CAcpB;AAGD,qBAAa,aAAc,SAAQ,SAAS;IAC1C,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,wBAAwB;IACrC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAED,qBAAa,aAAc,SAAQ,SAAS;IAC1C,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,uBAAuB;IACpC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAED,qBAAa,sBAAuB,SAAQ,SAAS;IACnD,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,wBAAwB;IACrC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAGD,qBAAa,cAAe,SAAQ,SAAS;aAOzB,UAAU,EAAE,MAAM;IANpC,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,yBAAyB;IACtC,QAAQ,CAAC,aAAa,QAAQ;gBAG5B,OAAO,EAAE,MAAM,EACC,UAAU,EAAE,MAAM,EAClC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAKnC,MAAM;;;;;;;;;CAMP;AAGD,qBAAa,aAAc,SAAQ,SAAS;IAC1C,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,oBAAoB;IACjC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAED,qBAAa,uBAAwB,SAAQ,SAAS;IACpD,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,+BAA+B;IAC5C,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAED,qBAAa,gBAAiB,SAAQ,SAAS;IAC7C,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,uBAAuB;IACpC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAGD,qBAAa,oBAAqB,SAAQ,SAAS;aAO/B,OAAO,EAAE,MAAM;aACf,aAAa,CAAC,EAAE,KAAK;IAPvC,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,4BAA4B;IACzC,QAAQ,CAAC,aAAa,QAAQ;gBAG5B,OAAO,EAAE,MAAM,EACC,OAAO,EAAE,MAAM,EACf,aAAa,CAAC,EAAE,KAAK,YAAA,EACrC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAKnC,MAAM;;;;;;;;;;CAOP;AAGD,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,yBAAyB;IACtC,QAAQ,CAAC,aAAa,SAAS;CAChC;AAGD,qBAAa,UAAW,SAAQ,SAAS;IACvC,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAGD,qBAAa,cAAe,SAAQ,SAAS;IAC3C,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,qBAAqB;IAClC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAGD,qBAAa,oBAAqB,SAAQ,SAAS;IACjD,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,4BAA4B;IACzC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAED,qBAAa,sBAAuB,SAAQ,SAAS;IACnD,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,8BAA8B;IAC3C,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAGD,qBAAa,qBAAsB,SAAQ,SAAS;IAClD,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,6BAA6B;IAC1C,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAED,qBAAa,sBAAuB,SAAQ,SAAS;IACnD,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,8BAA8B;IAC3C,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAGD,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,0BAA0B;IACvC,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAED,qBAAa,4BAA6B,SAAQ,SAAS;IACzD,QAAQ,CAAC,UAAU,OAAO;IAC1B,QAAQ,CAAC,IAAI,8BAA8B;IAC3C,QAAQ,CAAC,aAAa,QAAQ;CAC/B;AAGD,eAAO,MAAM,kBAAkB,GAAI,OAAO,KAAK,KAAG,OAEjD,CAAC;AAEF,eAAO,MAAM,qBAAqB,GAAI,OAAO,KAAK,KAAG,OAKpD,CAAC;AAEF,eAAO,MAAM,eAAe,GAAI,OAAO,KAAK,KAAG,OAI9C,CAAC;AAGF,eAAO,MAAM,kBAAkB,GAC7B,MAAM;IACJ,IAAI,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;CACzD,KACA,MAAM,CAAC,MAAM,EAAE,OAAO,CAaxB,CAAC;AAGF,eAAO,MAAM,mBAAmB,GAAI,OAAO,SAAS;;;;;mBA7MvC,MAAM;qBACJ,MAAM;oBACP,OAAO;;;;;;;;CA8NpB,CAAC"} \ No newline at end of file diff --git a/backend/dist/utils/errors.js b/backend/dist/utils/errors.js deleted file mode 100644 index dc9da37..0000000 --- a/backend/dist/utils/errors.js +++ /dev/null @@ -1,266 +0,0 @@ -"use strict"; -/** - * Custom error classes for the CEO Communication Platform - * Provides structured error handling with proper HTTP status codes - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.formatErrorResponse = exports.createErrorContext = exports.isDatabaseError = exports.isAuthenticationError = exports.isOperationalError = exports.InsufficientPermissionsError = exports.BusinessLogicError = exports.CommandValidationError = exports.CommandExecutionError = exports.SpeechRecognitionError = exports.VoiceProcessingError = exports.WebSocketError = exports.CacheError = exports.ConfigurationError = exports.ExternalServiceError = exports.TransactionError = exports.DatabaseConnectionError = exports.DatabaseError = exports.RateLimitError = exports.DuplicateResourceError = exports.ConflictError = exports.NotFoundError = exports.ValidationError = exports.InvalidTokenError = exports.TokenExpiredError = exports.AuthorizationError = exports.AuthenticationError = exports.BaseError = void 0; -class BaseError extends Error { - context; - constructor(message, context) { - super(message); - this.context = context; - this.name = this.constructor.name; - // Maintains proper stack trace for where our error was thrown - Error.captureStackTrace(this, this.constructor); - } - toJSON() { - return { - name: this.name, - message: this.message, - statusCode: this.statusCode, - code: this.code, - context: this.context, - stack: this.stack, - }; - } -} -exports.BaseError = BaseError; -// Authentication and Authorization Errors -class AuthenticationError extends BaseError { - statusCode = 401; - code = 'AUTHENTICATION_FAILED'; - isOperational = true; -} -exports.AuthenticationError = AuthenticationError; -class AuthorizationError extends BaseError { - statusCode = 403; - code = 'AUTHORIZATION_FAILED'; - isOperational = true; -} -exports.AuthorizationError = AuthorizationError; -class TokenExpiredError extends BaseError { - statusCode = 401; - code = 'TOKEN_EXPIRED'; - isOperational = true; -} -exports.TokenExpiredError = TokenExpiredError; -class InvalidTokenError extends BaseError { - statusCode = 401; - code = 'INVALID_TOKEN'; - isOperational = true; -} -exports.InvalidTokenError = InvalidTokenError; -// Validation Errors -class ValidationError extends BaseError { - validationErrors; - statusCode = 400; - code = 'VALIDATION_ERROR'; - isOperational = true; - constructor(message, validationErrors, context) { - super(message, context); - this.validationErrors = validationErrors; - this.validationErrors = validationErrors; - } - toJSON() { - return { - ...super.toJSON(), - validationErrors: this.validationErrors, - }; - } -} -exports.ValidationError = ValidationError; -// Resource Errors -class NotFoundError extends BaseError { - statusCode = 404; - code = 'RESOURCE_NOT_FOUND'; - isOperational = true; -} -exports.NotFoundError = NotFoundError; -class ConflictError extends BaseError { - statusCode = 409; - code = 'RESOURCE_CONFLICT'; - isOperational = true; -} -exports.ConflictError = ConflictError; -class DuplicateResourceError extends BaseError { - statusCode = 409; - code = 'DUPLICATE_RESOURCE'; - isOperational = true; -} -exports.DuplicateResourceError = DuplicateResourceError; -// Rate Limiting Errors -class RateLimitError extends BaseError { - retryAfter; - statusCode = 429; - code = 'RATE_LIMIT_EXCEEDED'; - isOperational = true; - constructor(message, retryAfter, context) { - super(message, context); - this.retryAfter = retryAfter; - } - toJSON() { - return { - ...super.toJSON(), - retryAfter: this.retryAfter, - }; - } -} -exports.RateLimitError = RateLimitError; -// Database Errors -class DatabaseError extends BaseError { - statusCode = 500; - code = 'DATABASE_ERROR'; - isOperational = true; -} -exports.DatabaseError = DatabaseError; -class DatabaseConnectionError extends BaseError { - statusCode = 503; - code = 'DATABASE_CONNECTION_ERROR'; - isOperational = true; -} -exports.DatabaseConnectionError = DatabaseConnectionError; -class TransactionError extends BaseError { - statusCode = 500; - code = 'TRANSACTION_ERROR'; - isOperational = true; -} -exports.TransactionError = TransactionError; -// External Service Errors -class ExternalServiceError extends BaseError { - service; - originalError; - statusCode = 502; - code = 'EXTERNAL_SERVICE_ERROR'; - isOperational = true; - constructor(message, service, originalError, context) { - super(message, context); - this.service = service; - this.originalError = originalError; - } - toJSON() { - return { - ...super.toJSON(), - service: this.service, - originalError: this.originalError?.message, - }; - } -} -exports.ExternalServiceError = ExternalServiceError; -// Configuration Errors -class ConfigurationError extends BaseError { - statusCode = 500; - code = 'CONFIGURATION_ERROR'; - isOperational = false; // Non-operational - requires restart/fix -} -exports.ConfigurationError = ConfigurationError; -// Cache Errors -class CacheError extends BaseError { - statusCode = 500; - code = 'CACHE_ERROR'; - isOperational = true; -} -exports.CacheError = CacheError; -// WebSocket Errors -class WebSocketError extends BaseError { - statusCode = 500; - code = 'WEBSOCKET_ERROR'; - isOperational = true; -} -exports.WebSocketError = WebSocketError; -// Voice Processing Errors (for Phase 2) -class VoiceProcessingError extends BaseError { - statusCode = 422; - code = 'VOICE_PROCESSING_ERROR'; - isOperational = true; -} -exports.VoiceProcessingError = VoiceProcessingError; -class SpeechRecognitionError extends BaseError { - statusCode = 422; - code = 'SPEECH_RECOGNITION_ERROR'; - isOperational = true; -} -exports.SpeechRecognitionError = SpeechRecognitionError; -// Command Execution Errors (for Phase 3) -class CommandExecutionError extends BaseError { - statusCode = 422; - code = 'COMMAND_EXECUTION_ERROR'; - isOperational = true; -} -exports.CommandExecutionError = CommandExecutionError; -class CommandValidationError extends BaseError { - statusCode = 400; - code = 'COMMAND_VALIDATION_ERROR'; - isOperational = true; -} -exports.CommandValidationError = CommandValidationError; -// Business Logic Errors -class BusinessLogicError extends BaseError { - statusCode = 422; - code = 'BUSINESS_LOGIC_ERROR'; - isOperational = true; -} -exports.BusinessLogicError = BusinessLogicError; -class InsufficientPermissionsError extends BaseError { - statusCode = 403; - code = 'INSUFFICIENT_PERMISSIONS'; - isOperational = true; -} -exports.InsufficientPermissionsError = InsufficientPermissionsError; -// Utility functions for error handling -const isOperationalError = (error) => { - return error instanceof BaseError && error.isOperational; -}; -exports.isOperationalError = isOperationalError; -const isAuthenticationError = (error) => { - return error instanceof AuthenticationError || - error instanceof AuthorizationError || - error instanceof TokenExpiredError || - error instanceof InvalidTokenError; -}; -exports.isAuthenticationError = isAuthenticationError; -const isDatabaseError = (error) => { - return error instanceof DatabaseError || - error instanceof DatabaseConnectionError || - error instanceof TransactionError; -}; -exports.isDatabaseError = isDatabaseError; -// Error context helpers -const createErrorContext = (req) => { - if (!req) - return {}; - return { - userId: req.user?.id, - userEmail: req.user?.email, - userRole: req.user?.role, - ip: req.ip, - method: req.method, - url: req.url, - userAgent: req.headers?.['user-agent'], - timestamp: new Date().toISOString(), - }; -}; -exports.createErrorContext = createErrorContext; -// Error response formatter -const formatErrorResponse = (error) => { - return { - error: { - name: error.name, - message: error.message, - code: error.code, - statusCode: error.statusCode, - ...(error instanceof ValidationError && { - validationErrors: error.validationErrors - }), - ...(error instanceof RateLimitError && { - retryAfter: error.retryAfter - }), - ...(error instanceof ExternalServiceError && { - service: error.service - }), - }, - timestamp: new Date().toISOString(), - }; -}; -exports.formatErrorResponse = formatErrorResponse; -//# sourceMappingURL=errors.js.map \ No newline at end of file diff --git a/backend/dist/utils/errors.js.map b/backend/dist/utils/errors.js.map deleted file mode 100644 index 9f5bb53..0000000 --- a/backend/dist/utils/errors.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/utils/logger.d.ts b/backend/dist/utils/logger.d.ts deleted file mode 100644 index d35581a..0000000 --- a/backend/dist/utils/logger.d.ts +++ /dev/null @@ -1,83 +0,0 @@ -import pino, { Logger } from 'pino'; -export interface ContextLogger extends Logger { - withContext(context: Record): Logger; -} -export declare const logger: ContextLogger; -export declare const loggers: { - readonly db: pino.Logger; - readonly auth: pino.Logger; - readonly api: pino.Logger; - readonly websocket: pino.Logger; - readonly cache: pino.Logger; - readonly voice: pino.Logger; - readonly ai: pino.Logger; - readonly commands: pino.Logger; - readonly analytics: pino.Logger; - readonly performance: pino.Logger; - readonly security: pino.Logger; -}; -export declare const performanceLogger: { - /** - * Track execution time of async operations - */ - trackAsyncOperation: (operation: () => Promise, operationName: string, context?: Record) => Promise; - /** - * Track sync operation performance - */ - trackSyncOperation: (operation: () => T, operationName: string, context?: Record) => T; -}; -export declare const securityLogger: { - /** - * Log authentication events - */ - logAuthEvent: (event: "login" | "logout" | "token_refresh" | "failed_login" | "login_attempt" | "registration_attempt" | "registration_success" | "password_reset_requested" | "password_reset" | "email_verified" | "token_generated" | "token_refreshed" | "authentication_success" | "refresh_token_expired" | "missing_token", context: { - userId?: string; - email?: string; - ip?: string; - userAgent?: string; - [key: string]: unknown; - }) => void; - /** - * Log authorization events - */ - logAuthzEvent: (event: "access_granted" | "access_denied", context: { - userId: string; - resource: string; - action: string; - ip?: string; - [key: string]: unknown; - }) => void; - /** - * Log security violations - */ - logSecurityViolation: (violation: string, context: Record) => void; -}; -export declare const startupLogger: { - /** - * Log startup steps with grouped output - */ - logStep: (step: string, status?: "starting" | "completed" | "failed") => void; - /** - * Log initialization summary - */ - logSummary: (services: Array<{ - name: string; - status: boolean; - duration?: number; - }>) => void; - /** - * Create a startup timer - */ - createTimer: (name: string) => { - end: () => number; - log: (status?: "completed" | "failed") => number; - }; -}; -export declare const metricsLogger: { - /** - * Log metrics only when significant changes occur - */ - logMetricsIfSignificant: (component: string, currentMetrics: Record, previousMetrics?: Record, threshold?: number) => void; -}; -export default logger; -//# sourceMappingURL=logger.d.ts.map \ No newline at end of file diff --git a/backend/dist/utils/logger.d.ts.map b/backend/dist/utils/logger.d.ts.map deleted file mode 100644 index 70b4d41..0000000 --- a/backend/dist/utils/logger.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAoCpC,MAAM,WAAW,aAAc,SAAQ,MAAM;IAC3C,WAAW,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC;CACvD;AAGD,eAAO,MAAM,MAAM,EAAE,aAEnB,CAAC;AAGH,eAAO,MAAM,OAAO;;;;;;;;;;;;CAiCV,CAAC;AAGX,eAAO,MAAM,iBAAiB;IAC5B;;OAEG;0BACyB,CAAC,aAChB,MAAM,OAAO,CAAC,CAAC,CAAC,iBACZ,MAAM,YACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAChC,OAAO,CAAC,CAAC,CAAC;IAiCb;;OAEG;yBACkB,CAAC,aACT,MAAM,CAAC,iBACH,MAAM,YACX,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAChC,CAAC;CAuBL,CAAC;AAGF,eAAO,MAAM,cAAc;IACzB;;OAEG;0BACmB,OAAO,GAAG,QAAQ,GAAG,eAAe,GAAG,cAAc,GAAG,eAAe,GAAG,sBAAsB,GAAG,sBAAsB,GAAG,0BAA0B,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,iBAAiB,GAAG,iBAAiB,GAAG,wBAAwB,GAAG,uBAAuB,GAAG,eAAe,WAAW;QAC1U,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB;IAID;;OAEG;2BACoB,gBAAgB,GAAG,eAAe,WAAW;QAClE,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;KACxB;IAID;;OAEG;sCAC+B,MAAM,WAAW,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAG3E,CAAC;AAGF,eAAO,MAAM,aAAa;IACxB;;OAEG;oBACa,MAAM,WAAU,UAAU,GAAG,WAAW,GAAG,QAAQ;IAcnE;;OAEG;2BACoB,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IASlF;;OAEG;wBACiB,MAAM;;uBAIR,WAAW,GAAG,QAAQ;;CAOzC,CAAC;AAGF,eAAO,MAAM,aAAa;IACxB;;OAEG;yCAEU,MAAM,kBACD,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,oBACjB,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;CAwBxC,CAAC;AAGF,eAAe,MAAM,CAAC"} \ No newline at end of file diff --git a/backend/dist/utils/logger.js b/backend/dist/utils/logger.js deleted file mode 100644 index 15a257a..0000000 --- a/backend/dist/utils/logger.js +++ /dev/null @@ -1,219 +0,0 @@ -"use strict"; -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.metricsLogger = exports.startupLogger = exports.securityLogger = exports.performanceLogger = exports.loggers = exports.logger = void 0; -const pino_1 = __importDefault(require("pino")); -const index_1 = require("@config/index"); -// Create logger configuration based on environment -const loggerConfig = { - level: index_1.config.logging.level, - formatters: { - level: (label) => ({ level: label }), - }, - timestamp: pino_1.default.stdTimeFunctions.isoTime, - ...(index_1.config.app.isDevelopment && index_1.config.logging.format === 'pretty' - ? { - transport: { - target: 'pino-pretty', - options: { - colorize: true, - translateTime: 'HH:MM:ss Z', - ignore: 'pid,hostname', - messageFormat: '{time} [{level}] {component}: {msg}', - hideObject: false, - singleLine: false, - }, - }, - } - : {}), - // Reduce noise from frequent operations - redact: { - paths: ['password', 'token', 'secret', 'key'], - censor: '[REDACTED]' - }, -}; -// Create base logger -const baseLogger = (0, pino_1.default)(loggerConfig); -// Enhanced logger with context support -exports.logger = Object.assign(baseLogger, { - withContext: (context) => baseLogger.child(context), -}); -// Specialized loggers for different components -exports.loggers = { - // Database operations - db: exports.logger.child({ component: 'database' }), - // Authentication and security - auth: exports.logger.child({ component: 'auth' }), - // API requests and responses - api: exports.logger.child({ component: 'api' }), - // WebSocket real-time communication - websocket: exports.logger.child({ component: 'websocket' }), - // Caching operations - cache: exports.logger.child({ component: 'cache' }), - // Voice processing (for Phase 2) - voice: exports.logger.child({ component: 'voice' }), - // AI operations (for Phase 2-3) - ai: exports.logger.child({ component: 'ai' }), - // Command execution (for Phase 3) - commands: exports.logger.child({ component: 'commands' }), - // Analytics and monitoring (for Phase 4) - analytics: exports.logger.child({ component: 'analytics' }), - // Performance monitoring - performance: exports.logger.child({ component: 'performance' }), - // Security events - security: exports.logger.child({ component: 'security' }), -}; -// Performance tracking utilities -exports.performanceLogger = { - /** - * Track execution time of async operations - */ - trackAsyncOperation: async (operation, operationName, context) => { - const startTime = Date.now(); - const operationLogger = exports.logger.withContext({ operation: operationName, ...context }); - try { - // Only log start for critical operations or when debug level is enabled - if (index_1.config.logging.level === 'debug' || operationName.includes('migration') || operationName.includes('startup')) { - operationLogger.debug('Operation started'); - } - const result = await operation(); - const duration = Date.now() - startTime; - // Only log completion for operations that take > 500ms or are critical - if (duration > 500 || operationName.includes('migration') || operationName.includes('startup')) { - operationLogger.info({ duration }, 'Operation completed'); - } - else if (index_1.config.logging.level === 'debug') { - operationLogger.debug({ duration }, 'Operation completed'); - } - // Log performance warning if operation takes too long - if (duration > 1000) { - exports.loggers.performance.warn({ operation: operationName, duration }, 'Slow operation detected'); - } - return result; - } - catch (error) { - const duration = Date.now() - startTime; - operationLogger.error({ error, duration }, 'Operation failed'); - throw error; - } - }, - /** - * Track sync operation performance - */ - trackSyncOperation: (operation, operationName, context) => { - const startTime = Date.now(); - const operationLogger = exports.logger.withContext({ operation: operationName, ...context }); - try { - operationLogger.debug('Sync operation started'); - const result = operation(); - const duration = Date.now() - startTime; - operationLogger.debug({ duration }, 'Sync operation completed'); - // Log performance warning for slow sync operations - if (duration > 100) { - exports.loggers.performance.warn({ operation: operationName, duration }, 'Slow sync operation'); - } - return result; - } - catch (error) { - const duration = Date.now() - startTime; - operationLogger.error({ error, duration }, 'Sync operation failed'); - throw error; - } - }, -}; -// Security event logging -exports.securityLogger = { - /** - * Log authentication events - */ - logAuthEvent: (event, context) => { - exports.loggers.security.info({ event, ...context }, 'Authentication event'); - }, - /** - * Log authorization events - */ - logAuthzEvent: (event, context) => { - exports.loggers.security.info({ event, ...context }, 'Authorization event'); - }, - /** - * Log security violations - */ - logSecurityViolation: (violation, context) => { - exports.loggers.security.error({ violation, ...context }, 'Security violation detected'); - }, -}; -// Startup logging utilities to reduce verbosity during initialization -exports.startupLogger = { - /** - * Log startup steps with grouped output - */ - logStep: (step, status = 'starting') => { - switch (status) { - case 'starting': - exports.logger.info(`📋 ${step}...`); - break; - case 'completed': - exports.logger.info(`✅ ${step}`); - break; - case 'failed': - exports.logger.error(`❌ ${step}`); - break; - } - }, - /** - * Log initialization summary - */ - logSummary: (services) => { - exports.logger.info('\n🚀 Server Initialization Summary:'); - services.forEach(service => { - const status = service.status ? '✅' : '❌'; - const duration = service.duration ? ` (${service.duration}ms)` : ''; - exports.logger.info(` ${status} ${service.name}${duration}`); - }); - }, - /** - * Create a startup timer - */ - createTimer: (name) => { - const start = Date.now(); - return { - end: () => Date.now() - start, - log: (status = 'completed') => { - const duration = Date.now() - start; - exports.startupLogger.logStep(`${name} (${duration}ms)`, status); - return duration; - } - }; - } -}; -// Utility to reduce metrics logging noise -exports.metricsLogger = { - /** - * Log metrics only when significant changes occur - */ - logMetricsIfSignificant: (component, currentMetrics, previousMetrics, threshold = 0.1) => { - if (!previousMetrics) { - exports.loggers[component]?.info({ metrics: currentMetrics }, `${component} metrics`); - return; - } - // Check if metrics have changed significantly - let hasSignificantChange = false; - for (const [key, value] of Object.entries(currentMetrics)) { - if (typeof value === 'number' && typeof previousMetrics[key] === 'number') { - const change = Math.abs(value - previousMetrics[key]) / (previousMetrics[key] || 1); - if (change > threshold) { - hasSignificantChange = true; - break; - } - } - } - if (hasSignificantChange) { - exports.loggers[component]?.info({ metrics: currentMetrics }, `${component} metrics`); - } - } -}; -// Export default logger -exports.default = exports.logger; -//# sourceMappingURL=logger.js.map \ No newline at end of file diff --git a/backend/dist/utils/logger.js.map b/backend/dist/utils/logger.js.map deleted file mode 100644 index 23ea260..0000000 --- a/backend/dist/utils/logger.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/utils/performance.d.ts b/backend/dist/utils/performance.d.ts deleted file mode 100644 index ffaacca..0000000 --- a/backend/dist/utils/performance.d.ts +++ /dev/null @@ -1,106 +0,0 @@ -/** - * Performance monitoring and metrics collection utilities - * Enterprise-grade performance tracking for the API - */ -export interface PerformanceMetric { - name: string; - duration: number; - timestamp: number; - context?: Record; - tags?: string[]; -} -export interface MemoryMetrics { - rss: number; - heapTotal: number; - heapUsed: number; - external: number; - arrayBuffers: number; -} -export interface CPUMetrics { - user: number; - system: number; -} -export declare class PerformanceMonitor { - private static instance; - private metrics; - private activeTimers; - private readonly maxMetricsPerType; - private constructor(); - static getInstance(): PerformanceMonitor; - /** - * Start timing an operation - */ - startTimer(operationName: string, context?: Record): string; - /** - * End timing an operation and record metric - */ - endTimer(timerId: string, tags?: string[]): PerformanceMetric | null; - /** - * Time a synchronous operation - */ - timeSync(operationName: string, operation: () => T, context?: Record): T; - /** - * Time an asynchronous operation - */ - timeAsync(operationName: string, operation: () => Promise, context?: Record): Promise; - /** - * Record a performance metric - */ - recordMetric(metric: PerformanceMetric): void; - /** - * Get performance statistics for an operation - */ - getStats(operationName: string): { - count: number; - min: number; - max: number; - avg: number; - p50: number; - p95: number; - p99: number; - } | null; - /** - * Get all performance statistics - */ - getAllStats(): Record>; - /** - * Get current memory usage - */ - getMemoryMetrics(): MemoryMetrics; - /** - * Get current CPU usage - */ - getCPUMetrics(): CPUMetrics; - /** - * Get comprehensive system metrics - */ - getSystemMetrics(): { - memory: MemoryMetrics; - cpu: CPUMetrics; - uptime: number; - performance: Record; - }; - /** - * Clear all metrics (useful for testing) - */ - clearMetrics(): void; - /** - * Log system metrics at regular intervals - */ - startMetricsLogging(intervalMs?: number): NodeJS.Timer; - private percentile; - private cleanup; -} -export declare const performanceMonitor: PerformanceMonitor; -export declare function Timed(operationName?: string): (target: any, propertyName: string, descriptor: PropertyDescriptor) => void; -export declare const createPerformanceMiddleware: () => (request: any, reply: any, next: () => void) => Promise; -export default performanceMonitor; -//# sourceMappingURL=performance.d.ts.map \ No newline at end of file diff --git a/backend/dist/utils/performance.d.ts.map b/backend/dist/utils/performance.d.ts.map deleted file mode 100644 index ba23992..0000000 --- a/backend/dist/utils/performance.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"performance.d.ts","sourceRoot":"","sources":["../../src/utils/performance.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/utils/performance.js b/backend/dist/utils/performance.js deleted file mode 100644 index 9e699ec..0000000 --- a/backend/dist/utils/performance.js +++ /dev/null @@ -1,320 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.createPerformanceMiddleware = exports.performanceMonitor = exports.PerformanceMonitor = void 0; -exports.Timed = Timed; -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("./logger"); -class PerformanceMonitor { - static instance; - metrics = new Map(); - activeTimers = new Map(); - maxMetricsPerType = 1000; // Prevent memory leaks - constructor() { - // Start periodic cleanup - setInterval(() => this.cleanup(), 60000); // Every minute - } - static getInstance() { - if (!PerformanceMonitor.instance) { - PerformanceMonitor.instance = new PerformanceMonitor(); - } - return PerformanceMonitor.instance; - } - /** - * Start timing an operation - */ - startTimer(operationName, context) { - const timerId = `${operationName}_${Date.now()}_${Math.random()}`; - this.activeTimers.set(timerId, perf_hooks_1.performance.now()); - logger_1.loggers.performance.debug?.({ - operation: operationName, - timerId, - context, - }, 'Performance timer started'); - return timerId; - } - /** - * End timing an operation and record metric - */ - endTimer(timerId, tags) { - const startTime = this.activeTimers.get(timerId); - if (!startTime) { - logger_1.loggers.performance.warn?.({ timerId }, 'Timer not found'); - return null; - } - const endTime = perf_hooks_1.performance.now(); - const duration = endTime - startTime; - this.activeTimers.delete(timerId); - const operationName = timerId.split('_')[0] || 'unknown'; - const metric = { - name: operationName, - duration, - timestamp: Date.now(), - ...(tags && { tags }), - }; - this.recordMetric(metric); - logger_1.loggers.performance.debug?.({ - operation: operationName, - duration, - timerId, - tags, - }, 'Performance timer completed'); - return metric; - } - /** - * Time a synchronous operation - */ - timeSync(operationName, operation, context) { - const startTime = perf_hooks_1.performance.now(); - try { - const result = operation(); - const duration = perf_hooks_1.performance.now() - startTime; - this.recordMetric({ - name: operationName, - duration, - timestamp: Date.now(), - ...(context && { context }), - }); - logger_1.loggers.performance.debug?.({ - operation: operationName, - duration, - context, - }, 'Sync operation timed'); - return result; - } - catch (error) { - const duration = perf_hooks_1.performance.now() - startTime; - logger_1.loggers.performance.error({ - operation: operationName, - duration, - error, - context, - }, 'Sync operation failed'); - throw error; - } - } - /** - * Time an asynchronous operation - */ - async timeAsync(operationName, operation, context) { - const startTime = perf_hooks_1.performance.now(); - try { - const result = await operation(); - const duration = perf_hooks_1.performance.now() - startTime; - this.recordMetric({ - name: operationName, - duration, - timestamp: Date.now(), - ...(context && { context }), - }); - logger_1.loggers.performance.debug?.({ - operation: operationName, - duration, - context, - }, 'Async operation timed'); - // Log slow operations - if (duration > 1000) { - logger_1.loggers.performance.warn?.({ - operation: operationName, - duration, - context, - }, 'Slow async operation detected'); - } - return result; - } - catch (error) { - const duration = perf_hooks_1.performance.now() - startTime; - logger_1.loggers.performance.error({ - operation: operationName, - duration, - error, - context, - }, 'Async operation failed'); - throw error; - } - } - /** - * Record a performance metric - */ - recordMetric(metric) { - if (!this.metrics.has(metric.name)) { - this.metrics.set(metric.name, []); - } - const metrics = this.metrics.get(metric.name); - metrics.push(metric); - // Prevent memory leaks by keeping only recent metrics - if (metrics.length > this.maxMetricsPerType) { - metrics.splice(0, metrics.length - this.maxMetricsPerType); - } - } - /** - * Get performance statistics for an operation - */ - getStats(operationName) { - const metrics = this.metrics.get(operationName); - if (!metrics || metrics.length === 0) { - return null; - } - const durations = metrics.map((m) => m.duration).sort((a, b) => a - b); - const count = durations.length; - return { - count, - min: durations[0] ?? 0, - max: durations[count - 1] ?? 0, - avg: durations.length > 0 ? durations.reduce((sum, d) => sum + d, 0) / count : 0, - p50: durations.length > 0 ? this.percentile(durations, 50) : 0, - p95: durations.length > 0 ? this.percentile(durations, 95) : 0, - p99: durations.length > 0 ? this.percentile(durations, 99) : 0, - }; - } - /** - * Get all performance statistics - */ - getAllStats() { - const stats = {}; - for (const operationName of this.metrics.keys()) { - stats[operationName] = this.getStats(operationName); - } - return stats; - } - /** - * Get current memory usage - */ - getMemoryMetrics() { - const memUsage = process.memoryUsage(); - return { - rss: Math.round(memUsage.rss / 1024 / 1024), // MB - heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024), // MB - heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024), // MB - external: Math.round(memUsage.external / 1024 / 1024), // MB - arrayBuffers: Math.round(memUsage.arrayBuffers / 1024 / 1024), // MB - }; - } - /** - * Get current CPU usage - */ - getCPUMetrics() { - const cpuUsage = process.cpuUsage(); - return { - user: cpuUsage.user, - system: cpuUsage.system, - }; - } - /** - * Get comprehensive system metrics - */ - getSystemMetrics() { - return { - memory: this.getMemoryMetrics(), - cpu: this.getCPUMetrics(), - uptime: process.uptime(), - performance: this.getAllStats(), - }; - } - /** - * Clear all metrics (useful for testing) - */ - clearMetrics() { - this.metrics.clear(); - this.activeTimers.clear(); - } - /** - * Log system metrics at regular intervals - */ - startMetricsLogging(intervalMs = 30000) { - return setInterval(() => { - const metrics = this.getSystemMetrics(); - logger_1.loggers.performance.info({ - memory: metrics.memory, - cpu: metrics.cpu, - uptime: metrics.uptime, - activeTimers: this.activeTimers.size, - metricsCount: Array.from(this.metrics.values()).reduce((sum, arr) => sum + arr.length, 0), - }, 'System metrics snapshot'); - // Log performance warnings - if (metrics.memory.heapUsed > 512) { - // More than 512MB - logger_1.loggers.performance.warn({ - heapUsed: metrics.memory.heapUsed, - }, 'High memory usage detected'); - } - }, intervalMs); - } - percentile(sortedArray, percentile) { - const index = (percentile / 100) * (sortedArray.length - 1); - const lower = Math.floor(index); - const upper = Math.ceil(index); - const weight = index % 1; - if (lower === upper) { - return sortedArray[lower] ?? 0; - } - const lowerValue = sortedArray[lower] ?? 0; - const upperValue = sortedArray[upper] ?? 0; - return lowerValue * (1 - weight) + upperValue * weight; - } - cleanup() { - const now = Date.now(); - const maxAge = 5 * 60 * 1000; // 5 minutes - for (const [operationName, metrics] of this.metrics.entries()) { - const validMetrics = metrics.filter((metric) => now - metric.timestamp < maxAge); - if (validMetrics.length === 0) { - this.metrics.delete(operationName); - } - else if (validMetrics.length !== metrics.length) { - this.metrics.set(operationName, validMetrics); - } - } - // Clean up orphaned timers (older than 1 hour) - for (const [timerId, startTime] of this.activeTimers.entries()) { - if (perf_hooks_1.performance.now() - startTime > 3600000) { - // 1 hour - this.activeTimers.delete(timerId); - logger_1.loggers.performance.warn({ timerId }, 'Orphaned timer cleaned up'); - } - } - } -} -exports.PerformanceMonitor = PerformanceMonitor; -// Export singleton instance -exports.performanceMonitor = PerformanceMonitor.getInstance(); -// Decorator for timing class methods -function Timed(operationName) { - return function (target, propertyName, descriptor) { - const method = descriptor.value; - const opName = operationName || `${target.constructor.name}.${propertyName}`; - descriptor.value = async function (...args) { - return await exports.performanceMonitor.timeAsync(opName, () => method.apply(this, args), { - className: target.constructor.name, - methodName: propertyName, - }); - }; - }; -} -// Utility functions -const createPerformanceMiddleware = () => { - return async (request, reply, next) => { - const timerId = exports.performanceMonitor.startTimer('api_request', { - method: request.method, - url: request.url, - userId: request.user?.userId, - }); - try { - await next(); - } - finally { - const metric = exports.performanceMonitor.endTimer(timerId, ['api', 'request']); - if (metric && metric.duration > 2000) { - // Log slow requests (>2s) - logger_1.loggers.performance.warn({ - method: request.method, - url: request.url, - duration: metric.duration, - userId: request.user?.userId, - }, 'Slow API request detected'); - } - } - }; -}; -exports.createPerformanceMiddleware = createPerformanceMiddleware; -// Export performance monitoring utilities -exports.default = exports.performanceMonitor; -//# sourceMappingURL=performance.js.map \ No newline at end of file diff --git a/backend/dist/utils/performance.js.map b/backend/dist/utils/performance.js.map deleted file mode 100644 index a1475c2..0000000 --- a/backend/dist/utils/performance.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"performance.js","sourceRoot":"","sources":["../../src/utils/performance.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/utils/validation.d.ts b/backend/dist/utils/validation.d.ts deleted file mode 100644 index 77d04a0..0000000 --- a/backend/dist/utils/validation.d.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { Static, TSchema } from '@sinclair/typebox'; -/** - * Common validation schemas and utilities - * TypeBox schemas for request/response validation - */ -export declare const UUIDSchema: import("@sinclair/typebox").TString; -export declare const EmailSchema: import("@sinclair/typebox").TString; -export declare const TimestampSchema: import("@sinclair/typebox").TString; -export declare const PaginationSchema: import("@sinclair/typebox").TObject<{ - limit: import("@sinclair/typebox").TOptional; - offset: import("@sinclair/typebox").TOptional; - sortBy: import("@sinclair/typebox").TOptional; - sortOrder: import("@sinclair/typebox").TOptional, import("@sinclair/typebox").TLiteral<"desc">]>>; -}>; -export declare const UserRoleSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"ceo">, import("@sinclair/typebox").TLiteral<"manager">, import("@sinclair/typebox").TLiteral<"staff">]>; -export declare const UserStatusSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"active">, import("@sinclair/typebox").TLiteral<"inactive">, import("@sinclair/typebox").TLiteral<"suspended">, import("@sinclair/typebox").TLiteral<"pending">]>; -export declare const TaskPrioritySchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"low">, import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"urgent">, import("@sinclair/typebox").TLiteral<"critical">]>; -export declare const TaskStatusSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"pending">, import("@sinclair/typebox").TLiteral<"in_progress">, import("@sinclair/typebox").TLiteral<"review">, import("@sinclair/typebox").TLiteral<"completed">, import("@sinclair/typebox").TLiteral<"cancelled">, import("@sinclair/typebox").TLiteral<"on_hold">]>; -export declare const BusinessValueSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"low">, import("@sinclair/typebox").TLiteral<"medium">, import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"critical">]>; -export declare const ChannelTypeSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"general">, import("@sinclair/typebox").TLiteral<"project">, import("@sinclair/typebox").TLiteral<"department">, import("@sinclair/typebox").TLiteral<"announcement">, import("@sinclair/typebox").TLiteral<"initiative">, import("@sinclair/typebox").TLiteral<"temporary">]>; -export declare const ChannelPrivacySchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"public">, import("@sinclair/typebox").TLiteral<"private">, import("@sinclair/typebox").TLiteral<"restricted">]>; -export declare const SuccessResponseSchema: import("@sinclair/typebox").TObject<{ - success: import("@sinclair/typebox").TBoolean; - message: import("@sinclair/typebox").TOptional; - timestamp: import("@sinclair/typebox").TString; -}>; -export declare const ErrorResponseSchema: import("@sinclair/typebox").TObject<{ - error: import("@sinclair/typebox").TObject<{ - message: import("@sinclair/typebox").TString; - code: import("@sinclair/typebox").TString; - statusCode: import("@sinclair/typebox").TInteger; - details: import("@sinclair/typebox").TOptional; - }>>>; - retryAfter: import("@sinclair/typebox").TOptional; - }>; - timestamp: import("@sinclair/typebox").TString; -}>; -export declare const PaginatedResponseSchema: (dataSchema: T) => import("@sinclair/typebox").TObject<{ - success: import("@sinclair/typebox").TBoolean; - data: import("@sinclair/typebox").TArray; - pagination: import("@sinclair/typebox").TObject<{ - total: import("@sinclair/typebox").TInteger; - limit: import("@sinclair/typebox").TInteger; - offset: import("@sinclair/typebox").TInteger; - hasMore: import("@sinclair/typebox").TBoolean; - }>; - timestamp: import("@sinclair/typebox").TString; -}>; -export declare class SchemaValidator { - /** - * Validate data against schema and throw ValidationError if invalid - */ - static validate(schema: T, data: unknown): Static; - /** - * Check if data is valid against schema - */ - static isValid(schema: T, data: unknown): boolean; - /** - * Clean data according to schema (remove extra properties) - */ - static clean(schema: T, data: unknown): Static; - /** - * Apply default values from schema - */ - static withDefaults(schema: T, data: unknown): Static; -} -export declare const CustomValidators: { - /** - * Validate password strength - */ - password: (password: string) => boolean; - /** - * Validate phone number format - */ - phoneNumber: (phone: string) => boolean; - /** - * Validate URL format - */ - url: (url: string) => boolean; - /** - * Validate slug format (for channels, etc.) - */ - slug: (slug: string) => boolean; - /** - * Validate color hex code - */ - hexColor: (color: string) => boolean; -}; -export declare const validateRequest: (schema: T) => (data: unknown) => Static; -export declare const ValidationPatterns: { - StrongPasswordSchema: import("@sinclair/typebox").TString; - PhoneNumberSchema: import("@sinclair/typebox").TString; - UrlSchema: import("@sinclair/typebox").TString; - SlugSchema: import("@sinclair/typebox").TString; - HexColorSchema: import("@sinclair/typebox").TString; - TagSchema: import("@sinclair/typebox").TString; - NameSchema: import("@sinclair/typebox").TString; -}; -export type ValidationPattern = keyof typeof ValidationPatterns; -//# sourceMappingURL=validation.d.ts.map \ No newline at end of file diff --git a/backend/dist/utils/validation.d.ts.map b/backend/dist/utils/validation.d.ts.map deleted file mode 100644 index 22f4f00..0000000 --- a/backend/dist/utils/validation.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAI1D;;;GAGG;AAGH,eAAO,MAAM,UAAU,qCAGrB,CAAC;AAEH,eAAO,MAAM,WAAW,qCAItB,CAAC;AAEH,eAAO,MAAM,eAAe,qCAG1B,CAAC;AAEH,eAAO,MAAM,gBAAgB;;;;;EAK3B,CAAC;AAGH,eAAO,MAAM,cAAc,mLAIzB,CAAC;AAEH,eAAO,MAAM,gBAAgB,4OAK3B,CAAC;AAGH,eAAO,MAAM,kBAAkB,mRAM7B,CAAC;AAEH,eAAO,MAAM,gBAAgB,mVAO3B,CAAC;AAEH,eAAO,MAAM,mBAAmB,mOAK9B,CAAC;AAGH,eAAO,MAAM,iBAAiB,yVAO5B,CAAC;AAEH,eAAO,MAAM,oBAAoB,2LAI/B,CAAC;AAGH,eAAO,MAAM,qBAAqB;;;;EAIhC,CAAC;AAEH,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;EAa9B,CAAC;AAEH,eAAO,MAAM,uBAAuB,GAAI,CAAC,SAAS,OAAO,EAAE,YAAY,CAAC;;;;;;;;;;EAUtE,CAAC;AAGH,qBAAa,eAAe;IAC1B;;OAEG;IACH,MAAM,CAAC,QAAQ,CAAC,CAAC,SAAS,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC;IAmBvE;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,CAAC,SAAS,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,OAAO;IAIpE;;OAEG;IACH,MAAM,CAAC,KAAK,CAAC,CAAC,SAAS,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC;IAIpE;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,CAAC,SAAS,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC;CAG5E;AAGD,eAAO,MAAM,gBAAgB;IAC3B;;OAEG;yBACkB,MAAM,KAAG,OAAO;IASrC;;OAEG;yBACkB,MAAM,KAAG,OAAO;IAKrC;;OAEG;eACQ,MAAM,KAAG,OAAO;IAS3B;;OAEG;iBACU,MAAM,KAAG,OAAO;IAK7B;;OAEG;sBACe,MAAM,KAAG,OAAO;CAInC,CAAC;AAGF,eAAO,MAAM,eAAe,GAAI,CAAC,SAAS,OAAO,EAAE,QAAQ,CAAC,MAClD,MAAM,OAAO,cAGtB,CAAC;AAGF,eAAO,MAAM,kBAAkB;;;;;;;;CAmD9B,CAAC;AAEF,MAAM,MAAM,iBAAiB,GAAG,MAAM,OAAO,kBAAkB,CAAC"} \ No newline at end of file diff --git a/backend/dist/utils/validation.js b/backend/dist/utils/validation.js deleted file mode 100644 index 837b72c..0000000 --- a/backend/dist/utils/validation.js +++ /dev/null @@ -1,254 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.ValidationPatterns = exports.validateRequest = exports.CustomValidators = exports.SchemaValidator = exports.PaginatedResponseSchema = exports.ErrorResponseSchema = exports.SuccessResponseSchema = exports.ChannelPrivacySchema = exports.ChannelTypeSchema = exports.BusinessValueSchema = exports.TaskStatusSchema = exports.TaskPrioritySchema = exports.UserStatusSchema = exports.UserRoleSchema = exports.PaginationSchema = exports.TimestampSchema = exports.EmailSchema = exports.UUIDSchema = void 0; -const typebox_1 = require("@sinclair/typebox"); -const value_1 = require("@sinclair/typebox/value"); -const errors_1 = require("./errors"); -/** - * Common validation schemas and utilities - * TypeBox schemas for request/response validation - */ -// Base schemas -exports.UUIDSchema = typebox_1.Type.String({ - format: 'uuid', - description: 'UUID v4 string' -}); -exports.EmailSchema = typebox_1.Type.String({ - format: 'email', - maxLength: 255, - description: 'Valid email address' -}); -exports.TimestampSchema = typebox_1.Type.String({ - format: 'date-time', - description: 'ISO 8601 timestamp' -}); -exports.PaginationSchema = typebox_1.Type.Object({ - limit: typebox_1.Type.Optional(typebox_1.Type.Integer({ minimum: 1, maximum: 100, default: 20 })), - offset: typebox_1.Type.Optional(typebox_1.Type.Integer({ minimum: 0, default: 0 })), - sortBy: typebox_1.Type.Optional(typebox_1.Type.String()), - sortOrder: typebox_1.Type.Optional(typebox_1.Type.Union([typebox_1.Type.Literal('asc'), typebox_1.Type.Literal('desc')], { default: 'desc' })) -}); -// User-related schemas -exports.UserRoleSchema = typebox_1.Type.Union([ - typebox_1.Type.Literal('ceo'), - typebox_1.Type.Literal('manager'), - typebox_1.Type.Literal('staff') -]); -exports.UserStatusSchema = typebox_1.Type.Union([ - typebox_1.Type.Literal('active'), - typebox_1.Type.Literal('inactive'), - typebox_1.Type.Literal('suspended'), - typebox_1.Type.Literal('pending') -]); -// Task-related schemas -exports.TaskPrioritySchema = typebox_1.Type.Union([ - typebox_1.Type.Literal('low'), - typebox_1.Type.Literal('medium'), - typebox_1.Type.Literal('high'), - typebox_1.Type.Literal('urgent'), - typebox_1.Type.Literal('critical') -]); -exports.TaskStatusSchema = typebox_1.Type.Union([ - typebox_1.Type.Literal('pending'), - typebox_1.Type.Literal('in_progress'), - typebox_1.Type.Literal('review'), - typebox_1.Type.Literal('completed'), - typebox_1.Type.Literal('cancelled'), - typebox_1.Type.Literal('on_hold') -]); -exports.BusinessValueSchema = typebox_1.Type.Union([ - typebox_1.Type.Literal('low'), - typebox_1.Type.Literal('medium'), - typebox_1.Type.Literal('high'), - typebox_1.Type.Literal('critical') -]); -// Channel-related schemas -exports.ChannelTypeSchema = typebox_1.Type.Union([ - typebox_1.Type.Literal('general'), - typebox_1.Type.Literal('project'), - typebox_1.Type.Literal('department'), - typebox_1.Type.Literal('announcement'), - typebox_1.Type.Literal('initiative'), - typebox_1.Type.Literal('temporary') -]); -exports.ChannelPrivacySchema = typebox_1.Type.Union([ - typebox_1.Type.Literal('public'), - typebox_1.Type.Literal('private'), - typebox_1.Type.Literal('restricted') -]); -// Common response schemas -exports.SuccessResponseSchema = typebox_1.Type.Object({ - success: typebox_1.Type.Boolean({ default: true }), - message: typebox_1.Type.Optional(typebox_1.Type.String()), - timestamp: exports.TimestampSchema -}); -exports.ErrorResponseSchema = typebox_1.Type.Object({ - error: typebox_1.Type.Object({ - message: typebox_1.Type.String(), - code: typebox_1.Type.String(), - statusCode: typebox_1.Type.Integer(), - details: typebox_1.Type.Optional(typebox_1.Type.Array(typebox_1.Type.Object({ - field: typebox_1.Type.String(), - message: typebox_1.Type.String(), - value: typebox_1.Type.Optional(typebox_1.Type.Any()) - }))), - retryAfter: typebox_1.Type.Optional(typebox_1.Type.Integer()) - }), - timestamp: exports.TimestampSchema -}); -const PaginatedResponseSchema = (dataSchema) => typebox_1.Type.Object({ - success: typebox_1.Type.Boolean({ default: true }), - data: typebox_1.Type.Array(dataSchema), - pagination: typebox_1.Type.Object({ - total: typebox_1.Type.Integer(), - limit: typebox_1.Type.Integer(), - offset: typebox_1.Type.Integer(), - hasMore: typebox_1.Type.Boolean() - }), - timestamp: exports.TimestampSchema -}); -exports.PaginatedResponseSchema = PaginatedResponseSchema; -// Validation utilities -class SchemaValidator { - /** - * Validate data against schema and throw ValidationError if invalid - */ - static validate(schema, data) { - const errors = [...value_1.Value.Errors(schema, data)]; - if (errors.length > 0) { - const validationErrors = errors.map(error => ({ - field: error.path.replace('/', '.').replace(/^\./, '') || 'root', - message: error.message, - value: error.value - })); - throw new errors_1.ValidationError('Validation failed', validationErrors); - } - return data; - } - /** - * Check if data is valid against schema - */ - static isValid(schema, data) { - return value_1.Value.Check(schema, data); - } - /** - * Clean data according to schema (remove extra properties) - */ - static clean(schema, data) { - return value_1.Value.Clean(schema, data); - } - /** - * Apply default values from schema - */ - static withDefaults(schema, data) { - return value_1.Value.Default(schema, data); - } -} -exports.SchemaValidator = SchemaValidator; -// Custom validators -exports.CustomValidators = { - /** - * Validate password strength - */ - password: (password) => { - if (password.length < 8) - return false; - if (!/[A-Z]/.test(password)) - return false; - if (!/[a-z]/.test(password)) - return false; - if (!/[0-9]/.test(password)) - return false; - if (!/[^A-Za-z0-9]/.test(password)) - return false; - return true; - }, - /** - * Validate phone number format - */ - phoneNumber: (phone) => { - const phoneRegex = /^\+?[1-9]\d{1,14}$/; - return phoneRegex.test(phone.replace(/[\s\-\(\)]/g, '')); - }, - /** - * Validate URL format - */ - url: (url) => { - try { - new URL(url); - return true; - } - catch { - return false; - } - }, - /** - * Validate slug format (for channels, etc.) - */ - slug: (slug) => { - const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; - return slugRegex.test(slug) && slug.length >= 2 && slug.length <= 50; - }, - /** - * Validate color hex code - */ - hexColor: (color) => { - const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; - return hexRegex.test(color); - } -}; -// Request validation middleware factory -const validateRequest = (schema) => { - return (data) => { - return SchemaValidator.validate(schema, data); - }; -}; -exports.validateRequest = validateRequest; -// Common validation patterns -exports.ValidationPatterns = { - // Strong password requirements - StrongPasswordSchema: typebox_1.Type.String({ - minLength: 8, - maxLength: 128, - pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]', - description: 'Password must contain at least 8 characters, including uppercase, lowercase, number, and special character' - }), - // Phone number schema - PhoneNumberSchema: typebox_1.Type.String({ - pattern: '^\\+?[1-9]\\d{1,14}$', - description: 'Valid international phone number' - }), - // URL schema - UrlSchema: typebox_1.Type.String({ - format: 'uri', - maxLength: 2048, - description: 'Valid HTTP(S) URL' - }), - // Slug schema (for channels, etc.) - SlugSchema: typebox_1.Type.String({ - pattern: '^[a-z0-9]+(?:-[a-z0-9]+)*$', - minLength: 2, - maxLength: 50, - description: 'Valid slug (lowercase letters, numbers, and hyphens)' - }), - // Hex color schema - HexColorSchema: typebox_1.Type.String({ - pattern: '^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$', - description: 'Valid hex color code' - }), - // Tag schema - TagSchema: typebox_1.Type.String({ - minLength: 1, - maxLength: 50, - pattern: '^[a-zA-Z0-9_-]+$', - description: 'Valid tag name' - }), - // Name schema (for users, channels, etc.) - NameSchema: typebox_1.Type.String({ - minLength: 1, - maxLength: 255, - pattern: '^[^\\s]+(\\s+[^\\s]+)*$', // No leading/trailing whitespace, no double spaces - description: 'Valid name (no leading/trailing spaces)' - }) -}; -//# sourceMappingURL=validation.js.map \ No newline at end of file diff --git a/backend/dist/utils/validation.js.map b/backend/dist/utils/validation.js.map deleted file mode 100644 index 9ba2d79..0000000 --- a/backend/dist/utils/validation.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/utils/validation.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/VoiceProcessingService.d.ts b/backend/dist/voice/VoiceProcessingService.d.ts deleted file mode 100644 index 6ff5893..0000000 --- a/backend/dist/voice/VoiceProcessingService.d.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Voice Processing Service - Phase 2 Integration Layer - * Orchestrates the complete voice processing pipeline - * - * Success Criteria Integration: - * - End-to-end voice command processing <2 seconds (simple) / <5 seconds (complex) - * - Coordinates audio stream, transcription, AI parsing, and execution - * - Provides comprehensive error handling and performance monitoring - */ -import { EventEmitter } from 'events'; -import { UserContext, ParsedCommand, VoiceProcessingMetrics } from './types'; -export interface VoiceProcessingOptions { - enableCaching?: boolean; - maxProcessingTime?: number; - qualityThreshold?: number; - streamingMode?: boolean; -} -export interface VoiceProcessingResult { - success: boolean; - command?: ParsedCommand; - metrics: VoiceProcessingMetrics; - error?: string; -} -export declare class VoiceProcessingService extends EventEmitter { - private audioStreamManager; - private whisperService; - private aiCommandParser; - private multiActionExecutor; - private performanceMetrics; - constructor(); - /** - * Process complete voice command from audio to parsed command - * This is the main integration point for the voice processing pipeline - */ - processVoiceCommand(audioBuffer: Buffer, userContext: UserContext, options?: VoiceProcessingOptions): Promise; - /** - * Process and execute complete voice command from audio to final result - * This includes transcription, AI parsing, and multi-action execution - */ - processAndExecuteVoiceCommand(audioBuffer: Buffer, userContext: UserContext, options?: VoiceProcessingOptions): Promise<{ - success: boolean; - command?: ParsedCommand; - executionResult?: any; - metrics: VoiceProcessingMetrics; - error?: string; - }>; - /** - * Process streaming audio for real-time transcription - */ - processStreamingAudio(audioStream: AsyncGenerator, userContext: UserContext, options?: VoiceProcessingOptions): Promise>>; - /** - * Get comprehensive service performance statistics - */ - getPerformanceStatistics(): { - overall: any; - audioProcessing: any; - transcription: any; - aiParsing: any; - }; - /** - * Get service health status - */ - getHealthStatus(): { - status: 'healthy' | 'degraded' | 'unhealthy'; - components: Record; - metrics: any; - }; - private validateAudioInput; - private validatePerformanceTargets; - private recordMetrics; - private calculateOverallStats; - private setupEventHandlers; -} -//# sourceMappingURL=VoiceProcessingService.d.ts.map \ No newline at end of file diff --git a/backend/dist/voice/VoiceProcessingService.d.ts.map b/backend/dist/voice/VoiceProcessingService.d.ts.map deleted file mode 100644 index d587ff7..0000000 --- a/backend/dist/voice/VoiceProcessingService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"VoiceProcessingService.d.ts","sourceRoot":"","sources":["../../src/voice/VoiceProcessingService.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAOtC,OAAO,EACL,WAAW,EACX,aAAa,EAGb,sBAAsB,EACvB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,aAAa,CAAC;IACxB,OAAO,EAAE,sBAAsB,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,sBAAuB,SAAQ,YAAY;IACtD,OAAO,CAAC,kBAAkB,CAAqB;IAC/C,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,mBAAmB,CAAsB;IACjD,OAAO,CAAC,kBAAkB,CAAgC;;IAe1D;;;OAGG;IACG,mBAAmB,CACvB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,qBAAqB,CAAC;IAgJjC;;;OAGG;IACG,6BAA6B,CACjC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC;QACT,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,CAAC,EAAE,aAAa,CAAC;QACxB,eAAe,CAAC,EAAE,GAAG,CAAC;QACtB,OAAO,EAAE,sBAAsB,CAAC;QAChC,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IA2GF;;OAEG;IACG,qBAAqB,CACzB,WAAW,EAAE,cAAc,CAAC,MAAM,CAAC,EACnC,WAAW,EAAE,WAAW,EACxB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC;IAwDlD;;OAEG;IACH,wBAAwB,IAAI;QAC1B,OAAO,EAAE,GAAG,CAAC;QACb,eAAe,EAAE,GAAG,CAAC;QACrB,aAAa,EAAE,GAAG,CAAC;QACnB,SAAS,EAAE,GAAG,CAAC;KAChB;IAWD;;OAEG;IACH,eAAe,IAAI;QACjB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;QAC7C,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAChC,OAAO,EAAE,GAAG,CAAC;KACd;YAsCa,kBAAkB;IAsBhC,OAAO,CAAC,0BAA0B;IAoBlC,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,qBAAqB;IA4C7B,OAAO,CAAC,kBAAkB;CAe3B"} \ No newline at end of file diff --git a/backend/dist/voice/VoiceProcessingService.js b/backend/dist/voice/VoiceProcessingService.js deleted file mode 100644 index 4d7e009..0000000 --- a/backend/dist/voice/VoiceProcessingService.js +++ /dev/null @@ -1,435 +0,0 @@ -"use strict"; -/** - * Voice Processing Service - Phase 2 Integration Layer - * Orchestrates the complete voice processing pipeline - * - * Success Criteria Integration: - * - End-to-end voice command processing <2 seconds (simple) / <5 seconds (complex) - * - Coordinates audio stream, transcription, AI parsing, and execution - * - Provides comprehensive error handling and performance monitoring - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VoiceProcessingService = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../utils/logger"); -const AudioStreamManager_1 = require("./stream/AudioStreamManager"); -const WhisperService_1 = require("./transcription/WhisperService"); -const AICommandParser_1 = require("../ai/commands/AICommandParser"); -const MultiActionExecutor_1 = require("../ai/execution/MultiActionExecutor"); -const types_1 = require("./types"); -class VoiceProcessingService extends events_1.EventEmitter { - audioStreamManager; - whisperService; - aiCommandParser; - multiActionExecutor; - performanceMetrics = []; - constructor() { - super(); - this.audioStreamManager = new AudioStreamManager_1.AudioStreamManager(); - this.whisperService = new WhisperService_1.WhisperService(); - this.aiCommandParser = new AICommandParser_1.AICommandParser(); - this.multiActionExecutor = new MultiActionExecutor_1.MultiActionExecutor(); - this.setupEventHandlers(); - logger_1.logger.info('Voice Processing Service initialized'); - } - /** - * Process complete voice command from audio to parsed command - * This is the main integration point for the voice processing pipeline - */ - async processVoiceCommand(audioBuffer, userContext, options = {}) { - const startTime = perf_hooks_1.performance.now(); - const commandId = `cmd-${Date.now()}-${Math.random().toString(36).substring(7)}`; - logger_1.logger.info('Starting voice command processing', { - commandId, - userId: userContext.userId, - audioSize: audioBuffer.length, - options, - }); - try { - // Step 1: Audio preprocessing and validation - const preprocessingStart = perf_hooks_1.performance.now(); - await this.validateAudioInput(audioBuffer); - const preprocessingTime = perf_hooks_1.performance.now() - preprocessingStart; - this.emit('preprocessing_complete', { - commandId, - processingTime: preprocessingTime, - }); - // Step 2: Transcription - const transcriptionStart = perf_hooks_1.performance.now(); - const transcriptOptions = {}; - if (userContext.language !== undefined) { - transcriptOptions.language = userContext.language; - } - const transcriptResult = await this.whisperService.transcribeAudio(audioBuffer, transcriptOptions); - const transcriptionTime = perf_hooks_1.performance.now() - transcriptionStart; - if (!transcriptResult.transcript || transcriptResult.transcript.trim().length === 0) { - throw new types_1.VoiceProcessingError('Empty transcription result'); - } - this.emit('transcription_complete', { - commandId, - transcript: transcriptResult.transcript, - confidence: transcriptResult.confidence, - processingTime: transcriptionTime, - }); - // Step 3: AI Command Parsing - const parsingStart = perf_hooks_1.performance.now(); - const parsedCommand = await this.aiCommandParser.parseVoiceCommand(transcriptResult.transcript, userContext, { - timeout: options.maxProcessingTime || 10000, - enhanceWithContext: true, - }); - const parsingTime = perf_hooks_1.performance.now() - parsingStart; - this.emit('parsing_complete', { - commandId, - intent: parsedCommand.intent, - actionCount: parsedCommand.actions.length, - confidence: parsedCommand.confidence, - processingTime: parsingTime, - }); - // Calculate total processing time - const totalTime = perf_hooks_1.performance.now() - startTime; - // Create comprehensive metrics - const metrics = { - transcriptionTime, - processingTime: parsingTime, - executionTime: 0, // Will be set when command is executed - totalTime, - accuracy: Math.min(transcriptResult.confidence, parsedCommand.confidence), - success: true, - commandId, - userId: userContext.userId, - timestamp: new Date().toISOString(), - }; - // Record metrics - this.recordMetrics(metrics); - // Check performance targets - this.validatePerformanceTargets(totalTime, parsedCommand.actions.length); - logger_1.logger.info('Voice command processing completed successfully', { - commandId, - totalTime: `${totalTime.toFixed(2)}ms`, - transcript: transcriptResult.transcript.substring(0, 100) + '...', - intent: parsedCommand.intent, - actionCount: parsedCommand.actions.length, - }); - this.emit('processing_complete', { - commandId, - success: true, - metrics, - }); - return { - success: true, - command: parsedCommand, - metrics, - }; - } - catch (error) { - const totalTime = perf_hooks_1.performance.now() - startTime; - const metrics = { - transcriptionTime: 0, - processingTime: 0, - executionTime: 0, - totalTime, - accuracy: 0, - success: false, - commandId, - userId: userContext.userId, - timestamp: new Date().toISOString(), - }; - this.recordMetrics(metrics); - logger_1.logger.error('Voice command processing failed', { - commandId, - error: error instanceof Error ? error.message : String(error), - totalTime: `${totalTime.toFixed(2)}ms`, - userId: userContext.userId, - }); - this.emit('processing_error', { - commandId, - error: error instanceof Error ? error.message : String(error), - metrics, - }); - return { - success: false, - error: error instanceof Error ? error.message : String(error), - metrics, - }; - } - } - /** - * Process and execute complete voice command from audio to final result - * This includes transcription, AI parsing, and multi-action execution - */ - async processAndExecuteVoiceCommand(audioBuffer, userContext, options = {}) { - const startTime = perf_hooks_1.performance.now(); - const commandId = `cmd-${Date.now()}-${Math.random().toString(36).substring(7)}`; - try { - // Step 1: Process voice command (transcription + AI parsing) - const processingResult = await this.processVoiceCommand(audioBuffer, userContext, options); - if (!processingResult.success || !processingResult.command) { - return { - success: false, - error: processingResult.error || 'Voice command processing failed', - metrics: processingResult.metrics, - }; - } - const parsedCommand = processingResult.command; - // Step 2: Execute multi-action command if it has actions - let executionResult; - if (parsedCommand.actions && parsedCommand.actions.length > 0) { - logger_1.logger.info('Executing multi-action command', { - commandId: parsedCommand.id, - actionCount: parsedCommand.actions.length, - userId: userContext.userId, - }); - const executionStart = perf_hooks_1.performance.now(); - executionResult = await this.multiActionExecutor.executeMultiActionCommand(parsedCommand, userContext, { - transactionTimeout: options.maxProcessingTime || 30000, - auditLogging: true, - progressTracking: true, - }); - const executionTime = perf_hooks_1.performance.now() - executionStart; - // Update metrics with execution time - processingResult.metrics.executionTime = executionTime; - processingResult.metrics.totalTime = perf_hooks_1.performance.now() - startTime; - this.emit('command_execution_complete', { - commandId: parsedCommand.id, - success: executionResult.success, - executionTime, - actionCount: executionResult.executedActions.length, - }); - logger_1.logger.info('Multi-action command execution completed', { - commandId: parsedCommand.id, - success: executionResult.success, - executionTime: `${executionTime.toFixed(2)}ms`, - successfulActions: executionResult.executedActions.length, - failedActions: executionResult.failedActions.length, - }); - } - const totalTime = perf_hooks_1.performance.now() - startTime; - processingResult.metrics.totalTime = totalTime; - return executionResult && !executionResult.success - ? { - success: false, - command: parsedCommand, - executionResult, - metrics: processingResult.metrics, - error: 'Command execution failed', - } - : { - success: processingResult.success, - command: parsedCommand, - executionResult, - metrics: processingResult.metrics, - }; - } - catch (error) { - const totalTime = perf_hooks_1.performance.now() - startTime; - const errorMetrics = { - transcriptionTime: 0, - processingTime: 0, - executionTime: 0, - totalTime, - accuracy: 0, - success: false, - commandId, - userId: userContext.userId, - timestamp: new Date().toISOString(), - }; - logger_1.logger.error('Complete voice command processing failed', { - commandId, - error: error instanceof Error ? error.message : String(error), - totalTime: `${totalTime.toFixed(2)}ms`, - userId: userContext.userId, - }); - return { - success: false, - error: error instanceof Error ? error.message : String(error), - metrics: errorMetrics, - }; - } - } - /** - * Process streaming audio for real-time transcription - */ - async processStreamingAudio(audioStream, userContext, options = {}) { - const streamId = `stream-${Date.now()}`; - logger_1.logger.info('Starting streaming audio processing', { - streamId, - userId: userContext.userId, - }); - return async function* () { - try { - const transcriptStream = await this.whisperService.transcribeStream(audioStream); - for await (const partialTranscript of transcriptStream) { - if (partialTranscript.transcript && partialTranscript.transcript.trim().length > 0) { - try { - // Parse partial transcript - const partialCommand = await this.aiCommandParser.parseVoiceCommand(partialTranscript.transcript, userContext, { timeout: 5000 }); - yield { - id: partialCommand.id, - transcript: partialTranscript.transcript, - intent: partialCommand.intent, - confidence: Math.min(partialTranscript.confidence || 0.5, partialCommand.confidence), - actions: partialCommand.actions.slice(0, 3), // Limit actions for streaming - timestamp: new Date().toISOString(), - }; - } - catch (error) { - logger_1.logger.warn('Partial command parsing failed', { - streamId, - error: error instanceof Error ? error.message : String(error), - transcript: partialTranscript.transcript?.substring(0, 50), - }); - } - } - } - } - catch (error) { - logger_1.logger.error('Streaming audio processing failed', { - streamId, - error: error instanceof Error ? error.message : String(error), - }); - yield { - error: error instanceof Error ? error.message : String(error), - timestamp: new Date().toISOString(), - }; - } - }.call(this); - } - /** - * Get comprehensive service performance statistics - */ - getPerformanceStatistics() { - const overallStats = this.calculateOverallStats(); - return { - overall: overallStats, - audioProcessing: this.audioStreamManager.getPerformanceMetrics(), - transcription: this.whisperService.getPerformanceStats(), - aiParsing: this.aiCommandParser.getPerformanceStats(), - }; - } - /** - * Get service health status - */ - getHealthStatus() { - const whisperHealth = this.whisperService.getHealthStatus(); - const overallStats = this.calculateOverallStats(); - let status = 'healthy'; - // Check overall performance - if (overallStats.averageProcessingTime > 5000) { - status = 'unhealthy'; - } - else if (overallStats.averageProcessingTime > 3000) { - status = 'degraded'; - } - // Check success rate - if (overallStats.successRate < 0.9) { - status = 'unhealthy'; - } - else if (overallStats.successRate < 0.95) { - status = 'degraded'; - } - // Factor in component health - if (whisperHealth.status === 'unhealthy') { - status = 'unhealthy'; - } - else if (whisperHealth.status === 'degraded' && status === 'healthy') { - status = 'degraded'; - } - return { - status, - components: { - whisper: whisperHealth, - audioStream: this.audioStreamManager.getPerformanceMetrics(), - aiParsing: this.aiCommandParser.getPerformanceStats(), - }, - metrics: overallStats, - }; - } - async validateAudioInput(audioBuffer) { - if (!audioBuffer || audioBuffer.length === 0) { - throw new types_1.VoiceProcessingError('Empty audio buffer'); - } - if (audioBuffer.length < 1000) { - throw new types_1.VoiceProcessingError('Audio buffer too small (minimum 1KB)'); - } - if (audioBuffer.length > 50 * 1024 * 1024) { - throw new types_1.VoiceProcessingError('Audio buffer too large (maximum 50MB)'); - } - // Basic audio format validation (check for WAV header) - if (audioBuffer.length > 12) { - const header = audioBuffer.toString('ascii', 0, 4); - if (header !== 'RIFF') { - logger_1.logger.warn('Audio format validation: Not a standard WAV file'); - } - } - } - validatePerformanceTargets(totalTime, actionCount) { - // Simple commands should complete in <2 seconds - if (actionCount <= 2 && totalTime > 2000) { - logger_1.logger.warn('Performance target missed: Simple command exceeded 2 seconds', { - totalTime: `${totalTime.toFixed(2)}ms`, - actionCount, - target: '2000ms', - }); - } - // Complex commands should complete in <5 seconds - if (actionCount > 2 && totalTime > 5000) { - logger_1.logger.warn('Performance target missed: Complex command exceeded 5 seconds', { - totalTime: `${totalTime.toFixed(2)}ms`, - actionCount, - target: '5000ms', - }); - } - } - recordMetrics(metrics) { - this.performanceMetrics.push(metrics); - // Keep only last 1000 metrics - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } - calculateOverallStats() { - if (this.performanceMetrics.length === 0) { - return { - totalCommands: 0, - successfulCommands: 0, - successRate: 0, - averageProcessingTime: 0, - averageTranscriptionTime: 0, - averageParsingTime: 0, - p95ProcessingTime: 0, - p99ProcessingTime: 0, - }; - } - const successfulMetrics = this.performanceMetrics.filter((m) => m.success); - const sortedTimes = this.performanceMetrics.map((m) => m.totalTime).sort((a, b) => a - b); - return { - totalCommands: this.performanceMetrics.length, - successfulCommands: successfulMetrics.length, - successRate: successfulMetrics.length / this.performanceMetrics.length, - averageProcessingTime: this.performanceMetrics.reduce((sum, m) => sum + m.totalTime, 0) / - this.performanceMetrics.length, - averageTranscriptionTime: successfulMetrics.reduce((sum, m) => sum + m.transcriptionTime, 0) / - Math.max(successfulMetrics.length, 1), - averageParsingTime: successfulMetrics.reduce((sum, m) => sum + m.processingTime, 0) / - Math.max(successfulMetrics.length, 1), - p95ProcessingTime: sortedTimes[Math.floor(sortedTimes.length * 0.95)] || 0, - p99ProcessingTime: sortedTimes[Math.floor(sortedTimes.length * 0.99)] || 0, - }; - } - setupEventHandlers() { - // Audio stream events - this.audioStreamManager.on('stream_error', (data) => { - this.emit('audio_stream_error', data); - }); - this.audioStreamManager.on('audio_segment_ready', (segment) => { - this.emit('audio_segment_ready', segment); - }); - // Add error handling for uncaught events - this.on('error', (error) => { - logger_1.logger.error('Voice processing service error', { error: error.message }); - }); - } -} -exports.VoiceProcessingService = VoiceProcessingService; -//# sourceMappingURL=VoiceProcessingService.js.map \ No newline at end of file diff --git a/backend/dist/voice/VoiceProcessingService.js.map b/backend/dist/voice/VoiceProcessingService.js.map deleted file mode 100644 index b67f9ba..0000000 --- a/backend/dist/voice/VoiceProcessingService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"VoiceProcessingService.js","sourceRoot":"","sources":["../../src/voice/VoiceProcessingService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/stream/AudioPreprocessor.d.ts b/backend/dist/voice/stream/AudioPreprocessor.d.ts deleted file mode 100644 index 92e9718..0000000 --- a/backend/dist/voice/stream/AudioPreprocessor.d.ts +++ /dev/null @@ -1,79 +0,0 @@ -/** - * Audio Preprocessor - Phase 2 Voice Processing Pipeline - * Real-time audio enhancement and noise reduction - * - * Success Criteria: - * - Noise reduction improves transcription accuracy by 15%+ - * - Volume normalization prevents clipping - * - Processing adds <50ms latency - * - Output optimized for Whisper API format - */ -export interface PreprocessorConfig { - noiseReduction: boolean; - volumeNormalization: boolean; - highPassFilter: boolean; - lowPassFilter: boolean; - agcEnabled: boolean; - targetRMS: number; - noiseGateThreshold: number; - highPassFreq: number; - lowPassFreq: number; - sampleRate: number; -} -export interface ProcessingResult { - processedAudio: Buffer; - processingTime: number; - applied: string[]; - metrics: { - inputRMS: number; - outputRMS: number; - noiseReduction: number; - clippingPrevented: boolean; - }; -} -export declare class AudioPreprocessor { - private config; - private performanceMetrics; - private runningRMS; - private noiseProfile; - private agcGain; - private dcOffset; - private highPassState; - private lowPassState; - constructor(config?: Partial); - /** - * Process audio buffer with all enabled preprocessing steps - * Target: <50ms processing time - */ - process(audioBuffer: Buffer): Promise; - /** - * Process with detailed results - */ - processDetailed(audioBuffer: Buffer): Promise; - /** - * Update noise profile for noise reduction - */ - updateNoiseProfile(noiseSample: Buffer): void; - /** - * Get performance statistics - */ - getPerformanceStats(): { - average: number; - p95: number; - p99: number; - count: number; - }; - private bufferToFloat32; - private float32ToBuffer; - private calculateRMS; - private removeDCOffset; - private applyHighPassFilter; - private applyLowPassFilter; - private applyNoiseGate; - private applyNoiseReduction; - private applyAutomaticGainControl; - private normalizeVolume; - private checkForClipping; - private recordPerformance; -} -//# sourceMappingURL=AudioPreprocessor.d.ts.map \ No newline at end of file diff --git a/backend/dist/voice/stream/AudioPreprocessor.d.ts.map b/backend/dist/voice/stream/AudioPreprocessor.d.ts.map deleted file mode 100644 index 378a786..0000000 --- a/backend/dist/voice/stream/AudioPreprocessor.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"AudioPreprocessor.d.ts","sourceRoot":"","sources":["../../../src/voice/stream/AudioPreprocessor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,OAAO,CAAC;IACxB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,cAAc,EAAE,OAAO,CAAC;IACxB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,gBAAgB;IAC/B,cAAc,EAAE,MAAM,CAAC;IACvB,cAAc,EAAE,MAAM,CAAC;IACvB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE;QACP,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;QAClB,cAAc,EAAE,MAAM,CAAC;QACvB,iBAAiB,EAAE,OAAO,CAAC;KAC5B,CAAC;CACH;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAqB;IACnC,OAAO,CAAC,kBAAkB,CAAgB;IAG1C,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,YAAY,CAA6B;IACjD,OAAO,CAAC,OAAO,CAAe;IAC9B,OAAO,CAAC,QAAQ,CAAa;IAG7B,OAAO,CAAC,aAAa,CAKnB;IACF,OAAO,CAAC,YAAY,CAKlB;gBAEU,MAAM,GAAE,OAAO,CAAC,kBAAkB,CAAM;IAkBpD;;;OAGG;IACG,OAAO,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAkGnD;;OAEG;IACG,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAyBrE;;OAEG;IACH,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IA0B7C;;OAEG;IACH,mBAAmB,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAmBnF,OAAO,CAAC,eAAe;IAQvB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,YAAY;IASpB,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,mBAAmB;IAkB3B,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,cAAc;IAgBtB,OAAO,CAAC,mBAAmB;IA6B3B,OAAO,CAAC,yBAAyB;IA6BjC,OAAO,CAAC,eAAe;IA2BvB,OAAO,CAAC,gBAAgB;IASxB,OAAO,CAAC,iBAAiB;CAQ1B"} \ No newline at end of file diff --git a/backend/dist/voice/stream/AudioPreprocessor.js b/backend/dist/voice/stream/AudioPreprocessor.js deleted file mode 100644 index 3fd5621..0000000 --- a/backend/dist/voice/stream/AudioPreprocessor.js +++ /dev/null @@ -1,369 +0,0 @@ -"use strict"; -/** - * Audio Preprocessor - Phase 2 Voice Processing Pipeline - * Real-time audio enhancement and noise reduction - * - * Success Criteria: - * - Noise reduction improves transcription accuracy by 15%+ - * - Volume normalization prevents clipping - * - Processing adds <50ms latency - * - Output optimized for Whisper API format - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AudioPreprocessor = void 0; -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -class AudioPreprocessor { - config; - performanceMetrics = []; - // State for running calculations - runningRMS = 0; - noiseProfile = null; - agcGain = 1.0; - dcOffset = 0; - // Filter state (simple IIR filters) - highPassState = { - x1: 0, - x2: 0, - y1: 0, - y2: 0, - }; - lowPassState = { - x1: 0, - x2: 0, - y1: 0, - y2: 0, - }; - constructor(config = {}) { - this.config = { - noiseReduction: true, - volumeNormalization: true, - highPassFilter: true, - lowPassFilter: false, - agcEnabled: true, - targetRMS: 0.1, // 10% of full scale - noiseGateThreshold: 0.01, // 1% of full scale - highPassFreq: 80, // Remove low frequency noise - lowPassFreq: 8000, // Remove high frequency noise above speech - sampleRate: 16000, - ...config, - }; - logger_1.logger.debug('Audio preprocessor initialized', { config: this.config }); - } - /** - * Process audio buffer with all enabled preprocessing steps - * Target: <50ms processing time - */ - async process(audioBuffer) { - const startTime = perf_hooks_1.performance.now(); - try { - // Convert to float samples for processing - const samples = this.bufferToFloat32(audioBuffer); - const originalRMS = this.calculateRMS(samples); - let processedSamples = new Float32Array(samples.length); - processedSamples.set(samples); - const appliedProcessing = []; - // Step 1: DC offset removal - processedSamples = this.removeDCOffset(processedSamples); - appliedProcessing.push('dc_removal'); - // Step 2: High-pass filter (remove low frequency noise) - if (this.config.highPassFilter) { - processedSamples = this.applyHighPassFilter(processedSamples); - appliedProcessing.push('high_pass_filter'); - } - // Step 3: Low-pass filter (anti-aliasing) - if (this.config.lowPassFilter) { - processedSamples = this.applyLowPassFilter(processedSamples); - appliedProcessing.push('low_pass_filter'); - } - // Step 4: Noise gate - processedSamples = this.applyNoiseGate(processedSamples); - appliedProcessing.push('noise_gate'); - // Step 5: Noise reduction - let noiseReductionAmount = 0; - if (this.config.noiseReduction) { - const result = this.applyNoiseReduction(processedSamples); - processedSamples = result.samples; - noiseReductionAmount = result.reductionAmount; - appliedProcessing.push('noise_reduction'); - } - // Step 6: Automatic Gain Control - if (this.config.agcEnabled) { - processedSamples = this.applyAutomaticGainControl(processedSamples); - appliedProcessing.push('agc'); - } - // Step 7: Volume normalization - let clippingPrevented = false; - if (this.config.volumeNormalization) { - const result = this.normalizeVolume(processedSamples); - processedSamples = result.samples; - clippingPrevented = result.clippingPrevented; - appliedProcessing.push('volume_normalization'); - } - // Convert back to buffer - const processedBuffer = this.float32ToBuffer(processedSamples); - const processingTime = perf_hooks_1.performance.now() - startTime; - // Record performance - this.recordPerformance(processingTime); - // Log slow processing - if (processingTime > 50) { - logger_1.logger.warn('Slow audio preprocessing', { - processingTime: `${processingTime.toFixed(2)}ms`, - bufferSize: audioBuffer.length, - appliedProcessing, - }); - } - const outputRMS = this.calculateRMS(processedSamples); - logger_1.logger.debug('Audio preprocessing complete', { - processingTime: `${processingTime.toFixed(2)}ms`, - inputRMS: originalRMS.toFixed(4), - outputRMS: outputRMS.toFixed(4), - appliedProcessing, - noiseReduction: noiseReductionAmount.toFixed(4), - }); - return processedBuffer; - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - const errorMessage = error instanceof Error ? error.message : String(error); - logger_1.logger.error('Audio preprocessing failed', { - error: errorMessage, - processingTime: `${processingTime.toFixed(2)}ms`, - bufferSize: audioBuffer.length, - }); - // Return original buffer on error - return audioBuffer; - } - } - /** - * Process with detailed results - */ - async processDetailed(audioBuffer) { - const startTime = perf_hooks_1.performance.now(); - const samples = this.bufferToFloat32(audioBuffer); - const inputRMS = this.calculateRMS(samples); - const processedBuffer = await this.process(audioBuffer); - const processedSamples = this.bufferToFloat32(processedBuffer); - const outputRMS = this.calculateRMS(processedSamples); - const processingTime = perf_hooks_1.performance.now() - startTime; - return { - processedAudio: processedBuffer, - processingTime, - applied: ['preprocessing_pipeline'], - metrics: { - inputRMS, - outputRMS, - noiseReduction: Math.max(0, inputRMS - outputRMS), - clippingPrevented: this.checkForClipping(processedSamples), - }, - }; - } - /** - * Update noise profile for noise reduction - */ - updateNoiseProfile(noiseSample) { - const samples = this.bufferToFloat32(noiseSample); - // Simple noise profiling - calculate spectral characteristics - this.noiseProfile = new Float32Array(256); // Frequency bins - // Simple FFT-like analysis (simplified for real-time processing) - for (let i = 0; i < this.noiseProfile.length; i++) { - let sum = 0; - const freqBin = (i / this.noiseProfile.length) * (this.config.sampleRate / 2); - // Calculate energy in this frequency bin - for (let j = 0; j < samples.length - 1; j++) { - const phase = (2 * Math.PI * freqBin * j) / this.config.sampleRate; - sum += samples[j] * Math.cos(phase); - } - this.noiseProfile[i] = Math.abs(sum) / samples.length; - } - logger_1.logger.info('Noise profile updated', { - sampleLength: samples.length, - profileBins: this.noiseProfile.length, - }); - } - /** - * Get performance statistics - */ - getPerformanceStats() { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] ?? 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] ?? 0; - return { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: this.performanceMetrics.length, - }; - } - bufferToFloat32(buffer) { - const samples = new Float32Array(buffer.length / 2); - for (let i = 0; i < samples.length; i++) { - samples[i] = buffer.readInt16LE(i * 2) / 32768.0; - } - return samples; - } - float32ToBuffer(samples) { - const buffer = Buffer.allocUnsafe(samples.length * 2); - for (let i = 0; i < samples.length; i++) { - const sample = Math.max(-1, Math.min(1, samples[i] ?? 0)); - buffer.writeInt16LE(Math.round(sample * 32767), i * 2); - } - return buffer; - } - calculateRMS(samples) { - let sum = 0; - for (let i = 0; i < samples.length; i++) { - const sample = samples[i] ?? 0; - sum += sample * sample; - } - return Math.sqrt(sum / samples.length); - } - removeDCOffset(samples) { - // Calculate DC offset - let sum = 0; - for (let i = 0; i < samples.length; i++) { - sum += samples[i] ?? 0; - } - const dcOffset = sum / samples.length; - // Remove DC offset - const output = new Float32Array(samples.length); - for (let i = 0; i < samples.length; i++) { - output[i] = (samples[i] ?? 0) - dcOffset; - } - this.dcOffset = dcOffset; - return output; - } - applyHighPassFilter(samples) { - const output = new Float32Array(samples.length); - // Simple high-pass filter (80Hz cutoff) - const fc = this.config.highPassFreq / this.config.sampleRate; - const alpha = 1 / (1 + 2 * Math.PI * fc); - for (let i = 0; i < samples.length; i++) { - if (i === 0) { - output[i] = alpha * (samples[i] ?? 0); - } - else { - output[i] = alpha * ((output[i - 1] ?? 0) + (samples[i] ?? 0) - (samples[i - 1] ?? 0)); - } - } - return output; - } - applyLowPassFilter(samples) { - const output = new Float32Array(samples.length); - // Simple low-pass filter - const fc = this.config.lowPassFreq / this.config.sampleRate; - const alpha = (2 * Math.PI * fc) / (1 + 2 * Math.PI * fc); - output[0] = samples[0] ?? 0; - for (let i = 1; i < samples.length; i++) { - output[i] = alpha * (samples[i] ?? 0) + (1 - alpha) * (output[i - 1] ?? 0); - } - return output; - } - applyNoiseGate(samples) { - const output = new Float32Array(samples.length); - const threshold = this.config.noiseGateThreshold; - for (let i = 0; i < samples.length; i++) { - const sample = samples[i] ?? 0; - if (Math.abs(sample) > threshold) { - output[i] = sample; - } - else { - output[i] = 0; // Gate the signal - } - } - return output; - } - applyNoiseReduction(samples) { - const output = new Float32Array(samples.length); - let reductionAmount = 0; - if (!this.noiseProfile) { - // Simple spectral subtraction without profile - for (let i = 0; i < samples.length; i++) { - const sample = samples[i] ?? 0; - const originalMagnitude = Math.abs(sample); - const reducedMagnitude = Math.max(0.1 * originalMagnitude, originalMagnitude - 0.02); - output[i] = sample >= 0 ? reducedMagnitude : -reducedMagnitude; - reductionAmount += originalMagnitude - Math.abs(output[i] ?? 0); - } - } - else { - // More sophisticated noise reduction would go here - // For now, use simple approach - for (let i = 0; i < samples.length; i++) { - const sample = samples[i] ?? 0; - output[i] = sample * 0.95; // Simple noise reduction - reductionAmount += Math.abs(sample) * 0.05; - } - } - return { samples: output, reductionAmount: reductionAmount / samples.length }; - } - applyAutomaticGainControl(samples) { - const output = new Float32Array(samples.length); - const currentRMS = this.calculateRMS(samples); - if (currentRMS > 0) { - const targetGain = this.config.targetRMS / currentRMS; - // Smooth gain changes to avoid artifacts - const maxGainChange = 1.1; // Max 10% gain change per frame - if (targetGain > this.agcGain * maxGainChange) { - this.agcGain *= maxGainChange; - } - else if (targetGain < this.agcGain / maxGainChange) { - this.agcGain /= maxGainChange; - } - else { - this.agcGain = targetGain; - } - // Apply gain with limiting - for (let i = 0; i < samples.length; i++) { - output[i] = Math.max(-1, Math.min(1, (samples[i] ?? 0) * this.agcGain)); - } - } - else { - // No signal, pass through - output.set(samples); - } - return output; - } - normalizeVolume(samples) { - const output = new Float32Array(samples.length); - let clippingPrevented = false; - // Find peak - let peak = 0; - for (let i = 0; i < samples.length; i++) { - peak = Math.max(peak, Math.abs(samples[i] ?? 0)); - } - if (peak > 0.95) { - // Prevent clipping - const scaleFactor = 0.95 / peak; - for (let i = 0; i < samples.length; i++) { - output[i] = (samples[i] ?? 0) * scaleFactor; - } - clippingPrevented = true; - } - else { - output.set(samples); - } - return { samples: output, clippingPrevented }; - } - checkForClipping(samples) { - for (let i = 0; i < samples.length; i++) { - if (Math.abs(samples[i] ?? 0) > 0.99) { - return true; - } - } - return false; - } - recordPerformance(time) { - this.performanceMetrics.push(time); - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} -exports.AudioPreprocessor = AudioPreprocessor; -//# sourceMappingURL=AudioPreprocessor.js.map \ No newline at end of file diff --git a/backend/dist/voice/stream/AudioPreprocessor.js.map b/backend/dist/voice/stream/AudioPreprocessor.js.map deleted file mode 100644 index 259945b..0000000 --- a/backend/dist/voice/stream/AudioPreprocessor.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"AudioPreprocessor.js","sourceRoot":"","sources":["../../../src/voice/stream/AudioPreprocessor.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/stream/AudioStreamManager.d.ts b/backend/dist/voice/stream/AudioStreamManager.d.ts deleted file mode 100644 index 26bd56d..0000000 --- a/backend/dist/voice/stream/AudioStreamManager.d.ts +++ /dev/null @@ -1,120 +0,0 @@ -/** - * Audio Stream Manager - Phase 2 Voice Processing Pipeline - * Handles WebRTC audio streaming with real-time processing - * - * Success Criteria: - * - WebRTC connection established with <100ms latency - * - Audio stream supports 16kHz, mono, PCM16 format - * - Stream handles 1KB chunks efficiently - * - Memory usage <50MB for 10 concurrent streams - */ -import { EventEmitter } from 'events'; -import { VoiceActivityDetector } from './VoiceActivityDetector'; -import { AudioPreprocessor } from './AudioPreprocessor'; -export interface AudioStreamConfig { - userId: string; - socketId: string; - sampleRate: number; - channels: number; - format: 'pcm16' | 'pcm32' | 'float32'; - chunkSize: number; - maxBufferSize?: number; - silenceThreshold?: number; - maxSegmentLength?: number; -} -export interface AudioSegment { - id: string; - audioData: Buffer; - sampleRate: number; - channels: number; - format: string; - duration: number; - timestamp: string; - userId: string; - socketId: string; -} -export interface ProcessingResult { - status: 'waiting' | 'processing' | 'segment_ready' | 'error'; - hasVoice?: boolean; - segment?: AudioSegment; - processingTime?: number; - error?: string; -} -export interface StreamStatus { - isActive: boolean; - bufferLevel: number; - lastActivity: number; - totalProcessed: number; - memoryUsage: number; -} -export declare class AudioStreamManager extends EventEmitter { - private activeStreams; - private vad; - private preprocessor; - private performanceMetrics; - constructor(); - /** - * Initialize a new audio stream - * Target: <100ms initialization time - */ - initializeStream(config: AudioStreamConfig): Promise; - /** - * Process audio chunk for a specific stream - * Target: <10ms processing time per chunk - */ - processAudioChunk(socketId: string, audioChunk: Buffer): Promise; - /** - * Close audio stream and cleanup resources - */ - closeStream(socketId: string): Promise; - /** - * Get stream status information - */ - getStreamStatus(socketId: string): StreamStatus | null; - /** - * Get all active streams - */ - getActiveStreams(): string[]; - /** - * Get performance metrics - */ - getPerformanceMetrics(): Record; - /** - * Get memory usage for all active streams - */ - getMemoryUsage(): { - total: number; - perStream: Record; - }; - private validateStreamConfig; - private setupStreamEventHandlers; - private cleanupInactiveStreams; - private recordMetric; -} -/** - * Individual Audio Stream Handler - */ -export declare class AudioStream extends EventEmitter { - config: AudioStreamConfig; - private vad; - private preprocessor; - private buffer; - private lastVoiceTime; - private lastActivity; - private isInitialized; - private totalProcessed; - constructor(config: AudioStreamConfig, vad: VoiceActivityDetector, preprocessor: AudioPreprocessor); - initialize(): Promise; - processChunk(chunk: Buffer): Promise; - close(): Promise; - getStatus(): StreamStatus; - getLastActivity(): number; - getMemoryUsage(): number; - private isSegmentComplete; - private createSegment; -} -//# sourceMappingURL=AudioStreamManager.d.ts.map \ No newline at end of file diff --git a/backend/dist/voice/stream/AudioStreamManager.d.ts.map b/backend/dist/voice/stream/AudioStreamManager.d.ts.map deleted file mode 100644 index 3b9bcf1..0000000 --- a/backend/dist/voice/stream/AudioStreamManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"AudioStreamManager.d.ts","sourceRoot":"","sources":["../../../src/voice/stream/AudioStreamManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/stream/AudioStreamManager.js b/backend/dist/voice/stream/AudioStreamManager.js deleted file mode 100644 index e46a827..0000000 --- a/backend/dist/voice/stream/AudioStreamManager.js +++ /dev/null @@ -1,395 +0,0 @@ -"use strict"; -/** - * Audio Stream Manager - Phase 2 Voice Processing Pipeline - * Handles WebRTC audio streaming with real-time processing - * - * Success Criteria: - * - WebRTC connection established with <100ms latency - * - Audio stream supports 16kHz, mono, PCM16 format - * - Stream handles 1KB chunks efficiently - * - Memory usage <50MB for 10 concurrent streams - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.AudioStream = exports.AudioStreamManager = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -const VoiceActivityDetector_1 = require("./VoiceActivityDetector"); -const AudioPreprocessor_1 = require("./AudioPreprocessor"); -const CircularBuffer_1 = require("./CircularBuffer"); -class AudioStreamManager extends events_1.EventEmitter { - activeStreams = new Map(); - vad; - preprocessor; - performanceMetrics = new Map(); - constructor() { - super(); - this.vad = new VoiceActivityDetector_1.VoiceActivityDetector({ - threshold: 0.5, - sensitivity: 0.8, - smoothing: 0.3, - }); - this.preprocessor = new AudioPreprocessor_1.AudioPreprocessor(); - // Setup periodic cleanup - setInterval(() => this.cleanupInactiveStreams(), 30000); // Every 30 seconds - } - /** - * Initialize a new audio stream - * Target: <100ms initialization time - */ - async initializeStream(config) { - const startTime = perf_hooks_1.performance.now(); - try { - // Validate configuration - this.validateStreamConfig(config); - // Check memory constraints (max 10 concurrent streams) - if (this.activeStreams.size >= 10) { - throw new Error('Maximum concurrent streams (10) reached'); - } - // Create new audio stream - const stream = new AudioStream(config, this.vad, this.preprocessor); - await stream.initialize(); - // Store stream - this.activeStreams.set(config.socketId, stream); - // Setup stream event handlers - this.setupStreamEventHandlers(stream); - const initTime = perf_hooks_1.performance.now() - startTime; - // Record performance metrics - this.recordMetric('stream_init_time', initTime); - logger_1.logger.info('Audio stream initialized', { - socketId: config.socketId, - userId: config.userId, - initTime: `${initTime.toFixed(2)}ms`, - activeStreams: this.activeStreams.size, - }); - // Emit stream ready event - this.emit('stream_initialized', { - socketId: config.socketId, - userId: config.userId, - config, - initTime, - }); - return stream; - } - catch (error) { - const initTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.error('Audio stream initialization failed', { - socketId: config.socketId, - error: error instanceof Error ? error.message : String(error), - initTime: `${initTime.toFixed(2)}ms`, - }); - throw error; - } - } - /** - * Process audio chunk for a specific stream - * Target: <10ms processing time per chunk - */ - async processAudioChunk(socketId, audioChunk) { - const startTime = perf_hooks_1.performance.now(); - try { - const stream = this.activeStreams.get(socketId); - if (!stream) { - throw new Error(`Audio stream not found: ${socketId}`); - } - // Validate chunk size and format - if (audioChunk.length === 0) { - return { status: 'waiting' }; - } - // Process audio chunk through the stream - const result = await stream.processChunk(audioChunk); - const processingTime = perf_hooks_1.performance.now() - startTime; - // Record performance metrics - this.recordMetric('chunk_processing_time', processingTime); - // Log slow processing - if (processingTime > 10) { - logger_1.logger.warn('Slow audio chunk processing', { - socketId, - processingTime: `${processingTime.toFixed(2)}ms`, - chunkSize: audioChunk.length, - }); - } - return { - ...result, - processingTime, - }; - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.error('Audio chunk processing failed', { - socketId, - error: error instanceof Error ? error.message : String(error), - processingTime: `${processingTime.toFixed(2)}ms`, - chunkSize: audioChunk.length, - }); - return { - status: 'error', - error: error instanceof Error ? error.message : String(error), - processingTime, - }; - } - } - /** - * Close audio stream and cleanup resources - */ - async closeStream(socketId) { - const stream = this.activeStreams.get(socketId); - if (!stream) { - return; - } - try { - await stream.close(); - this.activeStreams.delete(socketId); - logger_1.logger.info('Audio stream closed', { - socketId, - remainingStreams: this.activeStreams.size, - }); - this.emit('stream_closed', { socketId }); - } - catch (error) { - logger_1.logger.error('Error closing audio stream', { - socketId, - error: error instanceof Error ? error.message : String(error), - }); - } - } - /** - * Get stream status information - */ - getStreamStatus(socketId) { - const stream = this.activeStreams.get(socketId); - if (!stream) { - return null; - } - return stream.getStatus(); - } - /** - * Get all active streams - */ - getActiveStreams() { - return Array.from(this.activeStreams.keys()); - } - /** - * Get performance metrics - */ - getPerformanceMetrics() { - const metrics = {}; - for (const [key, values] of this.performanceMetrics.entries()) { - if (values.length === 0) - continue; - const sorted = [...values].sort((a, b) => a - b); - const average = values.reduce((sum, val) => sum + val, 0) / values.length; - const p95Index = Math.floor(sorted.length * 0.95); - const p95 = sorted[p95Index] || sorted[sorted.length - 1]; - metrics[key] = { - average: Math.round(average * 100) / 100, - p95: Math.round((p95 ?? 0) * 100) / 100, - count: values.length, - }; - } - return metrics; - } - /** - * Get memory usage for all active streams - */ - getMemoryUsage() { - let total = 0; - const perStream = {}; - for (const [socketId, stream] of this.activeStreams.entries()) { - const streamMemory = stream.getMemoryUsage(); - total += streamMemory; - perStream[socketId] = streamMemory; - } - return { total, perStream }; - } - validateStreamConfig(config) { - if (!config.userId || !config.socketId) { - throw new Error('userId and socketId are required'); - } - if (config.sampleRate !== 16000) { - throw new Error('Only 16kHz sample rate is supported'); - } - if (config.channels !== 1) { - throw new Error('Only mono audio is supported'); - } - if (config.format !== 'pcm16') { - throw new Error('Only PCM16 format is supported'); - } - if (config.chunkSize < 512 || config.chunkSize > 8192) { - throw new Error('Chunk size must be between 512 and 8192 bytes'); - } - } - setupStreamEventHandlers(stream) { - stream.on('chunk_processed', (data) => { - this.emit('stream_progress', { - socketId: stream.config.socketId, - userId: stream.config.userId, - ...data, - }); - }); - stream.on('segment_complete', (segment) => { - this.emit('audio_segment_ready', segment); - }); - stream.on('error', (error) => { - logger_1.logger.error('Audio stream error', { - socketId: stream.config.socketId, - error: error.message, - }); - this.emit('stream_error', { - socketId: stream.config.socketId, - error, - }); - }); - } - cleanupInactiveStreams() { - const now = Date.now(); - const inactiveThreshold = 5 * 60 * 1000; // 5 minutes - for (const [socketId, stream] of this.activeStreams.entries()) { - if (now - stream.getLastActivity() > inactiveThreshold) { - logger_1.logger.info('Cleaning up inactive stream', { socketId }); - this.closeStream(socketId).catch((error) => { - logger_1.logger.error('Error during stream cleanup', { socketId, error: error.message }); - }); - } - } - } - recordMetric(key, value) { - if (!this.performanceMetrics.has(key)) { - this.performanceMetrics.set(key, []); - } - const metrics = this.performanceMetrics.get(key); - metrics.push(value); - // Keep only last 1000 measurements - if (metrics.length > 1000) { - metrics.shift(); - } - } -} -exports.AudioStreamManager = AudioStreamManager; -/** - * Individual Audio Stream Handler - */ -class AudioStream extends events_1.EventEmitter { - config; - vad; - preprocessor; - buffer; - lastVoiceTime = 0; - lastActivity = 0; - isInitialized = false; - totalProcessed = 0; - constructor(config, vad, preprocessor) { - super(); - this.config = config; - this.vad = vad; - this.preprocessor = preprocessor; - const bufferSize = config.maxBufferSize || config.sampleRate * 30; // 30 seconds default - this.buffer = new CircularBuffer_1.CircularBuffer(bufferSize); - this.lastActivity = Date.now(); - } - async initialize() { - await this.buffer.initialize(); - this.lastVoiceTime = Date.now(); - this.isInitialized = true; - logger_1.logger.debug('Audio stream initialized', { - socketId: this.config.socketId, - bufferSize: this.buffer.getCapacity(), - }); - } - async processChunk(chunk) { - if (!this.isInitialized) { - throw new Error('Stream not initialized'); - } - this.lastActivity = Date.now(); - this.totalProcessed += chunk.length; - try { - // Preprocess audio chunk - const preprocessed = await this.preprocessor.process(chunk); - // Voice activity detection - const hasVoice = await this.vad.detectActivity(preprocessed); - if (hasVoice) { - this.lastVoiceTime = Date.now(); - this.buffer.write(preprocessed); - this.emit('chunk_processed', { - hasVoice: true, - chunkSize: chunk.length, - bufferLevel: this.buffer.getLevel(), - timestamp: new Date().toISOString(), - }); - } - else { - // Still add silence to buffer for context - this.buffer.write(preprocessed); - this.emit('chunk_processed', { - hasVoice: false, - chunkSize: chunk.length, - bufferLevel: this.buffer.getLevel(), - timestamp: new Date().toISOString(), - }); - } - // Check if segment is complete - if (this.isSegmentComplete()) { - const segment = await this.createSegment(); - this.emit('segment_complete', segment); - return { status: 'segment_ready', segment, hasVoice }; - } - return { status: hasVoice ? 'processing' : 'waiting', hasVoice }; - } - catch (error) { - this.emit('error', error); - return { - status: 'error', - error: error instanceof Error ? error.message : String(error), - }; - } - } - async close() { - this.isInitialized = false; - this.buffer.clear(); - this.removeAllListeners(); - } - getStatus() { - return { - isActive: this.isInitialized, - bufferLevel: this.buffer.getLevel(), - lastActivity: this.lastActivity, - totalProcessed: this.totalProcessed, - memoryUsage: this.getMemoryUsage(), - }; - } - getLastActivity() { - return this.lastActivity; - } - getMemoryUsage() { - return this.buffer.getMemoryUsage(); - } - isSegmentComplete() { - const silenceThreshold = this.config.silenceThreshold || 1500; // 1.5 seconds - const maxSegmentLength = this.config.maxSegmentLength || 30000; // 30 seconds - const silenceDuration = Date.now() - this.lastVoiceTime; - const bufferDuration = this.buffer.getDurationMs(); - return ((silenceDuration > silenceThreshold && bufferDuration > 1000) || - bufferDuration > maxSegmentLength); - } - async createSegment() { - const audioData = this.buffer.readAll(); - const duration = this.buffer.getDurationMs(); - const segment = { - id: crypto.randomUUID(), - audioData, - sampleRate: this.config.sampleRate, - channels: this.config.channels, - format: this.config.format, - duration, - timestamp: new Date().toISOString(), - userId: this.config.userId, - socketId: this.config.socketId, - }; - // Clear buffer for next segment - this.buffer.clear(); - this.lastVoiceTime = Date.now(); - return segment; - } -} -exports.AudioStream = AudioStream; -//# sourceMappingURL=AudioStreamManager.js.map \ No newline at end of file diff --git a/backend/dist/voice/stream/AudioStreamManager.js.map b/backend/dist/voice/stream/AudioStreamManager.js.map deleted file mode 100644 index 2a81524..0000000 --- a/backend/dist/voice/stream/AudioStreamManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"AudioStreamManager.js","sourceRoot":"","sources":["../../../src/voice/stream/AudioStreamManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/stream/CircularBuffer.d.ts b/backend/dist/voice/stream/CircularBuffer.d.ts deleted file mode 100644 index 0f84f03..0000000 --- a/backend/dist/voice/stream/CircularBuffer.d.ts +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Circular Buffer for Audio Data - * Memory-efficient buffer with automatic overflow protection - * - * Success Criteria: - * - Memory-efficient circular buffer implementation - * - Automatic segment detection based on silence - * - Handles variable-length audio segments - * - Buffer overflow protection - */ -export declare class CircularBuffer { - private buffer; - private writeIndex; - private readIndex; - private size; - private capacity; - private sampleRate; - private bytesPerSample; - constructor(capacityInSamples: number); - initialize(): Promise; - /** - * Write audio data to buffer - */ - write(data: Buffer): number; - /** - * Read specified number of bytes from buffer - */ - read(bytes: number): Buffer; - /** - * Read all available data from buffer - */ - readAll(): Buffer; - /** - * Peek at data without consuming it - */ - peek(bytes: number): Buffer; - /** - * Clear all data from buffer - */ - clear(): void; - /** - * Get current buffer level (0-1) - */ - getLevel(): number; - /** - * Get available space in bytes - */ - getAvailableSpace(): number; - /** - * Get current size in bytes - */ - getSize(): number; - /** - * Get buffer capacity in bytes - */ - getCapacity(): number; - /** - * Get buffer capacity in milliseconds - */ - getCapacityMs(): number; - /** - * Get current buffer duration in milliseconds - */ - getDurationMs(): number; - /** - * Get memory usage in bytes - */ - getMemoryUsage(): number; - /** - * Check if buffer is empty - */ - isEmpty(): boolean; - /** - * Check if buffer is full - */ - isFull(): boolean; - /** - * Get buffer statistics - */ - getStats(): { - size: number; - capacity: number; - level: number; - durationMs: number; - capacityMs: number; - memoryUsage: number; - }; - /** - * Resize buffer (creates new buffer and copies data) - */ - resize(newCapacityInSamples: number): void; - /** - * Get RMS energy level of buffer content (for VAD purposes) - */ - getRMSEnergy(): number; - /** - * Find silence periods in buffer (returns indices) - */ - findSilencePeriods(threshold?: number, minDurationMs?: number): Array<{ - start: number; - end: number; - }>; -} -//# sourceMappingURL=CircularBuffer.d.ts.map \ No newline at end of file diff --git a/backend/dist/voice/stream/CircularBuffer.d.ts.map b/backend/dist/voice/stream/CircularBuffer.d.ts.map deleted file mode 100644 index 7c354c3..0000000 --- a/backend/dist/voice/stream/CircularBuffer.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"CircularBuffer.d.ts","sourceRoot":"","sources":["../../../src/voice/stream/CircularBuffer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,qBAAa,cAAc;IACzB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAa;IAC9B,OAAO,CAAC,IAAI,CAAa;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,UAAU,CAAiB;IACnC,OAAO,CAAC,cAAc,CAAa;gBAEvB,iBAAiB,EAAE,MAAM;IAM/B,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAajC;;OAEG;IACH,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM;IAoC3B;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAa3B;;OAEG;IACH,OAAO,IAAI,MAAM;IAIjB;;OAEG;IACH,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM;IAa3B;;OAEG;IACH,KAAK,IAAI,IAAI;IAOb;;OAEG;IACH,QAAQ,IAAI,MAAM;IAIlB;;OAEG;IACH,iBAAiB,IAAI,MAAM;IAI3B;;OAEG;IACH,OAAO,IAAI,MAAM;IAIjB;;OAEG;IACH,WAAW,IAAI,MAAM;IAIrB;;OAEG;IACH,aAAa,IAAI,MAAM;IAKvB;;OAEG;IACH,aAAa,IAAI,MAAM;IAKvB;;OAEG;IACH,cAAc,IAAI,MAAM;IAIxB;;OAEG;IACH,OAAO,IAAI,OAAO;IAIlB;;OAEG;IACH,MAAM,IAAI,OAAO;IAIjB;;OAEG;IACH,QAAQ,IAAI;QACV,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,CAAC;KACrB;IAWD;;OAEG;IACH,MAAM,CAAC,oBAAoB,EAAE,MAAM,GAAG,IAAI;IAwB1C;;OAEG;IACH,YAAY,IAAI,MAAM;IAmBtB;;OAEG;IACH,kBAAkB,CAChB,SAAS,GAAE,MAAa,EACxB,aAAa,GAAE,MAAY,GAC1B,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAiDzC"} \ No newline at end of file diff --git a/backend/dist/voice/stream/CircularBuffer.js b/backend/dist/voice/stream/CircularBuffer.js deleted file mode 100644 index 3811641..0000000 --- a/backend/dist/voice/stream/CircularBuffer.js +++ /dev/null @@ -1,269 +0,0 @@ -"use strict"; -/** - * Circular Buffer for Audio Data - * Memory-efficient buffer with automatic overflow protection - * - * Success Criteria: - * - Memory-efficient circular buffer implementation - * - Automatic segment detection based on silence - * - Handles variable-length audio segments - * - Buffer overflow protection - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.CircularBuffer = void 0; -const logger_1 = require("../../utils/logger"); -class CircularBuffer { - buffer; - writeIndex = 0; - readIndex = 0; - size = 0; - capacity; - sampleRate = 16000; // Default 16kHz - bytesPerSample = 2; // 16-bit PCM - constructor(capacityInSamples) { - this.capacity = capacityInSamples * this.bytesPerSample; - this.buffer = Buffer.allocUnsafe(this.capacity); - this.buffer.fill(0); - } - async initialize() { - // Initialize buffer state - this.writeIndex = 0; - this.readIndex = 0; - this.size = 0; - this.buffer.fill(0); - logger_1.logger.debug('Circular buffer initialized', { - capacity: this.capacity, - capacityMs: this.getCapacityMs(), - }); - } - /** - * Write audio data to buffer - */ - write(data) { - if (data.length === 0) { - return 0; - } - const bytesToWrite = Math.min(data.length, this.capacity - this.size); - if (bytesToWrite < data.length) { - // Buffer overflow - log warning but continue with partial write - logger_1.logger.warn('Circular buffer overflow', { - requestedBytes: data.length, - availableBytes: bytesToWrite, - bufferSize: this.size, - capacity: this.capacity, - }); - } - let written = 0; - for (let i = 0; i < bytesToWrite; i++) { - this.buffer[this.writeIndex] = data[i] ?? 0; - this.writeIndex = (this.writeIndex + 1) % this.capacity; - written++; - // Update size only if buffer not full - if (this.size < this.capacity) { - this.size++; - } - else { - // If buffer is full, advance read index (overwrite oldest data) - this.readIndex = (this.readIndex + 1) % this.capacity; - } - } - return written; - } - /** - * Read specified number of bytes from buffer - */ - read(bytes) { - const bytesToRead = Math.min(bytes, this.size); - const result = Buffer.allocUnsafe(bytesToRead); - for (let i = 0; i < bytesToRead; i++) { - result[i] = this.buffer[this.readIndex] ?? 0; - this.readIndex = (this.readIndex + 1) % this.capacity; - this.size--; - } - return result; - } - /** - * Read all available data from buffer - */ - readAll() { - return this.read(this.size); - } - /** - * Peek at data without consuming it - */ - peek(bytes) { - const bytesToPeek = Math.min(bytes, this.size); - const result = Buffer.allocUnsafe(bytesToPeek); - let peekIndex = this.readIndex; - for (let i = 0; i < bytesToPeek; i++) { - result[i] = this.buffer[peekIndex] ?? 0; - peekIndex = (peekIndex + 1) % this.capacity; - } - return result; - } - /** - * Clear all data from buffer - */ - clear() { - this.writeIndex = 0; - this.readIndex = 0; - this.size = 0; - this.buffer.fill(0); - } - /** - * Get current buffer level (0-1) - */ - getLevel() { - return this.size / this.capacity; - } - /** - * Get available space in bytes - */ - getAvailableSpace() { - return this.capacity - this.size; - } - /** - * Get current size in bytes - */ - getSize() { - return this.size; - } - /** - * Get buffer capacity in bytes - */ - getCapacity() { - return this.capacity; - } - /** - * Get buffer capacity in milliseconds - */ - getCapacityMs() { - const samples = this.capacity / this.bytesPerSample; - return (samples / this.sampleRate) * 1000; - } - /** - * Get current buffer duration in milliseconds - */ - getDurationMs() { - const samples = this.size / this.bytesPerSample; - return (samples / this.sampleRate) * 1000; - } - /** - * Get memory usage in bytes - */ - getMemoryUsage() { - return this.capacity + 32 * 4; // Buffer + overhead for indices - } - /** - * Check if buffer is empty - */ - isEmpty() { - return this.size === 0; - } - /** - * Check if buffer is full - */ - isFull() { - return this.size === this.capacity; - } - /** - * Get buffer statistics - */ - getStats() { - return { - size: this.size, - capacity: this.capacity, - level: this.getLevel(), - durationMs: this.getDurationMs(), - capacityMs: this.getCapacityMs(), - memoryUsage: this.getMemoryUsage(), - }; - } - /** - * Resize buffer (creates new buffer and copies data) - */ - resize(newCapacityInSamples) { - const newCapacity = newCapacityInSamples * this.bytesPerSample; - const newBuffer = Buffer.allocUnsafe(newCapacity); - newBuffer.fill(0); - // Copy existing data to new buffer - const existingData = this.readAll(); - this.buffer = newBuffer; - this.capacity = newCapacity; - this.writeIndex = 0; - this.readIndex = 0; - this.size = 0; - // Write existing data back - this.write(existingData); - logger_1.logger.debug('Circular buffer resized', { - oldCapacity: this.capacity, - newCapacity, - preservedBytes: existingData.length, - }); - } - /** - * Get RMS energy level of buffer content (for VAD purposes) - */ - getRMSEnergy() { - if (this.size === 0) { - return 0; - } - let sum = 0; - let index = this.readIndex; - const samples = this.size / this.bytesPerSample; - for (let i = 0; i < samples; i++) { - // Read 16-bit sample (little-endian) - const sample = this.buffer.readInt16LE(index); - sum += sample * sample; - index = (index + this.bytesPerSample) % this.capacity; - } - return Math.sqrt(sum / samples) / 32768.0; // Normalize to 0-1 - } - /** - * Find silence periods in buffer (returns indices) - */ - findSilencePeriods(threshold = 0.01, minDurationMs = 500) { - const silencePeriods = []; - if (this.size === 0) { - return silencePeriods; - } - const samples = this.size / this.bytesPerSample; - const minSamples = (minDurationMs / 1000) * this.sampleRate; - let silenceStart = -1; - let index = this.readIndex; - for (let i = 0; i < samples; i++) { - const sample = Math.abs(this.buffer.readInt16LE(index)) / 32768.0; - if (sample < threshold) { - if (silenceStart === -1) { - silenceStart = i; - } - } - else { - if (silenceStart !== -1) { - const silenceDuration = i - silenceStart; - if (silenceDuration >= minSamples) { - silencePeriods.push({ - start: silenceStart, - end: i, - }); - } - silenceStart = -1; - } - } - index = (index + this.bytesPerSample) % this.capacity; - } - // Handle silence at end of buffer - if (silenceStart !== -1) { - const silenceDuration = samples - silenceStart; - if (silenceDuration >= minSamples) { - silencePeriods.push({ - start: silenceStart, - end: samples, - }); - } - } - return silencePeriods; - } -} -exports.CircularBuffer = CircularBuffer; -//# sourceMappingURL=CircularBuffer.js.map \ No newline at end of file diff --git a/backend/dist/voice/stream/CircularBuffer.js.map b/backend/dist/voice/stream/CircularBuffer.js.map deleted file mode 100644 index 3c33a83..0000000 --- a/backend/dist/voice/stream/CircularBuffer.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"CircularBuffer.js","sourceRoot":"","sources":["../../../src/voice/stream/CircularBuffer.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/stream/VoiceActivityDetector.d.ts b/backend/dist/voice/stream/VoiceActivityDetector.d.ts deleted file mode 100644 index b3082c1..0000000 --- a/backend/dist/voice/stream/VoiceActivityDetector.d.ts +++ /dev/null @@ -1,85 +0,0 @@ -/** - * Voice Activity Detector (VAD) - Phase 2 Voice Processing - * Real-time speech detection with background noise adaptation - * - * Success Criteria: - * - 95%+ accuracy in detecting speech vs silence - * - <10ms processing time per chunk - * - Adjustable sensitivity thresholds - * - Handles background noise effectively - */ -export interface VADConfig { - threshold: number; - sensitivity: number; - smoothing: number; - minSpeechDuration: number; - hangoverTime: number; -} -export interface VADResult { - hasVoice: boolean; - confidence: number; - energy: number; - backgroundNoise: number; - processingTime: number; -} -export declare class VoiceActivityDetector { - private config; - private energyHistory; - private backgroundNoise; - private speechStart; - private lastSpeechTime; - private isInSpeech; - private adaptiveThreshold; - private performanceMetrics; - private spectralCentroid; - private zeroCrossingRate; - private energyVariance; - constructor(config?: Partial); - /** - * Detect voice activity in audio chunk - * Target: <10ms processing time - */ - detectActivity(audioChunk: Buffer): Promise; - /** - * Get detailed VAD result with confidence and metrics - */ - detectActivityDetailed(audioChunk: Buffer): Promise; - /** - * Calibrate detector based on background noise sample - */ - calibrateThreshold(backgroundSample: Buffer): void; - /** - * Get confidence score for current audio chunk - */ - getConfidenceScore(audioChunk: Buffer): number; - /** - * Update sensitivity settings - */ - updateSensitivity(sensitivity: number): void; - /** - * Get current performance statistics - */ - getPerformanceStats(): { - average: number; - p95: number; - p99: number; - count: number; - }; - /** - * Reset VAD state - */ - reset(): void; - private analyzeAudio; - private makeDecision; - private bufferToSamples; - private calculateRMSEnergy; - private calculateSpectralCentroid; - private calculateZeroCrossingRate; - private updateEnergyHistory; - private calculateConfidence; - private updateBackgroundNoise; - private calculateDynamicThreshold; - private makeSpectralDecision; - private recordPerformance; -} -//# sourceMappingURL=VoiceActivityDetector.d.ts.map \ No newline at end of file diff --git a/backend/dist/voice/stream/VoiceActivityDetector.d.ts.map b/backend/dist/voice/stream/VoiceActivityDetector.d.ts.map deleted file mode 100644 index 677c152..0000000 --- a/backend/dist/voice/stream/VoiceActivityDetector.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"VoiceActivityDetector.d.ts","sourceRoot":"","sources":["../../../src/voice/stream/VoiceActivityDetector.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,MAAM,WAAW,SAAS;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,WAAW,CAAa;IAChC,OAAO,CAAC,cAAc,CAAa;IACnC,OAAO,CAAC,UAAU,CAAkB;IACpC,OAAO,CAAC,iBAAiB,CAAa;IACtC,OAAO,CAAC,kBAAkB,CAAgB;IAG1C,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,gBAAgB,CAAa;IACrC,OAAO,CAAC,cAAc,CAAa;gBAEvB,MAAM,GAAE,OAAO,CAAC,SAAS,CAAM;IAe3C;;;OAGG;IACG,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgC1D;;OAEG;IACG,sBAAsB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;IAkBpE;;OAEG;IACH,kBAAkB,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI;IAqBlD;;OAEG;IACH,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAK9C;;OAEG;IACH,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAS5C;;OAEG;IACH,mBAAmB,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE;IAmBnF;;OAEG;IACH,KAAK,IAAI,IAAI;IAcb,OAAO,CAAC,YAAY;IA8BpB,OAAO,CAAC,YAAY;IA4CpB,OAAO,CAAC,eAAe;IASvB,OAAO,CAAC,kBAAkB;IAU1B,OAAO,CAAC,yBAAyB;IAcjC,OAAO,CAAC,yBAAyB;IAYjC,OAAO,CAAC,mBAAmB;IAkB3B,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,qBAAqB;IAU7B,OAAO,CAAC,yBAAyB;IAiBjC,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,iBAAiB;CAQ1B"} \ No newline at end of file diff --git a/backend/dist/voice/stream/VoiceActivityDetector.js b/backend/dist/voice/stream/VoiceActivityDetector.js deleted file mode 100644 index 6b0ebb0..0000000 --- a/backend/dist/voice/stream/VoiceActivityDetector.js +++ /dev/null @@ -1,317 +0,0 @@ -"use strict"; -/** - * Voice Activity Detector (VAD) - Phase 2 Voice Processing - * Real-time speech detection with background noise adaptation - * - * Success Criteria: - * - 95%+ accuracy in detecting speech vs silence - * - <10ms processing time per chunk - * - Adjustable sensitivity thresholds - * - Handles background noise effectively - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.VoiceActivityDetector = void 0; -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -class VoiceActivityDetector { - config; - energyHistory = []; - backgroundNoise = 0; - speechStart = 0; - lastSpeechTime = 0; - isInSpeech = false; - adaptiveThreshold = 0; - performanceMetrics = []; - // Advanced features - spectralCentroid = 0; - zeroCrossingRate = 0; - energyVariance = 0; - constructor(config = {}) { - this.config = { - threshold: 0.02, // Base threshold - sensitivity: 0.8, // High sensitivity - smoothing: 0.95, // Slow adaptation to background noise - minSpeechDuration: 100, // 100ms minimum speech - hangoverTime: 200, // 200ms hangover - ...config, - }; - this.adaptiveThreshold = this.config.threshold; - logger_1.logger.debug('VAD initialized', { config: this.config }); - } - /** - * Detect voice activity in audio chunk - * Target: <10ms processing time - */ - async detectActivity(audioChunk) { - const startTime = perf_hooks_1.performance.now(); - try { - const result = this.analyzeAudio(audioChunk); - const processingTime = perf_hooks_1.performance.now() - startTime; - // Record performance - this.recordPerformance(processingTime); - // Log if processing is slow - if (processingTime > 10) { - logger_1.logger.warn('Slow VAD processing detected', { - processingTime: `${processingTime.toFixed(2)}ms`, - chunkSize: audioChunk.length, - }); - } - return this.makeDecision(result, processingTime); - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.error('VAD processing error', { - error: error instanceof Error ? error.message : String(error), - processingTime: `${processingTime.toFixed(2)}ms`, - chunkSize: audioChunk.length, - }); - return false; // Default to no voice on error - } - } - /** - * Get detailed VAD result with confidence and metrics - */ - async detectActivityDetailed(audioChunk) { - const startTime = perf_hooks_1.performance.now(); - const result = this.analyzeAudio(audioChunk); - const hasVoice = this.makeDecision(result, 0); - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformance(processingTime); - return { - hasVoice, - confidence: result.confidence, - energy: result.energy, - backgroundNoise: this.backgroundNoise, - processingTime, - }; - } - /** - * Calibrate detector based on background noise sample - */ - calibrateThreshold(backgroundSample) { - logger_1.logger.info('Starting VAD calibration'); - const analysis = this.analyzeAudio(backgroundSample); - // Set background noise level - this.backgroundNoise = analysis.energy; - // Calculate adaptive threshold - this.adaptiveThreshold = Math.max(this.config.threshold, this.backgroundNoise * 2.0 // Threshold should be 2x background noise - ); - logger_1.logger.info('VAD calibration complete', { - backgroundNoise: this.backgroundNoise.toFixed(4), - adaptiveThreshold: this.adaptiveThreshold.toFixed(4), - sampleSize: backgroundSample.length, - }); - } - /** - * Get confidence score for current audio chunk - */ - getConfidenceScore(audioChunk) { - const analysis = this.analyzeAudio(audioChunk); - return analysis.confidence; - } - /** - * Update sensitivity settings - */ - updateSensitivity(sensitivity) { - if (sensitivity < 0 || sensitivity > 1) { - throw new Error('Sensitivity must be between 0 and 1'); - } - this.config.sensitivity = sensitivity; - logger_1.logger.debug('VAD sensitivity updated', { sensitivity }); - } - /** - * Get current performance statistics - */ - getPerformanceStats() { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] ?? 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] ?? 0; - return { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: this.performanceMetrics.length, - }; - } - /** - * Reset VAD state - */ - reset() { - this.energyHistory = []; - this.backgroundNoise = 0; - this.speechStart = 0; - this.lastSpeechTime = 0; - this.isInSpeech = false; - this.adaptiveThreshold = this.config.threshold; - this.spectralCentroid = 0; - this.zeroCrossingRate = 0; - this.energyVariance = 0; - logger_1.logger.debug('VAD state reset'); - } - analyzeAudio(audioChunk) { - // Convert buffer to samples - const samples = this.bufferToSamples(audioChunk); - // Calculate RMS energy - const energy = this.calculateRMSEnergy(samples); - // Calculate spectral features - const spectralCentroid = this.calculateSpectralCentroid(samples); - const zeroCrossingRate = this.calculateZeroCrossingRate(samples); - // Update energy history for variance calculation - this.updateEnergyHistory(energy); - // Calculate confidence based on multiple features - const confidence = this.calculateConfidence(energy, spectralCentroid, zeroCrossingRate); - return { - energy, - confidence, - spectralCentroid, - zeroCrossingRate, - }; - } - makeDecision(analysis, processingTime) { - const now = Date.now(); - // Update background noise (slow adaptation) - this.updateBackgroundNoise(analysis.energy); - // Calculate dynamic threshold - const dynamicThreshold = this.calculateDynamicThreshold(); - // Primary decision based on energy - const energyDecision = analysis.energy > dynamicThreshold; - // Enhanced decision with spectral features - const spectralDecision = this.makeSpectralDecision(analysis); - // Combine decisions - let hasVoice = energyDecision && spectralDecision; - // Apply temporal logic - if (hasVoice) { - if (!this.isInSpeech) { - // Start of potential speech - this.speechStart = now; - } - this.lastSpeechTime = now; - this.isInSpeech = true; - } - else { - // Check hangover time - if (this.isInSpeech && now - this.lastSpeechTime < this.config.hangoverTime) { - hasVoice = true; // Continue detection during hangover period - } - else { - // Check minimum speech duration - if (this.isInSpeech && now - this.speechStart < this.config.minSpeechDuration) { - hasVoice = false; // Too short to be speech - } - this.isInSpeech = false; - } - } - return hasVoice; - } - bufferToSamples(buffer) { - const samples = new Float32Array(buffer.length / 2); - for (let i = 0; i < samples.length; i++) { - // Convert 16-bit PCM to float32 (-1 to 1) - samples[i] = buffer.readInt16LE(i * 2) / 32768.0; - } - return samples; - } - calculateRMSEnergy(samples) { - let sum = 0; - for (let i = 0; i < samples.length; i++) { - if (typeof samples[i] !== 'undefined') { - sum += samples[i] * samples[i]; - } - } - return Math.sqrt(sum / samples.length); - } - calculateSpectralCentroid(samples) { - // Simple approximation of spectral centroid - let weightedSum = 0; - let magnitudeSum = 0; - for (let i = 1; i < samples.length; i++) { - const magnitude = Math.abs(samples[i]); - weightedSum += i * magnitude; - magnitudeSum += magnitude; - } - return magnitudeSum > 0 ? weightedSum / magnitudeSum : 0; - } - calculateZeroCrossingRate(samples) { - let crossings = 0; - for (let i = 1; i < samples.length; i++) { - if (samples[i] >= 0 !== samples[i - 1] >= 0) { - crossings++; - } - } - return crossings / (samples.length - 1); - } - updateEnergyHistory(energy) { - this.energyHistory.push(energy); - // Keep only recent history (last 100 frames ~= 2 seconds at 50fps) - if (this.energyHistory.length > 100) { - this.energyHistory.shift(); - } - // Calculate energy variance - if (this.energyHistory.length > 10) { - const mean = this.energyHistory.reduce((sum, e) => sum + e, 0) / this.energyHistory.length; - const variance = this.energyHistory.reduce((sum, e) => sum + Math.pow(e - mean, 2), 0) / - this.energyHistory.length; - this.energyVariance = variance; - } - } - calculateConfidence(energy, spectralCentroid, zeroCrossingRate) { - // Base confidence from energy ratio - const energyRatio = this.backgroundNoise > 0 ? energy / this.backgroundNoise : energy / this.adaptiveThreshold; - let confidence = Math.min(1.0, energyRatio / 3.0); // Normalize to 0-1 - // Adjust based on spectral features - if (spectralCentroid > 0.1 && zeroCrossingRate > 0.05) { - confidence *= 1.2; // Boost confidence for speech-like spectral characteristics - } - if (zeroCrossingRate > 0.3) { - confidence *= 0.8; // Reduce confidence for very high ZCR (likely noise) - } - return Math.max(0, Math.min(1, confidence)); - } - updateBackgroundNoise(energy) { - if (this.backgroundNoise === 0) { - this.backgroundNoise = energy; - } - else { - // Slow adaptation using exponential moving average - this.backgroundNoise = - this.config.smoothing * this.backgroundNoise + (1 - this.config.smoothing) * energy; - } - } - calculateDynamicThreshold() { - // Base threshold adjusted by sensitivity - let threshold = this.adaptiveThreshold * (2.0 - this.config.sensitivity); - // Adjust based on background noise - if (this.backgroundNoise > 0) { - threshold = Math.max(threshold, this.backgroundNoise * 1.5); - } - // Adjust based on energy variance (more variable = lower threshold) - if (this.energyVariance > 0.001) { - threshold *= 0.9; - } - return threshold; - } - makeSpectralDecision(analysis) { - // Speech typically has: - // - Moderate spectral centroid (not too high, not too low) - // - Moderate zero crossing rate (not too high like noise, not too low like tones) - const { spectralCentroid, zeroCrossingRate } = analysis; - // Spectral centroid check (speech is usually in middle frequencies) - const spectralOk = spectralCentroid > 0.05 && spectralCentroid < 0.5; - // Zero crossing rate check (speech has moderate ZCR) - const zcrOk = zeroCrossingRate > 0.02 && zeroCrossingRate < 0.25; - return spectralOk && zcrOk; - } - recordPerformance(time) { - this.performanceMetrics.push(time); - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} -exports.VoiceActivityDetector = VoiceActivityDetector; -//# sourceMappingURL=VoiceActivityDetector.js.map \ No newline at end of file diff --git a/backend/dist/voice/stream/VoiceActivityDetector.js.map b/backend/dist/voice/stream/VoiceActivityDetector.js.map deleted file mode 100644 index 9b90dc8..0000000 --- a/backend/dist/voice/stream/VoiceActivityDetector.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"VoiceActivityDetector.js","sourceRoot":"","sources":["../../../src/voice/stream/VoiceActivityDetector.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/transcription/TranscriptionCache.d.ts b/backend/dist/voice/transcription/TranscriptionCache.d.ts deleted file mode 100644 index 975fa56..0000000 --- a/backend/dist/voice/transcription/TranscriptionCache.d.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Transcription Cache - Phase 2 Voice Processing - * Redis-based caching for Whisper API responses - * - * Success Criteria: - * - Audio fingerprinting for cache keys - * - 1-hour cache TTL for transcripts - * - 30% cache hit rate after 1 week of usage - * - <10ms cache lookup time - */ -import { TranscriptResult, TranscriptionOptions } from '../types'; -export interface CacheStats { - hits: number; - misses: number; - hitRate: number; - averageLookupTime: number; - totalKeys: number; - memoryUsage: number; -} -export declare class TranscriptionCache { - private redis; - private keyPrefix; - private defaultTTL; - private hits; - private misses; - private lookupTimes; - constructor(); - /** - * Generate cache key from audio buffer and options - * Uses audio fingerprinting for consistent keys - */ - generateCacheKey(audioBuffer: Buffer, options: TranscriptionOptions): string; - /** - * Get cached transcription result - * Target: <10ms lookup time - */ - get(key: string): Promise; - /** - * Cache transcription result - */ - set(key: string, result: TranscriptResult, ttl?: number): Promise; - /** - * Delete cached result - */ - delete(key: string): Promise; - /** - * Clear all cached results - */ - clear(): Promise; - /** - * Get cache statistics - */ - getStats(): CacheStats; - /** - * Get total number of cached keys - */ - getTotalKeys(): Promise; - /** - * Get memory usage information - */ - getMemoryUsage(): Promise; - /** - * Preload cache with common transcriptions - */ - preload(commonPhrases: Array<{ - audio: Buffer; - transcript: string; - }>): Promise; - /** - * Clean up expired entries (manual cleanup) - */ - cleanup(): Promise; - /** - * Close Redis connection - */ - close(): Promise; - private generateAudioFingerprint; - private recordLookupTime; -} -//# sourceMappingURL=TranscriptionCache.d.ts.map \ No newline at end of file diff --git a/backend/dist/voice/transcription/TranscriptionCache.d.ts.map b/backend/dist/voice/transcription/TranscriptionCache.d.ts.map deleted file mode 100644 index 7bb429a..0000000 --- a/backend/dist/voice/transcription/TranscriptionCache.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TranscriptionCache.d.ts","sourceRoot":"","sources":["../../../src/voice/transcription/TranscriptionCache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAElE,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,UAAU,CAAQ;IAG1B,OAAO,CAAC,IAAI,CAAK;IACjB,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,WAAW,CAAgB;;IAwBnC;;;OAGG;IACH,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,MAAM;IAgB5E;;;OAGG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,GAAG,IAAI,CAAC;IAiDxD;;OAEG;IACG,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyB7E;;OAEG;IACG,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYxC;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAc5B;;OAEG;IACH,QAAQ,IAAI,UAAU;IAmBtB;;OAEG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;IAYrC;;OAEG;IACG,cAAc,IAAI,OAAO,CAAC,MAAM,CAAC;IAYvC;;OAEG;IACG,OAAO,CAAC,aAAa,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAoBzF;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA0B9B;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAK5B,OAAO,CAAC,wBAAwB;IA+BhC,OAAO,CAAC,gBAAgB;CAQzB"} \ No newline at end of file diff --git a/backend/dist/voice/transcription/TranscriptionCache.js b/backend/dist/voice/transcription/TranscriptionCache.js deleted file mode 100644 index 3193094..0000000 --- a/backend/dist/voice/transcription/TranscriptionCache.js +++ /dev/null @@ -1,298 +0,0 @@ -"use strict"; -/** - * Transcription Cache - Phase 2 Voice Processing - * Redis-based caching for Whisper API responses - * - * Success Criteria: - * - Audio fingerprinting for cache keys - * - 1-hour cache TTL for transcripts - * - 30% cache hit rate after 1 week of usage - * - <10ms cache lookup time - */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.TranscriptionCache = void 0; -const crypto_1 = require("crypto"); -const perf_hooks_1 = require("perf_hooks"); -const ioredis_1 = __importDefault(require("ioredis")); -const logger_1 = require("../../utils/logger"); -class TranscriptionCache { - redis; - keyPrefix = 'whisper:cache:'; - defaultTTL = 3600; // 1 hour - // Statistics - hits = 0; - misses = 0; - lookupTimes = []; - constructor() { - this.redis = new ioredis_1.default({ - host: process.env.REDIS_HOST || 'localhost', - port: parseInt(process.env.REDIS_PORT || '6379'), - db: parseInt(process.env.REDIS_CACHE_DB || '1'), // Use separate DB for cache - keyPrefix: this.keyPrefix, - maxRetriesPerRequest: 3, - lazyConnect: true, - enableReadyCheck: true, - }); - this.redis.on('connect', () => { - logger_1.logger.info('Transcription cache connected to Redis'); - }); - this.redis.on('error', (error) => { - logger_1.logger.error('Transcription cache Redis error', { error: error.message }); - }); - logger_1.logger.debug('Transcription cache initialized'); - } - /** - * Generate cache key from audio buffer and options - * Uses audio fingerprinting for consistent keys - */ - generateCacheKey(audioBuffer, options) { - // Create hash of audio content - const audioHash = this.generateAudioFingerprint(audioBuffer); - // Include relevant options in key - const optionsKey = JSON.stringify({ - language: options.language || 'auto', - temperature: options.temperature || 0, - responseFormat: options.responseFormat || 'verbose_json', - }); - const optionsHash = (0, crypto_1.createHash)('md5').update(optionsKey).digest('hex').substring(0, 8); - return `${audioHash}:${optionsHash}`; - } - /** - * Get cached transcription result - * Target: <10ms lookup time - */ - async get(key) { - const startTime = perf_hooks_1.performance.now(); - try { - const cached = await this.redis.get(key); - const lookupTime = perf_hooks_1.performance.now() - startTime; - this.recordLookupTime(lookupTime); - if (cached) { - this.hits++; - // Extend TTL on cache hit - await this.redis.expire(key, this.defaultTTL); - const result = JSON.parse(cached); - logger_1.logger.debug('Cache hit', { - key, - lookupTime: `${lookupTime.toFixed(2)}ms`, - transcriptLength: result.transcript.length, - }); - return result; - } - else { - this.misses++; - logger_1.logger.debug('Cache miss', { - key, - lookupTime: `${lookupTime.toFixed(2)}ms`, - }); - return null; - } - } - catch (error) { - const lookupTime = perf_hooks_1.performance.now() - startTime; - this.misses++; - this.recordLookupTime(lookupTime); - logger_1.logger.error('Cache lookup failed', { - key, - error: error.message, - lookupTime: `${lookupTime.toFixed(2)}ms`, - }); - return null; - } - } - /** - * Cache transcription result - */ - async set(key, result, ttl) { - try { - const cacheData = { - ...result, - cachedAt: new Date().toISOString(), - // Remove processingTime from cache (will be recalculated) - processingTime: undefined, - }; - await this.redis.setex(key, ttl || this.defaultTTL, JSON.stringify(cacheData)); - logger_1.logger.debug('Result cached', { - key, - ttl: ttl || this.defaultTTL, - transcriptLength: result.transcript.length, - confidence: result.confidence, - }); - } - catch (error) { - logger_1.logger.error('Cache set failed', { - key, - error: error.message, - }); - } - } - /** - * Delete cached result - */ - async delete(key) { - try { - await this.redis.del(key); - logger_1.logger.debug('Cache entry deleted', { key }); - } - catch (error) { - logger_1.logger.error('Cache delete failed', { - key, - error: error.message, - }); - } - } - /** - * Clear all cached results - */ - async clear() { - try { - const keys = await this.redis.keys('*'); - if (keys.length > 0) { - await this.redis.del(...keys); - logger_1.logger.info('Cache cleared', { deletedKeys: keys.length }); - } - } - catch (error) { - logger_1.logger.error('Cache clear failed', { - error: error instanceof Error ? error.message : String(error), - }); - } - } - /** - * Get cache statistics - */ - getStats() { - const totalRequests = this.hits + this.misses; - const hitRate = totalRequests > 0 ? (this.hits / totalRequests) * 100 : 0; - const avgLookupTime = this.lookupTimes.length > 0 - ? this.lookupTimes.reduce((sum, time) => sum + time, 0) / this.lookupTimes.length - : 0; - return { - hits: this.hits, - misses: this.misses, - hitRate: Math.round(hitRate * 100) / 100, - averageLookupTime: Math.round(avgLookupTime * 100) / 100, - totalKeys: 0, // Will be populated by getTotalKeys() if needed - memoryUsage: 0, // Will be populated by getMemoryUsage() if needed - }; - } - /** - * Get total number of cached keys - */ - async getTotalKeys() { - try { - const keys = await this.redis.keys('*'); - return keys.length; - } - catch (error) { - logger_1.logger.error('Failed to get total keys', { - error: error instanceof Error ? error.message : String(error), - }); - return 0; - } - } - /** - * Get memory usage information - */ - async getMemoryUsage() { - try { - const info = await this.redis.memory('USAGE', this.keyPrefix + '*'); - return info || 0; - } - catch (error) { - logger_1.logger.error('Failed to get memory usage', { - error: error instanceof Error ? error.message : String(error), - }); - return 0; - } - } - /** - * Preload cache with common transcriptions - */ - async preload(commonPhrases) { - logger_1.logger.info('Starting cache preload', { phrasesCount: commonPhrases.length }); - const preloadPromises = commonPhrases.map(async ({ audio, transcript }) => { - const key = this.generateCacheKey(audio, {}); - const result = { - transcript, - confidence: 0.95, // High confidence for preloaded data - language: 'en', - processingTime: 0, - segments: [], - }; - await this.set(key, result, 86400); // Cache for 24 hours - }); - await Promise.allSettled(preloadPromises); - logger_1.logger.info('Cache preload completed'); - } - /** - * Clean up expired entries (manual cleanup) - */ - async cleanup() { - try { - // Redis automatically handles TTL cleanup, but we can do manual cleanup if needed - const keys = await this.redis.keys('*'); - let cleanedCount = 0; - for (const key of keys) { - const ttl = await this.redis.ttl(key); - if (ttl === -1) { - // No TTL set - await this.redis.expire(key, this.defaultTTL); - cleanedCount++; - } - } - logger_1.logger.info('Cache cleanup completed', { - totalKeys: keys.length, - cleanedKeys: cleanedCount, - }); - } - catch (error) { - logger_1.logger.error('Cache cleanup failed', { - error: error instanceof Error ? error.message : String(error), - }); - } - } - /** - * Close Redis connection - */ - async close() { - await this.redis.quit(); - logger_1.logger.info('Transcription cache connection closed'); - } - generateAudioFingerprint(audioBuffer) { - // Generate a more sophisticated fingerprint based on audio characteristics - const hash = (0, crypto_1.createHash)('sha256'); - // Add buffer content - hash.update(audioBuffer); - // Add buffer size - hash.update(audioBuffer.length.toString()); - // Simple audio characteristics (RMS energy in chunks) - const chunkSize = Math.max(1, Math.floor(audioBuffer.length / 10)); - for (let i = 0; i < audioBuffer.length; i += chunkSize) { - const chunk = audioBuffer.subarray(i, i + chunkSize); - let rms = 0; - // Calculate RMS for this chunk - for (let j = 0; j < chunk.length; j += 2) { - if (j + 1 < chunk.length) { - const sample = chunk.readInt16LE(j); - rms += sample * sample; - } - } - rms = Math.sqrt(rms / (chunk.length / 2)); - hash.update(Math.floor(rms).toString()); - } - return hash.digest('hex').substring(0, 16); // First 16 characters - } - recordLookupTime(time) { - this.lookupTimes.push(time); - // Keep only last 1000 measurements - if (this.lookupTimes.length > 1000) { - this.lookupTimes.shift(); - } - } -} -exports.TranscriptionCache = TranscriptionCache; -//# sourceMappingURL=TranscriptionCache.js.map \ No newline at end of file diff --git a/backend/dist/voice/transcription/TranscriptionCache.js.map b/backend/dist/voice/transcription/TranscriptionCache.js.map deleted file mode 100644 index e1e494a..0000000 --- a/backend/dist/voice/transcription/TranscriptionCache.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TranscriptionCache.js","sourceRoot":"","sources":["../../../src/voice/transcription/TranscriptionCache.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/transcription/WhisperConnectionPool.d.ts b/backend/dist/voice/transcription/WhisperConnectionPool.d.ts deleted file mode 100644 index fa469a6..0000000 --- a/backend/dist/voice/transcription/WhisperConnectionPool.d.ts +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Whisper Connection Pool - Phase 2 Voice Processing - * Pre-warmed connection pool for OpenAI Whisper API - * - * Success Criteria: - * - 5 pre-warmed connections maintained - * - Connection health monitoring - * - Automatic connection recovery - * - <50ms connection acquisition time - */ -import { AxiosInstance } from 'axios'; -export interface PoolStats { - totalConnections: number; - availableConnections: number; - activeConnections: number; - healthyConnections: number; - totalRequests: number; - failedRequests: number; - averageAcquisitionTime: number; -} -export declare class WhisperConnectionPool { - private poolSize; - private connections; - private availableConnections; - private activeConnections; - private connectionHealth; - private healthCheckInterval; - private warmupInterval; - private totalRequests; - private failedRequests; - private acquisitionTimes; - constructor(poolSize?: number); - /** - * Get a connection from the pool - * Target: <50ms acquisition time - */ - getConnection(): Promise; - /** - * Release a connection back to the pool - */ - releaseConnection(connection: AxiosInstance): void; - /** - * Get pool statistics - */ - getStats(): PoolStats; - /** - * Force health check on all connections - */ - checkAllConnectionsHealth(): Promise; - /** - * Shut down the connection pool - */ - shutdown(): Promise; - private initializePool; - private createConnection; - private acquireHealthyConnection; - private waitForConnection; - private replaceConnection; - private startHealthMonitoring; - private startConnectionWarming; - private performHealthCheck; - private checkConnectionHealth; - private warmupConnections; - private getHealthyConnectionCount; - private recordAcquisitionTime; -} -//# sourceMappingURL=WhisperConnectionPool.d.ts.map \ No newline at end of file diff --git a/backend/dist/voice/transcription/WhisperConnectionPool.d.ts.map b/backend/dist/voice/transcription/WhisperConnectionPool.d.ts.map deleted file mode 100644 index 3e6a646..0000000 --- a/backend/dist/voice/transcription/WhisperConnectionPool.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"WhisperConnectionPool.d.ts","sourceRoot":"","sources":["../../../src/voice/transcription/WhisperConnectionPool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAc,EAAE,aAAa,EAAc,MAAM,OAAO,CAAC;AAIzD,MAAM,WAAW,SAAS;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,qBAAa,qBAAqB;IAgBpB,OAAO,CAAC,QAAQ;IAf5B,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,oBAAoB,CAAuB;IACnD,OAAO,CAAC,iBAAiB,CAAiC;IAC1D,OAAO,CAAC,gBAAgB,CAGV;IACd,OAAO,CAAC,mBAAmB,CAA+B;IAC1D,OAAO,CAAC,cAAc,CAA+B;IAGrD,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,cAAc,CAAK;IAC3B,OAAO,CAAC,gBAAgB,CAAgB;gBAEpB,QAAQ,GAAE,MAAU;IAWxC;;;OAGG;IACG,aAAa,IAAI,OAAO,CAAC,aAAa,CAAC;IAoC7C;;OAEG;IACH,iBAAiB,CAAC,UAAU,EAAE,aAAa,GAAG,IAAI;IAqBlD;;OAEG;IACH,QAAQ,IAAI,SAAS;IAiBrB;;OAEG;IACG,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC;IAUhD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB/B,OAAO,CAAC,cAAc;IAkBtB,OAAO,CAAC,gBAAgB;YAoFV,wBAAwB;YA8BxB,iBAAiB;IAiB/B,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,qBAAqB;IAM7B,OAAO,CAAC,sBAAsB;YAMhB,kBAAkB;YAwBlB,qBAAqB;YAiCrB,iBAAiB;IAkB/B,OAAO,CAAC,yBAAyB;IAKjC,OAAO,CAAC,qBAAqB;CAQ9B"} \ No newline at end of file diff --git a/backend/dist/voice/transcription/WhisperConnectionPool.js b/backend/dist/voice/transcription/WhisperConnectionPool.js deleted file mode 100644 index 2c8fa5a..0000000 --- a/backend/dist/voice/transcription/WhisperConnectionPool.js +++ /dev/null @@ -1,369 +0,0 @@ -"use strict"; -/** - * Whisper Connection Pool - Phase 2 Voice Processing - * Pre-warmed connection pool for OpenAI Whisper API - * - * Success Criteria: - * - 5 pre-warmed connections maintained - * - Connection health monitoring - * - Automatic connection recovery - * - <50ms connection acquisition time - */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WhisperConnectionPool = void 0; -const axios_1 = __importDefault(require("axios")); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -class WhisperConnectionPool { - poolSize; - connections = []; - availableConnections = []; - activeConnections = new Set(); - connectionHealth = new Map(); - healthCheckInterval = null; - warmupInterval = null; - // Statistics - totalRequests = 0; - failedRequests = 0; - acquisitionTimes = []; - constructor(poolSize = 5) { - this.poolSize = poolSize; - this.initializePool(); - this.startHealthMonitoring(); - this.startConnectionWarming(); - logger_1.logger.info('Whisper connection pool initialized', { - poolSize: this.poolSize, - apiVersion: 'v1', - }); - } - /** - * Get a connection from the pool - * Target: <50ms acquisition time - */ - async getConnection() { - const startTime = perf_hooks_1.performance.now(); - this.totalRequests++; - try { - // Try to get a healthy available connection - const connection = await this.acquireHealthyConnection(); - const acquisitionTime = perf_hooks_1.performance.now() - startTime; - this.recordAcquisitionTime(acquisitionTime); - // Log slow acquisition - if (acquisitionTime > 50) { - logger_1.logger.warn('Slow connection acquisition', { - acquisitionTime: `${acquisitionTime.toFixed(2)}ms`, - availableConnections: this.availableConnections.length, - activeConnections: this.activeConnections.size, - }); - } - // Mark as active - this.activeConnections.add(connection); - return connection; - } - catch (error) { - this.failedRequests++; - logger_1.logger.error('Failed to acquire connection', { - error: error instanceof Error ? error.message : String(error), - availableConnections: this.availableConnections.length, - healthyConnections: this.getHealthyConnectionCount(), - }); - throw error; - } - } - /** - * Release a connection back to the pool - */ - releaseConnection(connection) { - if (!this.connections.includes(connection)) { - logger_1.logger.warn('Attempting to release unknown connection'); - return; - } - // Remove from active set - this.activeConnections.delete(connection); - // Add back to available pool if healthy - const health = this.connectionHealth.get(connection); - if (health?.healthy !== false) { - this.availableConnections.push(connection); - } - logger_1.logger.debug('Connection released', { - availableConnections: this.availableConnections.length, - activeConnections: this.activeConnections.size, - }); - } - /** - * Get pool statistics - */ - getStats() { - const avgAcquisitionTime = this.acquisitionTimes.length > 0 - ? this.acquisitionTimes.reduce((sum, time) => sum + time, 0) / this.acquisitionTimes.length - : 0; - return { - totalConnections: this.connections.length, - availableConnections: this.availableConnections.length, - activeConnections: this.activeConnections.size, - healthyConnections: this.getHealthyConnectionCount(), - totalRequests: this.totalRequests, - failedRequests: this.failedRequests, - averageAcquisitionTime: Math.round(avgAcquisitionTime * 100) / 100, - }; - } - /** - * Force health check on all connections - */ - async checkAllConnectionsHealth() { - logger_1.logger.info('Starting manual health check for all connections'); - const healthChecks = this.connections.map((conn) => this.checkConnectionHealth(conn)); - await Promise.allSettled(healthChecks); - const stats = this.getStats(); - logger_1.logger.info('Manual health check completed', stats); - } - /** - * Shut down the connection pool - */ - async shutdown() { - logger_1.logger.info('Shutting down Whisper connection pool'); - if (this.healthCheckInterval) { - clearInterval(this.healthCheckInterval); - } - if (this.warmupInterval) { - clearInterval(this.warmupInterval); - } - // Clear all arrays and maps - this.connections = []; - this.availableConnections = []; - this.activeConnections.clear(); - this.connectionHealth.clear(); - logger_1.logger.info('Whisper connection pool shut down'); - } - initializePool() { - for (let i = 0; i < this.poolSize; i++) { - const connection = this.createConnection(); - this.connections.push(connection); - this.availableConnections.push(connection); - this.connectionHealth.set(connection, { - healthy: true, - lastCheck: Date.now(), - failures: 0, - }); - } - logger_1.logger.debug('Connection pool initialized', { - totalConnections: this.connections.length, - availableConnections: this.availableConnections.length, - }); - } - createConnection() { - const connection = axios_1.default.create({ - baseURL: 'https://api.openai.com/v1', - timeout: 30000, // 30 second timeout for uploads - headers: { - Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, - 'User-Agent': 'CEO-Platform/1.0', - 'Keep-Alive': 'timeout=30, max=100', - }, - httpAgent: new (require('http').Agent)({ - keepAlive: true, - maxSockets: 10, - timeout: 30000, - }), - httpsAgent: new (require('https').Agent)({ - keepAlive: true, - maxSockets: 10, - timeout: 30000, - rejectUnauthorized: true, - }), - maxRedirects: 3, - validateStatus: (status) => status >= 200 && status < 300, - }); - // Add request interceptor for logging - connection.interceptors.request.use((config) => { - logger_1.logger.debug('Whisper API request', { - method: config.method?.toUpperCase(), - url: config.url, - timeout: config.timeout, - }); - return config; - }, (error) => { - logger_1.logger.error('Whisper API request error', { error: error.message }); - return Promise.reject(error); - }); - // Add response interceptor for error handling - connection.interceptors.response.use((response) => { - // Mark connection as healthy on successful response - const health = this.connectionHealth.get(connection); - if (health) { - health.healthy = true; - health.failures = 0; - health.lastCheck = Date.now(); - } - return response; - }, (error) => { - // Mark connection as potentially unhealthy on error - const health = this.connectionHealth.get(connection); - if (health) { - health.failures++; - health.lastCheck = Date.now(); - // Mark as unhealthy after 3 consecutive failures - if (health.failures >= 3) { - health.healthy = false; - logger_1.logger.warn('Connection marked as unhealthy', { - failures: health.failures, - errorStatus: error.response?.status, - errorMessage: error.message, - }); - } - } - logger_1.logger.error('Whisper API response error', { - status: error.response?.status, - message: error.message, - code: error.code, - }); - return Promise.reject(error); - }); - return connection; - } - async acquireHealthyConnection() { - const maxAttempts = 10; - let attempts = 0; - while (attempts < maxAttempts) { - // Get next available connection - const connection = this.availableConnections.shift(); - if (!connection) { - // No available connections - wait for one to be released - await this.waitForConnection(); - attempts++; - continue; - } - // Check if connection is healthy - const health = this.connectionHealth.get(connection); - if (health?.healthy !== false) { - return connection; - } - // Connection is unhealthy - try to replace it - logger_1.logger.warn('Replacing unhealthy connection'); - this.replaceConnection(connection); - attempts++; - } - throw new Error('Unable to acquire healthy connection after maximum attempts'); - } - async waitForConnection() { - return new Promise((resolve) => { - const checkInterval = setInterval(() => { - if (this.availableConnections.length > 0) { - clearInterval(checkInterval); - resolve(); - } - }, 10); // Check every 10ms - // Timeout after 5 seconds - setTimeout(() => { - clearInterval(checkInterval); - resolve(); - }, 5000); - }); - } - replaceConnection(oldConnection) { - // Remove from all collections - const connectionIndex = this.connections.indexOf(oldConnection); - if (connectionIndex !== -1) { - this.connections[connectionIndex] = this.createConnection(); - this.availableConnections.push(this.connections[connectionIndex]); - this.connectionHealth.set(this.connections[connectionIndex], { - healthy: true, - lastCheck: Date.now(), - failures: 0, - }); - } - this.connectionHealth.delete(oldConnection); - this.activeConnections.delete(oldConnection); - } - startHealthMonitoring() { - this.healthCheckInterval = setInterval(async () => { - await this.performHealthCheck(); - }, 60000); // Check every minute - } - startConnectionWarming() { - this.warmupInterval = setInterval(async () => { - await this.warmupConnections(); - }, 300000); // Warm up every 5 minutes - } - async performHealthCheck() { - logger_1.logger.debug('Starting connection health check'); - const healthPromises = this.connections.map((conn) => this.checkConnectionHealth(conn)); - await Promise.allSettled(healthPromises); - const stats = this.getStats(); - logger_1.logger.debug('Health check completed', { - healthyConnections: stats.healthyConnections, - totalConnections: stats.totalConnections, - }); - // Replace unhealthy connections - const unhealthyConnections = this.connections.filter((conn) => { - const health = this.connectionHealth.get(conn); - return health?.healthy === false; - }); - for (const conn of unhealthyConnections) { - this.replaceConnection(conn); - } - } - async checkConnectionHealth(connection) { - try { - // Make a lightweight request to check health - await connection.get('/models', { - timeout: 5000, - params: { limit: 1 }, - }); - // Mark as healthy - const health = this.connectionHealth.get(connection); - if (health) { - health.healthy = true; - health.failures = 0; - health.lastCheck = Date.now(); - } - } - catch (error) { - // Mark as potentially unhealthy - const health = this.connectionHealth.get(connection); - if (health) { - health.failures++; - health.lastCheck = Date.now(); - if (health.failures >= 2) { - health.healthy = false; - logger_1.logger.warn('Connection health check failed', { - failures: health.failures, - error: error instanceof Error ? error.message : String(error), - }); - } - } - } - } - async warmupConnections() { - // Warm up a subset of available connections - const connectionsToWarm = this.availableConnections.slice(0, 2); - const warmupPromises = connectionsToWarm.map(async (conn) => { - try { - await conn.get('/models', { timeout: 5000 }); - logger_1.logger.debug('Connection warmed up successfully'); - } - catch (error) { - logger_1.logger.warn('Connection warmup failed', { - error: error instanceof Error ? error.message : String(error), - }); - } - }); - await Promise.allSettled(warmupPromises); - } - getHealthyConnectionCount() { - return Array.from(this.connectionHealth.values()).filter((health) => health.healthy !== false) - .length; - } - recordAcquisitionTime(time) { - this.acquisitionTimes.push(time); - // Keep only last 100 measurements - if (this.acquisitionTimes.length > 100) { - this.acquisitionTimes.shift(); - } - } -} -exports.WhisperConnectionPool = WhisperConnectionPool; -//# sourceMappingURL=WhisperConnectionPool.js.map \ No newline at end of file diff --git a/backend/dist/voice/transcription/WhisperConnectionPool.js.map b/backend/dist/voice/transcription/WhisperConnectionPool.js.map deleted file mode 100644 index 8165d09..0000000 --- a/backend/dist/voice/transcription/WhisperConnectionPool.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"WhisperConnectionPool.js","sourceRoot":"","sources":["../../../src/voice/transcription/WhisperConnectionPool.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/transcription/WhisperService.d.ts b/backend/dist/voice/transcription/WhisperService.d.ts deleted file mode 100644 index 9fe6ae0..0000000 --- a/backend/dist/voice/transcription/WhisperService.d.ts +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Whisper Transcription Service - Phase 2 Voice Processing - * OpenAI Whisper API integration with connection pooling and caching - * - * Success Criteria: - * - Single audio transcription in <1.5 seconds - * - Batch processing for multiple segments - * - Error handling with automatic retry - * - 95%+ transcription accuracy for clear speech - */ -import { TranscriptResult, TranscriptionOptions, AudioSegment } from '../types'; -export declare class WhisperService { - private connectionPool; - private cache; - private performanceMetrics; - private requestCount; - constructor(); - /** - * Transcribe audio buffer to text - * Target: <1.5 seconds processing time - */ - transcribeAudio(audioBuffer: Buffer, options?: TranscriptionOptions): Promise; - /** - * Transcribe multiple audio segments in parallel - * Optimized for complex multi-segment commands - */ - transcribeBatch(audioSegments: AudioSegment[]): Promise; - /** - * Transcribe with streaming support for real-time processing - */ - transcribeStream(audioStream: AsyncGenerator): Promise>>; - /** - * Get service performance statistics - */ - getPerformanceStats(): Record; - /** - * Get service health status - */ - getHealthStatus(): { - status: 'healthy' | 'degraded' | 'unhealthy'; - metrics: Record; - connectionPool: any; - cache: any; - }; - private validateAudioBuffer; - private prepareFormData; - private processWhisperResponse; - private recordPerformanceMetric; -} -//# sourceMappingURL=WhisperService.d.ts.map \ No newline at end of file diff --git a/backend/dist/voice/transcription/WhisperService.d.ts.map b/backend/dist/voice/transcription/WhisperService.d.ts.map deleted file mode 100644 index 62bbdd0..0000000 --- a/backend/dist/voice/transcription/WhisperService.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"WhisperService.d.ts","sourceRoot":"","sources":["../../../src/voice/transcription/WhisperService.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,EAEL,gBAAgB,EAChB,oBAAoB,EACpB,YAAY,EACb,MAAM,UAAU,CAAC;AAIlB,qBAAa,cAAc;IACzB,OAAO,CAAC,cAAc,CAAwB;IAC9C,OAAO,CAAC,KAAK,CAAqB;IAClC,OAAO,CAAC,kBAAkB,CAAoC;IAC9D,OAAO,CAAC,YAAY,CAAK;;IAYzB;;;OAGG;IACG,eAAe,CACnB,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,gBAAgB,CAAC;IAoF5B;;;OAGG;IACG,eAAe,CAAC,aAAa,EAAE,YAAY,EAAE,GAAG,OAAO,CAAC,gBAAgB,EAAE,CAAC;IA4EjF;;OAEG;IACG,gBAAgB,CACpB,WAAW,EAAE,cAAc,CAAC,MAAM,CAAC,GAClC,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,gBAAgB,CAAC,CAAC,CAAC;IAgErD;;OAEG;IACH,mBAAmB,IAAI,MAAM,CAC3B,MAAM,EACN;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAC7D;IAsBD;;OAEG;IACH,eAAe,IAAI;QACjB,MAAM,EAAE,SAAS,GAAG,UAAU,GAAG,WAAW,CAAC;QAC7C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,cAAc,EAAE,GAAG,CAAC;QACpB,KAAK,EAAE,GAAG,CAAC;KACZ;IAkCD,OAAO,CAAC,mBAAmB;IAgB3B,OAAO,CAAC,eAAe;IA+BvB,OAAO,CAAC,sBAAsB;IA0C9B,OAAO,CAAC,uBAAuB;CAahC"} \ No newline at end of file diff --git a/backend/dist/voice/transcription/WhisperService.js b/backend/dist/voice/transcription/WhisperService.js deleted file mode 100644 index cc82adb..0000000 --- a/backend/dist/voice/transcription/WhisperService.js +++ /dev/null @@ -1,368 +0,0 @@ -"use strict"; -/** - * Whisper Transcription Service - Phase 2 Voice Processing - * OpenAI Whisper API integration with connection pooling and caching - * - * Success Criteria: - * - Single audio transcription in <1.5 seconds - * - Batch processing for multiple segments - * - Error handling with automatic retry - * - 95%+ transcription accuracy for clear speech - */ -var __importDefault = (this && this.__importDefault) || function (mod) { - return (mod && mod.__esModule) ? mod : { "default": mod }; -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.WhisperService = void 0; -const form_data_1 = __importDefault(require("form-data")); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../../utils/logger"); -const types_1 = require("../types"); -const WhisperConnectionPool_1 = require("./WhisperConnectionPool"); -const TranscriptionCache_1 = require("./TranscriptionCache"); -class WhisperService { - connectionPool; - cache; - performanceMetrics = new Map(); - requestCount = 0; - constructor() { - this.connectionPool = new WhisperConnectionPool_1.WhisperConnectionPool(5); - this.cache = new TranscriptionCache_1.TranscriptionCache(); - logger_1.logger.info('Whisper service initialized', { - poolSize: 5, - cacheEnabled: true, - }); - } - /** - * Transcribe audio buffer to text - * Target: <1.5 seconds processing time - */ - async transcribeAudio(audioBuffer, options = {}) { - const startTime = perf_hooks_1.performance.now(); - const requestId = `whisper-${++this.requestCount}-${Date.now()}`; - try { - // Validate input - this.validateAudioBuffer(audioBuffer); - // Check cache first - const cacheKey = this.cache.generateCacheKey(audioBuffer, options); - const cachedResult = await this.cache.get(cacheKey); - if (cachedResult) { - logger_1.logger.debug('Whisper cache hit', { requestId, cacheKey }); - return { - ...cachedResult, - processingTime: perf_hooks_1.performance.now() - startTime, - }; - } - // Get connection from pool - const connection = await this.connectionPool.getConnection(); - try { - // Prepare form data - const formData = this.prepareFormData(audioBuffer, options); - // Make API request - const response = await connection.post('/audio/transcriptions', formData, { - headers: formData.getHeaders(), - timeout: 15000, // 15 second timeout - maxContentLength: 25 * 1024 * 1024, // 25MB max - }); - const processingTime = perf_hooks_1.performance.now() - startTime; - // Process response - const result = this.processWhisperResponse(response.data, processingTime, requestId); - // Cache successful results - if (result.confidence > 0.7) { - await this.cache.set(cacheKey, result, 3600); // Cache for 1 hour - } - // Record metrics - this.recordPerformanceMetric('transcription_time', processingTime); - this.recordPerformanceMetric('transcription_success', 1); - logger_1.logger.info('Whisper transcription successful', { - requestId, - processingTime: `${processingTime.toFixed(2)}ms`, - transcriptLength: result.transcript.length, - confidence: result.confidence, - language: result.language, - }); - return result; - } - finally { - // Always release connection back to pool - this.connectionPool.releaseConnection(connection); - } - } - catch (error) { - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordPerformanceMetric('transcription_time', processingTime); - this.recordPerformanceMetric('transcription_error', 1); - logger_1.logger.error('Whisper transcription failed', { - requestId, - error: error instanceof Error ? error.message : String(error), - processingTime: `${processingTime.toFixed(2)}ms`, - audioSize: audioBuffer.length, - options, - }); - throw new types_1.VoiceProcessingError('Transcription failed', { - requestId, - processingTime, - originalError: error instanceof Error ? error.message : String(error), - audioSize: audioBuffer.length, - }); - } - } - /** - * Transcribe multiple audio segments in parallel - * Optimized for complex multi-segment commands - */ - async transcribeBatch(audioSegments) { - if (audioSegments.length === 0) { - return []; - } - const startTime = perf_hooks_1.performance.now(); - const batchId = `batch-${Date.now()}`; - logger_1.logger.info('Starting batch transcription', { - batchId, - segmentCount: audioSegments.length, - }); - try { - // Process segments in parallel (max 3 concurrent to avoid rate limiting) - const concurrencyLimit = 3; - const results = []; - for (let i = 0; i < audioSegments.length; i += concurrencyLimit) { - const batch = audioSegments.slice(i, i + concurrencyLimit); - const batchPromises = batch.map((segment, index) => this.transcribeAudio(segment.audioData, { - language: segment.language ?? '', - prompt: segment.context ?? '', - }).catch((error) => ({ - transcript: '', - confidence: 0, - language: 'unknown', - processingTime: 0, - error: error instanceof Error ? error.message : String(error), - }))); - const batchResults = await Promise.all(batchPromises); - results.push(...batchResults); - // Small delay between batches to respect rate limits - if (i + concurrencyLimit < audioSegments.length) { - await new Promise((resolve) => setTimeout(resolve, 100)); - } - } - const totalTime = perf_hooks_1.performance.now() - startTime; - const successfulResults = results.filter((r) => !r.error); - logger_1.logger.info('Batch transcription completed', { - batchId, - totalTime: `${totalTime.toFixed(2)}ms`, - totalSegments: audioSegments.length, - successfulTranscriptions: successfulResults.length, - failedTranscriptions: results.length - successfulResults.length, - }); - return results; - } - catch (error) { - const totalTime = perf_hooks_1.performance.now() - startTime; - logger_1.logger.error('Batch transcription failed', { - batchId, - error: error instanceof Error ? error.message : String(error), - totalTime: `${totalTime.toFixed(2)}ms`, - segmentCount: audioSegments.length, - }); - throw new types_1.VoiceProcessingError('Batch transcription failed', { - batchId, - totalTime, - segmentCount: audioSegments.length, - originalError: error instanceof Error ? error.message : String(error), - }); - } - } - /** - * Transcribe with streaming support for real-time processing - */ - async transcribeStream(audioStream) { - const streamId = `stream-${Date.now()}`; - logger_1.logger.info('Starting stream transcription', { streamId }); - return async function* () { - const bufferChunks = []; - let segmentBuffer = Buffer.alloc(0); - try { - for await (const chunk of audioStream) { - segmentBuffer = Buffer.concat([segmentBuffer, chunk]); - // Process when we have enough data (2 seconds of audio) - const minSize = 16000 * 2 * 2; // 2 seconds at 16kHz, 16-bit - if (segmentBuffer.length >= minSize) { - try { - const result = await this.transcribeAudio(segmentBuffer); - yield { - transcript: result.transcript, - confidence: result.confidence, - language: result.language, - processingTime: result.processingTime, - }; - // Reset buffer - segmentBuffer = Buffer.alloc(0); - } - catch (error) { - yield { - error: error instanceof Error ? error.message : String(error), - processingTime: 0, - }; - } - } - } - // Process remaining buffer - if (segmentBuffer.length > 0) { - try { - const result = await this.transcribeAudio(segmentBuffer); - yield result; - } - catch (error) { - yield { - error: error instanceof Error ? error.message : String(error), - processingTime: 0, - }; - } - } - } - catch (error) { - logger_1.logger.error('Stream transcription error', { - streamId, - error: error instanceof Error ? error.message : String(error), - }); - yield { - error: error instanceof Error ? error.message : String(error), - processingTime: 0, - }; - } - }.call(this); - } - /** - * Get service performance statistics - */ - getPerformanceStats() { - const stats = {}; - for (const [metric, values] of this.performanceMetrics.entries()) { - if (values.length === 0) - continue; - const sorted = [...values].sort((a, b) => a - b); - const average = values.reduce((sum, val) => sum + val, 0) / values.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - stats[metric] = { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: values.length, - }; - } - return stats; - } - /** - * Get service health status - */ - getHealthStatus() { - const stats = this.getPerformanceStats(); - const poolStats = this.connectionPool.getStats(); - const cacheStats = this.cache.getStats(); - let status = 'healthy'; - // Check if average transcription time is too high - const avgTime = stats.transcription_time?.average || 0; - if (avgTime > 3000) { - status = 'unhealthy'; - } - else if (avgTime > 2000) { - status = 'degraded'; - } - // Check error rate - const successCount = stats.transcription_success?.count || 0; - const errorCount = stats.transcription_error?.count || 0; - const errorRate = errorCount / (successCount + errorCount) || 0; - if (errorRate > 0.1) { - status = 'unhealthy'; - } - else if (errorRate > 0.05) { - status = 'degraded'; - } - return { - status, - metrics: stats, - connectionPool: poolStats, - cache: cacheStats, - }; - } - validateAudioBuffer(buffer) { - if (!buffer || buffer.length === 0) { - throw new Error('Audio buffer is empty'); - } - if (buffer.length > 25 * 1024 * 1024) { - // 25MB limit - throw new Error('Audio buffer too large (>25MB)'); - } - if (buffer.length < 1000) { - // Minimum reasonable size - throw new Error('Audio buffer too small (<1KB)'); - } - } - prepareFormData(audioBuffer, options) { - const formData = new form_data_1.default(); - // Add audio file - formData.append('file', audioBuffer, { - filename: 'audio.wav', - contentType: 'audio/wav', - }); - // Add model - formData.append('model', 'whisper-1'); - // Add response format - formData.append('response_format', options.responseFormat || 'verbose_json'); - // Add optional parameters - if (options.language) { - formData.append('language', options.language); - } - if (options.prompt) { - formData.append('prompt', options.prompt); - } - if (options.temperature !== undefined) { - formData.append('temperature', options.temperature.toString()); - } - return formData; - } - processWhisperResponse(responseData, processingTime, requestId) { - const transcript = responseData.text || ''; - const language = responseData.language || 'en'; - // Calculate confidence score from segments if available - let confidence = 0.9; // Default confidence - if (responseData.segments && responseData.segments.length > 0) { - const totalConfidence = responseData.segments.reduce((sum, segment) => sum + (segment.confidence || 0.9), 0); - confidence = Math.max(0.1, Math.min(1.0, totalConfidence / responseData.segments.length)); - } - // Validate transcript quality - if (transcript.length === 0) { - confidence = 0; - } - else if (transcript.length < 3) { - confidence *= 0.5; // Reduce confidence for very short transcripts - } - logger_1.logger.debug('Whisper response processed', { - requestId, - transcriptLength: transcript.length, - confidence, - language, - segmentCount: responseData.segments?.length || 0, - }); - return { - transcript, - confidence, - language, - processingTime, - segments: responseData.segments || [], - }; - } - recordPerformanceMetric(key, value) { - if (!this.performanceMetrics.has(key)) { - this.performanceMetrics.set(key, []); - } - const metrics = this.performanceMetrics.get(key); - metrics.push(value); - // Keep only last 1000 measurements - if (metrics.length > 1000) { - metrics.shift(); - } - } -} -exports.WhisperService = WhisperService; -//# sourceMappingURL=WhisperService.js.map \ No newline at end of file diff --git a/backend/dist/voice/transcription/WhisperService.js.map b/backend/dist/voice/transcription/WhisperService.js.map deleted file mode 100644 index 011c0bb..0000000 --- a/backend/dist/voice/transcription/WhisperService.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"WhisperService.js","sourceRoot":"","sources":["../../../src/voice/transcription/WhisperService.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/types.d.ts b/backend/dist/voice/types.d.ts deleted file mode 100644 index 1c23a01..0000000 --- a/backend/dist/voice/types.d.ts +++ /dev/null @@ -1,339 +0,0 @@ -/** - * Voice Processing Types and Interfaces - Phase 2 - * Central type definitions for voice processing pipeline - */ -export interface AudioStreamConfig { - userId: string; - socketId: string; - sampleRate: number; - channels: number; - format: 'pcm16' | 'pcm32' | 'float32'; - chunkSize: number; - maxBufferSize?: number; - silenceThreshold?: number; - maxSegmentLength?: number; -} -export interface AudioSegment { - id: string; - audioData: Buffer; - sampleRate: number; - channels: number; - format: string; - duration: number; - timestamp: string; - userId: string; - socketId: string; - language?: string; - context?: string; -} -export interface ProcessingResult { - status: 'waiting' | 'processing' | 'segment_ready' | 'error'; - hasVoice?: boolean; - segment?: AudioSegment; - processingTime?: number; - error?: string; -} -export interface StreamStatus { - isActive: boolean; - bufferLevel: number; - lastActivity: number; - totalProcessed: number; - memoryUsage: number; -} -export interface VADConfig { - threshold: number; - sensitivity: number; - smoothing: number; - minSpeechDuration: number; - hangoverTime: number; -} -export interface VADResult { - hasVoice: boolean; - confidence: number; - energy: number; - backgroundNoise: number; - processingTime: number; -} -export interface TranscriptionOptions { - language?: string; - prompt?: string; - temperature?: number; - responseFormat?: 'json' | 'text' | 'verbose_json'; -} -export interface TranscriptResult { - transcript: string; - confidence: number; - language: string; - processingTime: number; - segments?: TranscriptSegment[]; - error?: string; -} -export interface TranscriptSegment { - id: string; - start: number; - end: number; - text: string; - confidence: number; - words?: TranscriptWord[]; -} -export interface TranscriptWord { - word: string; - start: number; - end: number; - confidence: number; -} -export interface UserContext { - userId: string; - organizationId: string; - sessionId?: string; - language?: string; - timezone?: string; -} -export interface ContextData { - user: { - id: string; - name: string; - role: string; - email: string; - }; - organization: { - id: string; - name: string; - timezone: string; - }; - activeChannels: Array<{ - id: string; - name: string; - type: string; - memberCount: number; - }>; - recentTasks: Array<{ - id: string; - title: string; - status: string; - assignedTo: string[]; - dueDate?: string; - }>; - teamMembers: Array<{ - id: string; - name: string; - role: string; - status: 'online' | 'offline' | 'busy'; - }>; - temporal: { - currentTime: string; - timezone: string; - businessHours: { - start: string; - end: string; - }; - }; - conversation?: { - recentCommands: VoiceCommand[]; - context: string[]; - }; -} -export interface ParsedCommand { - id: string; - userId: string; - transcript: string; - originalTranscript: string; - intent: string; - confidence: number; - actions: CommandAction[]; - entities: ResolvedEntities; - contextReferences?: { - pronouns: string[]; - temporalContext: string[]; - implicitEntities: string[]; - }; - processingTime: number; - timestamp: string; -} -export interface CommandAction { - id?: string; - type: ActionType; - parameters: Record; - priority: number; - dependencies: string[]; - estimatedDuration?: string; - critical?: boolean; - order?: number; - validated?: boolean; -} -export type ActionType = 'CREATE_CHANNEL' | 'CREATE_TASK' | 'ASSIGN_USERS' | 'SEND_MESSAGE' | 'UPLOAD_FILE' | 'SET_DEADLINE' | 'CREATE_DEPENDENCY' | 'UPDATE_STATUS' | 'SCHEDULE_MEETING' | 'GENERATE_REPORT'; -export interface ResolvedEntities { - users: Array<{ - name: string; - resolved_id: string; - confidence: number; - }>; - channels: Array<{ - name: string; - resolved_id: string; - confidence: number; - }>; - tasks: Array<{ - title: string; - resolved_id: string; - confidence: number; - }>; - dates: Array<{ - text: string; - resolved_date: string; - confidence: number; - }>; - files: Array<{ - name: string; - resolved_id?: string; - confidence: number; - }>; -} -export interface ExecutionPlan { - id: string; - steps: ExecutionStep[]; - totalActions: number; - estimatedTime: number; - createdAt: string; -} -export interface ExecutionStep { - id: string; - actions: CommandAction[]; - parallel: boolean; - dependencies: string[]; - estimatedTime: number; -} -export interface ExecutionResult { - id: string; - commandId: string; - success: boolean; - totalActions: number; - successfulActions: number; - failedActions: number; - results: ActionResult[]; - error?: string; - executionTime: number; - rollbackApplied?: boolean; - timestamp: string; -} -export interface ActionResult { - actionId: string; - actionType: string; - success: boolean; - data?: any; - error?: string; - executionTime: number; - timestamp: string; -} -export interface VoiceCommand { - id: string; - userId: string; - transcript: string; - processedTranscript?: string; - intentAnalysis?: any; - actionsPlanned?: CommandAction[]; - actionsExecuted?: ActionResult[]; - executionStatus: 'pending' | 'processing' | 'completed' | 'failed' | 'partial'; - processingTime: number; - errorDetails?: any; - createdAt: string; -} -export interface VoiceFileUpload { - userId: string; - fileName: string; - contentType: string; - fileSize?: number; - description?: string; - targetChannels?: string[]; - targetTasks?: string[]; - transcript: string; -} -export interface FileUploadResult { - success: boolean; - fileId: string; - uploadUrl: string; - finalUrl: string; - expiresAt: string; -} -export interface FileOperationResult { - success: boolean; - operation: string; - result: any; -} -export interface BroadcastEvent { - type: string; - data: any; - timestamp: string; - source: string; -} -export interface BroadcastTarget { - type: 'user' | 'channel' | 'room'; - id: string; -} -export interface PerformanceMetrics { - component: string; - operation: string; - duration: number; - success: boolean; - metadata?: Record; - timestamp: string; -} -export interface VoiceProcessingMetrics { - transcriptionTime: number; - processingTime: number; - executionTime: number; - totalTime: number; - accuracy: number; - success: boolean; - commandId: string; - userId: string; - timestamp: string; -} -export declare class VoiceProcessingError extends Error { - context?: Record | undefined; - constructor(message: string, context?: Record | undefined); -} -export declare class AIProcessingError extends Error { - context?: Record | undefined; - constructor(message: string, context?: Record | undefined); -} -export declare class ExecutionError extends Error { - actionResult?: ActionResult | undefined; - constructor(message: string, actionResult?: ActionResult | undefined); -} -export declare class FileUploadError extends Error { - context?: Record | undefined; - constructor(message: string, context?: Record | undefined); -} -export interface ExecutionSummary { - totalActions: number; - successfulActions: number; - failedActions: number; - totalExecutionTime: number; - averageActionTime: number; - affectedEntities: string[]; - actionBreakdown: Record; -} -export interface AuditEntry { - id: string; - commandId: string; - userId: string; - organizationId: string; - sessionId: string; - timestamp: string; - originalTranscript: string; - parsedIntent: string; - executionSummary: ExecutionSummary; - success: boolean; - error?: string | undefined; - metadata: { - userAgent: string; - clientVersion: string; - processingTime: number; - }; -} -//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/backend/dist/voice/types.d.ts.map b/backend/dist/voice/types.d.ts.map deleted file mode 100644 index e51b43f..0000000 --- a/backend/dist/voice/types.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/voice/types.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/voice/types.js b/backend/dist/voice/types.js deleted file mode 100644 index 1ddb8ae..0000000 --- a/backend/dist/voice/types.js +++ /dev/null @@ -1,45 +0,0 @@ -"use strict"; -/** - * Voice Processing Types and Interfaces - Phase 2 - * Central type definitions for voice processing pipeline - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.FileUploadError = exports.ExecutionError = exports.AIProcessingError = exports.VoiceProcessingError = void 0; -// Error Types -class VoiceProcessingError extends Error { - context; - constructor(message, context) { - super(message); - this.context = context; - this.name = 'VoiceProcessingError'; - } -} -exports.VoiceProcessingError = VoiceProcessingError; -class AIProcessingError extends Error { - context; - constructor(message, context) { - super(message); - this.context = context; - this.name = 'AIProcessingError'; - } -} -exports.AIProcessingError = AIProcessingError; -class ExecutionError extends Error { - actionResult; - constructor(message, actionResult) { - super(message); - this.actionResult = actionResult; - this.name = 'ExecutionError'; - } -} -exports.ExecutionError = ExecutionError; -class FileUploadError extends Error { - context; - constructor(message, context) { - super(message); - this.context = context; - this.name = 'FileUploadError'; - } -} -exports.FileUploadError = FileUploadError; -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/backend/dist/voice/types.js.map b/backend/dist/voice/types.js.map deleted file mode 100644 index f9cba96..0000000 --- a/backend/dist/voice/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/voice/types.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAgVH,cAAc;AACd,MAAa,oBAAqB,SAAQ,KAAK;IAGpC;IAFT,YACE,OAAe,EACR,OAA6B;QAEpC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,YAAO,GAAP,OAAO,CAAsB;QAGpC,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACrC,CAAC;CACF;AARD,oDAQC;AAED,MAAa,iBAAkB,SAAQ,KAAK;IAGjC;IAFT,YACE,OAAe,EACR,OAA6B;QAEpC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,YAAO,GAAP,OAAO,CAAsB;QAGpC,IAAI,CAAC,IAAI,GAAG,mBAAmB,CAAC;IAClC,CAAC;CACF;AARD,8CAQC;AAED,MAAa,cAAe,SAAQ,KAAK;IAG9B;IAFT,YACE,OAAe,EACR,YAA2B;QAElC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,iBAAY,GAAZ,YAAY,CAAe;QAGlC,IAAI,CAAC,IAAI,GAAG,gBAAgB,CAAC;IAC/B,CAAC;CACF;AARD,wCAQC;AAED,MAAa,eAAgB,SAAQ,KAAK;IAG/B;IAFT,YACE,OAAe,EACR,OAA6B;QAEpC,KAAK,CAAC,OAAO,CAAC,CAAC;QAFR,YAAO,GAAP,OAAO,CAAsB;QAGpC,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;IAChC,CAAC;CACF;AARD,0CAQC"} \ No newline at end of file diff --git a/backend/dist/websocket/EnhancedSocketManager.d.ts b/backend/dist/websocket/EnhancedSocketManager.d.ts deleted file mode 100644 index 6ae4df3..0000000 --- a/backend/dist/websocket/EnhancedSocketManager.d.ts +++ /dev/null @@ -1,144 +0,0 @@ -/** - * Enhanced Socket Manager - Phase 2 Real-Time WebSocket Management - * Advanced WebSocket management with voice command event handling - * - * Success Criteria: - * - Connection management with automatic reconnection - * - Event routing and middleware support - * - Voice command event handling - * - Connection persistence and recovery - */ -import { Server as SocketIOServer } from 'socket.io'; -import { EventEmitter } from 'events'; -import { ISocketManager } from './types'; -export interface SocketConnection { - socketId: string; - userId: string; - organizationId: string; - connectionTime: number; - lastActivity: number; - subscriptions: Set; - metadata: Record; - isActive: boolean; - reconnectCount: number; -} -export interface ConnectionMetrics { - totalConnections: number; - activeConnections: number; - averageConnectionDuration: number; - reconnectionRate: number; - eventThroughput: number; - errorRate: number; -} -export interface VoiceCommandEvent { - eventType: 'command_start' | 'command_progress' | 'command_complete' | 'command_error'; - commandId: string; - userId: string; - organizationId: string; - data: any; - timestamp: string; -} -export declare class EnhancedSocketManager extends EventEmitter implements ISocketManager { - private io; - private connections; - private userConnections; - private executionEventManager; - private progressBroadcaster; - private entityUpdateBroadcaster; - private connectionMetrics; - private eventMetrics; - private errorCount; - private readonly heartbeatInterval; - private readonly connectionTimeout; - private heartbeatTimer?; - constructor(io: SocketIOServer); - /** - * Setup socket event handlers - */ - private setupSocketHandlers; - /** - * Handle new socket connection - */ - private handleConnection; - /** - * Setup event handlers for a socket - */ - private setupSocketEventHandlers; - /** - * Handle voice command events - */ - private handleVoiceCommandEvent; - /** - * Handle channel subscription - */ - private handleSubscription; - /** - * Handle channel unsubscription - */ - private handleUnsubscription; - /** - * Handle progress request - */ - private handleProgressRequest; - /** - * Handle entity update request - */ - private handleEntityUpdateRequest; - /** - * Handle socket disconnection - */ - private handleDisconnection; - /** - * Handle socket errors - */ - private handleSocketError; - /** - * Check if user is connected - */ - isUserConnected(userId: string): boolean; - /** - * Emit event to specific user - */ - emitToUser(userId: string, event: string, data: any): Promise; - /** - * Emit event to organization - */ - emitToOrganization(organizationId: string, event: string, data: any): Promise; - /** - * Start heartbeat monitoring - */ - private startHeartbeat; - /** - * Check and cleanup inactive connections - */ - private checkConnections; - /** - * Start metrics collection - */ - private startMetricsCollection; - /** - * Record connection duration metrics - */ - private recordConnectionMetrics; - /** - * Record event processing metrics - */ - private recordEventMetrics; - /** - * Get connection metrics - */ - getConnectionMetrics(): ConnectionMetrics; - /** - * Get all active connections - */ - getActiveConnections(): SocketConnection[]; - /** - * Get connections for specific user - */ - getUserConnections(userId: string): SocketConnection[]; - /** - * Cleanup and destroy - */ - destroy(): void; -} -//# sourceMappingURL=EnhancedSocketManager.d.ts.map \ No newline at end of file diff --git a/backend/dist/websocket/EnhancedSocketManager.d.ts.map b/backend/dist/websocket/EnhancedSocketManager.d.ts.map deleted file mode 100644 index 0d73cfe..0000000 --- a/backend/dist/websocket/EnhancedSocketManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EnhancedSocketManager.d.ts","sourceRoot":"","sources":["../../src/websocket/EnhancedSocketManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/websocket/EnhancedSocketManager.js b/backend/dist/websocket/EnhancedSocketManager.js deleted file mode 100644 index 7bb8705..0000000 --- a/backend/dist/websocket/EnhancedSocketManager.js +++ /dev/null @@ -1,483 +0,0 @@ -"use strict"; -/** - * Enhanced Socket Manager - Phase 2 Real-Time WebSocket Management - * Advanced WebSocket management with voice command event handling - * - * Success Criteria: - * - Connection management with automatic reconnection - * - Event routing and middleware support - * - Voice command event handling - * - Connection persistence and recovery - */ -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EnhancedSocketManager = void 0; -const events_1 = require("events"); -const perf_hooks_1 = require("perf_hooks"); -const logger_1 = require("../utils/logger"); -const ExecutionEventManager_1 = require("../realtime/broadcasting/ExecutionEventManager"); -const ProgressBroadcaster_1 = require("../realtime/broadcasting/ProgressBroadcaster"); -const EntityUpdateBroadcaster_1 = require("../realtime/broadcasting/EntityUpdateBroadcaster"); -class EnhancedSocketManager extends events_1.EventEmitter { - io; - connections = new Map(); - userConnections = new Map(); - executionEventManager; - progressBroadcaster; - entityUpdateBroadcaster; - connectionMetrics = []; - eventMetrics = []; - errorCount = 0; - heartbeatInterval = 30000; // 30 seconds - connectionTimeout = 300000; // 5 minutes - heartbeatTimer; - constructor(io) { - super(); - this.io = io; - // Initialize broadcasting components - this.executionEventManager = new ExecutionEventManager_1.ExecutionEventManager(this); - this.progressBroadcaster = new ProgressBroadcaster_1.ProgressBroadcaster(this); - this.entityUpdateBroadcaster = new EntityUpdateBroadcaster_1.EntityUpdateBroadcaster(this); - this.setupSocketHandlers(); - this.startHeartbeat(); - this.startMetricsCollection(); - logger_1.logger.info('Enhanced Socket Manager initialized'); - } - /** - * Setup socket event handlers - */ - setupSocketHandlers() { - this.io.on('connection', (socket) => { - this.handleConnection(socket); - }); - } - /** - * Handle new socket connection - */ - async handleConnection(socket) { - const startTime = perf_hooks_1.performance.now(); - try { - // Extract user info from socket (assuming it's added by auth middleware) - const userId = socket.data.userId; - const organizationId = socket.data.organizationId; - if (!userId || !organizationId) { - logger_1.logger.warn('Socket connection without proper authentication', { - socketId: socket.id, - }); - socket.disconnect(); - return; - } - // Create connection record - const connection = { - socketId: socket.id, - userId, - organizationId, - connectionTime: startTime, - lastActivity: startTime, - subscriptions: new Set(), - metadata: socket.data.metadata || {}, - isActive: true, - reconnectCount: socket.data.reconnectCount || 0, - }; - this.connections.set(socket.id, connection); - // Track user connections - if (!this.userConnections.has(userId)) { - this.userConnections.set(userId, new Set()); - } - this.userConnections.get(userId).add(socket.id); - // Setup socket event handlers - this.setupSocketEventHandlers(socket); - // Join organization room - socket.join(`org:${organizationId}`); - socket.join(`user:${userId}`); - logger_1.logger.info('Socket connected', { - socketId: socket.id, - userId, - organizationId, - reconnectCount: connection.reconnectCount, - }); - this.emit('connection_established', { - socketId: socket.id, - userId, - organizationId, - connectionTime: startTime, - }); - // Send connection acknowledgment - socket.emit('connection_ack', { - socketId: socket.id, - serverTime: new Date().toISOString(), - features: ['voice_commands', 'real_time_updates', 'progress_tracking'], - }); - } - catch (error) { - this.errorCount++; - logger_1.logger.error('Error handling socket connection', { - socketId: socket.id, - error: error.message, - }); - socket.disconnect(); - } - } - /** - * Setup event handlers for a socket - */ - setupSocketEventHandlers(socket) { - const connection = this.connections.get(socket.id); - if (!connection) - return; - // Voice command events - socket.on('voice_command_start', (data) => { - this.handleVoiceCommandEvent(socket, 'command_start', data); - }); - socket.on('voice_command_progress', (data) => { - this.handleVoiceCommandEvent(socket, 'command_progress', data); - }); - socket.on('voice_command_complete', (data) => { - this.handleVoiceCommandEvent(socket, 'command_complete', data); - }); - socket.on('voice_command_error', (data) => { - this.handleVoiceCommandEvent(socket, 'command_error', data); - }); - // Subscription management - socket.on('subscribe', (channels) => { - this.handleSubscription(socket, channels); - }); - socket.on('unsubscribe', (channels) => { - this.handleUnsubscription(socket, channels); - }); - // Progress tracking - socket.on('request_progress', (commandId) => { - this.handleProgressRequest(socket, commandId); - }); - // Entity updates - socket.on('request_entity_updates', (entityIds) => { - this.handleEntityUpdateRequest(socket, entityIds); - }); - // Heartbeat - socket.on('ping', () => { - connection.lastActivity = perf_hooks_1.performance.now(); - socket.emit('pong', { serverTime: Date.now() }); - }); - // Disconnection - socket.on('disconnect', (reason) => { - this.handleDisconnection(socket, reason); - }); - // Error handling - socket.on('error', (error) => { - this.handleSocketError(socket, error); - }); - } - /** - * Handle voice command events - */ - async handleVoiceCommandEvent(socket, eventType, data) { - const connection = this.connections.get(socket.id); - if (!connection) - return; - const startTime = perf_hooks_1.performance.now(); - try { - const voiceCommandEvent = { - eventType, - commandId: data.commandId, - userId: connection.userId, - organizationId: connection.organizationId, - data, - timestamp: new Date().toISOString(), - }; - // Update last activity - connection.lastActivity = perf_hooks_1.performance.now(); - // Route to appropriate handler based on event type - switch (eventType) { - case 'command_start': - await this.executionEventManager.broadcastCommandStart(data.commandId, connection.userId, connection.organizationId, data.affectedUsers || [], data // Pass the commandData as the fifth argument - ); - break; - case 'command_progress': - if (data.sessionId && data.stepId) { - await this.progressBroadcaster.updateStepProgress(data.sessionId, data.stepId, data.progress, data.status, data.result, data.error); - } - break; - case 'command_complete': - await this.executionEventManager.broadcastCommandComplete(data.commandId, connection.userId, connection.organizationId, data.result, data.affectedUsers || []); - break; - case 'command_error': - await this.executionEventManager.broadcastCommandError(data.commandId, connection.userId, connection.organizationId, data.error, data.affectedUsers || []); - break; - } - const processingTime = perf_hooks_1.performance.now() - startTime; - this.recordEventMetrics(processingTime); - logger_1.logger.debug('Voice command event processed', { - eventType, - commandId: data.commandId, - userId: connection.userId, - processingTime: `${processingTime.toFixed(2)}ms`, - }); - this.emit('voice_command_event', voiceCommandEvent); - } - catch (error) { - this.errorCount++; - logger_1.logger.error('Error handling voice command event', { - socketId: socket.id, - eventType, - error: error.message, - }); - socket.emit('event_error', { - eventType, - error: error.message, - timestamp: new Date().toISOString(), - }); - } - } - /** - * Handle channel subscription - */ - handleSubscription(socket, channels) { - const connection = this.connections.get(socket.id); - if (!connection) - return; - for (const channel of channels) { - connection.subscriptions.add(channel); - socket.join(channel); - } - logger_1.logger.debug('Socket subscribed to channels', { - socketId: socket.id, - channels, - totalSubscriptions: connection.subscriptions.size, - }); - } - /** - * Handle channel unsubscription - */ - handleUnsubscription(socket, channels) { - const connection = this.connections.get(socket.id); - if (!connection) - return; - for (const channel of channels) { - connection.subscriptions.delete(channel); - socket.leave(channel); - } - logger_1.logger.debug('Socket unsubscribed from channels', { - socketId: socket.id, - channels, - totalSubscriptions: connection.subscriptions.size, - }); - } - /** - * Handle progress request - */ - handleProgressRequest(socket, commandId) { - const connection = this.connections.get(socket.id); - if (!connection) - return; - const sessions = this.progressBroadcaster.getUserSessions(connection.userId); - const commandSession = sessions.find((s) => s.commandId === commandId); - if (commandSession) { - socket.emit('progress_update', { - sessionId: commandSession.sessionId, - commandId: commandSession.commandId, - // ... include full progress data - }); - } - else { - socket.emit('progress_not_found', { commandId }); - } - } - /** - * Handle entity update request - */ - handleEntityUpdateRequest(socket, entityIds) { - const connection = this.connections.get(socket.id); - if (!connection) - return; - // Get recent batches for organization - const recentBatches = this.entityUpdateBroadcaster.getRecentBatches(connection.organizationId, 5); - // Filter updates for requested entities - const relevantUpdates = recentBatches.flatMap((batch) => batch.updates.filter((update) => entityIds.includes(update.entityId) && update.affectedUsers.includes(connection.userId))); - socket.emit('entity_updates_history', { - entityIds, - updates: relevantUpdates, - timestamp: new Date().toISOString(), - }); - } - /** - * Handle socket disconnection - */ - handleDisconnection(socket, reason) { - const connection = this.connections.get(socket.id); - if (!connection) - return; - const connectionDuration = perf_hooks_1.performance.now() - connection.connectionTime; - this.recordConnectionMetrics(connectionDuration); - // Remove from user connections - const userSockets = this.userConnections.get(connection.userId); - if (userSockets) { - userSockets.delete(socket.id); - if (userSockets.size === 0) { - this.userConnections.delete(connection.userId); - } - } - // Remove connection - this.connections.delete(socket.id); - logger_1.logger.info('Socket disconnected', { - socketId: socket.id, - userId: connection.userId, - reason, - connectionDuration: `${connectionDuration.toFixed(2)}ms`, - reconnectCount: connection.reconnectCount, - }); - this.emit('connection_closed', { - socketId: socket.id, - userId: connection.userId, - reason, - connectionDuration, - }); - } - /** - * Handle socket errors - */ - handleSocketError(socket, error) { - this.errorCount++; - logger_1.logger.error('Socket error', { - socketId: socket.id, - error: error.message || error, - }); - this.emit('socket_error', { - socketId: socket.id, - error: error.message || error, - }); - } - /** - * Check if user is connected - */ - isUserConnected(userId) { - const userSockets = this.userConnections.get(userId); - return userSockets ? userSockets.size > 0 : false; - } - /** - * Emit event to specific user - */ - async emitToUser(userId, event, data) { - const userSockets = this.userConnections.get(userId); - if (!userSockets) - return; - const emitPromises = Array.from(userSockets).map(async (socketId) => { - const socket = this.io.sockets.sockets.get(socketId); - if (socket) { - socket.emit(event, data); - } - }); - await Promise.allSettled(emitPromises); - } - /** - * Emit event to organization - */ - async emitToOrganization(organizationId, event, data) { - this.io.to(`org:${organizationId}`).emit(event, data); - } - /** - * Start heartbeat monitoring - */ - startHeartbeat() { - this.heartbeatTimer = setInterval(() => { - this.checkConnections(); - }, this.heartbeatInterval); - } - /** - * Check and cleanup inactive connections - */ - checkConnections() { - const now = perf_hooks_1.performance.now(); - let inactiveCount = 0; - for (const [socketId, connection] of this.connections.entries()) { - const inactiveDuration = now - connection.lastActivity; - if (inactiveDuration > this.connectionTimeout) { - const socket = this.io.sockets.sockets.get(socketId); - if (socket) { - socket.disconnect(true); - inactiveCount++; - } - } - } - if (inactiveCount > 0) { - logger_1.logger.debug('Cleaned up inactive connections', { count: inactiveCount }); - } - } - /** - * Start metrics collection - */ - startMetricsCollection() { - setInterval(() => { - this.emit('metrics_update', this.getConnectionMetrics()); - }, 60000); // Every minute - } - /** - * Record connection duration metrics - */ - recordConnectionMetrics(duration) { - this.connectionMetrics.push(duration); - // Keep only last 1000 measurements - if (this.connectionMetrics.length > 1000) { - this.connectionMetrics.shift(); - } - } - /** - * Record event processing metrics - */ - recordEventMetrics(time) { - this.eventMetrics.push(time); - // Keep only last 1000 measurements - if (this.eventMetrics.length > 1000) { - this.eventMetrics.shift(); - } - } - /** - * Get connection metrics - */ - getConnectionMetrics() { - const totalConnections = this.connectionMetrics.length; - const activeConnections = this.connections.size; - const averageConnectionDuration = totalConnections > 0 - ? this.connectionMetrics.reduce((sum, duration) => sum + duration, 0) / totalConnections - : 0; - const reconnectCount = Array.from(this.connections.values()).reduce((sum, conn) => sum + conn.reconnectCount, 0); - const reconnectionRate = activeConnections > 0 ? reconnectCount / activeConnections : 0; - const eventThroughput = this.eventMetrics.length; - const errorRate = totalConnections > 0 ? this.errorCount / totalConnections : 0; - return { - totalConnections: totalConnections + activeConnections, - activeConnections, - averageConnectionDuration: Math.round(averageConnectionDuration * 100) / 100, - reconnectionRate: Math.round(reconnectionRate * 10000) / 10000, - eventThroughput, - errorRate: Math.round(errorRate * 10000) / 10000, - }; - } - /** - * Get all active connections - */ - getActiveConnections() { - return Array.from(this.connections.values()); - } - /** - * Get connections for specific user - */ - getUserConnections(userId) { - return Array.from(this.connections.values()).filter((conn) => conn.userId === userId); - } - /** - * Cleanup and destroy - */ - destroy() { - if (this.heartbeatTimer) { - clearInterval(this.heartbeatTimer); - } - // Cleanup broadcasting components - this.progressBroadcaster.destroy(); - this.entityUpdateBroadcaster.destroy(); - // Disconnect all sockets - this.io.disconnectSockets(true); - this.removeAllListeners(); - logger_1.logger.info('Enhanced Socket Manager destroyed'); - } -} -exports.EnhancedSocketManager = EnhancedSocketManager; -//# sourceMappingURL=EnhancedSocketManager.js.map \ No newline at end of file diff --git a/backend/dist/websocket/EnhancedSocketManager.js.map b/backend/dist/websocket/EnhancedSocketManager.js.map deleted file mode 100644 index 9757298..0000000 --- a/backend/dist/websocket/EnhancedSocketManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"EnhancedSocketManager.js","sourceRoot":"","sources":["../../src/websocket/EnhancedSocketManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/websocket/SocketManager.d.ts b/backend/dist/websocket/SocketManager.d.ts deleted file mode 100644 index 9ffa6d0..0000000 --- a/backend/dist/websocket/SocketManager.d.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { Server as SocketIOServer, Socket } from 'socket.io'; -import { Server as HTTPServer } from 'http'; -import { ISocketManager } from './types'; -/** - * WebSocket Manager for Real-Time Communication - * Enterprise-grade WebSocket handling with Redis clustering support - */ -export interface AuthenticatedSocket extends Socket { - userId?: string; - userRole?: 'ceo' | 'manager' | 'staff'; - userEmail?: string; - userName?: string; - channelIds?: string[]; - lastActivity?: Date; -} -export interface SocketEventData { - type: string; - payload: any; - timestamp: Date; - userId?: string; - channelId?: string; - taskId?: string; -} -export interface RoomInfo { - name: string; - type: 'channel' | 'task' | 'user' | 'global'; - memberCount: number; - members: string[]; -} -declare class SocketManager implements ISocketManager { - private io; - private httpServer; - private connectedUsers; - private userChannels; - private channelMembers; - private metrics; - /** - * Initialize WebSocket server - */ - initialize(httpServer: HTTPServer): Promise; - /** - * Setup authentication middleware - */ - private setupAuthentication; - /** - * Setup connection handlers - */ - private setupConnectionHandlers; - /** - * Handle new socket connection - */ - private handleConnection; - /** - * Setup event handlers for individual socket - */ - private setupSocketEventHandlers; - /** - * Handle socket disconnection - */ - private handleDisconnection; - /** - * Handle joining a channel - */ - private handleJoinChannel; - /** - * Handle leaving a channel - */ - private handleLeaveChannel; - /** - * Handle chat messages - */ - private handleChatMessage; - /** - * Handle task updates - */ - private handleTaskUpdate; - /** - * Handle voice commands (for Phase 2) - */ - private handleVoiceCommand; - /** - * Handle typing indicators - */ - private handleTyping; - /** - * Handle presence updates - */ - private handlePresenceUpdate; - /** - * Broadcast user status to relevant users - */ - private broadcastUserStatus; - /** - * Setup error handling - */ - private setupErrorHandling; - /** - * Start metrics collection - */ - private startMetricsCollection; - /** - * Send message to specific user - */ - sendToUser(userId: string, event: string, data: any): boolean; - /** - * Send message to channel - */ - sendToChannel(channelId: string, event: string, data: any): void; - /** - * Broadcast to all connected users - */ - broadcast(event: string, data: any): void; - /** - * Get connected users count - */ - getConnectedUsersCount(): number; - /** - * Get channel member count - */ - getChannelMemberCount(channelId: string): number; - /** - * Get all connected users in channel - */ - getChannelMembers(channelId: string): string[]; - /** - * Get server metrics - */ - getMetrics(): typeof this.metrics; - /** - * Check if user is online - */ - isUserOnline(userId: string): boolean; - /** - * Check if user is connected (alias for interface compatibility) - */ - isUserConnected(userId: string): boolean; - /** - * Emit to user (async version for interface compatibility) - */ - emitToUser(userId: string, event: string, data: any): Promise; - /** - * Get Socket.IO server instance - */ - getServer(): SocketIOServer | null; - /** - * Close WebSocket server - */ - close(): Promise; -} -export { SocketManager }; -export declare const socketManager: SocketManager; -export default socketManager; -//# sourceMappingURL=SocketManager.d.ts.map \ No newline at end of file diff --git a/backend/dist/websocket/SocketManager.d.ts.map b/backend/dist/websocket/SocketManager.d.ts.map deleted file mode 100644 index 9836ca0..0000000 --- a/backend/dist/websocket/SocketManager.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"SocketManager.d.ts","sourceRoot":"","sources":["../../src/websocket/SocketManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/websocket/SocketManager.js b/backend/dist/websocket/SocketManager.js deleted file mode 100644 index c44e1e4..0000000 --- a/backend/dist/websocket/SocketManager.js +++ /dev/null @@ -1,601 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.socketManager = exports.SocketManager = void 0; -const socket_io_1 = require("socket.io"); -const redis_adapter_1 = require("@socket.io/redis-adapter"); -const redis_1 = require("@config/redis"); -const index_1 = require("@config/index"); -const logger_1 = require("@utils/logger"); -const jwt_1 = require("@auth/jwt"); -const index_2 = require("@db/index"); -const errors_1 = require("@utils/errors"); -class SocketManager { - io = null; - httpServer = null; - connectedUsers = new Map(); - userChannels = new Map(); - channelMembers = new Map(); - // Event metrics - metrics = { - connections: 0, - disconnections: 0, - events: 0, - errors: 0, - rooms: 0, - totalUsers: 0, - }; - /** - * Initialize WebSocket server - */ - async initialize(httpServer) { - try { - logger_1.logger.info('Initializing WebSocket server...'); - this.httpServer = httpServer; - // Create Socket.IO server - this.io = new socket_io_1.Server(httpServer, { - path: '/socket.io', - cors: { - origin: index_1.config.api.cors.origin, - credentials: index_1.config.api.cors.credentials, - }, - pingTimeout: 60000, - pingInterval: 25000, - upgradeTimeout: 10000, - maxHttpBufferSize: 1e6, // 1MB - allowEIO3: true, - transports: ['websocket', 'polling'], - }); - // Setup Redis adapter for clustering - if (redis_1.redisManager.isRedisConnected()) { - const pubClient = redis_1.redisManager.getPublisher(); - const subClient = redis_1.redisManager.getSubscriber(); - this.io.adapter((0, redis_adapter_1.createAdapter)(pubClient, subClient)); - logger_1.logger.info('Redis adapter configured for Socket.IO clustering'); - } - // Setup authentication middleware - this.setupAuthentication(); - // Setup connection handlers - this.setupConnectionHandlers(); - // Setup error handling - this.setupErrorHandling(); - // Start metrics collection - this.startMetricsCollection(); - logger_1.logger.info('WebSocket server initialized'); - } - catch (error) { - logger_1.logger.error({ error }, 'Failed to initialize WebSocket server'); - throw new errors_1.WebSocketError('WebSocket initialization failed'); - } - } - /** - * Setup authentication middleware - */ - setupAuthentication() { - if (!this.io) - return; - this.io.use(async (socket, next) => { - try { - // Extract token from auth header or query parameter - const token = socket.handshake.auth?.token || socket.handshake.query?.token; - if (!token) { - logger_1.loggers.websocket.warn?.({ - socketId: socket.id, - ip: socket.handshake.address, - }, 'WebSocket connection attempt without token'); - return next(new errors_1.AuthenticationError('Authentication token required')); - } - // Verify JWT token - const payload = await jwt_1.jwtService.verifyAccessToken(token); - // Get user details - const user = await index_2.userRepository.findById(payload.userId); - if (!user || user.deleted_at) { - logger_1.loggers.websocket.warn?.({ - socketId: socket.id, - userId: payload.userId, - ip: socket.handshake.address, - }, 'WebSocket authentication failed - user not found'); - return next(new errors_1.AuthenticationError('User account not found')); - } - // Attach user information to socket - socket.userId = user.id; - socket.userRole = user.role; - socket.userEmail = user.email; - socket.userName = user.name; - socket.lastActivity = new Date(); - // Get user's channels for room management - // Note: This would need to be implemented in a channel service - socket.channelIds = []; // TODO: Get from channel service - logger_1.loggers.websocket.info?.({ - socketId: socket.id, - userId: user.id, - userRole: user.role, - ip: socket.handshake.address, - }, 'WebSocket authentication successful'); - next(); - } - catch (error) { - logger_1.loggers.websocket.error?.({ - error, - socketId: socket.id, - ip: socket.handshake.address, - }, 'WebSocket authentication error'); - if (error instanceof errors_1.AuthenticationError) { - next(error); - } - else { - next(new errors_1.AuthenticationError('Authentication failed')); - } - } - }); - } - /** - * Setup connection handlers - */ - setupConnectionHandlers() { - if (!this.io) - return; - this.io.on('connection', (socket) => { - this.handleConnection(socket); - }); - } - /** - * Handle new socket connection - */ - async handleConnection(socket) { - try { - this.metrics.connections++; - this.metrics.totalUsers = this.connectedUsers.size + 1; - // Store connected user - if (socket.userId) { - this.connectedUsers.set(socket.userId, socket); - // Update last active - index_2.userRepository.updateLastActive(socket.userId).catch((error) => { - logger_1.loggers.websocket.warn?.({ error, userId: socket.userId }, 'Failed to update user last active'); - }); - } - // Join user to their personal room - if (socket.userId) { - await socket.join(`user:${socket.userId}`); - } - // Join user to their channels - if (socket.channelIds) { - for (const channelId of socket.channelIds) { - await socket.join(`channel:${channelId}`); - // Track channel membership - if (!this.channelMembers.has(channelId)) { - this.channelMembers.set(channelId, new Set()); - } - this.channelMembers.get(channelId).add(socket.userId); - } - } - logger_1.loggers.websocket.info?.({ - socketId: socket.id, - userId: socket.userId, - userRole: socket.userRole, - channelCount: socket.channelIds?.length || 0, - totalConnections: this.metrics.connections, - }, 'WebSocket client connected'); - // Setup event handlers for this socket - this.setupSocketEventHandlers(socket); - // Emit connection success - socket.emit('connected', { - userId: socket.userId, - socketId: socket.id, - timestamp: new Date().toISOString(), - }); - // Notify others about user online status - if (socket.userId) { - this.broadcastUserStatus(socket.userId, 'online'); - } - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, socketId: socket.id }, 'Error handling socket connection'); - socket.disconnect(true); - } - } - /** - * Setup event handlers for individual socket - */ - setupSocketEventHandlers(socket) { - // Handle disconnection - socket.on('disconnect', (reason) => { - this.handleDisconnection(socket, reason); - }); - // Handle joining channels - socket.on('join_channel', async (data) => { - await this.handleJoinChannel(socket, data.channelId); - }); - // Handle leaving channels - socket.on('leave_channel', async (data) => { - await this.handleLeaveChannel(socket, data.channelId); - }); - // Handle chat messages - socket.on('chat_message', async (data) => { - await this.handleChatMessage(socket, data); - }); - // Handle task updates - socket.on('task_update', async (data) => { - await this.handleTaskUpdate(socket, data); - }); - // Handle voice command events (for Phase 2) - socket.on('voice_command', async (data) => { - await this.handleVoiceCommand(socket, data); - }); - // Handle typing indicators - socket.on('typing_start', (data) => { - this.handleTyping(socket, data.channelId, true); - }); - socket.on('typing_stop', (data) => { - this.handleTyping(socket, data.channelId, false); - }); - // Handle presence updates - socket.on('presence_update', (data) => { - this.handlePresenceUpdate(socket, data.status); - }); - // Generic event handler with rate limiting - socket.use(([event, ...args], next) => { - this.metrics.events++; - // Update last activity - socket.lastActivity = new Date(); - // TODO: Implement rate limiting per user/socket - next(); - }); - } - /** - * Handle socket disconnection - */ - handleDisconnection(socket, reason) { - this.metrics.disconnections++; - // Remove from connected users - if (socket.userId) { - this.connectedUsers.delete(socket.userId); - // Remove from channel membership tracking - for (const [channelId, members] of this.channelMembers.entries()) { - if (members.has(socket.userId)) { - members.delete(socket.userId); - if (members.size === 0) { - this.channelMembers.delete(channelId); - } - } - } - // Broadcast user offline status - this.broadcastUserStatus(socket.userId, 'offline'); - } - this.metrics.totalUsers = this.connectedUsers.size; - logger_1.loggers.websocket.info?.({ - socketId: socket.id, - userId: socket.userId, - reason, - totalConnections: this.connectedUsers.size, - }, 'WebSocket client disconnected'); - } - /** - * Handle joining a channel - */ - async handleJoinChannel(socket, channelId) { - try { - // TODO: Verify user has access to channel - // const hasAccess = await channelService.canUserAccess(channelId, socket.userId); - // if (!hasAccess) { - // socket.emit('error', { message: 'Access denied to channel' }); - // return; - // } - await socket.join(`channel:${channelId}`); - // Track membership - if (!this.channelMembers.has(channelId)) { - this.channelMembers.set(channelId, new Set()); - } - this.channelMembers.get(channelId).add(socket.userId); - // Notify channel members - socket.to(`channel:${channelId}`).emit('user_joined_channel', { - channelId, - userId: socket.userId, - userName: socket.userName, - timestamp: new Date().toISOString(), - }); - // Acknowledge join - socket.emit('channel_joined', { - channelId, - memberCount: this.channelMembers.get(channelId)?.size || 0, - }); - logger_1.loggers.websocket.debug?.({ - socketId: socket.id, - userId: socket.userId, - channelId, - }, 'User joined channel'); - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, socketId: socket.id, channelId }, 'Failed to join channel'); - socket.emit('error', { message: 'Failed to join channel' }); - } - } - /** - * Handle leaving a channel - */ - async handleLeaveChannel(socket, channelId) { - try { - await socket.leave(`channel:${channelId}`); - // Update membership tracking - const members = this.channelMembers.get(channelId); - if (members) { - members.delete(socket.userId); - if (members.size === 0) { - this.channelMembers.delete(channelId); - } - } - // Notify channel members - socket.to(`channel:${channelId}`).emit('user_left_channel', { - channelId, - userId: socket.userId, - userName: socket.userName, - timestamp: new Date().toISOString(), - }); - socket.emit('channel_left', { channelId }); - logger_1.loggers.websocket.debug?.({ - socketId: socket.id, - userId: socket.userId, - channelId, - }, 'User left channel'); - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, socketId: socket.id, channelId }, 'Failed to leave channel'); - } - } - /** - * Handle chat messages - */ - async handleChatMessage(socket, data) { - try { - // TODO: Validate message content and user permissions - const messageEvent = { - type: 'chat_message', - payload: { - channelId: data.channelId, - message: data.message, - messageType: data.type || 'text', - userId: socket.userId || 'unknown', - userName: socket.userName || 'Unknown User', - userRole: socket.userRole || 'staff', - }, - timestamp: new Date(), - userId: socket.userId || 'unknown', - channelId: data.channelId, - }; - // Broadcast to channel members - this.io?.to(`channel:${data.channelId}`).emit('chat_message', messageEvent.payload); - // TODO: Store message in database - // await messageService.createMessage(messageEvent.payload); - logger_1.loggers.websocket.debug?.({ - userId: socket.userId, - channelId: data.channelId, - messageType: data.type, - }, 'Chat message sent'); - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, userId: socket.userId }, 'Failed to handle chat message'); - socket.emit('error', { message: 'Failed to send message' }); - } - } - /** - * Handle task updates - */ - async handleTaskUpdate(socket, data) { - try { - // TODO: Validate user permissions for task updates - const taskEvent = { - type: 'task_update', - payload: { - taskId: data.taskId, - action: data.action, - updates: data.updates, - userId: socket.userId ?? 'unknown', - userName: socket.userName, - }, - timestamp: new Date(), - userId: socket.userId ?? 'unknown', - taskId: data.taskId, - }; - // Broadcast to task watchers and assignees - // TODO: Get task details to determine who should receive updates - this.io?.emit('task_update', taskEvent.payload); - logger_1.loggers.websocket.debug?.({ - userId: socket.userId, - taskId: data.taskId, - action: data.action, - }, 'Task update broadcasted'); - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, userId: socket.userId }, 'Failed to handle task update'); - socket.emit('error', { message: 'Failed to update task' }); - } - } - /** - * Handle voice commands (for Phase 2) - */ - async handleVoiceCommand(socket, data) { - try { - // Only CEO can use voice commands - if (socket.userRole !== 'ceo') { - socket.emit('error', { message: 'Voice commands are only available to CEO' }); - return; - } - // TODO: Process voice command - // This will be implemented in Phase 2 with OpenAI Whisper integration - logger_1.loggers.websocket.info?.({ - userId: socket.userId, - channelId: data.channelId, - hasAudioData: !!data.audioData, - hasTextCommand: !!data.command, - }, 'Voice command received'); - socket.emit('voice_command_received', { - status: 'processing', - timestamp: new Date().toISOString(), - }); - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, userId: socket.userId }, 'Failed to handle voice command'); - socket.emit('error', { message: 'Failed to process voice command' }); - } - } - /** - * Handle typing indicators - */ - handleTyping(socket, channelId, isTyping) { - socket.to(`channel:${channelId}`).emit('typing_indicator', { - channelId, - userId: socket.userId, - userName: socket.userName, - isTyping, - timestamp: new Date().toISOString(), - }); - } - /** - * Handle presence updates - */ - handlePresenceUpdate(socket, status) { - this.broadcastUserStatus(socket.userId, status); - } - /** - * Broadcast user status to relevant users - */ - broadcastUserStatus(userId, status) { - // TODO: Determine which users should receive this status update - // For now, broadcast to all connected users - this.io?.emit('user_status_update', { - userId, - status, - timestamp: new Date().toISOString(), - }); - } - /** - * Setup error handling - */ - setupErrorHandling() { - if (!this.io) - return; - this.io.on('error', (error) => { - this.metrics.errors++; - logger_1.loggers.websocket.error?.({ error }, 'Socket.IO server error'); - }); - this.io.engine.on('connection_error', (err) => { - this.metrics.errors++; - logger_1.loggers.websocket.error?.(err, 'Socket.IO connection error'); - }); - } - /** - * Start metrics collection - */ - startMetricsCollection() { - // Log metrics every 5 minutes - setInterval(() => { - const roomCount = this.io?.sockets.adapter.rooms.size || 0; - this.metrics.rooms = roomCount; - logger_1.loggers.websocket.info?.({ - metrics: this.metrics, - roomCount, - connectedUsers: this.connectedUsers.size, - channels: this.channelMembers.size, - }, 'WebSocket server metrics'); - }, 5 * 60 * 1000); - } - // Public API methods - /** - * Send message to specific user - */ - sendToUser(userId, event, data) { - const socket = this.connectedUsers.get(userId); - if (socket) { - socket.emit(event, data); - return true; - } - // Try sending to user room if socket not directly available - if (this.io) { - this.io.to(`user:${userId}`).emit(event, data); - return true; - } - return false; - } - /** - * Send message to channel - */ - sendToChannel(channelId, event, data) { - this.io?.to(`channel:${channelId}`).emit(event, data); - } - /** - * Broadcast to all connected users - */ - broadcast(event, data) { - this.io?.emit(event, data); - } - /** - * Get connected users count - */ - getConnectedUsersCount() { - return this.connectedUsers.size; - } - /** - * Get channel member count - */ - getChannelMemberCount(channelId) { - return this.channelMembers.get(channelId)?.size || 0; - } - /** - * Get all connected users in channel - */ - getChannelMembers(channelId) { - return Array.from(this.channelMembers.get(channelId) || []); - } - /** - * Get server metrics - */ - getMetrics() { - return { ...this.metrics }; - } - /** - * Check if user is online - */ - isUserOnline(userId) { - return this.connectedUsers.has(userId); - } - /** - * Check if user is connected (alias for interface compatibility) - */ - isUserConnected(userId) { - return this.isUserOnline(userId); - } - /** - * Emit to user (async version for interface compatibility) - */ - async emitToUser(userId, event, data) { - this.sendToUser(userId, event, data); - } - /** - * Get Socket.IO server instance - */ - getServer() { - return this.io; - } - /** - * Close WebSocket server - */ - async close() { - if (this.io) { - logger_1.logger.info('Closing WebSocket server...'); - // Disconnect all clients - this.io.disconnectSockets(true); - // Close server - this.io.close(); - this.io = null; - this.httpServer = null; - this.connectedUsers.clear(); - this.channelMembers.clear(); - this.userChannels.clear(); - logger_1.logger.info('WebSocket server closed'); - } - } -} -exports.SocketManager = SocketManager; -// Export singleton instance -exports.socketManager = new SocketManager(); -exports.default = exports.socketManager; -//# sourceMappingURL=SocketManager.js.map \ No newline at end of file diff --git a/backend/dist/websocket/SocketManager.js.map b/backend/dist/websocket/SocketManager.js.map deleted file mode 100644 index 5a0c353..0000000 --- a/backend/dist/websocket/SocketManager.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"SocketManager.js","sourceRoot":"","sources":["../../src/websocket/SocketManager.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/dist/websocket/index.d.ts b/backend/dist/websocket/index.d.ts deleted file mode 100644 index 24b1fc0..0000000 --- a/backend/dist/websocket/index.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * WebSocket module exports - * Real-time communication infrastructure for the CEO Communication Platform - */ -import { WebSocketUtils, EventBuilder } from './utils'; -export { socketManager, SocketManager } from './SocketManager'; -export { WebSocketUtils, EventBuilder } from './utils'; -export * from './types'; -declare const _default: { - WebSocketUtils: typeof WebSocketUtils; - EventBuilder: typeof EventBuilder; -}; -export default _default; -//# sourceMappingURL=index.d.ts.map \ No newline at end of file diff --git a/backend/dist/websocket/index.d.ts.map b/backend/dist/websocket/index.d.ts.map deleted file mode 100644 index c41b57e..0000000 --- a/backend/dist/websocket/index.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/websocket/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvD,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvD,cAAc,SAAS,CAAC;;;;;AAGxB,wBAGE"} \ No newline at end of file diff --git a/backend/dist/websocket/index.js b/backend/dist/websocket/index.js deleted file mode 100644 index d446239..0000000 --- a/backend/dist/websocket/index.js +++ /dev/null @@ -1,35 +0,0 @@ -"use strict"; -/** - * WebSocket module exports - * Real-time communication infrastructure for the CEO Communication Platform - */ -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __exportStar = (this && this.__exportStar) || function(m, exports) { - for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p); -}; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventBuilder = exports.WebSocketUtils = exports.SocketManager = exports.socketManager = void 0; -const utils_1 = require("./utils"); -var SocketManager_1 = require("./SocketManager"); -Object.defineProperty(exports, "socketManager", { enumerable: true, get: function () { return SocketManager_1.socketManager; } }); -Object.defineProperty(exports, "SocketManager", { enumerable: true, get: function () { return SocketManager_1.SocketManager; } }); -var utils_2 = require("./utils"); -Object.defineProperty(exports, "WebSocketUtils", { enumerable: true, get: function () { return utils_2.WebSocketUtils; } }); -Object.defineProperty(exports, "EventBuilder", { enumerable: true, get: function () { return utils_2.EventBuilder; } }); -__exportStar(require("./types"), exports); -// Re-export for easy access - only public API -exports.default = { - WebSocketUtils: utils_1.WebSocketUtils, - EventBuilder: utils_1.EventBuilder, -}; -//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/backend/dist/websocket/index.js.map b/backend/dist/websocket/index.js.map deleted file mode 100644 index 563436f..0000000 --- a/backend/dist/websocket/index.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/websocket/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;;AAGH,mCAAuD;AAEvD,iDAA+D;AAAtD,8GAAA,aAAa,OAAA;AAAE,8GAAA,aAAa,OAAA;AACrC,iCAAuD;AAA9C,uGAAA,cAAc,OAAA;AAAE,qGAAA,YAAY,OAAA;AACrC,0CAAwB;AAExB,8CAA8C;AAC9C,kBAAe;IACb,cAAc,EAAd,sBAAc;IACd,YAAY,EAAZ,oBAAY;CACb,CAAC"} \ No newline at end of file diff --git a/backend/dist/websocket/types.d.ts b/backend/dist/websocket/types.d.ts deleted file mode 100644 index 32b01bc..0000000 --- a/backend/dist/websocket/types.d.ts +++ /dev/null @@ -1,296 +0,0 @@ -/** - * WebSocket Event Types and Interfaces - * Comprehensive type definitions for real-time communication - */ -export interface BaseSocketEvent { - type: string; - timestamp: string; - userId: string; - userName: string; - userRole: 'ceo' | 'manager' | 'staff'; -} -export interface ConnectionEvent extends BaseSocketEvent { - type: 'user_connected' | 'user_disconnected'; - socketId: string; - channelIds: string[]; -} -export interface UserStatusEvent extends BaseSocketEvent { - type: 'user_status_update'; - status: 'online' | 'away' | 'busy' | 'offline'; - lastSeen?: string; -} -export interface ChannelEvent extends BaseSocketEvent { - channelId: string; -} -export interface ChannelJoinEvent extends ChannelEvent { - type: 'user_joined_channel' | 'user_left_channel'; - memberCount: number; -} -export interface ChannelUpdateEvent extends ChannelEvent { - type: 'channel_updated' | 'channel_created' | 'channel_deleted'; - updates: { - name?: string; - description?: string; - privacy?: 'public' | 'private' | 'restricted'; - settings?: Record; - }; -} -export interface ChatMessageEvent extends ChannelEvent { - type: 'chat_message'; - messageId: string; - message: string; - messageType: 'text' | 'file' | 'voice' | 'system'; - attachments?: Array<{ - id: string; - name: string; - type: string; - url: string; - size: number; - }>; - replyTo?: string; - edited?: boolean; - editedAt?: string; -} -export interface MessageUpdateEvent extends ChannelEvent { - type: 'message_updated' | 'message_deleted'; - messageId: string; - updates?: Partial; -} -export interface TypingEvent extends ChannelEvent { - type: 'typing_indicator'; - isTyping: boolean; -} -export interface TaskEvent extends BaseSocketEvent { - taskId: string; - channelId?: string; -} -export interface TaskUpdateEvent extends TaskEvent { - type: 'task_created' | 'task_updated' | 'task_deleted' | 'task_completed'; - task: { - id: string; - title: string; - description?: string; - status: 'pending' | 'in_progress' | 'review' | 'completed' | 'cancelled' | 'on_hold'; - priority: 'low' | 'medium' | 'high' | 'urgent' | 'critical'; - assignedTo: string[]; - dueDate?: string; - progress: number; - tags: string[]; - }; - action: 'create' | 'update' | 'delete' | 'assign' | 'unassign' | 'complete' | 'reopen'; - changes?: Record; -} -export interface TaskAssignmentEvent extends TaskEvent { - type: 'task_assigned' | 'task_unassigned'; - assignedTo: string[]; - assignedBy: string; - previousAssignees?: string[]; -} -export interface TaskCommentEvent extends TaskEvent { - type: 'task_comment_added' | 'task_comment_updated' | 'task_comment_deleted'; - commentId: string; - comment: string; - attachments?: Array<{ - id: string; - name: string; - url: string; - }>; -} -export interface VoiceCommandEvent extends BaseSocketEvent { - type: 'voice_command_received' | 'voice_command_processed' | 'voice_command_failed'; - commandId: string; - channelId?: string; - audioData?: string; - transcription?: string; - intent?: { - action: string; - entities: Record; - confidence: number; - }; - result?: { - success: boolean; - message: string; - actions: Array<{ - type: string; - target: string; - data: any; - }>; - }; - error?: string; -} -export interface NotificationEvent extends BaseSocketEvent { - type: 'notification'; - notificationId: string; - title: string; - message: string; - category: 'task' | 'channel' | 'mention' | 'system' | 'voice'; - priority: 'low' | 'medium' | 'high' | 'urgent'; - actionUrl?: string; - actionText?: string; - data?: Record; - readAt?: string; - expiresAt?: string; -} -export interface AnalyticsEvent extends BaseSocketEvent { - type: 'analytics_update'; - metric: string; - value: number | string | Record; - filters?: Record; - timeRange?: { - start: string; - end: string; - }; -} -export interface ErrorEvent { - type: 'error'; - code: string; - message: string; - details?: Record; - timestamp: string; - retryable: boolean; -} -export interface SystemEvent extends BaseSocketEvent { - type: 'system_announcement' | 'system_maintenance' | 'system_update'; - title: string; - message: string; - level: 'info' | 'warning' | 'critical'; - scheduledAt?: string; - affectedServices?: string[]; -} -export type SocketEvent = ConnectionEvent | UserStatusEvent | ChannelJoinEvent | ChannelUpdateEvent | ChatMessageEvent | MessageUpdateEvent | TypingEvent | TaskUpdateEvent | TaskAssignmentEvent | TaskCommentEvent | VoiceCommandEvent | NotificationEvent | AnalyticsEvent | ErrorEvent | SystemEvent; -export interface ClientToServerEvents { - authenticate: (token: string) => void; - join_channel: (data: { - channelId: string; - }) => void; - leave_channel: (data: { - channelId: string; - }) => void; - chat_message: (data: { - channelId: string; - message: string; - type?: 'text' | 'file' | 'voice'; - replyTo?: string; - attachments?: Array<{ - name: string; - type: string; - data: string; - }>; - }) => void; - task_update: (data: { - taskId: string; - updates: Record; - action: 'create' | 'update' | 'delete' | 'assign' | 'complete'; - }) => void; - task_comment: (data: { - taskId: string; - comment: string; - attachments?: Array<{ - name: string; - data: string; - }>; - }) => void; - voice_command: (data: { - audioData?: string; - command?: string; - channelId?: string; - }) => void; - typing_start: (data: { - channelId: string; - }) => void; - typing_stop: (data: { - channelId: string; - }) => void; - presence_update: (data: { - status: 'online' | 'away' | 'busy' | 'offline'; - }) => void; - mark_notification_read: (data: { - notificationId: string; - }) => void; - subscribe_to_analytics: (data: { - metrics: string[]; - filters?: Record; - }) => void; -} -export interface ServerToClientEvents { - connected: (data: { - userId: string; - socketId: string; - timestamp: string; - }) => void; - channel_joined: (data: { - channelId: string; - memberCount: number; - }) => void; - channel_left: (data: { - channelId: string; - }) => void; - user_joined_channel: (data: ChannelJoinEvent) => void; - user_left_channel: (data: ChannelJoinEvent) => void; - channel_updated: (data: ChannelUpdateEvent) => void; - chat_message: (data: ChatMessageEvent) => void; - message_updated: (data: MessageUpdateEvent) => void; - message_deleted: (data: MessageUpdateEvent) => void; - typing_indicator: (data: TypingEvent) => void; - task_update: (data: TaskUpdateEvent) => void; - task_assigned: (data: TaskAssignmentEvent) => void; - task_comment_added: (data: TaskCommentEvent) => void; - voice_command_received: (data: { - status: 'processing' | 'completed' | 'failed'; - timestamp: string; - commandId?: string; - }) => void; - voice_command_result: (data: VoiceCommandEvent) => void; - user_status_update: (data: UserStatusEvent) => void; - notification: (data: NotificationEvent) => void; - analytics_update: (data: AnalyticsEvent) => void; - system_announcement: (data: SystemEvent) => void; - error: (data: ErrorEvent) => void; -} -export type RoomType = 'user' | 'channel' | 'task' | 'global' | 'voice_session'; -export interface SocketPermissions { - canJoinChannel: (channelId: string, userId: string) => Promise; - canSendMessage: (channelId: string, userId: string) => Promise; - canUpdateTask: (taskId: string, userId: string, action: string) => Promise; - canUseVoiceCommands: (userId: string, userRole: string) => boolean; - canViewAnalytics: (userId: string, userRole: string) => boolean; -} -export interface RateLimitConfig { - messages: { - maxPerMinute: number; - maxPerHour: number; - }; - voiceCommands: { - maxPerMinute: number; - maxPerHour: number; - }; - taskUpdates: { - maxPerMinute: number; - }; -} -export interface SocketMiddleware { - name: string; - handler: (socket: any, next: (err?: Error) => void) => void; -} -export interface SocketEventHandler { - event: string; - handler: (socket: any, data: T) => Promise | void; - permissions?: string[]; - rateLimit?: { - maxRequests: number; - windowMs: number; - }; -} -/** - * Common interface for socket managers - * Used by broadcasting components to interact with socket managers - */ -export interface ISocketManager { - on?(event: string, listener: (...args: any[]) => void): any; - emit?(event: string, ...args: any[]): boolean; - isUserConnected(userId: string): boolean; - emitToUser(userId: string, event: string, data: any): Promise; - emitToChannel?(channelId: string, event: string, data: any): Promise; - getUserConnections?(userId: string): any[]; -} -//# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/backend/dist/websocket/types.d.ts.map b/backend/dist/websocket/types.d.ts.map deleted file mode 100644 index 1ddcce8..0000000 --- a/backend/dist/websocket/types.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/websocket/types.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,KAAK,GAAG,SAAS,GAAG,OAAO,CAAC;CACvC;AAGD,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,IAAI,EAAE,gBAAgB,GAAG,mBAAmB,CAAC;IAC7C,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,EAAE,CAAC;CACtB;AAED,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,IAAI,EAAE,oBAAoB,CAAC;IAC3B,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC/C,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAGD,MAAM,WAAW,YAAa,SAAQ,eAAe;IACnD,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,IAAI,EAAE,qBAAqB,GAAG,mBAAmB,CAAC;IAClD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;IAChE,OAAO,EAAE;QACP,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,QAAQ,GAAG,SAAS,GAAG,YAAY,CAAC;QAC9C,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAChC,CAAC;CACH;AAGD,MAAM,WAAW,gBAAiB,SAAQ,YAAY;IACpD,IAAI,EAAE,cAAc,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;IAClD,WAAW,CAAC,EAAE,KAAK,CAAC;QAClB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;KACd,CAAC,CAAC;IACH,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAmB,SAAQ,YAAY;IACtD,IAAI,EAAE,iBAAiB,GAAG,iBAAiB,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,WAAY,SAAQ,YAAY;IAC/C,IAAI,EAAE,kBAAkB,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAGD,MAAM,WAAW,SAAU,SAAQ,eAAe;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAgB,SAAQ,SAAS;IAChD,IAAI,EAAE,cAAc,GAAG,cAAc,GAAG,cAAc,GAAG,gBAAgB,CAAC;IAC1E,IAAI,EAAE;QACJ,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,GAAG,SAAS,CAAC;QACrF,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,UAAU,CAAC;QAC5D,UAAU,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,IAAI,EAAE,MAAM,EAAE,CAAC;KAChB,CAAC;IACF,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,GAAG,QAAQ,CAAC;IACvF,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC/B;AAED,MAAM,WAAW,mBAAoB,SAAQ,SAAS;IACpD,IAAI,EAAE,eAAe,GAAG,iBAAiB,CAAC;IAC1C,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,IAAI,EAAE,oBAAoB,GAAG,sBAAsB,GAAG,sBAAsB,CAAC;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,KAAK,CAAC;QAClB,EAAE,EAAE,MAAM,CAAC;QACX,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;KACb,CAAC,CAAC;CACJ;AAGD,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,IAAI,EAAE,wBAAwB,GAAG,yBAAyB,GAAG,sBAAsB,CAAC;IACpF,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE;QACP,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC9B,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,MAAM,CAAC,EAAE;QACP,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,KAAK,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,IAAI,EAAE,GAAG,CAAC;SACX,CAAC,CAAC;KACJ,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAGD,MAAM,WAAW,iBAAkB,SAAQ,eAAe;IACxD,IAAI,EAAE,cAAc,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC9D,QAAQ,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAGD,MAAM,WAAW,cAAe,SAAQ,eAAe;IACrD,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,SAAS,CAAC,EAAE;QACV,KAAK,EAAE,MAAM,CAAC;QACd,GAAG,EAAE,MAAM,CAAC;KACb,CAAC;CACH;AAGD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,OAAO,CAAC;CACpB;AAGD,MAAM,WAAW,WAAY,SAAQ,eAAe;IAClD,IAAI,EAAE,qBAAqB,GAAG,oBAAoB,GAAG,eAAe,CAAC;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IACvC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC7B;AAGD,MAAM,MAAM,WAAW,GACnB,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,kBAAkB,GAClB,gBAAgB,GAChB,kBAAkB,GAClB,WAAW,GACX,eAAe,GACf,mBAAmB,GACnB,gBAAgB,GAChB,iBAAiB,GACjB,iBAAiB,GACjB,cAAc,GACd,UAAU,GACV,WAAW,CAAC;AAGhB,MAAM,WAAW,oBAAoB;IAEnC,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAGtC,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACpD,aAAa,EAAE,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAGrD,YAAY,EAAE,CAAC,IAAI,EAAE;QACnB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;QACjC,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,WAAW,CAAC,EAAE,KAAK,CAAC;YAClB,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;SACd,CAAC,CAAC;KACJ,KAAK,IAAI,CAAC;IAGX,WAAW,EAAE,CAAC,IAAI,EAAE;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC7B,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,GAAG,UAAU,CAAC;KAChE,KAAK,IAAI,CAAC;IAEX,YAAY,EAAE,CAAC,IAAI,EAAE;QACnB,MAAM,EAAE,MAAM,CAAC;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,CAAC,EAAE,KAAK,CAAC;YAClB,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,EAAE,MAAM,CAAC;SACd,CAAC,CAAC;KACJ,KAAK,IAAI,CAAC;IAGX,aAAa,EAAE,CAAC,IAAI,EAAE;QACpB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,KAAK,IAAI,CAAC;IAGX,YAAY,EAAE,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACpD,WAAW,EAAE,CAAC,IAAI,EAAE;QAAE,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IACnD,eAAe,EAAE,CAAC,IAAI,EAAE;QACtB,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;KAC/C,KAAK,IAAI,CAAC;IAGX,sBAAsB,EAAE,CAAC,IAAI,EAAE;QAAE,cAAc,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC;IAGnE,sBAAsB,EAAE,CAAC,IAAI,EAAE;QAC7B,OAAO,EAAE,MAAM,EAAE,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;KAC9B,KAAK,IAAI,CAAC;CACZ;AAGD,MAAM,WAAW,oBAAoB;IAEnC,SAAS,EAAE,CAAC,IAAI,EAAE;QAChB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,MAAM,CAAC;QACjB,SAAS,EAAE,MAAM,CAAC;KACnB,KAAK,IAAI,CAAC;IAGX,cAAc,EAAE,CAAC,IAAI,EAAE;QACrB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;KACrB,KAAK,IAAI,CAAC;IAEX,YAAY,EAAE,CAAC,IAAI,EAAE;QACnB,SAAS,EAAE,MAAM,CAAC;KACnB,KAAK,IAAI,CAAC;IAEX,mBAAmB,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACtD,iBAAiB,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IACpD,eAAe,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IAGpD,YAAY,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAC/C,eAAe,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACpD,eAAe,EAAE,CAAC,IAAI,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACpD,gBAAgB,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IAG9C,WAAW,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IAC7C,aAAa,EAAE,CAAC,IAAI,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACnD,kBAAkB,EAAE,CAAC,IAAI,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAGrD,sBAAsB,EAAE,CAAC,IAAI,EAAE;QAC7B,MAAM,EAAE,YAAY,GAAG,WAAW,GAAG,QAAQ,CAAC;QAC9C,SAAS,EAAE,MAAM,CAAC;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,KAAK,IAAI,CAAC;IACX,oBAAoB,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAGxD,kBAAkB,EAAE,CAAC,IAAI,EAAE,eAAe,KAAK,IAAI,CAAC;IAGpD,YAAY,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAGhD,gBAAgB,EAAE,CAAC,IAAI,EAAE,cAAc,KAAK,IAAI,CAAC;IAGjD,mBAAmB,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IAGjD,KAAK,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;CACnC;AAGD,MAAM,MAAM,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,QAAQ,GAAG,eAAe,CAAC;AAGhF,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxE,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACxE,aAAa,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACpF,mBAAmB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IACnE,gBAAgB,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;CACjE;AAGD,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE;QACR,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,aAAa,EAAE;QACb,YAAY,EAAE,MAAM,CAAC;QACrB,UAAU,EAAE,MAAM,CAAC;KACpB,CAAC;IACF,WAAW,EAAE;QACX,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH;AAGD,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,GAAG,CAAC,EAAE,KAAK,KAAK,IAAI,KAAK,IAAI,CAAC;CAC7D;AAED,MAAM,WAAW,kBAAkB,CAAC,CAAC,GAAG,GAAG;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACxD,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,CAAC,EAAE;QACV,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,EAAE,MAAM,CAAC;KAClB,CAAC;CACH;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAE7B,EAAE,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,GAAG,GAAG,CAAC;IAC5D,IAAI,CAAC,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IAG9C,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC;IACzC,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAGpE,aAAa,CAAC,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E,kBAAkB,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,GAAG,EAAE,CAAC;CAC5C"} \ No newline at end of file diff --git a/backend/dist/websocket/types.js b/backend/dist/websocket/types.js deleted file mode 100644 index 73a7ef3..0000000 --- a/backend/dist/websocket/types.js +++ /dev/null @@ -1,8 +0,0 @@ -"use strict"; -/** - * WebSocket Event Types and Interfaces - * Comprehensive type definitions for real-time communication - */ -Object.defineProperty(exports, "__esModule", { value: true }); -// All types are exported individually, no default export needed for types -//# sourceMappingURL=types.js.map \ No newline at end of file diff --git a/backend/dist/websocket/types.js.map b/backend/dist/websocket/types.js.map deleted file mode 100644 index 8ecc736..0000000 --- a/backend/dist/websocket/types.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/websocket/types.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAgYH,0EAA0E"} \ No newline at end of file diff --git a/backend/dist/websocket/utils.d.ts b/backend/dist/websocket/utils.d.ts deleted file mode 100644 index 6faa653..0000000 --- a/backend/dist/websocket/utils.d.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { SocketEvent, NotificationEvent, TaskUpdateEvent, ChatMessageEvent, UserStatusEvent } from './types'; -/** - * WebSocket utility functions for real-time communication - * High-level helpers for common WebSocket operations - */ -export declare class WebSocketUtils { - /** - * Send notification to user - */ - static notifyUser(userId: string, notification: Omit): Promise; - /** - * Broadcast task update to relevant users - */ - static broadcastTaskUpdate(taskUpdate: Omit): Promise; - /** - * Broadcast message to channel - */ - static broadcastChannelMessage(message: Omit): Promise; - /** - * Update user online status - */ - static updateUserStatus(userId: string, status: 'online' | 'away' | 'busy' | 'offline'): Promise; - /** - * Send system announcement to all users - */ - static sendSystemAnnouncement(announcement: { - title: string; - message: string; - level: 'info' | 'warning' | 'critical'; - scheduledAt?: Date; - affectedServices?: string[]; - }): Promise; - /** - * Send event to all users in a channel - */ - static sendToChannel(channelId: string, eventType: string, data: any): Promise; - /** - * Cache events for offline users - */ - private static cacheEventForOfflineUsers; - /** - * Get offline events for user when they come online - */ - static getOfflineEventsForUser(userId: string): Promise; - /** - * Check if user is online - */ - static isUserOnline(userId: string): boolean; - /** - * Get online users count - */ - static getOnlineUsersCount(): number; - /** - * Get channel member count - */ - static getChannelMemberCount(channelId: string): number; - /** - * Get active channel members - */ - static getChannelMembers(channelId: string): string[]; - /** - * Send typing indicator - */ - static sendTypingIndicator(channelId: string, userId: string, isTyping: boolean): void; - /** - * Get WebSocket server metrics - */ - static getMetrics(): { - connections: number; - disconnections: number; - events: number; - errors: number; - rooms: number; - totalUsers: number; - }; - /** - * Send bulk notifications - */ - static sendBulkNotifications(userIds: string[], notification: Omit): Promise<{ - sent: number; - failed: number; - }>; - /** - * Create and send notification - */ - static createAndSendNotification(userId: string, notification: { - title: string; - message: string; - category: 'task' | 'channel' | 'mention' | 'system' | 'voice'; - priority?: 'low' | 'medium' | 'high' | 'urgent'; - actionUrl?: string; - actionText?: string; - data?: Record; - expiresAt?: Date; - }): Promise; - /** - * Voice command helpers (for Phase 2) - */ - static sendVoiceCommandResult(userId: string, commandId: string, result: { - success: boolean; - message: string; - actions?: Array<{ - type: string; - target: string; - data: any; - }>; - }): Promise; - /** - * Analytics update helpers (for Phase 4) - */ - static broadcastAnalyticsUpdate(metric: string, value: number | string | Record, filters?: Record): Promise; -} -/** - * WebSocket event builders for common scenarios - */ -export declare class EventBuilder { - /** - * Build task creation event - */ - static taskCreated(task: any, createdBy: string): TaskUpdateEvent; - /** - * Build task assignment event - */ - static taskAssigned(taskId: string, assignedTo: string[], assignedBy: string): TaskUpdateEvent; - /** - * Build channel message event - */ - static channelMessage(channelId: string, message: string, userId: string, messageType?: 'text' | 'file' | 'voice'): ChatMessageEvent; - /** - * Build user status event - */ - static userStatusUpdate(userId: string, status: 'online' | 'away' | 'busy' | 'offline'): UserStatusEvent; -} -declare const _default: { - WebSocketUtils: typeof WebSocketUtils; - EventBuilder: typeof EventBuilder; -}; -export default _default; -//# sourceMappingURL=utils.d.ts.map \ No newline at end of file diff --git a/backend/dist/websocket/utils.d.ts.map b/backend/dist/websocket/utils.d.ts.map deleted file mode 100644 index 76410e3..0000000 --- a/backend/dist/websocket/utils.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/websocket/utils.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,gBAAgB,EAChB,eAAe,EAChB,MAAM,SAAS,CAAC;AAEjB;;;GAGG;AAEH,qBAAa,cAAc;IACzB;;OAEG;WACU,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC;IA2B3J;;OAEG;WACU,mBAAmB,CAAC,UAAU,EAAE,IAAI,CAAC,eAAe,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAmC/F;;OAEG;WACU,uBAAuB,CAAC,OAAO,EAAE,IAAI,CAAC,gBAAgB,EAAE,WAAW,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBjG;;OAEG;WACU,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IA4B5G;;OAEG;WACU,sBAAsB,CAAC,YAAY,EAAE;QAChD,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;QACvC,WAAW,CAAC,EAAE,IAAI,CAAC;QACnB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC7B,GAAG,OAAO,CAAC,IAAI,CAAC;IAgCjB;;OAEG;WACU,aAAa,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC;IA8B1F;;OAEG;mBACkB,yBAAyB;IAoB9C;;OAEG;WACU,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IAY5E;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAI5C;;OAEG;IACH,MAAM,CAAC,mBAAmB,IAAI,MAAM;IAIpC;;OAEG;IACH,MAAM,CAAC,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAIvD;;OAEG;IACH,MAAM,CAAC,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE;IAIrD;;OAEG;IACH,MAAM,CAAC,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,IAAI;IAUtF;;OAEG;IACH,MAAM,CAAC,UAAU,IAAI;QACnB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC;QACvB,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,MAAM,CAAC;QACd,UAAU,EAAE,MAAM,CAAC;KACpB;IAID;;OAEG;WACU,qBAAqB,CAChC,OAAO,EAAE,MAAM,EAAE,EACjB,YAAY,EAAE,IAAI,CAAC,iBAAiB,EAAE,MAAM,GAAG,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,UAAU,CAAC,GAC/F,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IA8B5C;;OAEG;WACU,yBAAyB,CACpC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE;QACZ,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;QAC9D,QAAQ,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,CAAC;QAChD,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAC3B,SAAS,CAAC,EAAE,IAAI,CAAC;KAClB,GACA,OAAO,CAAC,OAAO,CAAC;IAWnB;;OAEG;WACU,sBAAsB,CACjC,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE;QACN,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,CAAC,EAAE,KAAK,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;YACb,MAAM,EAAE,MAAM,CAAC;YACf,IAAI,EAAE,GAAG,CAAC;SACX,CAAC,CAAC;KACJ,GACA,OAAO,CAAC,OAAO,CAAC;IAYnB;;OAEG;WACU,wBAAwB,CACnC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,EAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC;CAajB;AAED;;GAEG;AACH,qBAAa,YAAY;IACvB;;OAEG;IACH,MAAM,CAAC,WAAW,CAAC,IAAI,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,GAAG,eAAe;IAwBjE;;OAEG;IACH,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe;IAc9F;;OAEG;IACH,MAAM,CAAC,cAAc,CACnB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,WAAW,GAAE,MAAM,GAAG,MAAM,GAAG,OAAgB,GAC9C,gBAAgB;IAcnB;;OAEG;IACH,MAAM,CAAC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,eAAe;CAWzG;;;;;AAED,wBAAgD"} \ No newline at end of file diff --git a/backend/dist/websocket/utils.js b/backend/dist/websocket/utils.js deleted file mode 100644 index b31525a..0000000 --- a/backend/dist/websocket/utils.js +++ /dev/null @@ -1,397 +0,0 @@ -"use strict"; -Object.defineProperty(exports, "__esModule", { value: true }); -exports.EventBuilder = exports.WebSocketUtils = void 0; -const SocketManager_1 = require("./SocketManager"); -const logger_1 = require("@utils/logger"); -const CacheService_1 = require("../services/CacheService"); -/** - * WebSocket utility functions for real-time communication - * High-level helpers for common WebSocket operations - */ -class WebSocketUtils { - /** - * Send notification to user - */ - static async notifyUser(userId, notification) { - try { - // TODO: Get user details for complete notification - const notificationEvent = { - ...notification, - type: 'notification', - timestamp: new Date().toISOString(), - userId, // This would need to be the sender's userId - }; - const success = SocketManager_1.socketManager.sendToUser(userId, 'notification', notificationEvent); - if (success) { - logger_1.loggers.websocket.debug?.({ - userId, - notificationId: notification.notificationId, - category: notification.category, - }, 'Notification sent via WebSocket'); - } - return success; - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, userId }, 'Failed to send notification via WebSocket'); - return false; - } - } - /** - * Broadcast task update to relevant users - */ - static async broadcastTaskUpdate(taskUpdate) { - try { - const event = { - ...taskUpdate, - timestamp: new Date().toISOString(), - }; - // TODO: Determine who should receive this update based on: - // - Task assignees - // - Task watchers - // - Channel members if task is in a channel - // - Managers/CEO for important tasks - // For now, broadcast to all connected users - SocketManager_1.socketManager.broadcast('task_update', event); - // Also send to specific channel if task is in a channel - if (taskUpdate.channelId) { - SocketManager_1.socketManager.sendToChannel(taskUpdate.channelId, 'task_update', event); - } - // Cache the task update for offline users - await this.cacheEventForOfflineUsers('task_update', event); - logger_1.loggers.websocket.debug?.({ - taskId: taskUpdate.taskId, - action: taskUpdate.action, - userId: taskUpdate.userId, - }, 'Task update broadcasted'); - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, taskId: taskUpdate.taskId }, 'Failed to broadcast task update'); - } - } - /** - * Broadcast message to channel - */ - static async broadcastChannelMessage(message) { - try { - const event = { - ...message, - timestamp: new Date().toISOString(), - }; - // Send to channel members - SocketManager_1.socketManager.sendToChannel(message.channelId, 'chat_message', event); - // Cache for offline channel members - await this.cacheEventForOfflineUsers('chat_message', event, message.channelId); - logger_1.loggers.websocket.debug?.({ - channelId: message.channelId, - userId: message.userId, - messageType: message.messageType, - }, 'Channel message broadcasted'); - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, channelId: message.channelId }, 'Failed to broadcast channel message'); - } - } - /** - * Update user online status - */ - static async updateUserStatus(userId, status) { - try { - // TODO: Get user details - const statusEvent = { - type: 'user_status_update', - userId, - status, - timestamp: new Date().toISOString(), - lastSeen: new Date().toISOString(), // Always provide a value - }; - // Broadcast to all users (or just contacts/team members) - SocketManager_1.socketManager.broadcast('user_status_update', statusEvent); - // Cache the status - await CacheService_1.cacheService.users.set(`status:${userId}`, { status, lastSeen: statusEvent.lastSeen }, { ttl: 3600 } // 1 hour - ); - logger_1.loggers.websocket.debug?.({ userId, status }, 'User status updated'); - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, userId, status }, 'Failed to update user status'); - } - } - /** - * Send system announcement to all users - */ - static async sendSystemAnnouncement(announcement) { - try { - const systemEvent = { - type: 'system_announcement', - ...announcement, - timestamp: new Date().toISOString(), - userId: 'system', - userName: 'System', - userRole: 'system', - scheduledAt: announcement.scheduledAt?.toISOString(), - }; - SocketManager_1.socketManager.broadcast('system_announcement', systemEvent); - // Cache for offline users - await CacheService_1.cacheService.set(`system:announcement:${Date.now()}`, systemEvent, { ttl: 86400, tags: ['system', 'announcements'] }); - logger_1.loggers.websocket.info?.({ - title: announcement.title, - level: announcement.level, - affectedServices: announcement.affectedServices, - }, 'System announcement broadcasted'); - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, announcement }, 'Failed to send system announcement'); - } - } - /** - * Send event to all users in a channel - */ - static async sendToChannel(channelId, eventType, data) { - try { - const event = { - type: eventType, - channelId, - data, - timestamp: new Date().toISOString(), - }; - // Use socket manager to broadcast to channel - SocketManager_1.socketManager.sendToChannel(channelId, eventType, event); - // Cache for offline users - await this.cacheEventForOfflineUsers(eventType, event, channelId); - logger_1.loggers.websocket.info?.({ - eventType, - channelId, - dataKeys: Object.keys(data), - }, 'Event sent to channel'); - } - catch (error) { - logger_1.loggers.websocket.error?.({ - error, - eventType, - channelId - }, 'Failed to send event to channel'); - } - } - /** - * Cache events for offline users - */ - static async cacheEventForOfflineUsers(eventType, event, channelId) { - try { - // TODO: Get list of users who should receive this event but are offline - // For now, just cache the event with a general key - const cacheKey = `offline_events:${eventType}:${Date.now()}`; - await CacheService_1.cacheService.set(cacheKey, event, { - ttl: 86400, // 24 hours - tags: ['offline_events', eventType, channelId].filter(Boolean) - }); - } - catch (error) { - logger_1.loggers.websocket.warn?.({ error, eventType }, 'Failed to cache event for offline users'); - } - } - /** - * Get offline events for user when they come online - */ - static async getOfflineEventsForUser(userId) { - try { - // TODO: Implement offline event retrieval based on user's channels, tasks, etc. - // This would require a more sophisticated caching strategy - return []; - } - catch (error) { - logger_1.loggers.websocket.error?.({ error, userId }, 'Failed to get offline events for user'); - return []; - } - } - /** - * Check if user is online - */ - static isUserOnline(userId) { - return SocketManager_1.socketManager.isUserOnline(userId); - } - /** - * Get online users count - */ - static getOnlineUsersCount() { - return SocketManager_1.socketManager.getConnectedUsersCount(); - } - /** - * Get channel member count - */ - static getChannelMemberCount(channelId) { - return SocketManager_1.socketManager.getChannelMemberCount(channelId); - } - /** - * Get active channel members - */ - static getChannelMembers(channelId) { - return SocketManager_1.socketManager.getChannelMembers(channelId); - } - /** - * Send typing indicator - */ - static sendTypingIndicator(channelId, userId, isTyping) { - SocketManager_1.socketManager.sendToChannel(channelId, 'typing_indicator', { - type: 'typing_indicator', - channelId, - userId, - isTyping, - timestamp: new Date().toISOString(), - }); - } - /** - * Get WebSocket server metrics - */ - static getMetrics() { - return SocketManager_1.socketManager.getMetrics(); - } - /** - * Send bulk notifications - */ - static async sendBulkNotifications(userIds, notification) { - let sent = 0; - let failed = 0; - await Promise.allSettled(userIds.map(async (userId) => { - try { - const success = await this.notifyUser(userId, notification); - if (success) { - sent++; - } - else { - failed++; - } - } - catch (error) { - failed++; - logger_1.loggers.websocket.warn?.({ error, userId }, 'Failed to send bulk notification'); - } - })); - logger_1.loggers.websocket.info?.({ - notificationId: notification.notificationId, - totalUsers: userIds.length, - sent, - failed, - }, 'Bulk notifications sent'); - return { sent, failed }; - } - /** - * Create and send notification - */ - static async createAndSendNotification(userId, notification) { - const notificationEvent = { - notificationId: `notification_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - priority: notification.priority || 'medium', - ...notification, - expiresAt: notification.expiresAt?.toISOString() || new Date(Date.now() + 86400000).toISOString(), // Default to 24 hours - }; - return await this.notifyUser(userId, notificationEvent); - } - /** - * Voice command helpers (for Phase 2) - */ - static async sendVoiceCommandResult(userId, commandId, result) { - const voiceResult = { - type: 'voice_command_result', - commandId, - result, - timestamp: new Date().toISOString(), - userId, - }; - return SocketManager_1.socketManager.sendToUser(userId, 'voice_command_result', voiceResult); - } - /** - * Analytics update helpers (for Phase 4) - */ - static async broadcastAnalyticsUpdate(metric, value, filters) { - const analyticsEvent = { - type: 'analytics_update', - metric, - value, - filters, - timestamp: new Date().toISOString(), - }; - // Only send to users who have subscribed to analytics updates - // TODO: Implement analytics subscription management - SocketManager_1.socketManager.broadcast('analytics_update', analyticsEvent); - } -} -exports.WebSocketUtils = WebSocketUtils; -/** - * WebSocket event builders for common scenarios - */ -class EventBuilder { - /** - * Build task creation event - */ - static taskCreated(task, createdBy) { - return { - type: 'task_created', - taskId: task.id, - channelId: task.channel_id, - task: { - id: task.id, - title: task.title, - description: task.description, - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - dueDate: task.due_date, - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'create', - userId: createdBy, - userName: '', // TODO: Get from user data - userRole: 'staff', // TODO: Get from user data - timestamp: new Date().toISOString(), - }; - } - /** - * Build task assignment event - */ - static taskAssigned(taskId, assignedTo, assignedBy) { - return { - type: 'task_updated', - taskId, - task: {}, // Minimal data for assignment event - action: 'assign', - changes: { assigned_to: assignedTo }, - userId: assignedBy, - userName: '', // TODO: Get from user data - userRole: 'manager', // TODO: Get from user data - timestamp: new Date().toISOString(), - }; - } - /** - * Build channel message event - */ - static channelMessage(channelId, message, userId, messageType = 'text') { - return { - type: 'chat_message', - channelId, - messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - message, - messageType, - userId, - userName: '', // TODO: Get from user data - userRole: 'staff', // TODO: Get from user data - timestamp: new Date().toISOString(), - }; - } - /** - * Build user status event - */ - static userStatusUpdate(userId, status) { - return { - type: 'user_status_update', - status, - lastSeen: new Date().toISOString(), // Always provide a value to satisfy strict types - userId, - userName: '', // TODO: Get from user data - userRole: 'staff', // TODO: Get from user data - timestamp: new Date().toISOString(), - }; - } -} -exports.EventBuilder = EventBuilder; -exports.default = { WebSocketUtils, EventBuilder }; -//# sourceMappingURL=utils.js.map \ No newline at end of file diff --git a/backend/dist/websocket/utils.js.map b/backend/dist/websocket/utils.js.map deleted file mode 100644 index ad70367..0000000 --- a/backend/dist/websocket/utils.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/websocket/utils.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/docker/Dockerfile.dev b/backend/docker/Dockerfile.dev deleted file mode 100644 index 393c8a2..0000000 --- a/backend/docker/Dockerfile.dev +++ /dev/null @@ -1,39 +0,0 @@ -FROM node:20-alpine - -# Install build dependencies -RUN apk add --no-cache \ - python3 \ - make \ - g++ \ - && npm install -g tsx - -# Create app directory -WORKDIR /app - -# Copy package files -COPY package*.json ./ -COPY tsconfig.json ./ - -# Install dependencies -RUN npm ci --only=production=false - -# Create non-root user -RUN addgroup -g 1001 -S nodejs && \ - adduser -S nextjs -u 1001 - -# Copy source code -COPY src ./src - -# Set ownership -RUN chown -R nextjs:nodejs /app -USER nextjs - -# Expose port -EXPOSE 3000 - -# Health check -HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ - CMD node -e "require('http').get('http://localhost:3000/health', (r) => { process.exit(r.statusCode === 200 ? 0 : 1) })" - -# Start the application -CMD ["tsx", "watch", "src/server.ts"] \ No newline at end of file diff --git a/backend/docker/docker-compose.dev.yml b/backend/docker/docker-compose.dev.yml deleted file mode 100644 index c419950..0000000 --- a/backend/docker/docker-compose.dev.yml +++ /dev/null @@ -1,100 +0,0 @@ -version: '3.8' - -services: - # PostgreSQL Database - postgres: - image: postgres:15-alpine - container_name: ceo-platform-db - environment: - POSTGRES_DB: ceo_platform - POSTGRES_USER: ceo_user - POSTGRES_PASSWORD: ceo_password - POSTGRES_INITDB_ARGS: "--encoding=UTF-8 --lc-collate=en_US.UTF-8 --lc-ctype=en_US.UTF-8" - ports: - - "5432:5432" - volumes: - - postgres_data:/var/lib/postgresql/data - - ./postgres/init.sql:/docker-entrypoint-initdb.d/init.sql - command: > - postgres - -c max_connections=100 - -c shared_buffers=256MB - -c effective_cache_size=1GB - -c maintenance_work_mem=64MB - -c checkpoint_completion_target=0.9 - -c wal_buffers=16MB - -c default_statistics_target=100 - -c random_page_cost=1.1 - -c effective_io_concurrency=200 - -c work_mem=4MB - -c min_wal_size=1GB - -c max_wal_size=4GB - healthcheck: - test: ["CMD-SHELL", "pg_isready -U ceo_user -d ceo_platform"] - interval: 10s - timeout: 5s - retries: 5 - start_period: 30s - restart: unless-stopped - networks: - - ceo-network - - # Redis for caching and real-time - redis: - image: redis:7-alpine - container_name: ceo-platform-redis - ports: - - "6379:6379" - volumes: - - redis_data:/data - - ./redis/redis.conf:/etc/redis/redis.conf - command: redis-server /etc/redis/redis.conf - healthcheck: - test: ["CMD", "redis-cli", "ping"] - interval: 10s - timeout: 3s - retries: 5 - start_period: 30s - restart: unless-stopped - networks: - - ceo-network - - # Development API Server (optional - for containerized development) - api: - build: - context: .. - dockerfile: docker/Dockerfile.dev - container_name: ceo-platform-api - ports: - - "3000:3000" - environment: - NODE_ENV: development - DATABASE_URL: postgresql://ceo_user:ceo_password@postgres:5432/ceo_platform - REDIS_URL: redis://redis:6379 - JWT_SECRET: dev-jwt-secret-change-in-production - JWT_REFRESH_SECRET: dev-refresh-secret-change-in-production - volumes: - - ../src:/app/src - - ../package.json:/app/package.json - - ../tsconfig.json:/app/tsconfig.json - - ../.env:/app/.env - depends_on: - postgres: - condition: service_healthy - redis: - condition: service_healthy - restart: unless-stopped - networks: - - ceo-network - profiles: - - full-stack - -volumes: - postgres_data: - driver: local - redis_data: - driver: local - -networks: - ceo-network: - driver: bridge \ No newline at end of file diff --git a/backend/docker/postgres/init.sql b/backend/docker/postgres/init.sql deleted file mode 100644 index 2b59169..0000000 --- a/backend/docker/postgres/init.sql +++ /dev/null @@ -1,31 +0,0 @@ --- PostgreSQL initialization script for CEO Communication Platform --- Optimizes database for high-performance voice processing and real-time collaboration - --- Enable required extensions -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; -CREATE EXTENSION IF NOT EXISTS "pg_trgm"; -CREATE EXTENSION IF NOT EXISTS "btree_gin"; -CREATE EXTENSION IF NOT EXISTS "pgcrypto"; - --- Set optimal configuration for development -ALTER SYSTEM SET shared_preload_libraries = 'pg_stat_statements'; -ALTER SYSTEM SET pg_stat_statements.track = 'all'; -ALTER SYSTEM SET log_statement = 'all'; -ALTER SYSTEM SET log_duration = 'on'; -ALTER SYSTEM SET log_min_duration_statement = 100; - --- Create schema for application -CREATE SCHEMA IF NOT EXISTS app; - --- Set search path -ALTER DATABASE ceo_platform SET search_path TO app, public; - --- Performance optimizations -ALTER DATABASE ceo_platform SET random_page_cost = 1.1; -ALTER DATABASE ceo_platform SET effective_cache_size = '1GB'; -ALTER DATABASE ceo_platform SET work_mem = '4MB'; - --- Enable query plan caching -ALTER DATABASE ceo_platform SET plan_cache_mode = 'force_generic_plan'; - -COMMIT; \ No newline at end of file diff --git a/backend/docker/redis/redis.conf b/backend/docker/redis/redis.conf deleted file mode 100644 index 06793d1..0000000 --- a/backend/docker/redis/redis.conf +++ /dev/null @@ -1,60 +0,0 @@ -# Redis configuration for CEO Communication Platform -# Optimized for real-time collaboration and caching - -# Basic configuration -port 6379 -bind 0.0.0.0 -protected-mode no -timeout 300 -tcp-keepalive 300 - -# Memory management -maxmemory 512mb -maxmemory-policy allkeys-lru -maxmemory-samples 5 - -# Persistence for development (disable in production for pure cache) -save 900 1 -save 300 10 -save 60 10000 - -# Performance optimizations - -# Logging -loglevel notice -syslog-enabled no - -# Pub/Sub optimizations for real-time features -client-output-buffer-limit pubsub 32mb 8mb 60 - -# Connection limits -maxclients 1000 - -# Disable dangerous commands in production -# rename-command FLUSHDB "" -# rename-command FLUSHALL "" -# rename-command KEYS "" - -# Enable keyspace notifications for real-time features -notify-keyspace-events "Ex" - -# Performance tuning -hz 10 -dynamic-hz yes - -# Memory usage optimization -hash-max-ziplist-entries 512 -hash-max-ziplist-value 64 -list-max-ziplist-entries 512 -list-max-ziplist-value 64 -set-max-intset-entries 512 -zset-max-ziplist-entries 128 -zset-max-ziplist-value 64 - -# Background saving -stop-writes-on-bgsave-error yes -rdbcompression yes -rdbchecksum yes - -# Security (minimal for development) -requirepass "" \ No newline at end of file diff --git a/backend/jest.config.js b/backend/jest.config.js deleted file mode 100644 index 0ad5328..0000000 --- a/backend/jest.config.js +++ /dev/null @@ -1,237 +0,0 @@ -/** - * Jest Configuration - Phase 2 Testing Setup - * Comprehensive test configuration for Phase 2 implementation - */ - -module.exports = { - // Test environment - preset: 'ts-jest', - testEnvironment: 'node', - - // Root directories - roots: ['/src', '/tests'], - - // Test file patterns - testMatch: [ - '/tests/**/*.test.ts', - '/tests/**/*.spec.ts' - ], - - // File extensions to process - moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], - - // Transform configuration - transform: { - '^.+\\.(ts|tsx)$': 'ts-jest' - }, - - // Module name mapping for path resolution - moduleNameMapping: { - '^@/(.*)$': '/src/$1', - '^@tests/(.*)$': '/tests/$1' - }, - - // Setup files - setupFilesAfterEnv: [ - '/tests/setup/jest.setup.ts' - ], - - // Global setup and teardown - globalSetup: '/tests/setup/globalSetup.ts', - globalTeardown: '/tests/setup/globalTeardown.ts', - - // Coverage configuration - collectCoverage: true, - coverageDirectory: '/coverage', - coverageReporters: [ - 'text', - 'text-summary', - 'html', - 'lcov', - 'clover' - ], - - // Coverage collection patterns - collectCoverageFrom: [ - 'src/**/*.{ts,tsx}', - '!src/**/*.d.ts', - '!src/**/*.test.ts', - '!src/**/*.spec.ts', - '!src/types/**/*', - '!src/**/__tests__/**/*', - '!src/**/__mocks__/**/*' - ], - - // Coverage thresholds for Phase 2 requirements - coverageThreshold: { - global: { - branches: 80, - functions: 85, - lines: 85, - statements: 85 - }, - // Critical components need higher coverage - './src/services/ai/': { - branches: 90, - functions: 95, - lines: 95, - statements: 95 - }, - './src/ai/execution/': { - branches: 90, - functions: 95, - lines: 95, - statements: 95 - }, - './src/security/': { - branches: 95, - functions: 95, - lines: 95, - statements: 95 - }, - './src/files/': { - branches: 85, - functions: 90, - lines: 90, - statements: 90 - } - }, - - // Test timeout (important for integration tests) - testTimeout: 30000, - - // Verbose output for better debugging - verbose: true, - - // Clear mocks between tests - clearMocks: true, - resetMocks: true, - restoreMocks: true, - - // Error handling - errorOnDeprecated: true, - - // Test result processor for custom reporting - testResultsProcessor: '/tests/utils/testResultsProcessor.ts', - - // Custom reporters - reporters: [ - 'default', - [ - 'jest-junit', - { - outputDirectory: '/test-results', - outputName: 'junit.xml', - classNameTemplate: '{classname}', - titleTemplate: '{title}', - ancestorSeparator: ' › ', - usePathForSuiteName: true - } - ], - [ - 'jest-html-reporters', - { - publicPath: '/test-results', - filename: 'test-report.html', - expand: true, - hideIcon: false, - pageTitle: 'Phase 2 Test Results', - logoImgPath: undefined, - inlineSource: false - } - ] - ], - - // Test environment variables - testEnvironmentOptions: { - NODE_ENV: 'test' - }, - - // Global variables available in tests - globals: { - 'ts-jest': { - tsconfig: 'tsconfig.json', - isolatedModules: true, - diagnostics: { - ignoreCodes: [151001] - } - } - }, - - // Files to ignore during testing - testPathIgnorePatterns: [ - '/node_modules/', - '/dist/', - '/build/', - '/coverage/', - '/tests/fixtures/', - '/tests/utils/', - '/tests/setup/' - ], - - // Module paths to ignore for transformation - transformIgnorePatterns: [ - 'node_modules/(?!(some-esm-module|another-esm-module)/)' - ], - - // Watch mode configuration - watchPathIgnorePatterns: [ - '/node_modules/', - '/coverage/', - '/test-results/', - '/dist/', - '/build/' - ], - - // Snapshot testing - snapshotSerializers: [], - - // Custom matchers and expect extensions - setupFiles: [ - '/tests/setup/customMatchers.ts' - ], - - // Maximum worker processes (optimize for CI/local) - maxWorkers: process.env.CI ? 2 : '50%', - - // Cache configuration - cacheDirectory: '/.jest-cache', - - // Projects configuration for different test types - projects: [ - { - displayName: 'unit', - testMatch: ['/tests/unit/**/*.test.ts'], - testEnvironment: 'node' - }, - { - displayName: 'integration', - testMatch: ['/tests/integration/**/*.test.ts'], - testEnvironment: 'node', - testTimeout: 60000 // Longer timeout for integration tests - }, - { - displayName: 'performance', - testMatch: ['/tests/performance/**/*.test.ts'], - testEnvironment: 'node', - testTimeout: 120000, // 2 minutes for performance tests - maxWorkers: 1 // Run performance tests sequentially - } - ], - - // Fail fast configuration - bail: process.env.CI ? 1 : 0, // Stop after first failure in CI - - // Notify configuration (local development) - notify: !process.env.CI, - notifyMode: 'failure-change', - - // Silent mode for CI - silent: !!process.env.CI, - - // Force exit after tests complete - forceExit: true, - - // Detect open handles (useful for debugging) - detectOpenHandles: !process.env.CI -}; \ No newline at end of file diff --git a/backend/migrations/1640995200000_create_users.sql b/backend/migrations/1640995200000_create_users.sql deleted file mode 100644 index 9310bdc..0000000 --- a/backend/migrations/1640995200000_create_users.sql +++ /dev/null @@ -1,193 +0,0 @@ --- Create Users table with enterprise features --- Foundation for all user management and authentication - -CREATE TABLE IF NOT EXISTS users ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - email VARCHAR(255) UNIQUE NOT NULL, - name VARCHAR(255) NOT NULL, - password_hash VARCHAR(255) NOT NULL, - - -- Role-based access control - role VARCHAR(50) NOT NULL CHECK (role IN ('ceo', 'manager', 'staff')), - - -- User profile information - avatar_url VARCHAR(500), - phone VARCHAR(50), - department VARCHAR(100), - job_title VARCHAR(100), - - -- Internationalization and preferences - language_preference VARCHAR(10) DEFAULT 'en', - timezone VARCHAR(100) DEFAULT 'UTC', - - -- Notification preferences (JSONB for flexibility) - notification_settings JSONB DEFAULT '{ - "push_notifications": true, - "email_notifications": true, - "sms_notifications": false, - "voice_command_confirmations": true, - "real_time_updates": true, - "quiet_hours": { - "enabled": false, - "start": "22:00", - "end": "08:00" - } - }', - - -- Security and session management - failed_login_attempts INTEGER DEFAULT 0, - account_locked_until TIMESTAMP WITH TIME ZONE, - password_reset_token VARCHAR(255), - password_reset_expires TIMESTAMP WITH TIME ZONE, - email_verification_token VARCHAR(255), - email_verified BOOLEAN DEFAULT false, - - -- Activity tracking - last_active TIMESTAMP WITH TIME ZONE, - last_login TIMESTAMP WITH TIME ZONE, - login_count INTEGER DEFAULT 0, - - -- Voice processing preferences (for Phase 2) - voice_settings JSONB DEFAULT '{ - "enabled": true, - "language": "en-US", - "voice_training_completed": false, - "wake_word_enabled": false, - "processing_speed": "normal", - "confirmation_required": true - }', - - -- Audit fields - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - created_by UUID, - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID -); - --- Performance indexes -CREATE INDEX IF NOT EXISTS idx_users_email ON users(email) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_users_role ON users(role) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_users_last_active ON users(last_active) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_users_department ON users(department) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_users_created_at ON users(created_at); - --- Full-text search index for user search -CREATE INDEX IF NOT EXISTS idx_users_search ON users USING gin(to_tsvector('english', name || ' ' || email || ' ' || COALESCE(department, '') || ' ' || COALESCE(job_title, ''))) WHERE deleted_at IS NULL; - --- Notification settings search (for advanced notification routing) -CREATE INDEX IF NOT EXISTS idx_users_notification_settings ON users USING gin(notification_settings) WHERE deleted_at IS NULL; - --- Constraint to ensure CEO is unique (business rule) -CREATE UNIQUE INDEX IF NOT EXISTS idx_users_unique_ceo ON users(role) WHERE role = 'ceo' AND deleted_at IS NULL; - --- Security constraint: prevent password reset token reuse -CREATE UNIQUE INDEX IF NOT EXISTS idx_users_password_reset_token ON users(password_reset_token) WHERE password_reset_token IS NOT NULL; - --- Function to update the updated_at timestamp -CREATE OR REPLACE FUNCTION update_updated_at_column() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = NOW(); - NEW.version = OLD.version + 1; - RETURN NEW; -END; -$$ LANGUAGE 'plpgsql'; - --- Trigger to automatically update updated_at and version -DROP TRIGGER IF EXISTS trigger_users_updated_at ON users; -CREATE TRIGGER trigger_users_updated_at - BEFORE UPDATE ON users - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - --- Function for soft delete -CREATE OR REPLACE FUNCTION soft_delete_user() -RETURNS TRIGGER AS $$ -BEGIN - UPDATE users - SET deleted_at = NOW(), deleted_by = COALESCE(current_setting('app.current_user_id', true)::UUID, OLD.id) - WHERE id = OLD.id; - RETURN NULL; -- Prevent actual delete -END; -$$ LANGUAGE 'plpgsql'; - --- Trigger for soft delete protection -DROP TRIGGER IF EXISTS trigger_users_soft_delete ON users; -CREATE TRIGGER trigger_users_soft_delete - BEFORE DELETE ON users - FOR EACH ROW - EXECUTE FUNCTION soft_delete_user(); - --- Security function to hash passwords (for stored procedures) -CREATE OR REPLACE FUNCTION hash_password(password TEXT) -RETURNS TEXT AS $$ -BEGIN - -- This will be handled by the application layer using bcrypt - -- This function is a placeholder for future stored procedure needs - RAISE EXCEPTION 'Password hashing must be done by application layer'; -END; -$$ LANGUAGE 'plpgsql'; - --- Utility view for active users (excluding soft-deleted) -CREATE OR REPLACE VIEW active_users AS -SELECT - id, - email, - name, - role, - avatar_url, - phone, - department, - job_title, - language_preference, - timezone, - notification_settings, - voice_settings, - last_active, - last_login, - login_count, - email_verified, - created_at, - updated_at, - version -FROM users -WHERE deleted_at IS NULL; - --- Utility view for user statistics (for analytics) -CREATE OR REPLACE VIEW user_stats AS -SELECT - COUNT(*) as total_users, - COUNT(*) FILTER (WHERE role = 'ceo') as ceo_count, - COUNT(*) FILTER (WHERE role = 'manager') as manager_count, - COUNT(*) FILTER (WHERE role = 'staff') as staff_count, - COUNT(*) FILTER (WHERE last_active > NOW() - INTERVAL '24 hours') as active_24h, - COUNT(*) FILTER (WHERE last_active > NOW() - INTERVAL '7 days') as active_7d, - COUNT(*) FILTER (WHERE email_verified = true) as verified_users, - AVG(login_count) as avg_login_count -FROM active_users; - --- Add foreign key constraints after table creation to avoid circular dependency -DO $$ BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_users_created_by') THEN - ALTER TABLE users ADD CONSTRAINT fk_users_created_by - FOREIGN KEY (created_by) REFERENCES users(id); - END IF; -END $$; - -DO $$ BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_users_deleted_by') THEN - ALTER TABLE users ADD CONSTRAINT fk_users_deleted_by - FOREIGN KEY (deleted_by) REFERENCES users(id); - END IF; -END $$; - -COMMENT ON TABLE users IS 'Core user management table with enterprise features for CEO communication platform'; -COMMENT ON COLUMN users.notification_settings IS 'JSONB field storing user-specific notification preferences for all channels'; -COMMENT ON COLUMN users.voice_settings IS 'JSONB field storing voice processing preferences and training data'; -COMMENT ON COLUMN users.version IS 'Optimistic locking version for concurrent updates'; -COMMENT ON VIEW active_users IS 'Active users view excluding soft-deleted records'; -COMMENT ON VIEW user_stats IS 'User statistics for dashboard and analytics'; \ No newline at end of file diff --git a/backend/migrations/1640995201000_create_categories.sql b/backend/migrations/1640995201000_create_categories.sql deleted file mode 100644 index fed93a4..0000000 --- a/backend/migrations/1640995201000_create_categories.sql +++ /dev/null @@ -1,321 +0,0 @@ --- Create Categories table for channel organization --- Supports hierarchical organization structure - -CREATE TABLE IF NOT EXISTS categories ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name VARCHAR(100) NOT NULL, - description TEXT, - - -- Visual customization - color VARCHAR(7) CHECK (color ~ '^#[0-9A-Fa-f]{6}$'), -- Hex color validation - icon VARCHAR(100), -- Icon identifier (e.g., 'marketing', 'finance', 'development') - - -- Hierarchy support - parent_id UUID, - sort_order INTEGER DEFAULT 0, - - -- Priority and visibility - priority_level INTEGER DEFAULT 1 CHECK (priority_level BETWEEN 1 AND 10), - is_system_category BOOLEAN DEFAULT false, -- System vs user-created categories - is_active BOOLEAN DEFAULT true, - - -- Access control - visibility VARCHAR(20) DEFAULT 'public' CHECK (visibility IN ('public', 'restricted', 'private')), - allowed_roles TEXT[] DEFAULT ARRAY['ceo', 'manager', 'staff'], -- Roles that can access this category - - -- Metadata for extensibility - metadata JSONB DEFAULT '{}', - - -- Enterprise features - department_mapping VARCHAR(100), -- Map to organizational departments - cost_center VARCHAR(50), -- For financial tracking - approval_required BOOLEAN DEFAULT false, -- Whether channels in this category require approval - - -- Usage statistics (updated by triggers) - channel_count INTEGER DEFAULT 0, - active_channel_count INTEGER DEFAULT 0, - - -- Audit fields - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - created_by UUID NOT NULL REFERENCES users(id), - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id) -); - --- Performance indexes -CREATE INDEX IF NOT EXISTS idx_categories_name ON categories(name) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_categories_parent_id ON categories(parent_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_categories_sort_order ON categories(sort_order); -CREATE INDEX IF NOT EXISTS idx_categories_priority ON categories(priority_level DESC); -CREATE INDEX IF NOT EXISTS idx_categories_department ON categories(department_mapping) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_categories_system ON categories(is_system_category) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_categories_active ON categories(is_active) WHERE deleted_at IS NULL; - --- Hierarchy queries optimization (adjacency list to nested set conversion) -CREATE INDEX IF NOT EXISTS idx_categories_hierarchy ON categories(parent_id, sort_order) WHERE deleted_at IS NULL; - --- Full-text search for category discovery -CREATE INDEX IF NOT EXISTS idx_categories_search ON categories USING gin(to_tsvector('english', name || ' ' || COALESCE(description, ''))) WHERE deleted_at IS NULL; - --- Metadata search support -CREATE INDEX IF NOT EXISTS idx_categories_metadata ON categories USING gin(metadata) WHERE deleted_at IS NULL; - --- Role-based access search -CREATE INDEX IF NOT EXISTS idx_categories_roles ON categories USING gin(allowed_roles) WHERE deleted_at IS NULL; - --- Constraint: prevent circular references in hierarchy -DO $$ BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'check_no_self_reference') THEN - ALTER TABLE categories ADD CONSTRAINT check_no_self_reference CHECK (id != parent_id); - END IF; -END $$; - --- Unique constraint for category names within same parent (case-insensitive) -CREATE UNIQUE INDEX IF NOT EXISTS idx_categories_unique_name_per_parent -ON categories(LOWER(name), COALESCE(parent_id, '00000000-0000-0000-0000-000000000000'::UUID)) -WHERE deleted_at IS NULL; - --- Trigger to update updated_at and version -DROP TRIGGER IF EXISTS trigger_categories_updated_at ON categories; -CREATE TRIGGER trigger_categories_updated_at - BEFORE UPDATE ON categories - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - --- Function to update category statistics -CREATE OR REPLACE FUNCTION update_category_stats() -RETURNS TRIGGER AS $$ -BEGIN - -- Update channel count for the category - UPDATE categories - SET - channel_count = ( - SELECT COUNT(*) - FROM channels - WHERE category_id = COALESCE(NEW.category_id, OLD.category_id) - AND deleted_at IS NULL - ), - active_channel_count = ( - SELECT COUNT(*) - FROM channels - WHERE category_id = COALESCE(NEW.category_id, OLD.category_id) - AND status = 'active' - AND deleted_at IS NULL - ) - WHERE id = COALESCE(NEW.category_id, OLD.category_id); - - RETURN COALESCE(NEW, OLD); -END; -$$ LANGUAGE 'plpgsql'; - --- Function for hierarchical category path -CREATE OR REPLACE FUNCTION get_category_path(category_id UUID) -RETURNS TEXT AS $$ -DECLARE - path TEXT := ''; - current_id UUID := category_id; - current_name TEXT; - current_parent UUID; - depth INTEGER := 0; - max_depth INTEGER := 10; -- Prevent infinite loops -BEGIN - WHILE current_id IS NOT NULL AND depth < max_depth LOOP - SELECT name, parent_id INTO current_name, current_parent - FROM categories - WHERE id = current_id AND deleted_at IS NULL; - - IF current_name IS NULL THEN - EXIT; - END IF; - - IF path = '' THEN - path := current_name; - ELSE - path := current_name || ' > ' || path; - END IF; - - current_id := current_parent; - depth := depth + 1; - END LOOP; - - RETURN path; -END; -$$ LANGUAGE 'plpgsql'; - --- Function to get all subcategories (recursive) -CREATE OR REPLACE FUNCTION get_subcategories(parent_category_id UUID) -RETURNS TABLE( - id UUID, - name VARCHAR(100), - level INTEGER -) AS $$ -WITH RECURSIVE subcategories AS ( - -- Base case: direct children - SELECT c.id, c.name, 1 as level - FROM categories c - WHERE c.parent_id = parent_category_id - AND c.deleted_at IS NULL - - UNION ALL - - -- Recursive case: children of children - SELECT c.id, c.name, sc.level + 1 - FROM categories c - JOIN subcategories sc ON c.parent_id = sc.id - WHERE c.deleted_at IS NULL - AND sc.level < 10 -- Prevent infinite recursion -) -SELECT * FROM subcategories ORDER BY level, name; -$$ LANGUAGE 'sql'; - --- Soft delete function -CREATE OR REPLACE FUNCTION soft_delete_category() -RETURNS TRIGGER AS $$ -BEGIN - -- Check if category has active channels - IF EXISTS( - SELECT 1 FROM channels - WHERE category_id = OLD.id - AND deleted_at IS NULL - AND status = 'active' - ) THEN - RAISE EXCEPTION 'Cannot delete category with active channels'; - END IF; - - -- Soft delete the category - UPDATE categories - SET deleted_at = NOW(), deleted_by = COALESCE(current_setting('app.current_user_id', true)::UUID, OLD.created_by) - WHERE id = OLD.id; - - RETURN NULL; -- Prevent actual delete -END; -$$ LANGUAGE 'plpgsql'; - --- Trigger for soft delete protection -DROP TRIGGER IF EXISTS trigger_categories_soft_delete ON categories; -CREATE TRIGGER trigger_categories_soft_delete - BEFORE DELETE ON categories - FOR EACH ROW - EXECUTE FUNCTION soft_delete_category(); - --- Create system default categories -INSERT INTO categories (id, name, description, color, icon, is_system_category, priority_level, created_by) -SELECT - gen_random_uuid(), - 'General', - 'General communication and announcements', - '#6B7280', - 'chat', - true, - 10, - u.id -FROM users u WHERE u.role = 'ceo' AND u.deleted_at IS NULL LIMIT 1 -ON CONFLICT DO NOTHING; - -INSERT INTO categories (id, name, description, color, icon, is_system_category, priority_level, created_by) -SELECT - gen_random_uuid(), - 'Projects', - 'Project-specific communication channels', - '#3B82F6', - 'folder', - true, - 9, - u.id -FROM users u WHERE u.role = 'ceo' AND u.deleted_at IS NULL LIMIT 1 -ON CONFLICT DO NOTHING; - -INSERT INTO categories (id, name, description, color, icon, is_system_category, priority_level, created_by) -SELECT - gen_random_uuid(), - 'Departments', - 'Department-based communication', - '#10B981', - 'building', - true, - 8, - u.id -FROM users u WHERE u.role = 'ceo' AND u.deleted_at IS NULL LIMIT 1 -ON CONFLICT DO NOTHING; - -INSERT INTO categories (id, name, description, color, icon, is_system_category, priority_level, created_by) -SELECT - gen_random_uuid(), - 'Urgent', - 'High-priority and urgent communications', - '#EF4444', - 'alert', - true, - 10, - u.id -FROM users u WHERE u.role = 'ceo' AND u.deleted_at IS NULL LIMIT 1 -ON CONFLICT DO NOTHING; - --- Utility views -CREATE OR REPLACE VIEW active_categories AS -SELECT - id, - name, - description, - color, - icon, - parent_id, - sort_order, - priority_level, - visibility, - allowed_roles, - department_mapping, - channel_count, - active_channel_count, - get_category_path(id) as full_path, - created_at, - updated_at, - version -FROM categories -WHERE deleted_at IS NULL AND is_active = true; - --- Category hierarchy view (for tree display) -CREATE OR REPLACE VIEW category_hierarchy AS -WITH RECURSIVE hierarchy AS ( - -- Root categories - SELECT - id, name, parent_id, sort_order, priority_level, - 0 as depth, - name::TEXT as path, - ARRAY[sort_order] as sort_path - FROM categories - WHERE parent_id IS NULL AND deleted_at IS NULL - - UNION ALL - - -- Child categories - SELECT - c.id, c.name, c.parent_id, c.sort_order, c.priority_level, - h.depth + 1, - (h.path || ' > ' || c.name)::TEXT, - h.sort_path || c.sort_order - FROM categories c - JOIN hierarchy h ON c.parent_id = h.id - WHERE c.deleted_at IS NULL -) -SELECT * FROM hierarchy ORDER BY sort_path; - --- Add self-referencing foreign key constraint after table creation -DO $$ BEGIN - IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'fk_categories_parent_id') THEN - ALTER TABLE categories ADD CONSTRAINT fk_categories_parent_id - FOREIGN KEY (parent_id) REFERENCES categories(id) ON DELETE RESTRICT; - END IF; -END $$; - -COMMENT ON TABLE categories IS 'Hierarchical category system for organizing channels and communication'; -COMMENT ON COLUMN categories.metadata IS 'JSONB field for extensible category properties'; -COMMENT ON COLUMN categories.allowed_roles IS 'Array of user roles allowed to access this category'; -COMMENT ON FUNCTION get_category_path(UUID) IS 'Returns full hierarchical path for a category'; -COMMENT ON FUNCTION get_subcategories(UUID) IS 'Returns all subcategories recursively'; -COMMENT ON VIEW active_categories IS 'Active categories with computed full paths'; -COMMENT ON VIEW category_hierarchy IS 'Hierarchical view of all categories for tree display'; diff --git a/backend/migrations/1640995202000_create_channels.sql b/backend/migrations/1640995202000_create_channels.sql deleted file mode 100644 index 949da94..0000000 --- a/backend/migrations/1640995202000_create_channels.sql +++ /dev/null @@ -1,452 +0,0 @@ --- Create Channels table - Core collaboration entity --- Advanced channel system with real-time collaboration features - -CREATE TABLE IF NOT EXISTS channels ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name VARCHAR(200) NOT NULL, - description TEXT, - - -- Organization and categorization - category_id UUID REFERENCES categories(id) ON DELETE RESTRICT, - - -- Channel type and behavior - channel_type VARCHAR(50) DEFAULT 'project' CHECK (channel_type IN ('project', 'department', 'initiative', 'temporary', 'emergency', 'announcement')), - privacy_level VARCHAR(20) DEFAULT 'public' CHECK (privacy_level IN ('public', 'private', 'restricted')), - status VARCHAR(20) DEFAULT 'active' CHECK (status IN ('active', 'archived', 'paused', 'completed')), - - -- Access control and permissions - created_by UUID NOT NULL REFERENCES users(id), - owned_by UUID NOT NULL REFERENCES users(id), -- Can be transferred - moderators UUID[] DEFAULT '{}', -- Array of user IDs who can moderate - - -- Member management - members UUID[] DEFAULT '{}', -- Array of user IDs - member_count INTEGER DEFAULT 0, - max_members INTEGER DEFAULT 100, - auto_join_roles TEXT[] DEFAULT '{}', -- Roles that auto-join this channel - - -- Channel behavior settings - settings JSONB DEFAULT '{ - "allow_voice_commands": true, - "voice_command_roles": ["ceo", "manager"], - "allow_file_uploads": true, - "allow_external_sharing": false, - "message_retention_days": 90, - "require_approval_for_join": false, - "notification_level": "all", - "read_receipts_enabled": true, - "typing_indicators_enabled": true, - "thread_replies_enabled": true, - "message_reactions_enabled": true, - "voice_transcription_enabled": true - }', - - -- Integration settings (for Phase 2-4) - integrations JSONB DEFAULT '{ - "calendar_integration": false, - "email_forwarding": false, - "webhook_urls": [], - "ai_assistant_enabled": true, - "auto_summary_enabled": false, - "task_creation_enabled": true - }', - - -- Analytics and activity tracking - activity_stats JSONB DEFAULT '{ - "total_messages": 0, - "total_files": 0, - "total_tasks": 0, - "last_activity": null, - "most_active_user": null, - "average_response_time": null, - "peak_activity_hours": [] - }', - - -- Project management features - project_info JSONB DEFAULT '{ - "start_date": null, - "end_date": null, - "budget": null, - "priority": "medium", - "tags": [], - "milestones": [], - "deliverables": [], - "stakeholders": [] - }', - - -- Schedule and time management - schedule JSONB DEFAULT '{ - "timezone": "UTC", - "working_hours": { - "monday": {"start": "09:00", "end": "17:00"}, - "tuesday": {"start": "09:00", "end": "17:00"}, - "wednesday": {"start": "09:00", "end": "17:00"}, - "thursday": {"start": "09:00", "end": "17:00"}, - "friday": {"start": "09:00", "end": "17:00"}, - "saturday": null, - "sunday": null - }, - "meeting_schedule": [], - "deadline_reminders": true - }', - - -- Archival and retention - archived_at TIMESTAMP WITH TIME ZONE, - archived_by UUID REFERENCES users(id), - archive_reason TEXT, - retention_until TIMESTAMP WITH TIME ZONE, -- Auto-delete date - - -- Audit fields - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - last_activity_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id) -); - --- Performance indexes -CREATE INDEX IF NOT EXISTS idx_channels_name ON channels(name) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_category_id ON channels(category_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_type ON channels(channel_type) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_status ON channels(status) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_privacy ON channels(privacy_level) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_created_by ON channels(created_by) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_owned_by ON channels(owned_by) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_last_activity ON channels(last_activity_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_created_at ON channels(created_at DESC); - --- Member-based queries (GIN index for array operations) -CREATE INDEX IF NOT EXISTS idx_channels_members ON channels USING gin(members) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_moderators ON channels USING gin(moderators) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_auto_join_roles ON channels USING gin(auto_join_roles) WHERE deleted_at IS NULL; - --- Full-text search index -CREATE INDEX IF NOT EXISTS idx_channels_search ON channels USING gin(to_tsvector('english', name || ' ' || COALESCE(description, ''))) WHERE deleted_at IS NULL; - --- JSONB indexes for settings and metadata -CREATE INDEX IF NOT EXISTS idx_channels_settings ON channels USING gin(settings) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_project_info ON channels USING gin(project_info) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_integrations ON channels USING gin(integrations) WHERE deleted_at IS NULL; - --- Composite indexes for common queries -CREATE INDEX IF NOT EXISTS idx_channels_category_status ON channels(category_id, status) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_type_privacy ON channels(channel_type, privacy_level) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channels_active_recent ON channels(last_activity_at DESC) WHERE status = 'active' AND deleted_at IS NULL; - --- Unique constraint for channel names within category (case-insensitive) -CREATE UNIQUE INDEX IF NOT EXISTS idx_channels_unique_name_per_category -ON channels(LOWER(name), category_id) -WHERE deleted_at IS NULL; - --- Constraint: owner must be a member -DO $$ BEGIN - IF NOT EXISTS ( - SELECT 1 FROM pg_constraint c - JOIN pg_class t ON c.conrelid = t.oid - WHERE c.conname = 'check_owner_is_member' - AND t.relname = 'channels' - ) THEN - ALTER TABLE channels ADD CONSTRAINT check_owner_is_member - CHECK (owned_by = ANY(members) OR array_length(members, 1) IS NULL); - END IF; -END $$; - --- Constraint: member count should match array length -CREATE OR REPLACE FUNCTION validate_member_count() -RETURNS TRIGGER AS $$ -BEGIN - NEW.member_count := array_length(NEW.members, 1); - IF NEW.member_count IS NULL THEN - NEW.member_count := 0; - END IF; - - -- Check max members limit - IF NEW.member_count > NEW.max_members THEN - RAISE EXCEPTION 'Channel member count (%) exceeds maximum allowed (%)', NEW.member_count, NEW.max_members; - END IF; - - RETURN NEW; -END; -$$ LANGUAGE 'plpgsql'; - --- Trigger to validate and update member count -DROP TRIGGER IF EXISTS trigger_channels_validate_members ON channels; -CREATE TRIGGER trigger_channels_validate_members - BEFORE INSERT OR UPDATE OF members ON channels - FOR EACH ROW - EXECUTE FUNCTION validate_member_count(); - --- Trigger to update updated_at and version -DROP TRIGGER IF EXISTS trigger_channels_updated_at ON channels; -CREATE TRIGGER trigger_channels_updated_at - BEFORE UPDATE ON channels - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - --- Function to update channel activity -CREATE OR REPLACE FUNCTION update_channel_activity() -RETURNS TRIGGER AS $$ -BEGIN - UPDATE channels - SET last_activity_at = NOW() - WHERE id = NEW.channel_id; - - RETURN NEW; -END; -$$ LANGUAGE 'plpgsql'; - --- Function to add member to channel -CREATE OR REPLACE FUNCTION add_channel_member( - channel_id UUID, - user_id UUID, - added_by UUID DEFAULT NULL -) RETURNS BOOLEAN AS $$ -DECLARE - current_members UUID[]; - max_allowed INTEGER; - current_count INTEGER; -BEGIN - -- Get current members and limits - SELECT members, max_members INTO current_members, max_allowed - FROM channels - WHERE id = channel_id AND deleted_at IS NULL; - - IF NOT FOUND THEN - RAISE EXCEPTION 'Channel not found'; - END IF; - - -- Check if user is already a member - IF user_id = ANY(current_members) THEN - RETURN FALSE; -- Already a member - END IF; - - -- Check member limit - current_count := array_length(current_members, 1); - IF current_count IS NULL THEN - current_count := 0; - END IF; - - IF current_count >= max_allowed THEN - RAISE EXCEPTION 'Channel has reached maximum member limit'; - END IF; - - -- Add member - UPDATE channels - SET - members = array_append(members, user_id), - member_count = current_count + 1, - last_activity_at = NOW() - WHERE id = channel_id; - - -- Log the addition (could be extended to an audit table) - INSERT INTO channel_member_history (channel_id, user_id, action, performed_by, performed_at) - VALUES (channel_id, user_id, 'added', COALESCE(added_by, user_id), NOW()) - ON CONFLICT DO NOTHING; -- In case the table doesn't exist yet - - RETURN TRUE; -END; -$$ LANGUAGE 'plpgsql'; - --- Function to remove member from channel -CREATE OR REPLACE FUNCTION remove_channel_member( - channel_id UUID, - user_id UUID, - removed_by UUID DEFAULT NULL -) RETURNS BOOLEAN AS $$ -DECLARE - current_members UUID[]; -BEGIN - -- Get current members - SELECT members INTO current_members - FROM channels - WHERE id = channel_id AND deleted_at IS NULL; - - IF NOT FOUND THEN - RAISE EXCEPTION 'Channel not found'; - END IF; - - -- Check if user is a member - IF NOT (user_id = ANY(current_members)) THEN - RETURN FALSE; -- Not a member - END IF; - - -- Remove member - UPDATE channels - SET - members = array_remove(members, user_id), - member_count = array_length(array_remove(members, user_id), 1), - moderators = array_remove(moderators, user_id), -- Remove from moderators too - last_activity_at = NOW() - WHERE id = channel_id; - - -- Handle member count null case - UPDATE channels - SET member_count = 0 - WHERE id = channel_id AND member_count IS NULL; - - -- Log the removal - INSERT INTO channel_member_history (channel_id, user_id, action, performed_by, performed_at) - VALUES (channel_id, user_id, 'removed', COALESCE(removed_by, user_id), NOW()) - ON CONFLICT DO NOTHING; - - RETURN TRUE; -END; -$$ LANGUAGE 'plpgsql'; - --- Function to check if user can access channel -CREATE OR REPLACE FUNCTION can_user_access_channel( - channel_id UUID, - user_id UUID, - user_role TEXT -) RETURNS BOOLEAN AS $$ -DECLARE - channel_privacy VARCHAR(20); - channel_members UUID[]; - auto_join_roles TEXT[]; - channel_status VARCHAR(20); -BEGIN - SELECT privacy_level, members, auto_join_roles, status - INTO channel_privacy, channel_members, auto_join_roles, channel_status - FROM channels - WHERE id = channel_id AND deleted_at IS NULL; - - IF NOT FOUND THEN - RETURN FALSE; - END IF; - - -- Archived channels are read-only for members - IF channel_status = 'archived' THEN - RETURN user_id = ANY(channel_members); - END IF; - - -- CEO can access everything - IF user_role = 'ceo' THEN - RETURN TRUE; - END IF; - - -- Public channels are accessible to all - IF channel_privacy = 'public' THEN - RETURN TRUE; - END IF; - - -- Private channels require membership - IF channel_privacy = 'private' THEN - RETURN user_id = ANY(channel_members); - END IF; - - -- Restricted channels check auto-join roles or membership - IF channel_privacy = 'restricted' THEN - RETURN user_role = ANY(auto_join_roles) OR user_id = ANY(channel_members); - END IF; - - RETURN FALSE; -END; -$$ LANGUAGE 'plpgsql'; - --- Soft delete function -CREATE OR REPLACE FUNCTION soft_delete_channel() -RETURNS TRIGGER AS $$ -BEGIN - -- Archive the channel instead of deleting if it has significant activity - UPDATE channels - SET - deleted_at = NOW(), - deleted_by = COALESCE(current_setting('app.current_user_id', true)::UUID, OLD.owned_by), - status = 'archived', - archived_at = NOW(), - archived_by = COALESCE(current_setting('app.current_user_id', true)::UUID, OLD.owned_by), - archive_reason = 'Channel deleted' - WHERE id = OLD.id; - - RETURN NULL; -- Prevent actual delete -END; -$$ LANGUAGE 'plpgsql'; - --- Trigger for soft delete protection -DROP TRIGGER IF EXISTS trigger_channels_soft_delete ON channels; -CREATE TRIGGER trigger_channels_soft_delete - BEFORE DELETE ON channels - FOR EACH ROW - EXECUTE FUNCTION soft_delete_channel(); - --- Create channel member history table for audit trail -CREATE TABLE IF NOT EXISTS channel_member_history ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - channel_id UUID NOT NULL REFERENCES channels(id), - user_id UUID NOT NULL REFERENCES users(id), - action VARCHAR(20) NOT NULL CHECK (action IN ('added', 'removed', 'promoted', 'demoted')), - performed_by UUID NOT NULL REFERENCES users(id), - performed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - metadata JSONB DEFAULT '{}' -); - -CREATE INDEX IF NOT EXISTS idx_channel_member_history_channel ON channel_member_history(channel_id); -CREATE INDEX IF NOT EXISTS idx_channel_member_history_user ON channel_member_history(user_id); -CREATE INDEX IF NOT EXISTS idx_channel_member_history_performed_at ON channel_member_history(performed_at DESC); - --- Utility views -CREATE OR REPLACE VIEW active_channels AS -SELECT - c.id, - c.name, - c.description, - c.category_id, - cat.name as category_name, - c.channel_type, - c.privacy_level, - c.status, - c.owned_by, - owner.name as owner_name, - c.member_count, - c.max_members, - c.settings, - c.project_info, - c.last_activity_at, - c.created_at, - c.updated_at -FROM channels c -LEFT JOIN categories cat ON c.category_id = cat.id -LEFT JOIN users owner ON c.owned_by = owner.id -WHERE c.deleted_at IS NULL; - --- Channel with member details view -CREATE OR REPLACE VIEW channels_with_members AS -SELECT - c.id, - c.name, - c.description, - c.category_id, - c.channel_type, - c.privacy_level, - c.status, - c.member_count, - ARRAY( - SELECT json_build_object( - 'id', u.id, - 'name', u.name, - 'email', u.email, - 'role', u.role, - 'avatar_url', u.avatar_url - ) - FROM users u - WHERE u.id = ANY(c.members) - AND u.deleted_at IS NULL - ) as member_details, - c.last_activity_at, - c.created_at -FROM channels c -WHERE c.deleted_at IS NULL; - -COMMENT ON TABLE channels IS 'Core collaboration channels with advanced real-time features'; -COMMENT ON COLUMN channels.members IS 'Array of user IDs who are members of this channel'; -COMMENT ON COLUMN channels.settings IS 'JSONB configuration for channel behavior and features'; -COMMENT ON COLUMN channels.integrations IS 'JSONB configuration for external integrations'; -COMMENT ON COLUMN channels.activity_stats IS 'JSONB tracking channel activity and analytics'; -COMMENT ON FUNCTION add_channel_member(UUID, UUID, UUID) IS 'Add a user to a channel with validation'; -COMMENT ON FUNCTION remove_channel_member(UUID, UUID, UUID) IS 'Remove a user from a channel'; -COMMENT ON FUNCTION can_user_access_channel(UUID, UUID, TEXT) IS 'Check if user has access to channel based on privacy and role'; -COMMENT ON VIEW active_channels IS 'Active channels with category and owner information'; -COMMENT ON VIEW channels_with_members IS 'Channels with detailed member information for UI'; \ No newline at end of file diff --git a/backend/migrations/1640995203000_create_tasks.sql b/backend/migrations/1640995203000_create_tasks.sql deleted file mode 100644 index 0cc39aa..0000000 --- a/backend/migrations/1640995203000_create_tasks.sql +++ /dev/null @@ -1,509 +0,0 @@ --- Create Tasks table - Advanced task management with dependencies --- Core entity for work assignment and tracking - -CREATE TABLE IF NOT EXISTS tasks ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - title VARCHAR(300) NOT NULL, - description TEXT, - - -- Task organization - channel_id UUID REFERENCES channels(id) ON DELETE CASCADE, - parent_task_id UUID, -- For subtasks - - -- Assignment and ownership - created_by UUID NOT NULL REFERENCES users(id), - assigned_to UUID[] DEFAULT '{}', -- Array of user IDs for multi-assignment - owned_by UUID REFERENCES users(id), -- Primary assignee/owner - - -- Task classification - priority VARCHAR(20) DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high', 'urgent', 'critical')), - status VARCHAR(20) DEFAULT 'pending' CHECK (status IN ('pending', 'in_progress', 'review', 'completed', 'cancelled', 'on_hold')), - task_type VARCHAR(50) DEFAULT 'general' CHECK (task_type IN ('general', 'project', 'maintenance', 'emergency', 'research', 'approval')), - - -- Complexity and estimation - complexity INTEGER DEFAULT 1 CHECK (complexity BETWEEN 1 AND 10), - estimated_hours DECIMAL(5,2), - actual_hours DECIMAL(5,2) DEFAULT 0, - story_points INTEGER, - - -- Time management - due_date TIMESTAMP WITH TIME ZONE, - start_date TIMESTAMP WITH TIME ZONE, - completed_at TIMESTAMP WITH TIME ZONE, - - -- Progress tracking - progress_percentage INTEGER DEFAULT 0 CHECK (progress_percentage BETWEEN 0 AND 100), - - -- Task metadata and customization - tags VARCHAR(50)[] DEFAULT '{}', - labels JSONB DEFAULT '{}', -- Flexible key-value labels - custom_fields JSONB DEFAULT '{}', -- Organization-specific fields - - -- Voice command integration (Phase 2) - voice_created BOOLEAN DEFAULT false, - voice_command_id UUID, -- Reference to voice command that created this task - voice_instructions TEXT, -- Original voice instruction for context - - -- AI and automation features (Phase 3-4) - ai_generated BOOLEAN DEFAULT false, - ai_suggestions JSONB DEFAULT '{}', -- AI suggestions for optimization - automation_rules JSONB DEFAULT '{}', -- Automated actions and triggers - - -- Collaboration features - watchers UUID[] DEFAULT '{}', -- Users who want notifications - comments_count INTEGER DEFAULT 0, - attachments_count INTEGER DEFAULT 0, - - -- Business context - business_value VARCHAR(20) DEFAULT 'medium' CHECK (business_value IN ('low', 'medium', 'high', 'critical')), - cost_center VARCHAR(50), - budget_impact DECIMAL(10,2), - - -- Quality assurance - acceptance_criteria TEXT, - definition_of_done TEXT, - quality_score INTEGER CHECK (quality_score BETWEEN 1 AND 10), - - -- Integration and external references - external_references JSONB DEFAULT '{}', -- Links to external systems - integrations JSONB DEFAULT '{ - "calendar_event": null, - "email_thread": null, - "document_links": [], - "meeting_recordings": [] - }', - - -- Recurrence (for recurring tasks) - recurrence_pattern JSONB DEFAULT NULL, -- Cron-like pattern for recurring tasks - recurrence_parent_id UUID, -- Parent task for recurring instances - is_recurring BOOLEAN DEFAULT false, - - -- Audit and tracking - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - last_activity_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id) -); - --- Performance indexes -CREATE INDEX IF NOT EXISTS idx_tasks_title ON tasks(title) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_channel_id ON tasks(channel_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_parent_task_id ON tasks(parent_task_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_created_by ON tasks(created_by) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_owned_by ON tasks(owned_by) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_status ON tasks(status) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_priority ON tasks(priority) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_due_date ON tasks(due_date) WHERE deleted_at IS NULL AND due_date IS NOT NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_completed_at ON tasks(completed_at) WHERE deleted_at IS NULL AND completed_at IS NOT NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_last_activity ON tasks(last_activity_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_created_at ON tasks(created_at DESC); - --- Multi-assignment queries (GIN index for array operations) -CREATE INDEX IF NOT EXISTS idx_tasks_assigned_to ON tasks USING gin(assigned_to) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_watchers ON tasks USING gin(watchers) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_tags ON tasks USING gin(tags) WHERE deleted_at IS NULL; - --- Full-text search index (removed for now due to IMMUTABLE requirement) --- CREATE INDEX IF NOT EXISTS idx_tasks_search ON tasks USING gin(to_tsvector('english', title || ' ' || COALESCE(description, '') || ' ' || array_to_string(tags, ' '))) WHERE deleted_at IS NULL; - --- JSONB indexes for metadata -CREATE INDEX IF NOT EXISTS idx_tasks_labels ON tasks USING gin(labels) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_custom_fields ON tasks USING gin(custom_fields) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_ai_suggestions ON tasks USING gin(ai_suggestions) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_external_refs ON tasks USING gin(external_references) WHERE deleted_at IS NULL; - --- Composite indexes for common queries -CREATE INDEX IF NOT EXISTS idx_tasks_channel_status ON tasks(channel_id, status) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_assignee_status ON tasks USING gin(assigned_to) WHERE status IN ('pending', 'in_progress') AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_priority_due_date ON tasks(priority, due_date) WHERE deleted_at IS NULL; --- Overdue tasks index (removed NOW() function as it's not immutable for index predicates) --- CREATE INDEX IF NOT EXISTS idx_tasks_overdue ON tasks(due_date) WHERE due_date < NOW() AND status NOT IN ('completed', 'cancelled') AND deleted_at IS NULL; - --- Voice integration indexes (for Phase 2) -CREATE INDEX IF NOT EXISTS idx_tasks_voice_created ON tasks(voice_created) WHERE voice_created = true AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_voice_command_id ON tasks(voice_command_id) WHERE voice_command_id IS NOT NULL AND deleted_at IS NULL; - --- Recurrence indexes -CREATE INDEX IF NOT EXISTS idx_tasks_recurring ON tasks(is_recurring) WHERE is_recurring = true AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_tasks_recurrence_parent ON tasks(recurrence_parent_id) WHERE recurrence_parent_id IS NOT NULL AND deleted_at IS NULL; - --- Constraint: subtasks cannot have subtasks beyond certain depth -CREATE OR REPLACE FUNCTION check_task_depth() -RETURNS TRIGGER AS $$ -DECLARE - depth INTEGER := 0; - current_parent UUID := NEW.parent_task_id; - max_depth INTEGER := 5; -- Maximum nesting depth -BEGIN - -- Check task nesting depth - WHILE current_parent IS NOT NULL AND depth < max_depth LOOP - SELECT parent_task_id INTO current_parent - FROM tasks - WHERE id = current_parent AND deleted_at IS NULL; - - depth := depth + 1; - END LOOP; - - IF depth >= max_depth THEN - RAISE EXCEPTION 'Task nesting depth exceeds maximum allowed (%))', max_depth; - END IF; - - -- Prevent self-reference - IF NEW.id = NEW.parent_task_id THEN - RAISE EXCEPTION 'Task cannot be its own parent'; - END IF; - - RETURN NEW; -END; -$$ LANGUAGE 'plpgsql'; - --- Trigger to validate task hierarchy -CREATE TRIGGER trigger_tasks_validate_hierarchy - BEFORE INSERT OR UPDATE OF parent_task_id ON tasks - FOR EACH ROW - WHEN (NEW.parent_task_id IS NOT NULL) - EXECUTE FUNCTION check_task_depth(); - --- Function to update task progress and completion -CREATE OR REPLACE FUNCTION update_task_completion() -RETURNS TRIGGER AS $$ -BEGIN - -- Auto-complete when progress reaches 100% - IF NEW.progress_percentage = 100 AND NEW.status != 'completed' THEN - NEW.status := 'completed'; - NEW.completed_at := NOW(); - END IF; - - -- Reset completion when moving away from completed status - IF NEW.status != 'completed' AND OLD.status = 'completed' THEN - NEW.completed_at := NULL; - IF NEW.progress_percentage = 100 THEN - NEW.progress_percentage := 90; -- Slightly less than complete - END IF; - END IF; - - -- Update last activity - NEW.last_activity_at := NOW(); - - RETURN NEW; -END; -$$ LANGUAGE 'plpgsql'; - --- Trigger for task completion logic -CREATE TRIGGER trigger_tasks_completion - BEFORE UPDATE OF progress_percentage, status ON tasks - FOR EACH ROW - EXECUTE FUNCTION update_task_completion(); - --- Trigger to update updated_at and version -CREATE TRIGGER trigger_tasks_updated_at - BEFORE UPDATE ON tasks - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - --- Function to assign task to users -CREATE OR REPLACE FUNCTION assign_task( - task_id UUID, - user_ids UUID[], - assigned_by UUID DEFAULT NULL -) RETURNS BOOLEAN AS $$ -DECLARE - current_assignees UUID[]; - new_assignees UUID[]; - user_id UUID; -BEGIN - -- Get current assignees - SELECT assigned_to INTO current_assignees - FROM tasks - WHERE id = task_id AND deleted_at IS NULL; - - IF NOT FOUND THEN - RAISE EXCEPTION 'Task not found'; - END IF; - - -- Combine current and new assignees (remove duplicates) - new_assignees := current_assignees; - FOREACH user_id IN ARRAY user_ids LOOP - IF NOT (user_id = ANY(new_assignees)) THEN - new_assignees := array_append(new_assignees, user_id); - END IF; - END LOOP; - - -- Update the task - UPDATE tasks - SET - assigned_to = new_assignees, - last_activity_at = NOW(), - -- Set primary owner to first assignee if not set - owned_by = COALESCE(owned_by, new_assignees[1]) - WHERE id = task_id; - - -- Log assignment (could be extended to an audit table) - INSERT INTO task_assignment_history (task_id, user_ids, action, performed_by, performed_at) - VALUES (task_id, user_ids, 'assigned', COALESCE(assigned_by, user_ids[1]), NOW()) - ON CONFLICT DO NOTHING; - - RETURN TRUE; -END; -$$ LANGUAGE 'plpgsql'; - --- Function to unassign users from task -CREATE OR REPLACE FUNCTION unassign_task( - task_id UUID, - user_ids UUID[], - unassigned_by UUID DEFAULT NULL -) RETURNS BOOLEAN AS $$ -DECLARE - current_assignees UUID[]; - new_assignees UUID[]; - user_id UUID; -BEGIN - -- Get current assignees - SELECT assigned_to INTO current_assignees - FROM tasks - WHERE id = task_id AND deleted_at IS NULL; - - IF NOT FOUND THEN - RAISE EXCEPTION 'Task not found'; - END IF; - - -- Remove specified users - new_assignees := current_assignees; - FOREACH user_id IN ARRAY user_ids LOOP - new_assignees := array_remove(new_assignees, user_id); - END LOOP; - - -- Update the task - UPDATE tasks - SET - assigned_to = new_assignees, - last_activity_at = NOW(), - -- Clear owned_by if owner was unassigned - owned_by = CASE - WHEN owned_by = ANY(user_ids) THEN new_assignees[1] - ELSE owned_by - END - WHERE id = task_id; - - -- Log unassignment - INSERT INTO task_assignment_history (task_id, user_ids, action, performed_by, performed_at) - VALUES (task_id, user_ids, 'unassigned', COALESCE(unassigned_by, user_ids[1]), NOW()) - ON CONFLICT DO NOTHING; - - RETURN TRUE; -END; -$$ LANGUAGE 'plpgsql'; - --- Function to get user's tasks with filters -CREATE OR REPLACE FUNCTION get_user_tasks( - user_id UUID, - task_status VARCHAR(20)[] DEFAULT NULL, - include_watching BOOLEAN DEFAULT true -) RETURNS TABLE( - task_id UUID, - title VARCHAR(300), - priority VARCHAR(20), - status VARCHAR(20), - due_date TIMESTAMP WITH TIME ZONE, - channel_name VARCHAR(200), - is_assigned BOOLEAN, - is_watching BOOLEAN -) AS $$ -BEGIN - RETURN QUERY - SELECT - t.id, - t.title, - t.priority, - t.status, - t.due_date, - c.name as channel_name, - (user_id = ANY(t.assigned_to)) as is_assigned, - (user_id = ANY(t.watchers)) as is_watching - FROM tasks t - LEFT JOIN channels c ON t.channel_id = c.id - WHERE t.deleted_at IS NULL - AND ( - user_id = ANY(t.assigned_to) OR - (include_watching AND user_id = ANY(t.watchers)) - ) - AND (task_status IS NULL OR t.status = ANY(task_status)) - ORDER BY - CASE t.priority - WHEN 'critical' THEN 5 - WHEN 'urgent' THEN 4 - WHEN 'high' THEN 3 - WHEN 'medium' THEN 2 - ELSE 1 - END DESC, - t.due_date ASC NULLS LAST, - t.created_at DESC; -END; -$$ LANGUAGE 'plpgsql'; - --- Function to get task hierarchy (parent and children) - TEMPORARILY DISABLED --- CREATE OR REPLACE FUNCTION get_task_hierarchy(root_task_id UUID) --- RETURNS TABLE( --- task_id UUID, --- title VARCHAR(300), --- parent_id UUID, --- level INTEGER, --- path TEXT --- ) AS $$ --- WITH RECURSIVE task_tree AS ( --- -- Root task --- SELECT --- id as task_id, --- title, --- parent_task_id as parent_id, --- 0 as level, --- title::TEXT as path --- FROM tasks --- WHERE id = root_task_id AND deleted_at IS NULL --- --- UNION ALL --- --- -- Child tasks --- SELECT --- t.id, --- t.title, --- t.parent_task_id, --- tt.level + 1, --- (tt.path || ' > ' || t.title)::TEXT --- FROM tasks t --- JOIN task_tree tt ON t.parent_task_id = tt.task_id --- WHERE t.deleted_at IS NULL --- AND tt.level < 10 -- Prevent infinite recursion --- ) --- SELECT * FROM task_tree ORDER BY level, title; --- $$ LANGUAGE 'sql'; - --- Soft delete function -CREATE OR REPLACE FUNCTION soft_delete_task() -RETURNS TRIGGER AS $$ -BEGIN - -- Also soft delete subtasks - UPDATE tasks - SET - deleted_at = NOW(), - deleted_by = COALESCE(current_setting('app.current_user_id', true)::UUID, OLD.owned_by), - status = 'cancelled' - WHERE parent_task_id = OLD.id AND deleted_at IS NULL; - - -- Soft delete the main task - UPDATE tasks - SET - deleted_at = NOW(), - deleted_by = COALESCE(current_setting('app.current_user_id', true)::UUID, OLD.owned_by), - status = 'cancelled' - WHERE id = OLD.id; - - RETURN NULL; -- Prevent actual delete -END; -$$ LANGUAGE 'plpgsql'; - --- Trigger for soft delete protection -CREATE TRIGGER trigger_tasks_soft_delete - BEFORE DELETE ON tasks - FOR EACH ROW - EXECUTE FUNCTION soft_delete_task(); - --- Create task assignment history table -CREATE TABLE IF NOT EXISTS task_assignment_history ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - task_id UUID NOT NULL REFERENCES tasks(id), - user_ids UUID[] NOT NULL, - action VARCHAR(20) NOT NULL CHECK (action IN ('assigned', 'unassigned', 'reassigned')), - performed_by UUID NOT NULL REFERENCES users(id), - performed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - metadata JSONB DEFAULT '{}' -); - -CREATE INDEX IF NOT EXISTS idx_task_assignment_history_task ON task_assignment_history(task_id); -CREATE INDEX IF NOT EXISTS idx_task_assignment_history_performed_at ON task_assignment_history(performed_at DESC); - --- Utility views -CREATE VIEW active_tasks AS -SELECT - t.id, - t.title, - t.description, - t.channel_id, - c.name as channel_name, - t.priority, - t.status, - t.progress_percentage, - t.due_date, - t.owned_by, - owner.name as owner_name, - array_length(t.assigned_to, 1) as assignee_count, - t.complexity, - t.estimated_hours, - t.actual_hours, - t.tags, - t.voice_created, - t.created_at, - t.updated_at, - t.last_activity_at -FROM tasks t -LEFT JOIN channels c ON t.channel_id = c.id -LEFT JOIN users owner ON t.owned_by = owner.id -WHERE t.deleted_at IS NULL; - --- Overdue tasks view -CREATE VIEW overdue_tasks AS -SELECT - t.id, - t.title, - t.channel_id, - c.name as channel_name, - t.priority, - t.due_date, - t.owned_by, - owner.name as owner_name, - t.assigned_to, - (NOW() - t.due_date) as overdue_duration, - t.progress_percentage -FROM tasks t -LEFT JOIN channels c ON t.channel_id = c.id -LEFT JOIN users owner ON t.owned_by = owner.id -WHERE t.deleted_at IS NULL -AND t.due_date < NOW() -AND t.status NOT IN ('completed', 'cancelled') -ORDER BY t.due_date ASC; - --- Task statistics view -CREATE VIEW task_statistics AS -SELECT - COUNT(*) as total_tasks, - COUNT(*) FILTER (WHERE status = 'completed') as completed_tasks, - COUNT(*) FILTER (WHERE status = 'in_progress') as in_progress_tasks, - COUNT(*) FILTER (WHERE status = 'pending') as pending_tasks, - COUNT(*) FILTER (WHERE due_date < NOW() AND status NOT IN ('completed', 'cancelled')) as overdue_tasks, - COUNT(*) FILTER (WHERE voice_created = true) as voice_created_tasks, - AVG(actual_hours) FILTER (WHERE actual_hours > 0) as avg_completion_hours, - AVG(progress_percentage) as avg_progress_percentage -FROM active_tasks; - --- Add self-referencing foreign key constraints after table creation -ALTER TABLE tasks ADD CONSTRAINT fk_tasks_parent_task_id - FOREIGN KEY (parent_task_id) REFERENCES tasks(id) ON DELETE CASCADE; - -ALTER TABLE tasks ADD CONSTRAINT fk_tasks_recurrence_parent_id - FOREIGN KEY (recurrence_parent_id) REFERENCES tasks(id); - -COMMENT ON TABLE tasks IS 'Advanced task management with multi-assignment, hierarchy, and voice integration'; -COMMENT ON COLUMN tasks.assigned_to IS 'Array of user IDs for multi-assignment support'; -COMMENT ON COLUMN tasks.voice_created IS 'Flag indicating task was created via voice command'; -COMMENT ON COLUMN tasks.ai_suggestions IS 'JSONB field for AI-generated optimization suggestions'; -COMMENT ON COLUMN tasks.recurrence_pattern IS 'JSONB field for recurring task patterns'; -COMMENT ON FUNCTION assign_task(UUID, UUID[], UUID) IS 'Assign multiple users to a task with audit trail'; -COMMENT ON FUNCTION get_user_tasks(UUID, VARCHAR(20)[], BOOLEAN) IS 'Get filtered tasks for a specific user'; --- COMMENT ON FUNCTION get_task_hierarchy(UUID) IS 'Get complete task hierarchy starting from root task'; -COMMENT ON VIEW active_tasks IS 'Active tasks with channel and owner information'; -COMMENT ON VIEW overdue_tasks IS 'Tasks that are past their due date and not completed'; -COMMENT ON VIEW task_statistics IS 'Aggregate statistics for task management dashboard'; \ No newline at end of file diff --git a/backend/migrations/1640995204000_create_messages_notifications.sql b/backend/migrations/1640995204000_create_messages_notifications.sql deleted file mode 100644 index ddd6d70..0000000 --- a/backend/migrations/1640995204000_create_messages_notifications.sql +++ /dev/null @@ -1,430 +0,0 @@ --- Create Messages and Notifications tables --- Real-time communication and notification system - --- Messages table for channel communication -CREATE TABLE IF NOT EXISTS messages ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - -- Message context - channel_id UUID REFERENCES channels(id) ON DELETE CASCADE, - task_id UUID REFERENCES tasks(id) ON DELETE CASCADE, -- Optional task-specific messages - user_id UUID NOT NULL REFERENCES users(id), - - -- Message content - content TEXT NOT NULL, - message_type VARCHAR(20) DEFAULT 'text' CHECK (message_type IN ('text', 'voice', 'file', 'system', 'command_result', 'ai_response')), - - -- Voice message support (Phase 2) - voice_data JSONB DEFAULT NULL, -- Voice message metadata (duration, transcription, etc.) - transcription TEXT, -- Auto-transcribed text from voice - - -- File attachments - attachments JSONB DEFAULT '[]', -- Array of file attachments with metadata - - -- Message relationships - reply_to UUID, -- Thread support - thread_root UUID, -- Root message of thread - - -- Message state - is_edited BOOLEAN DEFAULT false, - is_pinned BOOLEAN DEFAULT false, - is_announcement BOOLEAN DEFAULT false, - - -- Reactions and interactions - reactions JSONB DEFAULT '{}', -- Emoji reactions with user counts - mentions UUID[] DEFAULT '{}', -- Array of mentioned user IDs - - -- AI and automation features (Phase 3-4) - ai_generated BOOLEAN DEFAULT false, - ai_context JSONB DEFAULT NULL, -- AI processing context - command_execution_id UUID, -- Link to command execution for command results - - -- Metadata and customization - metadata JSONB DEFAULT '{}', - formatting JSONB DEFAULT '{}', -- Rich text formatting information - - -- Audit and tracking - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - edited_at TIMESTAMP WITH TIME ZONE, - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id) -); - --- Performance indexes for messages -CREATE INDEX IF NOT EXISTS idx_messages_channel_id ON messages(channel_id, created_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_messages_task_id ON messages(task_id, created_at DESC) WHERE deleted_at IS NULL AND task_id IS NOT NULL; -CREATE INDEX IF NOT EXISTS idx_messages_user_id ON messages(user_id, created_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_messages_reply_to ON messages(reply_to) WHERE deleted_at IS NULL AND reply_to IS NOT NULL; -CREATE INDEX IF NOT EXISTS idx_messages_thread_root ON messages(thread_root, created_at ASC) WHERE deleted_at IS NULL AND thread_root IS NOT NULL; -CREATE INDEX IF NOT EXISTS idx_messages_type ON messages(message_type) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at DESC); - --- Full-text search for message content (removed due to IMMUTABLE requirement) --- CREATE INDEX IF NOT EXISTS idx_messages_search ON messages USING gin(to_tsvector('english', content || ' ' || COALESCE(transcription, ''))) WHERE deleted_at IS NULL; - --- Mentions and reactions indexes -CREATE INDEX IF NOT EXISTS idx_messages_mentions ON messages USING gin(mentions) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_messages_reactions ON messages USING gin(reactions) WHERE deleted_at IS NULL; - --- AI and voice message indexes -CREATE INDEX IF NOT EXISTS idx_messages_ai_generated ON messages(ai_generated) WHERE ai_generated = true AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_messages_voice ON messages(message_type) WHERE message_type = 'voice' AND deleted_at IS NULL; - --- Pinned and announcement messages -CREATE INDEX IF NOT EXISTS idx_messages_pinned ON messages(channel_id, is_pinned) WHERE is_pinned = true AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_messages_announcements ON messages(channel_id, is_announcement) WHERE is_announcement = true AND deleted_at IS NULL; - --- Notifications table for multi-channel notification system -CREATE TABLE IF NOT EXISTS notifications ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - -- Notification target - user_id UUID NOT NULL REFERENCES users(id), - - -- Notification content - title VARCHAR(200) NOT NULL, - content TEXT, - - -- Notification classification - type VARCHAR(50) NOT NULL CHECK (type IN ( - 'task_assigned', 'task_updated', 'task_completed', 'task_overdue', - 'channel_invite', 'channel_message', 'channel_mention', - 'deadline_reminder', 'meeting_reminder', - 'voice_command_result', 'ai_suggestion', - 'system_alert', 'security_alert', - 'custom' - )), - - -- Context and references - entity_type VARCHAR(20) CHECK (entity_type IN ('task', 'channel', 'message', 'user', 'system')), - entity_id UUID, -- Reference to the related entity - - -- Priority and urgency - priority VARCHAR(20) DEFAULT 'medium' CHECK (priority IN ('low', 'medium', 'high', 'urgent', 'critical')), - urgency_score INTEGER DEFAULT 5 CHECK (urgency_score BETWEEN 1 AND 10), - - -- Delivery channels - delivery_channels JSONB DEFAULT '{ - "push": true, - "email": false, - "sms": false, - "in_app": true, - "websocket": true - }', - - -- Delivery tracking - delivery_status JSONB DEFAULT '{ - "push": {"sent": false, "delivered": false, "opened": false}, - "email": {"sent": false, "delivered": false, "opened": false}, - "sms": {"sent": false, "delivered": false}, - "in_app": {"seen": false, "read": false}, - "websocket": {"sent": false, "acknowledged": false} - }', - - -- Scheduling and timing - scheduled_for TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - expires_at TIMESTAMP WITH TIME ZONE, - - -- Interaction tracking - read_at TIMESTAMP WITH TIME ZONE, - acknowledged_at TIMESTAMP WITH TIME ZONE, - action_taken_at TIMESTAMP WITH TIME ZONE, - - -- Delivery attempts and retries - delivery_attempts INTEGER DEFAULT 0, - max_delivery_attempts INTEGER DEFAULT 3, - last_delivery_attempt TIMESTAMP WITH TIME ZONE, - - -- Grouping and batching - group_key VARCHAR(100), -- For batching similar notifications - batch_id UUID, -- For bulk operations - - -- Customization and metadata - metadata JSONB DEFAULT '{}', - action_buttons JSONB DEFAULT '[]', -- Action buttons for rich notifications - deep_link_url VARCHAR(500), -- Deep link for mobile apps - - -- AI and personalization (Phase 4) - personalization_data JSONB DEFAULT '{}', - ai_optimized BOOLEAN DEFAULT false, - send_time_optimized BOOLEAN DEFAULT false, - - -- Audit fields - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - sent_at TIMESTAMP WITH TIME ZONE, - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id) -); - --- Performance indexes for notifications -CREATE INDEX IF NOT EXISTS idx_notifications_user_id ON notifications(user_id, created_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_notifications_type ON notifications(type) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_notifications_priority ON notifications(priority, urgency_score DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_notifications_entity ON notifications(entity_type, entity_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_notifications_scheduled ON notifications(scheduled_for) WHERE scheduled_for IS NOT NULL AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_notifications_unread ON notifications(user_id) WHERE read_at IS NULL AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_notifications_group_key ON notifications(group_key, user_id) WHERE group_key IS NOT NULL AND deleted_at IS NULL; - --- Delivery status queries (removed NOW() function) --- CREATE INDEX IF NOT EXISTS idx_notifications_pending_delivery ON notifications(scheduled_for) WHERE sent_at IS NULL AND scheduled_for <= NOW() AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_notifications_failed_delivery ON notifications(delivery_attempts, last_delivery_attempt) WHERE delivery_attempts < max_delivery_attempts AND deleted_at IS NULL; - --- Expiration cleanup (removed NOW() function) --- CREATE INDEX IF NOT EXISTS idx_notifications_expired ON notifications(expires_at) WHERE expires_at IS NOT NULL AND expires_at < NOW(); - --- Update triggers for messages -CREATE TRIGGER trigger_messages_updated_at - BEFORE UPDATE ON messages - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - --- Update triggers for notifications -CREATE TRIGGER trigger_notifications_updated_at - BEFORE UPDATE ON notifications - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - --- Function to create notification -CREATE OR REPLACE FUNCTION create_notification( - user_id UUID, - title VARCHAR(200), - content TEXT, - notification_type VARCHAR(50), - entity_type VARCHAR(20) DEFAULT NULL, - entity_id UUID DEFAULT NULL, - priority VARCHAR(20) DEFAULT 'medium', - scheduled_for TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - delivery_channels JSONB DEFAULT NULL -) RETURNS UUID AS $$ -DECLARE - notification_id UUID; - user_settings JSONB; - default_channels JSONB; -BEGIN - -- Get user's notification preferences - SELECT notification_settings INTO user_settings - FROM users - WHERE id = user_id AND deleted_at IS NULL; - - -- Set default delivery channels based on user preferences - default_channels := COALESCE(delivery_channels, '{ - "push": true, - "email": false, - "sms": false, - "in_app": true, - "websocket": true - }'::JSONB); - - -- Override with user preferences if available - IF user_settings IS NOT NULL THEN - default_channels := jsonb_set( - default_channels, - '{push}', - COALESCE(user_settings->'push_notifications', 'true'::JSONB) - ); - default_channels := jsonb_set( - default_channels, - '{email}', - COALESCE(user_settings->'email_notifications', 'false'::JSONB) - ); - default_channels := jsonb_set( - default_channels, - '{sms}', - COALESCE(user_settings->'sms_notifications', 'false'::JSONB) - ); - END IF; - - -- Insert notification - INSERT INTO notifications ( - user_id, title, content, type, entity_type, entity_id, - priority, scheduled_for, delivery_channels - ) VALUES ( - user_id, title, content, notification_type, entity_type, entity_id, - priority, scheduled_for, default_channels - ) RETURNING id INTO notification_id; - - RETURN notification_id; -END; -$$ LANGUAGE 'plpgsql'; - --- Function to mark notification as read -CREATE OR REPLACE FUNCTION mark_notification_read( - notification_id UUID, - user_id UUID -) RETURNS BOOLEAN AS $$ -DECLARE - notification_found BOOLEAN := false; -BEGIN - UPDATE notifications - SET - read_at = NOW(), - delivery_status = jsonb_set( - delivery_status, - '{in_app,read}', - 'true'::JSONB - ) - WHERE id = notification_id - AND user_id = mark_notification_read.user_id - AND deleted_at IS NULL - AND read_at IS NULL; - - GET DIAGNOSTICS notification_found = ROW_COUNT; - RETURN notification_found > 0; -END; -$$ LANGUAGE 'plpgsql'; - --- Function to send message with automatic notifications -CREATE OR REPLACE FUNCTION send_message( - channel_id UUID, - user_id UUID, - content TEXT, - message_type VARCHAR(20) DEFAULT 'text', - reply_to UUID DEFAULT NULL, - mentions UUID[] DEFAULT NULL -) RETURNS UUID AS $$ -DECLARE - message_id UUID; - channel_members UUID[]; - mention_user_id UUID; - channel_name VARCHAR(200); -BEGIN - -- Insert the message - INSERT INTO messages ( - channel_id, user_id, content, message_type, reply_to, mentions - ) VALUES ( - channel_id, user_id, content, message_type, reply_to, mentions - ) RETURNING id INTO message_id; - - -- Get channel info - SELECT name, members INTO channel_name, channel_members - FROM channels - WHERE id = channel_id AND deleted_at IS NULL; - - -- Create notifications for mentions - IF mentions IS NOT NULL AND array_length(mentions, 1) > 0 THEN - FOREACH mention_user_id IN ARRAY mentions LOOP - IF mention_user_id != user_id THEN -- Don't notify self - PERFORM create_notification( - mention_user_id, - 'You were mentioned in ' || channel_name, - 'You were mentioned in a message: ' || left(content, 100) || '...', - 'channel_mention', - 'message', - message_id, - 'high' - ); - END IF; - END LOOP; - END IF; - - -- Update channel activity - UPDATE channels - SET last_activity_at = NOW() - WHERE id = channel_id; - - RETURN message_id; -END; -$$ LANGUAGE 'plpgsql'; - --- Soft delete functions -CREATE OR REPLACE FUNCTION soft_delete_message() -RETURNS TRIGGER AS $$ -BEGIN - UPDATE messages - SET deleted_at = NOW(), deleted_by = COALESCE(current_setting('app.current_user_id', true)::UUID, OLD.user_id) - WHERE id = OLD.id; - RETURN NULL; -END; -$$ LANGUAGE 'plpgsql'; - -CREATE OR REPLACE FUNCTION soft_delete_notification() -RETURNS TRIGGER AS $$ -BEGIN - UPDATE notifications - SET deleted_at = NOW(), deleted_by = COALESCE(current_setting('app.current_user_id', true)::UUID, OLD.user_id) - WHERE id = OLD.id; - RETURN NULL; -END; -$$ LANGUAGE 'plpgsql'; - --- Soft delete triggers -CREATE TRIGGER trigger_messages_soft_delete - BEFORE DELETE ON messages - FOR EACH ROW - EXECUTE FUNCTION soft_delete_message(); - -CREATE TRIGGER trigger_notifications_soft_delete - BEFORE DELETE ON notifications - FOR EACH ROW - EXECUTE FUNCTION soft_delete_notification(); - --- Utility views -CREATE VIEW active_messages AS -SELECT - m.id, - m.channel_id, - c.name as channel_name, - m.task_id, - m.user_id, - u.name as user_name, - u.avatar_url, - m.content, - m.message_type, - m.transcription, - m.reply_to, - m.is_edited, - m.is_pinned, - m.reactions, - m.mentions, - m.created_at, - m.updated_at -FROM messages m -LEFT JOIN channels c ON m.channel_id = c.id -LEFT JOIN users u ON m.user_id = u.id -WHERE m.deleted_at IS NULL; - -CREATE VIEW unread_notifications AS -SELECT - n.id, - n.user_id, - n.title, - n.content, - n.type, - n.priority, - n.urgency_score, - n.entity_type, - n.entity_id, - n.scheduled_for, - n.expires_at, - n.deep_link_url, - n.created_at, - (NOW() - n.created_at) as age -FROM notifications n -WHERE n.deleted_at IS NULL -AND n.read_at IS NULL -AND (n.expires_at IS NULL OR n.expires_at > NOW()) -ORDER BY n.urgency_score DESC, n.created_at DESC; - -COMMENT ON TABLE messages IS 'Real-time messaging system with voice support and rich features'; -COMMENT ON TABLE notifications IS 'Multi-channel notification system with delivery tracking'; -COMMENT ON COLUMN messages.voice_data IS 'JSONB field containing voice message metadata and transcription'; --- Add self-referencing foreign key constraints for messages after table creation -ALTER TABLE messages ADD CONSTRAINT fk_messages_reply_to - FOREIGN KEY (reply_to) REFERENCES messages(id) ON DELETE SET NULL; - -ALTER TABLE messages ADD CONSTRAINT fk_messages_thread_root - FOREIGN KEY (thread_root) REFERENCES messages(id) ON DELETE SET NULL; - -COMMENT ON COLUMN notifications.delivery_channels IS 'JSONB configuration for notification delivery methods'; -COMMENT ON COLUMN notifications.delivery_status IS 'JSONB tracking delivery status across all channels'; -COMMENT ON FUNCTION create_notification IS 'Create notification with user preference integration'; -COMMENT ON FUNCTION send_message IS 'Send message with automatic mention notifications'; -COMMENT ON VIEW unread_notifications IS 'Active unread notifications ordered by urgency'; \ No newline at end of file diff --git a/backend/migrations/1640995205000_create_voice_and_relationships.sql b/backend/migrations/1640995205000_create_voice_and_relationships.sql deleted file mode 100644 index 7207774..0000000 --- a/backend/migrations/1640995205000_create_voice_and_relationships.sql +++ /dev/null @@ -1,493 +0,0 @@ --- Create Voice Commands, Task Dependencies, and Relationship tables --- Foundation for Phase 2 voice processing and Phase 3 complex relationships - --- Voice Commands table (Phase 2 preparation) -CREATE TABLE IF NOT EXISTS voice_commands ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - -- Command source - user_id UUID NOT NULL REFERENCES users(id), - - -- Voice processing pipeline - transcript TEXT NOT NULL, - processed_transcript TEXT, -- Cleaned and corrected transcript - confidence_score DECIMAL(3,2) CHECK (confidence_score BETWEEN 0 AND 1), - language_detected VARCHAR(10) DEFAULT 'en-US', - - -- AI processing results - intent_analysis JSONB DEFAULT '{}', -- AI-analyzed intent and entities - actions_planned JSONB DEFAULT '[]', -- Array of planned actions - actions_executed JSONB DEFAULT '[]', -- Array of executed actions with results - - -- Execution tracking - execution_status VARCHAR(20) DEFAULT 'pending' CHECK (execution_status IN ('pending', 'processing', 'completed', 'failed', 'partial')), - processing_time_ms INTEGER, - - -- Context and environment - context JSONB DEFAULT '{}', -- Command context (current channel, active tasks, etc.) - device_info JSONB DEFAULT '{}', -- Device and environment information - - -- Audio metadata - audio_file_url VARCHAR(500), -- URL to stored audio file - audio_duration_ms INTEGER, - audio_quality_score DECIMAL(3,2), - - -- Error handling and debugging - error_details JSONB DEFAULT NULL, - debug_info JSONB DEFAULT '{}', - retry_count INTEGER DEFAULT 0, - - -- Performance and analytics - user_satisfaction_score INTEGER CHECK (user_satisfaction_score BETWEEN 1 AND 5), - correction_applied BOOLEAN DEFAULT false, - correction_text TEXT, - - -- Audit fields - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - processing_started_at TIMESTAMP WITH TIME ZONE, - processing_completed_at TIMESTAMP WITH TIME ZONE, - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id) -); - --- Performance indexes for voice commands -CREATE INDEX IF NOT EXISTS idx_voice_commands_user_id ON voice_commands(user_id, created_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_voice_commands_status ON voice_commands(execution_status) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_voice_commands_language ON voice_commands(language_detected) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_voice_commands_processing_time ON voice_commands(processing_time_ms) WHERE processing_time_ms IS NOT NULL AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_voice_commands_confidence ON voice_commands(confidence_score DESC) WHERE deleted_at IS NULL; - --- Full-text search on transcripts (removed due to IMMUTABLE requirement) --- CREATE INDEX IF NOT EXISTS idx_voice_commands_transcript ON voice_commands USING gin(to_tsvector('english', transcript || ' ' || COALESCE(processed_transcript, ''))) WHERE deleted_at IS NULL; - --- Analytics indexes -CREATE INDEX IF NOT EXISTS idx_voice_commands_satisfaction ON voice_commands(user_satisfaction_score) WHERE user_satisfaction_score IS NOT NULL AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_voice_commands_failed ON voice_commands(execution_status, error_details) WHERE execution_status = 'failed' AND deleted_at IS NULL; - --- Task Dependencies table (Phase 3 preparation) -CREATE TABLE IF NOT EXISTS task_dependencies ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - -- Dependency relationship - task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE, - depends_on_task_id UUID NOT NULL REFERENCES tasks(id) ON DELETE CASCADE, - - -- Dependency type and strength - dependency_type VARCHAR(20) NOT NULL CHECK (dependency_type IN ('blocks', 'requires', 'follows', 'parallel', 'optional')), - dependency_strength VARCHAR(20) DEFAULT 'strong' CHECK (dependency_strength IN ('weak', 'medium', 'strong', 'critical')), - - -- Scheduling impact - lag_time_hours INTEGER DEFAULT 0, -- Time delay between tasks - lead_time_hours INTEGER DEFAULT 0, -- Time buffer before dependent task - - -- Dependency metadata - description TEXT, - business_reason TEXT, -- Why this dependency exists - - -- Voice creation tracking (Phase 2) - voice_created BOOLEAN DEFAULT false, - voice_command_id UUID REFERENCES voice_commands(id), - - -- AI suggestions (Phase 4) - ai_suggested BOOLEAN DEFAULT false, - ai_confidence DECIMAL(3,2), - - -- Status and validation - is_active BOOLEAN DEFAULT true, - validation_status VARCHAR(20) DEFAULT 'valid' CHECK (validation_status IN ('valid', 'invalid', 'warning', 'needs_review')), - validation_notes TEXT, - - -- Audit fields - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - created_by UUID NOT NULL REFERENCES users(id), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id), - - -- Unique constraint to prevent duplicate dependencies - UNIQUE(task_id, depends_on_task_id) -); - --- Performance indexes for task dependencies -CREATE INDEX IF NOT EXISTS idx_task_dependencies_task_id ON task_dependencies(task_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_task_dependencies_depends_on ON task_dependencies(depends_on_task_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_task_dependencies_type ON task_dependencies(dependency_type) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_task_dependencies_active ON task_dependencies(is_active) WHERE is_active = true AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_task_dependencies_voice ON task_dependencies(voice_created) WHERE voice_created = true AND deleted_at IS NULL; - --- Composite index for dependency resolution -CREATE INDEX IF NOT EXISTS idx_task_dependencies_resolution ON task_dependencies(depends_on_task_id, dependency_type, is_active) WHERE deleted_at IS NULL; - --- Channel Relationships table (Phase 3 preparation) -CREATE TABLE IF NOT EXISTS channel_relationships ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - -- Relationship definition - channel_id UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE, - related_channel_id UUID NOT NULL REFERENCES channels(id) ON DELETE CASCADE, - - -- Relationship type - relationship_type VARCHAR(30) NOT NULL CHECK (relationship_type IN ('parent_child', 'collaborative', 'sequential', 'competitive', 'shared_resource', 'mirror')), - - -- Relationship properties - relationship_strength VARCHAR(20) DEFAULT 'medium' CHECK (relationship_strength IN ('weak', 'medium', 'strong')), - is_bidirectional BOOLEAN DEFAULT true, - - -- Data sharing and permissions - share_members BOOLEAN DEFAULT false, - share_tasks BOOLEAN DEFAULT false, - share_files BOOLEAN DEFAULT false, - permission_inheritance VARCHAR(20) DEFAULT 'none' CHECK (permission_inheritance IN ('none', 'read', 'write', 'admin')), - - -- Automation rules - sync_settings JSONB DEFAULT '{}', - auto_actions JSONB DEFAULT '[]', - - -- Voice creation tracking - voice_created BOOLEAN DEFAULT false, - voice_command_id UUID REFERENCES voice_commands(id), - - -- Status and metadata - is_active BOOLEAN DEFAULT true, - description TEXT, - metadata JSONB DEFAULT '{}', - - -- Audit fields - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - created_by UUID NOT NULL REFERENCES users(id), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id), - - -- Prevent self-reference and duplicate relationships - CHECK (channel_id != related_channel_id), - UNIQUE(channel_id, related_channel_id, relationship_type) -); - --- Performance indexes for channel relationships -CREATE INDEX IF NOT EXISTS idx_channel_relationships_channel ON channel_relationships(channel_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channel_relationships_related ON channel_relationships(related_channel_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channel_relationships_type ON channel_relationships(relationship_type) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channel_relationships_active ON channel_relationships(is_active) WHERE is_active = true AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_channel_relationships_bidirectional ON channel_relationships(is_bidirectional) WHERE is_bidirectional = true AND deleted_at IS NULL; - --- Shared Resources table (Phase 1 foundation, expanded in later phases) -CREATE TABLE IF NOT EXISTS shared_resources ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - -- Resource identification - name VARCHAR(300) NOT NULL, - description TEXT, - - -- Resource type and content - resource_type VARCHAR(30) NOT NULL CHECK (resource_type IN ('document', 'link', 'image', 'video', 'template', 'dataset', 'code', 'other')), - mime_type VARCHAR(100), - file_size BIGINT, - - -- Content storage - content_url VARCHAR(500), -- URL to stored content (S3, etc.) - content_data JSONB DEFAULT NULL, -- Inline content for small resources - preview_url VARCHAR(500), -- Preview/thumbnail URL - - -- Access control - visibility VARCHAR(20) DEFAULT 'channel' CHECK (visibility IN ('private', 'channel', 'organization', 'public')), - access_permissions JSONB DEFAULT '{"read": true, "write": false, "delete": false}', - - -- Version control - version_number VARCHAR(20) DEFAULT '1.0', - parent_resource_id UUID, - is_latest_version BOOLEAN DEFAULT true, - - -- Usage tracking - download_count INTEGER DEFAULT 0, - view_count INTEGER DEFAULT 0, - last_accessed_at TIMESTAMP WITH TIME ZONE, - - -- AI and automation features (Phase 4) - ai_tags VARCHAR(50)[] DEFAULT '{}', - ai_summary TEXT, - search_keywords VARCHAR(100)[] DEFAULT '{}', - - -- Integration and metadata - external_id VARCHAR(100), -- ID in external system - source_system VARCHAR(50), -- Where it came from - metadata JSONB DEFAULT '{}', - tags VARCHAR(50)[] DEFAULT '{}', - - -- Voice integration - voice_uploaded BOOLEAN DEFAULT false, - voice_command_id UUID REFERENCES voice_commands(id), - - -- Audit fields - uploaded_by UUID NOT NULL REFERENCES users(id), - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id) -); - --- Performance indexes for shared resources -CREATE INDEX IF NOT EXISTS idx_shared_resources_name ON shared_resources(name) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_shared_resources_type ON shared_resources(resource_type) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_shared_resources_uploaded_by ON shared_resources(uploaded_by) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_shared_resources_visibility ON shared_resources(visibility) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_shared_resources_latest ON shared_resources(is_latest_version) WHERE is_latest_version = true AND deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_shared_resources_parent ON shared_resources(parent_resource_id) WHERE parent_resource_id IS NOT NULL AND deleted_at IS NULL; - --- Full-text search (removed due to IMMUTABLE requirement) --- CREATE INDEX IF NOT EXISTS idx_shared_resources_search ON shared_resources USING gin(to_tsvector('english', name || ' ' || COALESCE(description, '') || ' ' || array_to_string(tags, ' '))) WHERE deleted_at IS NULL; - --- Tags and AI features -CREATE INDEX IF NOT EXISTS idx_shared_resources_tags ON shared_resources USING gin(tags) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_shared_resources_ai_tags ON shared_resources USING gin(ai_tags) WHERE deleted_at IS NULL; - --- Resource Links table (connects resources to entities) -CREATE TABLE IF NOT EXISTS resource_links ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - -- Link definition - resource_id UUID NOT NULL REFERENCES shared_resources(id) ON DELETE CASCADE, - entity_type VARCHAR(20) NOT NULL CHECK (entity_type IN ('channel', 'task', 'message', 'user', 'category')), - entity_id UUID NOT NULL, -- Polymorphic reference - - -- Link properties - link_type VARCHAR(30) DEFAULT 'attachment' CHECK (link_type IN ('attachment', 'reference', 'template', 'requirement', 'example', 'background')), - is_required BOOLEAN DEFAULT false, - display_order INTEGER DEFAULT 0, - - -- Access and permissions - inherited_permissions BOOLEAN DEFAULT true, - custom_permissions JSONB DEFAULT NULL, - - -- Metadata and context - link_context TEXT, -- Why this resource is linked - metadata JSONB DEFAULT '{}', - - -- Voice creation tracking - voice_created BOOLEAN DEFAULT false, - voice_command_id UUID REFERENCES voice_commands(id), - - -- Audit fields - created_by UUID NOT NULL REFERENCES users(id), - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id), - - -- Unique constraint to prevent duplicate links - UNIQUE(resource_id, entity_type, entity_id) -); - --- Performance indexes for resource links -CREATE INDEX IF NOT EXISTS idx_resource_links_resource ON resource_links(resource_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_resource_links_entity ON resource_links(entity_type, entity_id) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_resource_links_type ON resource_links(link_type) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_resource_links_required ON resource_links(is_required) WHERE is_required = true AND deleted_at IS NULL; - --- Update triggers for all new tables -CREATE TRIGGER trigger_voice_commands_updated_at - BEFORE UPDATE ON voice_commands - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - -CREATE TRIGGER trigger_task_dependencies_updated_at - BEFORE UPDATE ON task_dependencies - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - -CREATE TRIGGER trigger_channel_relationships_updated_at - BEFORE UPDATE ON channel_relationships - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - -CREATE TRIGGER trigger_shared_resources_updated_at - BEFORE UPDATE ON shared_resources - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - -CREATE TRIGGER trigger_resource_links_updated_at - BEFORE UPDATE ON resource_links - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - --- Functions for dependency management -CREATE OR REPLACE FUNCTION check_circular_dependency( - new_task_id UUID, - new_depends_on_id UUID -) RETURNS BOOLEAN AS $$ -DECLARE - current_task UUID := new_depends_on_id; - visited_tasks UUID[] := ARRAY[]::UUID[]; - depth INTEGER := 0; - max_depth INTEGER := 20; -BEGIN - -- Traverse dependency chain to detect cycles - WHILE current_task IS NOT NULL AND depth < max_depth LOOP - -- Check if we've seen this task before (cycle detected) - IF current_task = ANY(visited_tasks) OR current_task = new_task_id THEN - RETURN true; -- Circular dependency detected - END IF; - - -- Add current task to visited list - visited_tasks := array_append(visited_tasks, current_task); - - -- Get next task in dependency chain - SELECT depends_on_task_id INTO current_task - FROM task_dependencies - WHERE task_id = current_task - AND is_active = true - AND deleted_at IS NULL - LIMIT 1; - - depth := depth + 1; - END LOOP; - - RETURN false; -- No circular dependency -END; -$$ LANGUAGE 'plpgsql'; - --- Function to validate task dependency before insert/update -CREATE OR REPLACE FUNCTION validate_task_dependency() -RETURNS TRIGGER AS $$ -BEGIN - -- Prevent circular dependencies - IF check_circular_dependency(NEW.task_id, NEW.depends_on_task_id) THEN - RAISE EXCEPTION 'Circular dependency detected between tasks % and %', NEW.task_id, NEW.depends_on_task_id; - END IF; - - -- Prevent self-dependency - IF NEW.task_id = NEW.depends_on_task_id THEN - RAISE EXCEPTION 'Task cannot depend on itself'; - END IF; - - RETURN NEW; -END; -$$ LANGUAGE 'plpgsql'; - --- Trigger to validate dependencies -CREATE TRIGGER trigger_validate_task_dependency - BEFORE INSERT OR UPDATE ON task_dependencies - FOR EACH ROW - EXECUTE FUNCTION validate_task_dependency(); - --- Soft delete functions -CREATE OR REPLACE FUNCTION soft_delete_voice_command() -RETURNS TRIGGER AS $$ -BEGIN - UPDATE voice_commands - SET deleted_at = NOW(), deleted_by = COALESCE(current_setting('app.current_user_id', true)::UUID, OLD.user_id) - WHERE id = OLD.id; - RETURN NULL; -END; -$$ LANGUAGE 'plpgsql'; - --- Additional soft delete functions for other tables... -CREATE OR REPLACE FUNCTION soft_delete_generic() -RETURNS TRIGGER AS $$ -DECLARE - user_id_field UUID; -BEGIN - -- Try to find a user ID field for audit trail - user_id_field := COALESCE( - OLD.created_by, - OLD.user_id, - OLD.uploaded_by, - current_setting('app.current_user_id', true)::UUID - ); - - EXECUTE format('UPDATE %I SET deleted_at = NOW(), deleted_by = $1 WHERE id = $2', TG_TABLE_NAME) - USING user_id_field, OLD.id; - - RETURN NULL; -END; -$$ LANGUAGE 'plpgsql'; - --- Apply soft delete triggers -CREATE TRIGGER trigger_voice_commands_soft_delete - BEFORE DELETE ON voice_commands - FOR EACH ROW - EXECUTE FUNCTION soft_delete_voice_command(); - -CREATE TRIGGER trigger_task_dependencies_soft_delete - BEFORE DELETE ON task_dependencies - FOR EACH ROW - EXECUTE FUNCTION soft_delete_generic(); - -CREATE TRIGGER trigger_channel_relationships_soft_delete - BEFORE DELETE ON channel_relationships - FOR EACH ROW - EXECUTE FUNCTION soft_delete_generic(); - -CREATE TRIGGER trigger_shared_resources_soft_delete - BEFORE DELETE ON shared_resources - FOR EACH ROW - EXECUTE FUNCTION soft_delete_generic(); - -CREATE TRIGGER trigger_resource_links_soft_delete - BEFORE DELETE ON resource_links - FOR EACH ROW - EXECUTE FUNCTION soft_delete_generic(); - --- Utility views -CREATE VIEW voice_command_analytics AS -SELECT - DATE_TRUNC('day', created_at) as date, - COUNT(*) as total_commands, - COUNT(*) FILTER (WHERE execution_status = 'completed') as successful_commands, - COUNT(*) FILTER (WHERE execution_status = 'failed') as failed_commands, - AVG(processing_time_ms) as avg_processing_time, - AVG(confidence_score) as avg_confidence, - COUNT(DISTINCT user_id) as unique_users -FROM voice_commands -WHERE deleted_at IS NULL -AND created_at >= NOW() - INTERVAL '30 days' -GROUP BY DATE_TRUNC('day', created_at) -ORDER BY date DESC; - -CREATE VIEW task_dependency_graph AS -SELECT - td.task_id, - t.title as task_title, - td.depends_on_task_id, - dt.title as depends_on_title, - td.dependency_type, - td.dependency_strength, - td.is_active, - c.name as channel_name -FROM task_dependencies td -JOIN tasks t ON td.task_id = t.id -JOIN tasks dt ON td.depends_on_task_id = dt.id -LEFT JOIN channels c ON t.channel_id = c.id -WHERE td.deleted_at IS NULL -AND td.is_active = true -AND t.deleted_at IS NULL -AND dt.deleted_at IS NULL; - -COMMENT ON TABLE voice_commands IS 'Voice command processing pipeline with AI integration'; -COMMENT ON TABLE task_dependencies IS 'Task dependency management with circular reference prevention'; -COMMENT ON TABLE channel_relationships IS 'Complex channel relationships for organizational structure'; --- Add self-referencing foreign key constraint for shared_resources after table creation -ALTER TABLE shared_resources ADD CONSTRAINT fk_shared_resources_parent_resource_id - FOREIGN KEY (parent_resource_id) REFERENCES shared_resources(id); - -COMMENT ON TABLE shared_resources IS 'Shared resource management with version control'; -COMMENT ON TABLE resource_links IS 'Polymorphic links between resources and entities'; -COMMENT ON FUNCTION check_circular_dependency(UUID, UUID) IS 'Detect circular dependencies in task relationships'; -COMMENT ON VIEW voice_command_analytics IS 'Analytics for voice command performance and usage'; -COMMENT ON VIEW task_dependency_graph IS 'Visual representation of task dependencies'; \ No newline at end of file diff --git a/backend/migrations/1724121000000_increase_token_column_length.sql b/backend/migrations/1724121000000_increase_token_column_length.sql deleted file mode 100644 index 6477de7..0000000 --- a/backend/migrations/1724121000000_increase_token_column_length.sql +++ /dev/null @@ -1,15 +0,0 @@ --- Increase token column lengths to accommodate JWT tokens --- JWT tokens are typically 300-500 characters, much longer than VARCHAR(255) - --- Increase email verification token column length -ALTER TABLE users ALTER COLUMN email_verification_token TYPE TEXT; - --- Increase password reset token column length -ALTER TABLE users ALTER COLUMN password_reset_token TYPE TEXT; - --- Update the unique constraint for password reset tokens -DROP INDEX IF EXISTS idx_users_password_reset_token; -CREATE UNIQUE INDEX idx_users_password_reset_token ON users(password_reset_token) WHERE password_reset_token IS NOT NULL; - -COMMENT ON COLUMN users.email_verification_token IS 'JWT token for email verification (TEXT to accommodate full JWT length)'; -COMMENT ON COLUMN users.password_reset_token IS 'Token for password reset (TEXT to accommodate various token formats)'; \ No newline at end of file diff --git a/backend/migrations/1724122000000_create_file_management_tables.sql b/backend/migrations/1724122000000_create_file_management_tables.sql deleted file mode 100644 index 4eb0383..0000000 --- a/backend/migrations/1724122000000_create_file_management_tables.sql +++ /dev/null @@ -1,323 +0,0 @@ --- File Management System Tables Migration --- Phase 2 Implementation - Voice-driven file operations - --- Create organizations table first (required for foreign key constraints) -CREATE TABLE IF NOT EXISTS organizations ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name VARCHAR(255) NOT NULL, - domain VARCHAR(100), - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() -); - --- Files table for storing file metadata -CREATE TABLE IF NOT EXISTS files ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - name VARCHAR(255) NOT NULL, - original_name VARCHAR(255) NOT NULL, - size BIGINT NOT NULL CHECK (size > 0), - content_type VARCHAR(100) NOT NULL, - s3_key TEXT NOT NULL UNIQUE, - uploaded_by UUID NOT NULL, - organization_id UUID NOT NULL, - status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'uploading', 'completed', 'failed', 'deleted')), - description TEXT, - tags JSONB DEFAULT '[]', - download_count INTEGER DEFAULT 0, - error_message TEXT, - uploaded_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - last_downloaded TIMESTAMP WITH TIME ZONE, - - -- Foreign key constraints - CONSTRAINT fk_files_uploaded_by FOREIGN KEY (uploaded_by) REFERENCES users(id) ON DELETE SET NULL, - CONSTRAINT fk_files_organization FOREIGN KEY (organization_id) REFERENCES organizations(id) ON DELETE CASCADE -); - --- File entity links table for linking files to channels, tasks, users, projects -CREATE TABLE IF NOT EXISTS file_entity_links ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - file_id UUID NOT NULL, - entity_type VARCHAR(20) NOT NULL CHECK (entity_type IN ('channel', 'task', 'user', 'project')), - entity_id UUID NOT NULL, - link_type VARCHAR(20) NOT NULL DEFAULT 'attachment' CHECK (link_type IN ('attachment', 'share', 'reference')), - linked_by UUID NOT NULL, - linked_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - - -- Foreign key constraints - CONSTRAINT fk_file_links_file FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE, - CONSTRAINT fk_file_links_linked_by FOREIGN KEY (linked_by) REFERENCES users(id) ON DELETE SET NULL, - - -- Unique constraint to prevent duplicate links - UNIQUE(file_id, entity_type, entity_id, link_type) -); - --- File access logs table for analytics and audit -CREATE TABLE IF NOT EXISTS file_access_logs ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - file_id UUID NOT NULL, - user_id UUID NOT NULL, - action VARCHAR(20) NOT NULL CHECK (action IN ('download', 'view', 'share', 'delete')), - ip_address INET, - user_agent TEXT, - accessed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - - -- Foreign key constraints - CONSTRAINT fk_access_logs_file FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE, - CONSTRAINT fk_access_logs_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL -); - --- File sharing permissions table -CREATE TABLE IF NOT EXISTS file_sharing_permissions ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - file_id UUID NOT NULL, - shared_with_user_id UUID, - shared_with_channel_id UUID, - permission_level VARCHAR(20) NOT NULL DEFAULT 'view' CHECK (permission_level IN ('view', 'download', 'edit', 'admin')), - shared_by UUID NOT NULL, - expires_at TIMESTAMP WITH TIME ZONE, - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - - -- Foreign key constraints - CONSTRAINT fk_sharing_file FOREIGN KEY (file_id) REFERENCES files(id) ON DELETE CASCADE, - CONSTRAINT fk_sharing_user FOREIGN KEY (shared_with_user_id) REFERENCES users(id) ON DELETE CASCADE, - CONSTRAINT fk_sharing_channel FOREIGN KEY (shared_with_channel_id) REFERENCES channels(id) ON DELETE CASCADE, - CONSTRAINT fk_sharing_shared_by FOREIGN KEY (shared_by) REFERENCES users(id) ON DELETE SET NULL, - - -- Ensure either user or channel is specified, not both - CHECK ( - (shared_with_user_id IS NOT NULL AND shared_with_channel_id IS NULL) OR - (shared_with_user_id IS NULL AND shared_with_channel_id IS NOT NULL) - ) -); - - --- Create indexes for optimal query performance -CREATE INDEX IF NOT EXISTS idx_files_organization_status ON files(organization_id, status); -CREATE INDEX IF NOT EXISTS idx_files_uploaded_by ON files(uploaded_by); -CREATE INDEX IF NOT EXISTS idx_files_status ON files(status); -CREATE INDEX IF NOT EXISTS idx_files_uploaded_at ON files(uploaded_at DESC); -CREATE INDEX IF NOT EXISTS idx_files_tags ON files USING GIN(tags); -CREATE INDEX IF NOT EXISTS idx_files_name_search ON files USING GIN(to_tsvector('english', name || ' ' || COALESCE(description, ''))); - -CREATE INDEX IF NOT EXISTS idx_file_entity_links_file ON file_entity_links(file_id); -CREATE INDEX IF NOT EXISTS idx_file_entity_links_entity ON file_entity_links(entity_type, entity_id); -CREATE INDEX IF NOT EXISTS idx_file_entity_links_linked_by ON file_entity_links(linked_by); - -CREATE INDEX IF NOT EXISTS idx_file_access_logs_file ON file_access_logs(file_id); -CREATE INDEX IF NOT EXISTS idx_file_access_logs_user ON file_access_logs(user_id); -CREATE INDEX IF NOT EXISTS idx_file_access_logs_accessed_at ON file_access_logs(accessed_at DESC); - -CREATE INDEX IF NOT EXISTS idx_file_sharing_file ON file_sharing_permissions(file_id); -CREATE INDEX IF NOT EXISTS idx_file_sharing_user ON file_sharing_permissions(shared_with_user_id); -CREATE INDEX IF NOT EXISTS idx_file_sharing_channel ON file_sharing_permissions(shared_with_channel_id); - --- Add some sample data for testing (optional - only in development) -DO $$ -BEGIN - -- Only insert test data if we're not in production - IF current_setting('server_version_num')::integer >= 120000 THEN - -- Insert a test organization if none exists - INSERT INTO organizations (id, name, domain) - VALUES ('550e8400-e29b-41d4-a716-446655440000', 'CEO Communication Platform', 'ceocomm.platform') - ON CONFLICT (id) DO NOTHING; - END IF; -END $$; - --- Add file management specific functions - --- Function to cleanup expired file sharing permissions -CREATE OR REPLACE FUNCTION cleanup_expired_file_permissions() -RETURNS INTEGER AS $$ -DECLARE - deleted_count INTEGER; -BEGIN - DELETE FROM file_sharing_permissions - WHERE expires_at IS NOT NULL AND expires_at < NOW(); - - GET DIAGNOSTICS deleted_count = ROW_COUNT; - - RETURN deleted_count; -END; -$$ LANGUAGE plpgsql; - --- Function to get file statistics for an organization -CREATE OR REPLACE FUNCTION get_organization_file_stats(org_id UUID) -RETURNS TABLE ( - total_files BIGINT, - total_size BIGINT, - files_by_status JSONB, - top_uploaders JSONB, - recent_uploads BIGINT -) AS $$ -BEGIN - RETURN QUERY - WITH stats AS ( - SELECT - COUNT(*) as total_files, - SUM(f.size) as total_size, - jsonb_object_agg(f.status, status_counts.count) as files_by_status - FROM files f - LEFT JOIN ( - SELECT status, COUNT(*) as count - FROM files - WHERE organization_id = org_id AND status != 'deleted' - GROUP BY status - ) status_counts ON f.status = status_counts.status - WHERE f.organization_id = org_id AND f.status != 'deleted' - ), - top_users AS ( - SELECT jsonb_agg( - jsonb_build_object( - 'user_id', uploaded_by, - 'file_count', count, - 'total_size', total_size - ) ORDER BY count DESC - ) as top_uploaders - FROM ( - SELECT uploaded_by, COUNT(*) as count, SUM(size) as total_size - FROM files - WHERE organization_id = org_id AND status = 'completed' - GROUP BY uploaded_by - ORDER BY count DESC - LIMIT 10 - ) t - ), - recent AS ( - SELECT COUNT(*) as recent_uploads - FROM files - WHERE organization_id = org_id - AND uploaded_at >= NOW() - INTERVAL '7 days' - AND status = 'completed' - ) - SELECT - s.total_files, - s.total_size, - s.files_by_status, - tu.top_uploaders, - r.recent_uploads - FROM stats s - CROSS JOIN top_users tu - CROSS JOIN recent r; -END; -$$ LANGUAGE plpgsql; - --- Function to search files with full-text search -CREATE OR REPLACE FUNCTION search_files_fulltext( - org_id UUID, - search_query TEXT, - user_id UUID DEFAULT NULL, - file_limit INTEGER DEFAULT 20, - file_offset INTEGER DEFAULT 0 -) -RETURNS TABLE ( - id UUID, - name VARCHAR, - original_name VARCHAR, - size BIGINT, - content_type VARCHAR, - uploaded_by UUID, - uploaded_at TIMESTAMP WITH TIME ZONE, - description TEXT, - tags JSONB, - rank REAL -) AS $$ -BEGIN - RETURN QUERY - SELECT - f.id, - f.name, - f.original_name, - f.size, - f.content_type, - f.uploaded_by, - f.uploaded_at, - f.description, - f.tags, - ts_rank(to_tsvector('english', f.name || ' ' || COALESCE(f.description, '')), plainto_tsquery('english', search_query)) as rank - FROM files f - WHERE f.organization_id = org_id - AND f.status = 'completed' - AND (user_id IS NULL OR f.uploaded_by = user_id) - AND to_tsvector('english', f.name || ' ' || COALESCE(f.description, '')) @@ plainto_tsquery('english', search_query) - ORDER BY rank DESC, f.uploaded_at DESC - LIMIT file_limit - OFFSET file_offset; -END; -$$ LANGUAGE plpgsql; - --- Add audit trigger for file operations -CREATE OR REPLACE FUNCTION audit_file_changes() -RETURNS TRIGGER AS $$ -BEGIN - IF TG_OP = 'UPDATE' THEN - -- Log file status changes - IF OLD.status != NEW.status THEN - INSERT INTO audit_log ( - table_name, - operation, - record_id, - user_id, - old_values, - new_values, - timestamp - ) VALUES ( - 'files', - 'status_change', - NEW.id::TEXT, - NEW.uploaded_by, - jsonb_build_object('status', OLD.status), - jsonb_build_object('status', NEW.status), - NOW() - ); - END IF; - ELSIF TG_OP = 'DELETE' THEN - INSERT INTO audit_log ( - table_name, - operation, - record_id, - user_id, - old_values, - timestamp - ) VALUES ( - 'files', - 'delete', - OLD.id::TEXT, - OLD.uploaded_by, - row_to_json(OLD)::jsonb, - NOW() - ); - END IF; - - RETURN COALESCE(NEW, OLD); -END; -$$ LANGUAGE plpgsql; - --- Create audit trigger if audit_log table exists -DO $$ -BEGIN - IF EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name = 'audit_log') THEN - CREATE TRIGGER audit_file_changes_trigger - AFTER UPDATE OR DELETE ON files - FOR EACH ROW EXECUTE FUNCTION audit_file_changes(); - END IF; -END $$; - --- Add comments for documentation -COMMENT ON TABLE files IS 'Stores metadata for all uploaded files in the voice-driven system'; -COMMENT ON TABLE file_entity_links IS 'Links files to various entities like channels, tasks, users, and projects'; -COMMENT ON TABLE file_access_logs IS 'Audit trail for file access operations'; -COMMENT ON TABLE file_sharing_permissions IS 'Manages file sharing permissions with expiration support'; - -COMMENT ON COLUMN files.s3_key IS 'Unique S3 object key for file storage'; -COMMENT ON COLUMN files.tags IS 'JSON array of tags for categorization and search'; -COMMENT ON COLUMN files.status IS 'File upload and processing status'; - --- Migration complete notification -DO $$ -BEGIN - RAISE NOTICE 'File Management System tables created successfully'; - RAISE NOTICE 'Tables: files, file_entity_links, file_access_logs, file_sharing_permissions'; - RAISE NOTICE 'Indexes: Optimized for search, filtering, and performance'; - RAISE NOTICE 'Functions: cleanup_expired_file_permissions, get_organization_file_stats, search_files_fulltext'; -END $$; \ No newline at end of file diff --git a/backend/migrations/20250101000001_create_announcements_table.sql b/backend/migrations/20250101000001_create_announcements_table.sql deleted file mode 100644 index f185ce4..0000000 --- a/backend/migrations/20250101000001_create_announcements_table.sql +++ /dev/null @@ -1,73 +0,0 @@ --- Create announcements table -CREATE EXTENSION IF NOT EXISTS "uuid-ossp"; - -CREATE TABLE announcements ( - id UUID DEFAULT uuid_generate_v4() PRIMARY KEY, - title VARCHAR(200) NOT NULL, - content TEXT NOT NULL, - type VARCHAR(20) NOT NULL CHECK (type IN ('info', 'warning', 'success', 'error', 'feature', 'maintenance')), - priority VARCHAR(10) NOT NULL CHECK (priority IN ('low', 'medium', 'high', 'critical')), - target_audience VARCHAR(20) NOT NULL CHECK (target_audience IN ('all', 'admins', 'developers', 'designers', 'managers')), - scheduled_for TIMESTAMP WITH TIME ZONE, - expires_at TIMESTAMP WITH TIME ZONE, - action_button_text VARCHAR(50), - action_button_url TEXT, - image_url TEXT, - created_by UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, - published BOOLEAN DEFAULT false, - read_by JSONB DEFAULT '[]'::jsonb, - created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP, - version INTEGER DEFAULT 1, - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id) -); - --- Create indexes for better query performance -CREATE INDEX idx_announcements_created_by ON announcements(created_by); -CREATE INDEX idx_announcements_type ON announcements(type); -CREATE INDEX idx_announcements_priority ON announcements(priority); -CREATE INDEX idx_announcements_target_audience ON announcements(target_audience); -CREATE INDEX idx_announcements_published ON announcements(published); -CREATE INDEX idx_announcements_scheduled_for ON announcements(scheduled_for); -CREATE INDEX idx_announcements_expires_at ON announcements(expires_at); -CREATE INDEX idx_announcements_created_at ON announcements(created_at); -CREATE INDEX idx_announcements_deleted_at ON announcements(deleted_at); - --- Create a composite index for user-specific queries -CREATE INDEX idx_announcements_user_relevant ON announcements(published, target_audience, expires_at, scheduled_for) -WHERE deleted_at IS NULL; - --- Create trigger for updating updated_at timestamp -CREATE OR REPLACE FUNCTION update_announcements_updated_at() -RETURNS TRIGGER AS $$ -BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; -END; -$$ language 'plpgsql'; - -CREATE TRIGGER trigger_announcements_updated_at - BEFORE UPDATE ON announcements - FOR EACH ROW - EXECUTE FUNCTION update_announcements_updated_at(); - --- Add comments for documentation -COMMENT ON TABLE announcements IS 'System-wide announcements that can be targeted to specific user roles'; -COMMENT ON COLUMN announcements.id IS 'Unique identifier for the announcement'; -COMMENT ON COLUMN announcements.title IS 'Title of the announcement (max 200 chars)'; -COMMENT ON COLUMN announcements.content IS 'Full content/message of the announcement'; -COMMENT ON COLUMN announcements.type IS 'Type of announcement: info, warning, success, error, feature, maintenance'; -COMMENT ON COLUMN announcements.priority IS 'Priority level: low, medium, high, critical'; -COMMENT ON COLUMN announcements.target_audience IS 'Who should see this announcement: all, admins, developers, designers, managers'; -COMMENT ON COLUMN announcements.scheduled_for IS 'When the announcement should become visible (NULL = immediate)'; -COMMENT ON COLUMN announcements.expires_at IS 'When the announcement should stop being visible (NULL = never expires)'; -COMMENT ON COLUMN announcements.action_button_text IS 'Optional action button text'; -COMMENT ON COLUMN announcements.action_button_url IS 'Optional action button URL'; -COMMENT ON COLUMN announcements.image_url IS 'Optional image URL for the announcement'; -COMMENT ON COLUMN announcements.created_by IS 'User ID who created the announcement (typically CEO)'; -COMMENT ON COLUMN announcements.published IS 'Whether the announcement is published and visible'; -COMMENT ON COLUMN announcements.read_by IS 'JSON array of user IDs who have read this announcement'; -COMMENT ON COLUMN announcements.version IS 'Version number for optimistic locking'; -COMMENT ON COLUMN announcements.deleted_at IS 'Soft delete timestamp'; -COMMENT ON COLUMN announcements.deleted_by IS 'User ID who deleted the announcement'; \ No newline at end of file diff --git a/backend/migrations/20250101000002_create_activities_table.sql b/backend/migrations/20250101000002_create_activities_table.sql deleted file mode 100644 index 3aea012..0000000 --- a/backend/migrations/20250101000002_create_activities_table.sql +++ /dev/null @@ -1,84 +0,0 @@ --- Create Activities table for activity feed and notifications --- Foundation for user activity tracking and notifications - -CREATE TABLE IF NOT EXISTS activities ( - id UUID PRIMARY KEY DEFAULT gen_random_uuid(), - - -- References to related entities - channel_id UUID REFERENCES channels(id) ON DELETE CASCADE, - task_id UUID REFERENCES tasks(id) ON DELETE CASCADE, - user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, - - -- Activity classification - activity_type VARCHAR(30) NOT NULL CHECK (activity_type IN ( - 'message', 'task_created', 'task_updated', 'task_completed', 'task_assigned', - 'member_joined', 'member_left', 'file_uploaded', 'channel_updated', - 'channel_created', 'reaction_added', 'mention', 'voice_command', 'ai_response' - )), - - -- Activity content - title VARCHAR(300) NOT NULL, - description TEXT, - metadata JSONB DEFAULT '{}', - - -- Read tracking - read_by UUID[] DEFAULT '{}', -- Array of user IDs who have read this activity - - -- Priority and categorization - priority VARCHAR(10) DEFAULT 'low' CHECK (priority IN ('low', 'medium', 'high')), - category VARCHAR(10) DEFAULT 'system' CHECK (category IN ('task', 'channel', 'system', 'social')), - - -- Entity references (polymorphic) - referenced_entity_id UUID, - referenced_entity_type VARCHAR(20) CHECK (referenced_entity_type IN ('task', 'message', 'channel', 'user', 'file')), - - -- Compatibility fields for API routes - type VARCHAR(30) GENERATED ALWAYS AS (activity_type) STORED, - related_id UUID GENERATED ALWAYS AS (referenced_entity_id) STORED, - - -- Audit fields - created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - version INTEGER DEFAULT 1, - - -- Soft delete support - deleted_at TIMESTAMP WITH TIME ZONE, - deleted_by UUID REFERENCES users(id) -); - --- Performance indexes for activities -CREATE INDEX IF NOT EXISTS idx_activities_user_id ON activities(user_id, created_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_activities_channel_id ON activities(channel_id, created_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_activities_task_id ON activities(task_id, created_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_activities_type ON activities(activity_type) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_activities_category ON activities(category) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_activities_priority ON activities(priority) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_activities_created_at ON activities(created_at DESC) WHERE deleted_at IS NULL; - --- Composite indexes for common queries -CREATE INDEX IF NOT EXISTS idx_activities_user_channel ON activities(user_id, channel_id, created_at DESC) WHERE deleted_at IS NULL; -CREATE INDEX IF NOT EXISTS idx_activities_type_channel ON activities(activity_type, channel_id, created_at DESC) WHERE deleted_at IS NULL; - --- Index for read tracking queries -CREATE INDEX IF NOT EXISTS idx_activities_read_by ON activities USING gin(read_by) WHERE deleted_at IS NULL; - --- Update trigger for activities (drop if exists first) -DROP TRIGGER IF EXISTS trigger_activities_updated_at ON activities; -CREATE TRIGGER trigger_activities_updated_at - BEFORE UPDATE ON activities - FOR EACH ROW - EXECUTE FUNCTION update_updated_at_column(); - --- Soft delete trigger for activities (drop if exists first) -DROP TRIGGER IF EXISTS trigger_activities_soft_delete ON activities; -CREATE TRIGGER trigger_activities_soft_delete - BEFORE DELETE ON activities - FOR EACH ROW - EXECUTE FUNCTION soft_delete_generic(); - -COMMENT ON TABLE activities IS 'Activity feed and notifications for user actions'; -COMMENT ON COLUMN activities.activity_type IS 'Type of activity that occurred'; -COMMENT ON COLUMN activities.read_by IS 'Array of user IDs who have read this activity'; -COMMENT ON COLUMN activities.metadata IS 'Additional activity-specific data'; -COMMENT ON COLUMN activities.priority IS 'Activity priority for notification purposes'; -COMMENT ON COLUMN activities.category IS 'Activity category for filtering and organization'; \ No newline at end of file diff --git a/backend/package-lock.json b/backend/package-lock.json deleted file mode 100644 index 038fa17..0000000 --- a/backend/package-lock.json +++ /dev/null @@ -1,12458 +0,0 @@ -{ - "name": "ceo-communication-backend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "ceo-communication-backend", - "version": "1.0.0", - "license": "PRIVATE", - "dependencies": { - "@aws-sdk/client-s3": "^3.478.0", - "@aws-sdk/lib-storage": "^3.478.0", - "@aws-sdk/s3-request-presigner": "^3.478.0", - "@fastify/compress": "^6.5.0", - "@fastify/cors": "^8.4.2", - "@fastify/helmet": "^11.1.1", - "@fastify/jwt": "^7.2.4", - "@fastify/multipart": "^8.0.0", - "@fastify/rate-limit": "^9.1.0", - "@fastify/static": "^6.12.0", - "@fastify/swagger": "^8.12.0", - "@fastify/swagger-ui": "^2.1.0", - "@sinclair/typebox": "^0.31.28", - "@socket.io/redis-adapter": "^8.2.1", - "@types/nodemailer": "^7.0.0", - "ajv": "^8.12.0", - "ajv-formats": "^2.1.1", - "aws-sdk": "^2.1489.0", - "axios": "^1.6.2", - "bcryptjs": "^2.4.3", - "bull": "^4.12.2", - "compression": "^1.7.4", - "dotenv": "^16.3.1", - "fastify": "^4.24.3", - "form-data": "^4.0.0", - "helmet": "^7.1.0", - "ioredis": "^5.3.2", - "jsonwebtoken": "^9.0.2", - "mime-types": "^2.1.35", - "multer": "^1.4.5-lts.1", - "node-cron": "^3.0.3", - "node-pg-migrate": "^6.2.2", - "nodemailer": "^7.0.5", - "openai": "^4.20.1", - "pg": "^8.11.3", - "pino": "^8.16.2", - "redis": "^4.6.10", - "socket.io": "^4.7.4", - "stream-buffers": "^3.0.2", - "uuid": "^9.0.1", - "wav": "^1.0.2", - "winston": "^3.11.0", - "zod": "^3.22.4" - }, - "devDependencies": { - "@faker-js/faker": "^8.3.1", - "@types/bcryptjs": "^2.4.6", - "@types/compression": "^1.7.5", - "@types/jest": "^29.5.8", - "@types/jsonwebtoken": "^9.0.5", - "@types/mime-types": "^2.1.4", - "@types/multer": "^1.4.11", - "@types/node": "^20.9.0", - "@types/pg": "^8.10.7", - "@types/stream-buffers": "^3.0.7", - "@types/supertest": "^2.0.16", - "@types/uuid": "^9.0.7", - "@typescript-eslint/eslint-plugin": "^6.12.0", - "@typescript-eslint/parser": "^6.12.0", - "dotenv-cli": "^10.0.0", - "eslint": "^8.54.0", - "jest": "^29.7.0", - "nodemon": "^3.0.1", - "pino-pretty": "^13.1.1", - "prettier": "^3.1.0", - "rimraf": "^5.0.5", - "supertest": "^6.3.3", - "ts-jest": "^29.1.1", - "tsx": "^4.6.0", - "typescript": "^5.2.2" - }, - "engines": { - "node": ">=20.0.0", - "npm": ">=10.0.0" - } - }, - "node_modules/@ampproject/remapping": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", - "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@aws-crypto/crc32": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", - "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/crc32c": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz", - "integrity": "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz", - "integrity": "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha1-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", - "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-js": "^5.2.0", - "@aws-crypto/supports-web-crypto": "^5.2.0", - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "@aws-sdk/util-locate-window": "^3.0.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/sha256-js": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", - "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/util": "^5.2.0", - "@aws-sdk/types": "^3.222.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/@aws-crypto/supports-web-crypto": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", - "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", - "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "^3.222.0", - "@smithy/util-utf8": "^2.0.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", - "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", - "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", - "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^2.2.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@aws-sdk/client-s3": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-s3/-/client-s3-3.864.0.tgz", - "integrity": "sha512-QGYi9bWliewxumsvbJLLyx9WC0a4DP4F+utygBcq0zwPxaM0xDfBspQvP1dsepi7mW5aAjZmJ2+Xb7X0EhzJ/g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha1-browser": "5.2.0", - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.864.0", - "@aws-sdk/credential-provider-node": "3.864.0", - "@aws-sdk/middleware-bucket-endpoint": "3.862.0", - "@aws-sdk/middleware-expect-continue": "3.862.0", - "@aws-sdk/middleware-flexible-checksums": "3.864.0", - "@aws-sdk/middleware-host-header": "3.862.0", - "@aws-sdk/middleware-location-constraint": "3.862.0", - "@aws-sdk/middleware-logger": "3.862.0", - "@aws-sdk/middleware-recursion-detection": "3.862.0", - "@aws-sdk/middleware-sdk-s3": "3.864.0", - "@aws-sdk/middleware-ssec": "3.862.0", - "@aws-sdk/middleware-user-agent": "3.864.0", - "@aws-sdk/region-config-resolver": "3.862.0", - "@aws-sdk/signature-v4-multi-region": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.862.0", - "@aws-sdk/util-user-agent-browser": "3.862.0", - "@aws-sdk/util-user-agent-node": "3.864.0", - "@aws-sdk/xml-builder": "3.862.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.8.0", - "@smithy/eventstream-serde-browser": "^4.0.5", - "@smithy/eventstream-serde-config-resolver": "^4.1.3", - "@smithy/eventstream-serde-node": "^4.0.5", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-blob-browser": "^4.0.5", - "@smithy/hash-node": "^4.0.5", - "@smithy/hash-stream-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/md5-js": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.18", - "@smithy/middleware-retry": "^4.1.19", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.26", - "@smithy/util-defaults-mode-node": "^4.0.26", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "@smithy/util-waiter": "^4.0.7", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sesv2": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sesv2/-/client-sesv2-3.864.0.tgz", - "integrity": "sha512-pwn4/3bs7ccucS9sYpMbzptEhEFQQy8TXtmKNzmyY7OIDBGTiJrxsWYDTULO4nxsMmGXi39mSEowlK4QUCyC+w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.864.0", - "@aws-sdk/credential-provider-node": "3.864.0", - "@aws-sdk/middleware-host-header": "3.862.0", - "@aws-sdk/middleware-logger": "3.862.0", - "@aws-sdk/middleware-recursion-detection": "3.862.0", - "@aws-sdk/middleware-user-agent": "3.864.0", - "@aws-sdk/region-config-resolver": "3.862.0", - "@aws-sdk/signature-v4-multi-region": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.862.0", - "@aws-sdk/util-user-agent-browser": "3.862.0", - "@aws-sdk/util-user-agent-node": "3.864.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.8.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.18", - "@smithy/middleware-retry": "^4.1.19", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.26", - "@smithy/util-defaults-mode-node": "^4.0.26", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/client-sso": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/client-sso/-/client-sso-3.864.0.tgz", - "integrity": "sha512-THiOp0OpQROEKZ6IdDCDNNh3qnNn/kFFaTSOiugDpgcE5QdsOxh1/RXq7LmHpTJum3cmnFf8jG59PHcz9Tjnlw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.864.0", - "@aws-sdk/middleware-host-header": "3.862.0", - "@aws-sdk/middleware-logger": "3.862.0", - "@aws-sdk/middleware-recursion-detection": "3.862.0", - "@aws-sdk/middleware-user-agent": "3.864.0", - "@aws-sdk/region-config-resolver": "3.862.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.862.0", - "@aws-sdk/util-user-agent-browser": "3.862.0", - "@aws-sdk/util-user-agent-node": "3.864.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.8.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.18", - "@smithy/middleware-retry": "^4.1.19", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.26", - "@smithy/util-defaults-mode-node": "^4.0.26", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/core": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.864.0.tgz", - "integrity": "sha512-LFUREbobleHEln+Zf7IG83lAZwvHZG0stI7UU0CtwyuhQy5Yx0rKksHNOCmlM7MpTEbSCfntEhYi3jUaY5e5lg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@aws-sdk/xml-builder": "3.862.0", - "@smithy/core": "^3.8.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/property-provider": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/signature-v4": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-utf8": "^4.0.0", - "fast-xml-parser": "5.2.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-env": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.864.0.tgz", - "integrity": "sha512-StJPOI2Rt8UE6lYjXUpg6tqSZaM72xg46ljPg8kIevtBAAfdtq9K20qT/kSliWGIBocMFAv0g2mC0hAa+ECyvg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-http": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.864.0.tgz", - "integrity": "sha512-E/RFVxGTuGnuD+9pFPH2j4l6HvrXzPhmpL8H8nOoJUosjx7d4v93GJMbbl1v/fkDLqW9qN4Jx2cI6PAjohA6OA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/property-provider": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/util-stream": "^4.2.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-ini": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.864.0.tgz", - "integrity": "sha512-PlxrijguR1gxyPd5EYam6OfWLarj2MJGf07DvCx9MAuQkw77HBnsu6+XbV8fQriFuoJVTBLn9ROhMr/ROAYfUg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/credential-provider-env": "3.864.0", - "@aws-sdk/credential-provider-http": "3.864.0", - "@aws-sdk/credential-provider-process": "3.864.0", - "@aws-sdk/credential-provider-sso": "3.864.0", - "@aws-sdk/credential-provider-web-identity": "3.864.0", - "@aws-sdk/nested-clients": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-node": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.864.0.tgz", - "integrity": "sha512-2BEymFeXURS+4jE9tP3vahPwbYRl0/1MVaFZcijj6pq+nf5EPGvkFillbdBRdc98ZI2NedZgSKu3gfZXgYdUhQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/credential-provider-env": "3.864.0", - "@aws-sdk/credential-provider-http": "3.864.0", - "@aws-sdk/credential-provider-ini": "3.864.0", - "@aws-sdk/credential-provider-process": "3.864.0", - "@aws-sdk/credential-provider-sso": "3.864.0", - "@aws-sdk/credential-provider-web-identity": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-process": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.864.0.tgz", - "integrity": "sha512-Zxnn1hxhq7EOqXhVYgkF4rI9MnaO3+6bSg/tErnBQ3F8kDpA7CFU24G1YxwaJXp2X4aX3LwthefmSJHwcVP/2g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-sso": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.864.0.tgz", - "integrity": "sha512-UPyPNQbxDwHVGmgWdGg9/9yvzuedRQVF5jtMkmP565YX9pKZ8wYAcXhcYdNPWFvH0GYdB0crKOmvib+bmCuwkw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/client-sso": "3.864.0", - "@aws-sdk/core": "3.864.0", - "@aws-sdk/token-providers": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/credential-provider-web-identity": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.864.0.tgz", - "integrity": "sha512-nNcjPN4SYg8drLwqK0vgVeSvxeGQiD0FxOaT38mV2H8cu0C5NzpvA+14Xy+W6vT84dxgmJYKk71Cr5QL2Oz+rA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/nested-clients": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/lib-storage": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/lib-storage/-/lib-storage-3.864.0.tgz", - "integrity": "sha512-Me/HlMXXPv3tStPQufdwnYGholY14JmmzCdOjhnG7gnaClBEnroZKcHuQhrgMm+KyfbzCQ2+9YHsULOfFrg7Mw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.18", - "@smithy/smithy-client": "^4.4.10", - "buffer": "5.6.0", - "events": "3.3.0", - "stream-browserify": "3.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "@aws-sdk/client-s3": "^3.864.0" - } - }, - "node_modules/@aws-sdk/lib-storage/node_modules/buffer": { - "version": "5.6.0", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", - "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4" - } - }, - "node_modules/@aws-sdk/lib-storage/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/@aws-sdk/middleware-bucket-endpoint": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.862.0.tgz", - "integrity": "sha512-Wcsc7VPLjImQw+CP1/YkwyofMs9Ab6dVq96iS8p0zv0C6YTaMjvillkau4zFfrrrTshdzFWKptIFhKK8Zsei1g==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-expect-continue": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.862.0.tgz", - "integrity": "sha512-oG3AaVUJ+26p0ESU4INFn6MmqqiBFZGrebST66Or+YBhteed2rbbFl7mCfjtPWUFgquQlvT1UP19P3LjQKeKpw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-flexible-checksums": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.864.0.tgz", - "integrity": "sha512-MvakvzPZi9uyP3YADuIqtk/FAcPFkyYFWVVMf5iFs/rCdk0CUzn02Qf4CSuyhbkS6Y0KrAsMgKR4MgklPU79Wg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@aws-crypto/crc32c": "5.2.0", - "@aws-crypto/util": "5.2.0", - "@aws-sdk/core": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-host-header": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.862.0.tgz", - "integrity": "sha512-jDje8dCFeFHfuCAxMDXBs8hy8q9NCTlyK4ThyyfAj3U4Pixly2mmzY2u7b7AyGhWsjJNx8uhTjlYq5zkQPQCYw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-location-constraint": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.862.0.tgz", - "integrity": "sha512-MnwLxCw7Cc9OngEH3SHFhrLlDI9WVxaBkp3oTsdY9JE7v8OE38wQ9vtjaRsynjwu0WRtrctSHbpd7h/QVvtjyA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-logger": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.862.0.tgz", - "integrity": "sha512-N/bXSJznNBR/i7Ofmf9+gM6dx/SPBK09ZWLKsW5iQjqKxAKn/2DozlnE54uiEs1saHZWoNDRg69Ww4XYYSlG1Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-recursion-detection": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.862.0.tgz", - "integrity": "sha512-KVoo3IOzEkTq97YKM4uxZcYFSNnMkhW/qj22csofLegZi5fk90ztUnnaeKfaEJHfHp/tm1Y3uSoOXH45s++kKQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-sdk-s3": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.864.0.tgz", - "integrity": "sha512-GjYPZ6Xnqo17NnC8NIQyvvdzzO7dm+Ks7gpxD/HsbXPmV2aEfuFveJXneGW9e1BheSKFff6FPDWu8Gaj2Iu1yg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-arn-parser": "3.804.0", - "@smithy/core": "^3.8.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/signature-v4": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-ssec": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-ssec/-/middleware-ssec-3.862.0.tgz", - "integrity": "sha512-72VtP7DZC8lYTE2L3Efx2BrD98oe9WTK8X6hmd3WTLkbIjvgWQWIdjgaFXBs8WevsXkewIctfyA3KEezvL5ggw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/middleware-user-agent": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.864.0.tgz", - "integrity": "sha512-wrddonw4EyLNSNBrApzEhpSrDwJiNfjxDm5E+bn8n32BbAojXASH8W8jNpxz/jMgNkkJNxCfyqybGKzBX0OhbQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.862.0", - "@smithy/core": "^3.8.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/nested-clients": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.864.0.tgz", - "integrity": "sha512-H1C+NjSmz2y8Tbgh7Yy89J20yD/hVyk15hNoZDbCYkXg0M358KS7KVIEYs8E2aPOCr1sK3HBE819D/yvdMgokA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/sha256-browser": "5.2.0", - "@aws-crypto/sha256-js": "5.2.0", - "@aws-sdk/core": "3.864.0", - "@aws-sdk/middleware-host-header": "3.862.0", - "@aws-sdk/middleware-logger": "3.862.0", - "@aws-sdk/middleware-recursion-detection": "3.862.0", - "@aws-sdk/middleware-user-agent": "3.864.0", - "@aws-sdk/region-config-resolver": "3.862.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-endpoints": "3.862.0", - "@aws-sdk/util-user-agent-browser": "3.862.0", - "@aws-sdk/util-user-agent-node": "3.864.0", - "@smithy/config-resolver": "^4.1.5", - "@smithy/core": "^3.8.0", - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/hash-node": "^4.0.5", - "@smithy/invalid-dependency": "^4.0.5", - "@smithy/middleware-content-length": "^4.0.5", - "@smithy/middleware-endpoint": "^4.1.18", - "@smithy/middleware-retry": "^4.1.19", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-body-length-node": "^4.0.0", - "@smithy/util-defaults-mode-browser": "^4.0.26", - "@smithy/util-defaults-mode-node": "^4.0.26", - "@smithy/util-endpoints": "^3.0.7", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/region-config-resolver": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.862.0.tgz", - "integrity": "sha512-VisR+/HuVFICrBPY+q9novEiE4b3mvDofWqyvmxHcWM7HumTz9ZQSuEtnlB/92GVM3KDUrR9EmBHNRrfXYZkcQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/s3-request-presigner": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/s3-request-presigner/-/s3-request-presigner-3.864.0.tgz", - "integrity": "sha512-IiVFDxabrqTB1A9qZI6IEa3cOgF2eciUG4UX27HzkMY6UXG0EZhnGkgkgHYMt6j2hGAFOvAh0ogv/XxZLg6Zaw==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/signature-v4-multi-region": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@aws-sdk/util-format-url": "3.862.0", - "@smithy/middleware-endpoint": "^4.1.18", - "@smithy/protocol-http": "^5.1.3", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/signature-v4-multi-region": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.864.0.tgz", - "integrity": "sha512-w2HIn/WIcUyv1bmyCpRUKHXB5KdFGzyxPkp/YK5g+/FuGdnFFYWGfcO8O+How4jwrZTarBYsAHW9ggoKvwr37w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-sdk-s3": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/signature-v4": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/token-providers": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.864.0.tgz", - "integrity": "sha512-gTc2QHOBo05SCwVA65dUtnJC6QERvFaPiuppGDSxoF7O5AQNK0UR/kMSenwLqN8b5E1oLYvQTv3C1idJLRX0cg==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/core": "3.864.0", - "@aws-sdk/nested-clients": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/types": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.862.0.tgz", - "integrity": "sha512-Bei+RL0cDxxV+lW2UezLbCYYNeJm6Nzee0TpW0FfyTRBhH9C1XQh4+x+IClriXvgBnRquTMMYsmJfvx8iyLKrg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-arn-parser": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-arn-parser/-/util-arn-parser-3.804.0.tgz", - "integrity": "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-endpoints": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.862.0.tgz", - "integrity": "sha512-eCZuScdE9MWWkHGM2BJxm726MCmWk/dlHjOKvkM0sN1zxBellBMw5JohNss1Z8/TUmnW2gb9XHTOiHuGjOdksA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-endpoints": "^3.0.7", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-format-url": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.862.0.tgz", - "integrity": "sha512-4kd2PYUMA/fAnIcVVwBIDCa2KCuUPrS3ELgScLjBaESP0NN+K163m40U5RbzNec/elOcJHR8lEThzzSb7vXH6w==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/querystring-builder": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-locate-window": { - "version": "3.804.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.804.0.tgz", - "integrity": "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@aws-sdk/util-user-agent-browser": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.862.0.tgz", - "integrity": "sha512-BmPTlm0r9/10MMr5ND9E92r8KMZbq5ltYXYpVcUbAsnB1RJ8ASJuRoLne5F7mB3YMx0FJoOTuSq7LdQM3LgW3Q==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/types": "3.862.0", - "@smithy/types": "^4.3.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - } - }, - "node_modules/@aws-sdk/util-user-agent-node": { - "version": "3.864.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.864.0.tgz", - "integrity": "sha512-d+FjUm2eJEpP+FRpVR3z6KzMdx1qwxEYDz8jzNKwxYLBBquaBaP/wfoMtMQKAcbrR7aT9FZVZF7zDgzNxUvQlQ==", - "license": "Apache-2.0", - "dependencies": { - "@aws-sdk/middleware-user-agent": "3.864.0", - "@aws-sdk/types": "3.862.0", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - }, - "peerDependencies": { - "aws-crt": ">=1.0.0" - }, - "peerDependenciesMeta": { - "aws-crt": { - "optional": true - } - } - }, - "node_modules/@aws-sdk/xml-builder": { - "version": "3.862.0", - "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.862.0.tgz", - "integrity": "sha512-6Ed0kmC1NMbuFTEgNmamAUU1h5gShgxL1hBVLbEzUa3trX5aJBz1vU4bXaBTvOYUAnOHtiy1Ml4AMStd6hJnFA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", - "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.3.tgz", - "integrity": "sha512-yDBHV9kQNcr2/sUr9jghVyz9C3Y5G2zUM2H2lo+9mKv4sFgbA8s8Z9t8D1jiTkGoO/NoIfKMyKWr4s6CN23ZwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.3", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.3", - "@babel/types": "^7.28.2", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.3.tgz", - "integrity": "sha512-PTNtvUQihsAsDHMOP5pfobP8C6CM4JWXmP8DrEIt46c3r2bf87Ua1zoqevsMo9g+tWDwgWrFP5EIxuBx5RudAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.3.tgz", - "integrity": "sha512-7+Ey1mAgYqFAx2h0RuoxcQT5+MlG3GTV0TQrgr7/ZliKsm/MNDxVVutlWaziMq7wJNAz8MTqz55XLpWvva6StA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", - "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.12.13" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-class-static-block": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", - "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-attributes": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", - "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-import-meta": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", - "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", - "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", - "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", - "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.10.4" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.8.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-private-property-in-object": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", - "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-top-level-await": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", - "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.14.5" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", - "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.3", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.3.tgz", - "integrity": "sha512-7w4kZYHneL3A6NP2nxzHvT3HCZ7puDZZjFMqDpBPECub79sTtSO5CGXDkKrTQq8ksAwfD/XI2MRFX23njdDaIQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.3", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.2", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", - "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", - "license": "MIT", - "engines": { - "node": ">=0.1.90" - } - }, - "node_modules/@dabh/diagnostics": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz", - "integrity": "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA==", - "license": "MIT", - "dependencies": { - "colorspace": "1.1.x", - "enabled": "2.0.x", - "kuler": "^2.0.0" - } - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.9.tgz", - "integrity": "sha512-OaGtL73Jck6pBKjNIe24BnFE6agGl+6KxDtTfHhy1HmhthfKouEcOhqpSL64K4/0WCtbKFLOdzD/44cJ4k9opA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.9.tgz", - "integrity": "sha512-5WNI1DaMtxQ7t7B6xa572XMXpHAaI/9Hnhk8lcxF4zVN4xstUgTlvuGDorBguKEnZO70qwEcLpfifMLoxiPqHQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.9.tgz", - "integrity": "sha512-IDrddSmpSv51ftWslJMvl3Q2ZT98fUSL2/rlUXuVqRXHCs5EUF1/f+jbjF5+NG9UffUDMCiTyh8iec7u8RlTLg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.9.tgz", - "integrity": "sha512-I853iMZ1hWZdNllhVZKm34f4wErd4lMyeV7BLzEExGEIZYsOzqDWDf+y082izYUE8gtJnYHdeDpN/6tUdwvfiw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.9.tgz", - "integrity": "sha512-XIpIDMAjOELi/9PB30vEbVMs3GV1v2zkkPnuyRRURbhqjyzIINwj+nbQATh4H9GxUgH1kFsEyQMxwiLFKUS6Rg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.9.tgz", - "integrity": "sha512-jhHfBzjYTA1IQu8VyrjCX4ApJDnH+ez+IYVEoJHeqJm9VhG9Dh2BYaJritkYK3vMaXrf7Ogr/0MQ8/MeIefsPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.9.tgz", - "integrity": "sha512-z93DmbnY6fX9+KdD4Ue/H6sYs+bhFQJNCPZsi4XWJoYblUqT06MQUdBCpcSfuiN72AbqeBFu5LVQTjfXDE2A6Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.9.tgz", - "integrity": "sha512-mrKX6H/vOyo5v71YfXWJxLVxgy1kyt1MQaD8wZJgJfG4gq4DpQGpgTB74e5yBeQdyMTbgxp0YtNj7NuHN0PoZg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.9.tgz", - "integrity": "sha512-HBU2Xv78SMgaydBmdor38lg8YDnFKSARg1Q6AT0/y2ezUAKiZvc211RDFHlEZRFNRVhcMamiToo7bDx3VEOYQw==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.9.tgz", - "integrity": "sha512-BlB7bIcLT3G26urh5Dmse7fiLmLXnRlopw4s8DalgZ8ef79Jj4aUcYbk90g8iCa2467HX8SAIidbL7gsqXHdRw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.9.tgz", - "integrity": "sha512-e7S3MOJPZGp2QW6AK6+Ly81rC7oOSerQ+P8L0ta4FhVi+/j/v2yZzx5CqqDaWjtPFfYz21Vi1S0auHrap3Ma3A==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.9.tgz", - "integrity": "sha512-Sbe10Bnn0oUAB2AalYztvGcK+o6YFFA/9829PhOCUS9vkJElXGdphz0A3DbMdP8gmKkqPmPcMJmJOrI3VYB1JQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.9.tgz", - "integrity": "sha512-YcM5br0mVyZw2jcQeLIkhWtKPeVfAerES5PvOzaDxVtIyZ2NUBZKNLjC5z3/fUlDgT6w89VsxP2qzNipOaaDyA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.9.tgz", - "integrity": "sha512-++0HQvasdo20JytyDpFvQtNrEsAgNG2CY1CLMwGXfFTKGBGQT3bOeLSYE2l1fYdvML5KUuwn9Z8L1EWe2tzs1w==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.9.tgz", - "integrity": "sha512-uNIBa279Y3fkjV+2cUjx36xkx7eSjb8IvnL01eXUKXez/CBHNRw5ekCGMPM0BcmqBxBcdgUWuUXmVWwm4CH9kg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.9.tgz", - "integrity": "sha512-Mfiphvp3MjC/lctb+7D287Xw1DGzqJPb/J2aHHcHxflUo+8tmN/6d4k6I2yFR7BVo5/g7x2Monq4+Yew0EHRIA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.9.tgz", - "integrity": "sha512-iSwByxzRe48YVkmpbgoxVzn76BXjlYFXC7NvLYq+b+kDjyyk30J0JY47DIn8z1MO3K0oSl9fZoRmZPQI4Hklzg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.9.tgz", - "integrity": "sha512-9jNJl6FqaUG+COdQMjSCGW4QiMHH88xWbvZ+kRVblZsWrkXlABuGdFJ1E9L7HK+T0Yqd4akKNa/lO0+jDxQD4Q==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.9.tgz", - "integrity": "sha512-RLLdkflmqRG8KanPGOU7Rpg829ZHu8nFy5Pqdi9U01VYtG9Y0zOG6Vr2z4/S+/3zIyOxiK6cCeYNWOFR9QP87g==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.9.tgz", - "integrity": "sha512-YaFBlPGeDasft5IIM+CQAhJAqS3St3nJzDEgsgFixcfZeyGPCd6eJBWzke5piZuZ7CtL656eOSYKk4Ls2C0FRQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.9.tgz", - "integrity": "sha512-1MkgTCuvMGWuqVtAvkpkXFmtL8XhWy+j4jaSO2wxfJtilVCi0ZE37b8uOdMItIHz4I6z1bWWtEX4CJwcKYLcuA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.9.tgz", - "integrity": "sha512-4Xd0xNiMVXKh6Fa7HEJQbrpP3m3DDn43jKxMjxLLRjWnRsfxjORYJlXPO4JNcXtOyfajXorRKY9NkOpTHptErg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.9.tgz", - "integrity": "sha512-WjH4s6hzo00nNezhp3wFIAfmGZ8U7KtrJNlFMRKxiI9mxEK1scOMAaa9i4crUtu+tBr+0IN6JCuAcSBJZfnphw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.9.tgz", - "integrity": "sha512-mGFrVJHmZiRqmP8xFOc6b84/7xa5y5YvR1x8djzXpJBSv/UsNK6aqec+6JDjConTgvvQefdGhFDAs2DLAds6gQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.9.tgz", - "integrity": "sha512-b33gLVU2k11nVx1OhX3C8QQP6UHQK4ZtN56oFWvVXvz2VkDoe6fbG8TOgHFxEvqeqohmRnIHe5A1+HADk4OQww==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.9.tgz", - "integrity": "sha512-PPOl1mi6lpLNQxnGoyAfschAodRFYXJ+9fs6WHXz7CSWKbOqiMZsubC+BQsVKuul+3vKLuwTHsS2c2y9EoKwxQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", - "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@faker-js/faker": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/@faker-js/faker/-/faker-8.4.1.tgz", - "integrity": "sha512-XQ3cU+Q8Uqmrbf2e0cIC/QN43sTBSC8KF12u29Mb47tWrt2hAgBXSgpZMj4Ao8Uk0iJcU99QsOCaIL8934obCg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/fakerjs" - } - ], - "license": "MIT", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0", - "npm": ">=6.14.13" - } - }, - "node_modules/@fastify/accept-negotiator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@fastify/accept-negotiator/-/accept-negotiator-1.1.0.tgz", - "integrity": "sha512-OIHZrb2ImZ7XG85HXOONLcJWGosv7sIvM2ifAPQVhg9Lv7qdmMBNVaai4QTdyuaqbKM5eO6sLSQOYI7wEQeCJQ==", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/@fastify/ajv-compiler": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz", - "integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.0.0" - } - }, - "node_modules/@fastify/ajv-compiler/node_modules/fast-uri": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", - "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==", - "license": "MIT" - }, - "node_modules/@fastify/busboy": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-3.2.0.tgz", - "integrity": "sha512-m9FVDXU3GT2ITSe0UaMA5rU3QkfC/UXtCU8y0gSN/GugTqtVldOBWIB5V6V3sbmenVZUIpU6f+mPEO2+m5iTaA==", - "license": "MIT" - }, - "node_modules/@fastify/compress": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/@fastify/compress/-/compress-6.5.0.tgz", - "integrity": "sha512-AqUOK714jY7qkzbQbS4zyI4yNFgnRoOJ3eH/oV1T9f5fFdPDRdrFxm5de1ya5n+as4bvitjwU9EY7zvtT9pI2A==", - "license": "MIT", - "dependencies": { - "@fastify/accept-negotiator": "^1.1.0", - "fastify-plugin": "^4.5.0", - "into-stream": "^6.0.0", - "mime-db": "^1.52.0", - "minipass": "^7.0.2", - "peek-stream": "^1.1.3", - "pump": "^3.0.0", - "pumpify": "^2.0.1" - } - }, - "node_modules/@fastify/cors": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/@fastify/cors/-/cors-8.5.0.tgz", - "integrity": "sha512-/oZ1QSb02XjP0IK1U0IXktEsw/dUBTxJOW7IpIeO8c/tNalw/KjoNSJv1Sf6eqoBPO+TDGkifq6ynFK3v68HFQ==", - "license": "MIT", - "dependencies": { - "fastify-plugin": "^4.0.0", - "mnemonist": "0.39.6" - } - }, - "node_modules/@fastify/deepmerge": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-2.0.2.tgz", - "integrity": "sha512-3wuLdX5iiiYeZWP6bQrjqhrcvBIf0NHbQH1Ur1WbHvoiuTYUEItgygea3zs8aHpiitn0lOB8gX20u1qO+FDm7Q==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/@fastify/error": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-3.4.1.tgz", - "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==", - "license": "MIT" - }, - "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", - "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", - "license": "MIT", - "dependencies": { - "fast-json-stringify": "^5.7.0" - } - }, - "node_modules/@fastify/helmet": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@fastify/helmet/-/helmet-11.1.1.tgz", - "integrity": "sha512-pjJxjk6SLEimITWadtYIXt6wBMfFC1I6OQyH/jYVCqSAn36sgAIFjeNiibHtifjCd+e25442pObis3Rjtame6A==", - "license": "MIT", - "dependencies": { - "fastify-plugin": "^4.2.1", - "helmet": "^7.0.0" - } - }, - "node_modules/@fastify/jwt": { - "version": "7.2.4", - "resolved": "https://registry.npmjs.org/@fastify/jwt/-/jwt-7.2.4.tgz", - "integrity": "sha512-aWJzVb3iZb9xIPjfut8YOrkNEKrZA9xyF2C2Hv9nTheFp7CQPGIZMNTyf3848BsD27nw0JLk8jVLZ2g2DfJOoQ==", - "license": "MIT", - "dependencies": { - "@fastify/error": "^3.0.0", - "@lukeed/ms": "^2.0.0", - "fast-jwt": "^3.3.2", - "fastify-plugin": "^4.0.0", - "steed": "^1.1.3" - } - }, - "node_modules/@fastify/merge-json-schemas": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", - "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - } - }, - "node_modules/@fastify/multipart": { - "version": "8.3.1", - "resolved": "https://registry.npmjs.org/@fastify/multipart/-/multipart-8.3.1.tgz", - "integrity": "sha512-pncbnG28S6MIskFSVRtzTKE9dK+GrKAJl0NbaQ/CG8ded80okWFsYKzSlP9haaLNQhNRDOoHqmGQNvgbiPVpWQ==", - "license": "MIT", - "dependencies": { - "@fastify/busboy": "^3.0.0", - "@fastify/deepmerge": "^2.0.0", - "@fastify/error": "^4.0.0", - "fastify-plugin": "^4.0.0", - "secure-json-parse": "^2.4.0", - "stream-wormhole": "^1.1.0" - } - }, - "node_modules/@fastify/multipart/node_modules/@fastify/error": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@fastify/error/-/error-4.2.0.tgz", - "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/@fastify/rate-limit": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@fastify/rate-limit/-/rate-limit-9.1.0.tgz", - "integrity": "sha512-h5dZWCkuZXN0PxwqaFQLxeln8/LNwQwH9popywmDCFdKfgpi4b/HoMH1lluy6P+30CG9yzzpSpwTCIPNB9T1JA==", - "license": "MIT", - "dependencies": { - "@lukeed/ms": "^2.0.1", - "fastify-plugin": "^4.0.0", - "toad-cache": "^3.3.1" - } - }, - "node_modules/@fastify/send": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@fastify/send/-/send-2.1.0.tgz", - "integrity": "sha512-yNYiY6sDkexoJR0D8IDy3aRP3+L4wdqCpvx5WP+VtEU58sn7USmKynBzDQex5X42Zzvw2gNzzYgP90UfWShLFA==", - "license": "MIT", - "dependencies": { - "@lukeed/ms": "^2.0.1", - "escape-html": "~1.0.3", - "fast-decode-uri-component": "^1.0.1", - "http-errors": "2.0.0", - "mime": "^3.0.0" - } - }, - "node_modules/@fastify/static": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/@fastify/static/-/static-6.12.0.tgz", - "integrity": "sha512-KK1B84E6QD/FcQWxDI2aiUCwHxMJBI1KeCUzm1BwYpPY1b742+jeKruGHP2uOluuM6OkBPI8CIANrXcCRtC2oQ==", - "license": "MIT", - "dependencies": { - "@fastify/accept-negotiator": "^1.0.0", - "@fastify/send": "^2.0.0", - "content-disposition": "^0.5.3", - "fastify-plugin": "^4.0.0", - "glob": "^8.0.1", - "p-limit": "^3.1.0" - } - }, - "node_modules/@fastify/swagger": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/@fastify/swagger/-/swagger-8.15.0.tgz", - "integrity": "sha512-zy+HEEKFqPMS2sFUsQU5X0MHplhKJvWeohBwTCkBAJA/GDYGLGUWQaETEhptiqxK7Hs0fQB9B4MDb3pbwIiCwA==", - "license": "MIT", - "dependencies": { - "fastify-plugin": "^4.0.0", - "json-schema-resolver": "^2.0.0", - "openapi-types": "^12.0.0", - "rfdc": "^1.3.0", - "yaml": "^2.2.2" - } - }, - "node_modules/@fastify/swagger-ui": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@fastify/swagger-ui/-/swagger-ui-2.1.0.tgz", - "integrity": "sha512-mu0C28kMEQDa3miE8f3LmI/OQSmqaKS3dYhZVFO5y4JdgBIPbzZj6COCoRU/P/9nu7UogzzcCJtg89wwLwKtWg==", - "license": "MIT", - "dependencies": { - "@fastify/static": "^6.0.0", - "fastify-plugin": "^4.0.0", - "openapi-types": "^12.0.2", - "rfdc": "^1.3.0", - "yaml": "^2.2.2" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", - "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", - "deprecated": "Use @eslint/config-array instead", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.3", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/@ioredis/commands": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ioredis/commands/-/commands-1.3.0.tgz", - "integrity": "sha512-M/T6Zewn7sDaBQEqIZ8Rb+i9y8qfGmq+5SDFSf9sA2lUZTmdDLVdOiQaeDp+Q4wElZ9HG1GAX5KhDaidp6LQsQ==", - "license": "MIT" - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-regex": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.0.tgz", - "integrity": "sha512-TKY5pyBkHyADOPYlRT9Lx6F544mPl0vS5Ew7BJ45hA08Q+t3GjbueLliBWN3sMICk6+y7HdyxSzC4bWS8baBdg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@isaacs/cliui/node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@isaacs/cliui/node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "license": "MIT", - "dependencies": { - "sprintf-js": "~1.0.2" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", - "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", - "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/core": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", - "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/reporters": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-changed-files": "^29.7.0", - "jest-config": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-resolve-dependencies": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "jest-watcher": "^29.7.0", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/environment": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", - "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.7.0", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/expect-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", - "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/fake-timers": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", - "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@sinonjs/fake-timers": "^10.0.2", - "@types/node": "*", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/globals": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", - "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/types": "^29.6.3", - "jest-mock": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/reporters": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", - "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "@types/node": "*", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^6.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.1.3", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "slash": "^3.0.0", - "string-length": "^4.0.1", - "strip-ansi": "^6.0.0", - "v8-to-istanbul": "^9.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/@jest/reporters/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@jest/reporters/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@jest/reporters/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@jest/schemas": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", - "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@sinclair/typebox": "^0.27.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/schemas/node_modules/@sinclair/typebox": { - "version": "0.27.8", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", - "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jest/source-map": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", - "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.18", - "callsites": "^3.0.0", - "graceful-fs": "^4.2.9" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-result": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", - "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/test-sequencer": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", - "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/transform": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", - "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/types": "^29.6.3", - "@jridgewell/trace-mapping": "^0.3.18", - "babel-plugin-istanbul": "^6.1.1", - "chalk": "^4.0.0", - "convert-source-map": "^2.0.0", - "fast-json-stable-stringify": "^2.1.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "micromatch": "^4.0.4", - "pirates": "^4.0.4", - "slash": "^3.0.0", - "write-file-atomic": "^4.0.2" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jest/types": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", - "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^17.0.8", - "chalk": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.30", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz", - "integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@lukeed/ms": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@lukeed/ms/-/ms-2.0.2.tgz", - "integrity": "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-arm64/-/msgpackr-extract-darwin-arm64-3.0.3.tgz", - "integrity": "sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-darwin-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-darwin-x64/-/msgpackr-extract-darwin-x64-3.0.3.tgz", - "integrity": "sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm/-/msgpackr-extract-linux-arm-3.0.3.tgz", - "integrity": "sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==", - "cpu": [ - "arm" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-arm64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-arm64/-/msgpackr-extract-linux-arm64-3.0.3.tgz", - "integrity": "sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-linux-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-linux-x64/-/msgpackr-extract-linux-x64-3.0.3.tgz", - "integrity": "sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@msgpackr-extract/msgpackr-extract-win32-x64": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@msgpackr-extract/msgpackr-extract-win32-x64/-/msgpackr-extract-win32-x64-3.0.3.tgz", - "integrity": "sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@noble/hashes": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", - "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.21.3 || >=16" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@paralleldrive/cuid2": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@paralleldrive/cuid2/-/cuid2-2.2.2.tgz", - "integrity": "sha512-ZOBkgDwEdoYVlSeRbYYXs0S9MejQofiVYoTbKzy/6GQa39/q5tQU2IX46+shYnUkpEl3wc+J6wRlar7r2EK2xA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@noble/hashes": "^1.1.5" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@redis/bloom": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", - "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/client": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.6.1.tgz", - "integrity": "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==", - "license": "MIT", - "dependencies": { - "cluster-key-slot": "1.1.2", - "generic-pool": "3.9.0", - "yallist": "4.0.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/@redis/client/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, - "node_modules/@redis/graph": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", - "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/json": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.7.tgz", - "integrity": "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/search": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.2.0.tgz", - "integrity": "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@redis/time-series": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.1.0.tgz", - "integrity": "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==", - "license": "MIT", - "peerDependencies": { - "@redis/client": "^1.0.0" - } - }, - "node_modules/@sinclair/typebox": { - "version": "0.31.28", - "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.31.28.tgz", - "integrity": "sha512-/s55Jujywdw/Jpan+vsy6JZs1z2ZTGxTmbZTPiuSL2wz9mfzA2gN1zzaqmvfi4pq+uOt7Du85fkiwv5ymW84aQ==", - "license": "MIT" - }, - "node_modules/@sinonjs/commons": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", - "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "type-detect": "4.0.8" - } - }, - "node_modules/@sinonjs/fake-timers": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", - "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@sinonjs/commons": "^3.0.0" - } - }, - "node_modules/@smithy/abort-controller": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.0.5.tgz", - "integrity": "sha512-jcrqdTQurIrBbUm4W2YdLVMQDoL0sA9DTxYd2s+R/y+2U9NLOP7Xf/YqfSg1FZhlZIYEnvk2mwbyvIfdLEPo8g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader/-/chunked-blob-reader-5.0.0.tgz", - "integrity": "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/chunked-blob-reader-native": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-4.0.0.tgz", - "integrity": "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/config-resolver": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.1.5.tgz", - "integrity": "sha512-viuHMxBAqydkB0AfWwHIdwf/PRH2z5KHGUzqyRtS/Wv+n3IHI993Sk76VCA7dD/+GzgGOmlJDITfPcJC1nIVIw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "@smithy/util-config-provider": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/core": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.8.0.tgz", - "integrity": "sha512-EYqsIYJmkR1VhVE9pccnk353xhs+lB6btdutJEtsp7R055haMJp2yE16eSxw8fv+G0WUY6vqxyYOP8kOqawxYQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/middleware-serde": "^4.0.9", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-body-length-browser": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-stream": "^4.2.4", - "@smithy/util-utf8": "^4.0.0", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/credential-provider-imds": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.0.7.tgz", - "integrity": "sha512-dDzrMXA8d8riFNiPvytxn0mNwR4B3h8lgrQ5UjAGu6T9z/kRg/Xncf4tEQHE/+t25sY8IH3CowcmWi+1U5B1Gw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/property-provider": "^4.0.5", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-codec": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.0.5.tgz", - "integrity": "sha512-miEUN+nz2UTNoRYRhRqVTJCx7jMeILdAurStT2XoS+mhokkmz1xAPp95DFW9Gxt4iF2VBqpeF9HbTQ3kY1viOA==", - "license": "Apache-2.0", - "dependencies": { - "@aws-crypto/crc32": "5.2.0", - "@smithy/types": "^4.3.2", - "@smithy/util-hex-encoding": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-browser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.0.5.tgz", - "integrity": "sha512-LCUQUVTbM6HFKzImYlSB9w4xafZmpdmZsOh9rIl7riPC3osCgGFVP+wwvYVw6pXda9PPT9TcEZxaq3XE81EdJQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-config-resolver": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.1.3.tgz", - "integrity": "sha512-yTTzw2jZjn/MbHu1pURbHdpjGbCuMHWncNBpJnQAPxOVnFUAbSIUSwafiphVDjNV93TdBJWmeVAds7yl5QCkcA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-node": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.0.5.tgz", - "integrity": "sha512-lGS10urI4CNzz6YlTe5EYG0YOpsSp3ra8MXyco4aqSkQDuyZPIw2hcaxDU82OUVtK7UY9hrSvgWtpsW5D4rb4g==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-serde-universal": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/eventstream-serde-universal": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.0.5.tgz", - "integrity": "sha512-JFnmu4SU36YYw3DIBVao3FsJh4Uw65vVDIqlWT4LzR6gXA0F3KP0IXFKKJrhaVzCBhAuMsrUUaT5I+/4ZhF7aw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/eventstream-codec": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/fetch-http-handler": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.1.1.tgz", - "integrity": "sha512-61WjM0PWmZJR+SnmzaKI7t7G0UkkNFboDpzIdzSoy7TByUzlxo18Qlh9s71qug4AY4hlH/CwXdubMtkcNEb/sQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.3", - "@smithy/querystring-builder": "^4.0.5", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-blob-browser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-blob-browser/-/hash-blob-browser-4.0.5.tgz", - "integrity": "sha512-F7MmCd3FH/Q2edhcKd+qulWkwfChHbc9nhguBlVjSUE6hVHhec3q6uPQ+0u69S6ppvLtR3eStfCuEKMXBXhvvA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/chunked-blob-reader": "^5.0.0", - "@smithy/chunked-blob-reader-native": "^4.0.0", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-node": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.0.5.tgz", - "integrity": "sha512-cv1HHkKhpyRb6ahD8Vcfb2Hgz67vNIXEp2vnhzfxLFGRukLCNEA5QdsorbUEzXma1Rco0u3rx5VTqbM06GcZqQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/hash-stream-node": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/hash-stream-node/-/hash-stream-node-4.0.5.tgz", - "integrity": "sha512-IJuDS3+VfWB67UC0GU0uYBG/TA30w+PlOaSo0GPm9UHS88A6rCP6uZxNjNYiyRtOcjv7TXn/60cW8ox1yuZsLg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/invalid-dependency": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.0.5.tgz", - "integrity": "sha512-IVnb78Qtf7EJpoEVo7qJ8BEXQwgC4n3igeJNNKEj/MLYtapnx8A67Zt/J3RXAj2xSO1910zk0LdFiygSemuLow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/is-array-buffer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.0.0.tgz", - "integrity": "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/md5-js": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/md5-js/-/md5-js-4.0.5.tgz", - "integrity": "sha512-8n2XCwdUbGr8W/XhMTaxILkVlw2QebkVTn5tm3HOcbPbOpWg89zr6dPXsH8xbeTsbTXlJvlJNTQsKAIoqQGbdA==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-content-length": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.0.5.tgz", - "integrity": "sha512-l1jlNZoYzoCC7p0zCtBDE5OBXZ95yMKlRlftooE5jPWQn4YBPLgsp+oeHp7iMHaTGoUdFqmHOPa8c9G3gBsRpQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-endpoint": { - "version": "4.1.18", - "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.1.18.tgz", - "integrity": "sha512-ZhvqcVRPZxnZlokcPaTwb+r+h4yOIOCJmx0v2d1bpVlmP465g3qpVSf7wxcq5zZdu4jb0H4yIMxuPwDJSQc3MQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.8.0", - "@smithy/middleware-serde": "^4.0.9", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "@smithy/url-parser": "^4.0.5", - "@smithy/util-middleware": "^4.0.5", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-retry": { - "version": "4.1.19", - "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.1.19.tgz", - "integrity": "sha512-X58zx/NVECjeuUB6A8HBu4bhx72EoUz+T5jTMIyeNKx2lf+Gs9TmWPNNkH+5QF0COjpInP/xSpJGJ7xEnAklQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/protocol-http": "^5.1.3", - "@smithy/service-error-classification": "^4.0.7", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-retry": "^4.0.7", - "@types/uuid": "^9.0.1", - "tslib": "^2.6.2", - "uuid": "^9.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-serde": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.0.9.tgz", - "integrity": "sha512-uAFFR4dpeoJPGz8x9mhxp+RPjo5wW0QEEIPPPbLXiRRWeCATf/Km3gKIVR5vaP8bN1kgsPhcEeh+IZvUlBv6Xg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/middleware-stack": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.0.5.tgz", - "integrity": "sha512-/yoHDXZPh3ocRVyeWQFvC44u8seu3eYzZRveCMfgMOBcNKnAmOvjbL9+Cp5XKSIi9iYA9PECUuW2teDAk8T+OQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-config-provider": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.1.4.tgz", - "integrity": "sha512-+UDQV/k42jLEPPHSn39l0Bmc4sB1xtdI9Gd47fzo/0PbXzJ7ylgaOByVjF5EeQIumkepnrJyfx86dPa9p47Y+w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.0.5", - "@smithy/shared-ini-file-loader": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/node-http-handler": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.1.1.tgz", - "integrity": "sha512-RHnlHqFpoVdjSPPiYy/t40Zovf3BBHc2oemgD7VsVTFFZrU5erFFe0n52OANZZ/5sbshgD93sOh5r6I35Xmpaw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/querystring-builder": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/property-provider": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.0.5.tgz", - "integrity": "sha512-R/bswf59T/n9ZgfgUICAZoWYKBHcsVDurAGX88zsiUtOTA/xUAPyiT+qkNCPwFn43pZqN84M4MiUsbSGQmgFIQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/protocol-http": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.1.3.tgz", - "integrity": "sha512-fCJd2ZR7D22XhDY0l+92pUag/7je2BztPRQ01gU5bMChcyI0rlly7QFibnYHzcxDvccMjlpM/Q1ev8ceRIb48w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-builder": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.0.5.tgz", - "integrity": "sha512-NJeSCU57piZ56c+/wY+AbAw6rxCCAOZLCIniRE7wqvndqxcKKDOXzwWjrY7wGKEISfhL9gBbAaWWgHsUGedk+A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "@smithy/util-uri-escape": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/querystring-parser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.0.5.tgz", - "integrity": "sha512-6SV7md2CzNG/WUeTjVe6Dj8noH32r4MnUeFKZrnVYsQxpGSIcphAanQMayi8jJLZAWm6pdM9ZXvKCpWOsIGg0w==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/service-error-classification": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.0.7.tgz", - "integrity": "sha512-XvRHOipqpwNhEjDf2L5gJowZEm5nsxC16pAZOeEcsygdjv9A2jdOh3YoDQvOXBGTsaJk6mNWtzWalOB9976Wlg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/shared-ini-file-loader": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.0.5.tgz", - "integrity": "sha512-YVVwehRDuehgoXdEL4r1tAAzdaDgaC9EQvhK0lEbfnbrd0bd5+CTQumbdPryX3J2shT7ZqQE+jPW4lmNBAB8JQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/signature-v4": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.1.3.tgz", - "integrity": "sha512-mARDSXSEgllNzMw6N+mC+r1AQlEBO3meEAkR/UlfAgnMzJUB3goRBWgip1EAMG99wh36MDqzo86SfIX5Y+VEaw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-middleware": "^4.0.5", - "@smithy/util-uri-escape": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/smithy-client": { - "version": "4.4.10", - "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.4.10.tgz", - "integrity": "sha512-iW6HjXqN0oPtRS0NK/zzZ4zZeGESIFcxj2FkWed3mcK8jdSdHzvnCKXSjvewESKAgGKAbJRA+OsaqKhkdYRbQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/core": "^3.8.0", - "@smithy/middleware-endpoint": "^4.1.18", - "@smithy/middleware-stack": "^4.0.5", - "@smithy/protocol-http": "^5.1.3", - "@smithy/types": "^4.3.2", - "@smithy/util-stream": "^4.2.4", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/types": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.3.2.tgz", - "integrity": "sha512-QO4zghLxiQ5W9UZmX2Lo0nta2PuE1sSrXUYDoaB6HMR762C0P7v/HEPHf6ZdglTVssJG1bsrSBxdc3quvDSihw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/url-parser": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.0.5.tgz", - "integrity": "sha512-j+733Um7f1/DXjYhCbvNXABV53NyCRRA54C7bNEIxNPs0YjfRxeMKjjgm2jvTYrciZyCjsicHwQ6Q0ylo+NAUw==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/querystring-parser": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-base64": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.0.0.tgz", - "integrity": "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-browser": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.0.0.tgz", - "integrity": "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-body-length-node": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.0.0.tgz", - "integrity": "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-buffer-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.0.0.tgz", - "integrity": "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/is-array-buffer": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-config-provider": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.0.0.tgz", - "integrity": "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-browser": { - "version": "4.0.26", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.0.26.tgz", - "integrity": "sha512-xgl75aHIS/3rrGp7iTxQAOELYeyiwBu+eEgAk4xfKwJJ0L8VUjhO2shsDpeil54BOFsqmk5xfdesiewbUY5tKQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/property-provider": "^4.0.5", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "bowser": "^2.11.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-defaults-mode-node": { - "version": "4.0.26", - "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.0.26.tgz", - "integrity": "sha512-z81yyIkGiLLYVDetKTUeCZQ8x20EEzvQjrqJtb/mXnevLq2+w3XCEWTJ2pMp401b6BkEkHVfXb/cROBpVauLMQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/config-resolver": "^4.1.5", - "@smithy/credential-provider-imds": "^4.0.7", - "@smithy/node-config-provider": "^4.1.4", - "@smithy/property-provider": "^4.0.5", - "@smithy/smithy-client": "^4.4.10", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-endpoints": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.0.7.tgz", - "integrity": "sha512-klGBP+RpBp6V5JbrY2C/VKnHXn3d5V2YrifZbmMY8os7M6m8wdYFoO6w/fe5VkP+YVwrEktW3IWYaSQVNZJ8oQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/node-config-provider": "^4.1.4", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-hex-encoding": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.0.0.tgz", - "integrity": "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-middleware": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.0.5.tgz", - "integrity": "sha512-N40PfqsZHRSsByGB81HhSo+uvMxEHT+9e255S53pfBw/wI6WKDI7Jw9oyu5tJTLwZzV5DsMha3ji8jk9dsHmQQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-retry": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.0.7.tgz", - "integrity": "sha512-TTO6rt0ppK70alZpkjwy+3nQlTiqNfoXja+qwuAchIEAIoSZW8Qyd76dvBv3I5bCpE38APafG23Y/u270NspiQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/service-error-classification": "^4.0.7", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-stream": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.2.4.tgz", - "integrity": "sha512-vSKnvNZX2BXzl0U2RgCLOwWaAP9x/ddd/XobPK02pCbzRm5s55M53uwb1rl/Ts7RXZvdJZerPkA+en2FDghLuQ==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/fetch-http-handler": "^5.1.1", - "@smithy/node-http-handler": "^4.1.1", - "@smithy/types": "^4.3.2", - "@smithy/util-base64": "^4.0.0", - "@smithy/util-buffer-from": "^4.0.0", - "@smithy/util-hex-encoding": "^4.0.0", - "@smithy/util-utf8": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-uri-escape": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.0.0.tgz", - "integrity": "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg==", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-utf8": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.0.0.tgz", - "integrity": "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/util-buffer-from": "^4.0.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@smithy/util-waiter": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@smithy/util-waiter/-/util-waiter-4.0.7.tgz", - "integrity": "sha512-mYqtQXPmrwvUljaHyGxYUIIRI3qjBTEb/f5QFi3A6VlxhpmZd5mWXn9W+qUkf2pVE1Hv3SqxefiZOPGdxmO64A==", - "license": "Apache-2.0", - "dependencies": { - "@smithy/abort-controller": "^4.0.5", - "@smithy/types": "^4.3.2", - "tslib": "^2.6.2" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@socket.io/component-emitter": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", - "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==", - "license": "MIT" - }, - "node_modules/@socket.io/redis-adapter": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/@socket.io/redis-adapter/-/redis-adapter-8.3.0.tgz", - "integrity": "sha512-ly0cra+48hDmChxmIpnESKrc94LjRL80TEmZVscuQ/WWkRP81nNj8W8cCGMqbI4L6NCuAaPRSzZF1a9GlAxxnA==", - "license": "MIT", - "dependencies": { - "debug": "~4.3.1", - "notepack.io": "~3.0.1", - "uid2": "1.0.0" - }, - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "socket.io-adapter": "^2.5.4" - } - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/bcryptjs": { - "version": "2.4.6", - "resolved": "https://registry.npmjs.org/@types/bcryptjs/-/bcryptjs-2.4.6.tgz", - "integrity": "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/body-parser": { - "version": "1.19.6", - "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", - "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/connect": "*", - "@types/node": "*" - } - }, - "node_modules/@types/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*", - "@types/node": "*" - } - }, - "node_modules/@types/connect": { - "version": "3.4.38", - "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", - "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/cookiejar": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@types/cookiejar/-/cookiejar-2.1.5.tgz", - "integrity": "sha512-he+DHOWReW0nghN24E1WUqM0efK4kI9oTqDm6XmK8ZPe2djZ90BSNdGnIyCLzCPw7/pogPlGbzI2wHGGmi4O/Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/cors": { - "version": "2.8.19", - "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", - "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/express": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.3.tgz", - "integrity": "sha512-wGA0NX93b19/dZC1J18tKWVIYWyyF2ZjT9vin/NRu0qzzvfVzWjs04iq2rQ3H65vCTQYlRqs3YHfY7zjdV+9Kw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/body-parser": "*", - "@types/express-serve-static-core": "^5.0.0", - "@types/serve-static": "*" - } - }, - "node_modules/@types/express-serve-static-core": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.7.tgz", - "integrity": "sha512-R+33OsgWw7rOhD1emjU7dzCDHucJrgJXMA5PYCzJxVil0dsyx5iBEPHqpPfiKNJQb7lZ1vxwoLR4Z87bBUpeGQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "@types/qs": "*", - "@types/range-parser": "*", - "@types/send": "*" - } - }, - "node_modules/@types/graceful-fs": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", - "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/http-errors": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", - "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", - "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", - "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-coverage": "*" - } - }, - "node_modules/@types/istanbul-reports": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", - "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@types/jest": { - "version": "29.5.14", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", - "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" - } - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/jsonwebtoken": { - "version": "9.0.10", - "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", - "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/ms": "*", - "@types/node": "*" - } - }, - "node_modules/@types/methods": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz", - "integrity": "sha512-ymXWVrDiCxTBE3+RIrrP533E70eA+9qu7zdWoHuOmGujkYtzf4HQF96b8nwHLqhuf4ykX61IGRIB38CC6/sImQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mime": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", - "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mime-types": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", - "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/multer": { - "version": "1.4.13", - "resolved": "https://registry.npmjs.org/@types/multer/-/multer-1.4.13.tgz", - "integrity": "sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/express": "*" - } - }, - "node_modules/@types/node": { - "version": "20.19.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.11.tgz", - "integrity": "sha512-uug3FEEGv0r+jrecvUUpbY8lLisvIjg6AAic6a2bSP5OEOLeJsDSnvhCDov7ipFFMXS3orMpzlmi0ZcuGkBbow==", - "license": "MIT", - "dependencies": { - "undici-types": "~6.21.0" - } - }, - "node_modules/@types/node-fetch": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", - "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "form-data": "^4.0.4" - } - }, - "node_modules/@types/nodemailer": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-7.0.0.tgz", - "integrity": "sha512-APKHivjoJ1OyLrlhdJ/QJpnj78OaGSHQ18J12CeOVbffvBgUjqBiIzCW2lnRuwQiLxCpahgMf+T7G2dZBvgeYw==", - "license": "MIT", - "dependencies": { - "@aws-sdk/client-sesv2": "^3.839.0", - "@types/node": "*" - } - }, - "node_modules/@types/pg": { - "version": "8.15.5", - "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.15.5.tgz", - "integrity": "sha512-LF7lF6zWEKxuT3/OR8wAZGzkg4ENGXFNyiV/JeOt9z5B+0ZVwbql9McqX5c/WStFq1GaGso7H1AzP/qSzmlCKQ==", - "license": "MIT", - "dependencies": { - "@types/node": "*", - "pg-protocol": "*", - "pg-types": "^2.2.0" - } - }, - "node_modules/@types/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/range-parser": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", - "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/semver": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.0.tgz", - "integrity": "sha512-k107IF4+Xr7UHjwDc7Cfd6PRQfbdkiRabXGRjo07b4WyPahFBZCZ1sE+BNxYIJPPg73UkfOsVOLwqVc/6ETrIA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/send": { - "version": "0.17.5", - "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", - "integrity": "sha512-z6F2D3cOStZvuk2SaP6YrwkNO65iTZcwA2ZkSABegdkAh/lf+Aa/YQndZVfmEXT5vgAp6zv06VQ3ejSVjAny4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/mime": "^1", - "@types/node": "*" - } - }, - "node_modules/@types/serve-static": { - "version": "1.15.8", - "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.8.tgz", - "integrity": "sha512-roei0UY3LhpOJvjbIP6ZZFngyLKl5dskOtDhxY5THRSpO+ZI+nzJ+m5yUMzGrp89YRa7lvknKkMYjqQFGwA7Sg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/http-errors": "*", - "@types/node": "*", - "@types/send": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/stream-buffers": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/@types/stream-buffers/-/stream-buffers-3.0.7.tgz", - "integrity": "sha512-azOCy05sXVXrO+qklf0c/B07H/oHaIuDDAiHPVwlk3A9Ek+ksHyTeMajLZl3r76FxpPpxem//4Te61G1iW3Giw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/superagent": { - "version": "8.1.9", - "resolved": "https://registry.npmjs.org/@types/superagent/-/superagent-8.1.9.tgz", - "integrity": "sha512-pTVjI73witn+9ILmoJdajHGW2jkSaOzhiFYF1Rd3EQ94kymLqB9PjD9ISg7WaALC7+dCHT0FGe9T2LktLq/3GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/cookiejar": "^2.1.5", - "@types/methods": "^1.1.4", - "@types/node": "*", - "form-data": "^4.0.0" - } - }, - "node_modules/@types/supertest": { - "version": "2.0.16", - "resolved": "https://registry.npmjs.org/@types/supertest/-/supertest-2.0.16.tgz", - "integrity": "sha512-6c2ogktZ06tr2ENoZivgm7YnprnhYE4ZoXGMY+oA7IuAf17M8FWvujXZGmxLv8y0PTyts4x5A+erSwVUFA8XSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/superagent": "*" - } - }, - "node_modules/@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT" - }, - "node_modules/@types/uuid": { - "version": "9.0.8", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz", - "integrity": "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==", - "license": "MIT" - }, - "node_modules/@types/yargs": { - "version": "17.0.33", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", - "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/yargs-parser": "*" - } - }, - "node_modules/@types/yargs-parser": { - "version": "21.0.3", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", - "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", - "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/type-utils": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "graphemer": "^1.4.0", - "ignore": "^5.2.4", - "natural-compare": "^1.4.0", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", - "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", - "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", - "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/typescript-estree": "6.21.0", - "@typescript-eslint/utils": "6.21.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", - "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", - "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/visitor-keys": "6.21.0", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "minimatch": "9.0.3", - "semver": "^7.5.4", - "ts-api-utils": "^1.0.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", - "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@types/json-schema": "^7.0.12", - "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.21.0", - "@typescript-eslint/types": "6.21.0", - "@typescript-eslint/typescript-estree": "6.21.0", - "semver": "^7.5.4" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^7.0.0 || ^8.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", - "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "6.21.0", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^16.0.0 || >=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "dev": true, - "license": "ISC" - }, - "node_modules/abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "license": "MIT", - "dependencies": { - "event-target-shim": "^5.0.0" - }, - "engines": { - "node": ">=6.5" - } - }, - "node_modules/abstract-logging": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz", - "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", - "license": "MIT" - }, - "node_modules/accepts": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", - "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", - "license": "MIT", - "dependencies": { - "mime-types": "~2.1.34", - "negotiator": "0.6.3" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/accepts/node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/ajv": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", - "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-uri": "^3.0.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/ansi-escapes": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", - "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.21.3" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-escapes/node_modules/type-fest": { - "version": "0.21.3", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", - "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/append-field": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", - "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw==", - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true, - "license": "Python-2.0" - }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/asap": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", - "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", - "dev": true, - "license": "MIT" - }, - "node_modules/asn1.js": { - "version": "5.4.1", - "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", - "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", - "license": "MIT", - "dependencies": { - "bn.js": "^4.0.0", - "inherits": "^2.0.1", - "minimalistic-assert": "^1.0.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/async": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", - "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/atomic-sleep": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", - "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/available-typed-arrays": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", - "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", - "license": "MIT", - "dependencies": { - "possible-typed-array-names": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/avvio": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/avvio/-/avvio-8.4.0.tgz", - "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==", - "license": "MIT", - "dependencies": { - "@fastify/error": "^3.3.0", - "fastq": "^1.17.1" - } - }, - "node_modules/aws-sdk": { - "version": "2.1692.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1692.0.tgz", - "integrity": "sha512-x511uiJ/57FIsbgUe5csJ13k3uzu25uWQE+XqfBis/sB0SFoiElJWXRkgEAUh0U6n40eT3ay5Ue4oPkRMu1LYw==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "buffer": "4.9.2", - "events": "1.1.1", - "ieee754": "1.1.13", - "jmespath": "0.16.0", - "querystring": "0.2.0", - "sax": "1.2.1", - "url": "0.10.3", - "util": "^0.12.4", - "uuid": "8.0.0", - "xml2js": "0.6.2" - }, - "engines": { - "node": ">= 10.0.0" - } - }, - "node_modules/aws-sdk/node_modules/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-jOXGuXZAWdsTH7eZLtyXMqUb9EcWMGZNbL9YcGBJl4MH4nrxHmZJhEHvyLFrkxo+28uLb/NYRcStH48fnD0Vzw==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/axios": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.11.0.tgz", - "integrity": "sha512-1Lx3WLFQWm3ooKDYZD1eXmoGO9fxYQjrycfHFC8P0sCfQVXyROp0p9PFWBehewBOdCwHc+f/b8I0fMto5eSfwA==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/babel-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", - "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/transform": "^29.7.0", - "@types/babel__core": "^7.1.14", - "babel-plugin-istanbul": "^6.1.1", - "babel-preset-jest": "^29.6.3", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.8.0" - } - }, - "node_modules/babel-plugin-istanbul": { - "version": "6.1.1", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", - "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^5.0.4", - "test-exclude": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", - "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-plugin-istanbul/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/babel-plugin-jest-hoist": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", - "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__core": "^7.1.14", - "@types/babel__traverse": "^7.0.6" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/babel-preset-current-node-syntax": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", - "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.12.13", - "@babel/plugin-syntax-class-static-block": "^7.14.5", - "@babel/plugin-syntax-import-attributes": "^7.24.7", - "@babel/plugin-syntax-import-meta": "^7.10.4", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.10.4", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-private-property-in-object": "^7.14.5", - "@babel/plugin-syntax-top-level-await": "^7.14.5" - }, - "peerDependencies": { - "@babel/core": "^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/babel-preset-jest": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", - "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", - "dev": true, - "license": "MIT", - "dependencies": { - "babel-plugin-jest-hoist": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/base64-js": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/base64id": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/base64id/-/base64id-2.0.0.tgz", - "integrity": "sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==", - "license": "MIT", - "engines": { - "node": "^4.5.0 || >= 5.9" - } - }, - "node_modules/bcryptjs": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz", - "integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ==", - "license": "MIT" - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/bn.js": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.2.tgz", - "integrity": "sha512-n4DSx829VRTRByMRGdjQ9iqsN0Bh4OolPsFnaZBLcbi8iXcB+kJ9s7EnRt4wILZNV3kPLHkRVfOc/HvhC3ovDw==", - "license": "MIT" - }, - "node_modules/bowser": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.12.0.tgz", - "integrity": "sha512-HcOcTudTeEWgbHh0Y1Tyb6fdeR71m4b/QACf0D4KswGTsNeIJQmg38mRENZPAYPZvGFN3fk3604XbQEPdxXdKg==", - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.25.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.2.tgz", - "integrity": "sha512-0si2SJK3ooGzIawRu61ZdPCO1IncZwS8IzuX73sPZsXW6EQ/w/DAfPyKI8l1ETTCr2MnvqWitmlCUxgdul45jA==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "caniuse-lite": "^1.0.30001733", - "electron-to-chromium": "^1.5.199", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "node-int64": "^0.4.0" - } - }, - "node_modules/buffer": { - "version": "4.9.2", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", - "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", - "license": "MIT", - "dependencies": { - "base64-js": "^1.0.2", - "ieee754": "^1.1.4", - "isarray": "^1.0.0" - } - }, - "node_modules/buffer-alloc": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", - "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", - "license": "MIT", - "dependencies": { - "buffer-alloc-unsafe": "^1.1.0", - "buffer-fill": "^1.0.0" - } - }, - "node_modules/buffer-alloc-unsafe": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", - "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==", - "license": "MIT" - }, - "node_modules/buffer-equal-constant-time": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", - "license": "BSD-3-Clause" - }, - "node_modules/buffer-fill": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", - "integrity": "sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", - "license": "MIT" - }, - "node_modules/buffer-from": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", - "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT" - }, - "node_modules/bull": { - "version": "4.16.5", - "resolved": "https://registry.npmjs.org/bull/-/bull-4.16.5.tgz", - "integrity": "sha512-lDsx2BzkKe7gkCYiT5Acj02DpTwDznl/VNN7Psn7M3USPG7Vs/BaClZJJTAG+ufAR9++N1/NiUTdaFBWDIl5TQ==", - "license": "MIT", - "dependencies": { - "cron-parser": "^4.9.0", - "get-port": "^5.1.1", - "ioredis": "^5.3.2", - "lodash": "^4.17.21", - "msgpackr": "^1.11.2", - "semver": "^7.5.2", - "uuid": "^8.3.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/bull/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/bytes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", - "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/call-bind": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", - "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.0", - "es-define-property": "^1.0.0", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/call-bound": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", - "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "get-intrinsic": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001735", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001735.tgz", - "integrity": "sha512-EV/laoX7Wq2J9TQlyIXRxTJqIw4sxfXS4OYgudGxBYRuTv0q7AM6yMEpU/Vo1I94thg9U6EZ2NfZx9GJq83u7w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/ci-info": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", - "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/sibiraj-s" - } - ], - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/cjs-module-lexer": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", - "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/cluster-key-slot": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", - "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/color": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", - "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", - "license": "MIT", - "dependencies": { - "color-convert": "^1.9.3", - "color-string": "^1.6.0" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT" - }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, - "node_modules/color/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "license": "MIT", - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/color/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "license": "MIT" - }, - "node_modules/colorette": { - "version": "2.0.20", - "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", - "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/colorspace": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/colorspace/-/colorspace-1.1.4.tgz", - "integrity": "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w==", - "license": "MIT", - "dependencies": { - "color": "^3.1.3", - "text-hex": "1.0.x" - } - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/component-emitter": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", - "integrity": "sha512-T0+barUSQRTUQASh8bx02dl+DhF54GtIDY13Y3m9oWTklKbb3Wv974meRpeZ3lp1JpLVECWWNHC4vaG2XHXouQ==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/compressible": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", - "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", - "license": "MIT", - "dependencies": { - "mime-db": ">= 1.43.0 < 2" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/compression": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz", - "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==", - "license": "MIT", - "dependencies": { - "bytes": "3.1.2", - "compressible": "~2.0.18", - "debug": "2.6.9", - "negotiator": "~0.6.4", - "on-headers": "~1.1.0", - "safe-buffer": "5.2.1", - "vary": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/compression/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/compression/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "engines": [ - "node >= 0.8" - ], - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/content-disposition": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", - "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", - "license": "MIT", - "dependencies": { - "safe-buffer": "5.2.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/cookiejar": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.4.tgz", - "integrity": "sha512-LDx6oHrK+PhzLKJU9j5S7/Y3jM/mUHvD/DeI1WQmJn652iPC5Y4TBzC9l+5OMOXlyTTA+SmVUPm0HQUwpD5Jqw==", - "dev": true, - "license": "MIT" - }, - "node_modules/core-util-is": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", - "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT" - }, - "node_modules/cors": { - "version": "2.8.5", - "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", - "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", - "license": "MIT", - "dependencies": { - "object-assign": "^4", - "vary": "^1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/create-jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", - "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.9", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "prompts": "^2.0.1" - }, - "bin": { - "create-jest": "bin/create-jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/cron-parser": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz", - "integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==", - "license": "MIT", - "dependencies": { - "luxon": "^3.2.1" - }, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/dateformat": { - "version": "4.6.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", - "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decamelize": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-5.0.1.tgz", - "integrity": "sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/dedent": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", - "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "babel-plugin-macros": "^3.1.0" - }, - "peerDependenciesMeta": { - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/deepmerge": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", - "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.10" - } - }, - "node_modules/depd": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", - "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/detect-libc": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", - "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", - "license": "Apache-2.0", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/dezalgo": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/dezalgo/-/dezalgo-1.0.4.tgz", - "integrity": "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==", - "dev": true, - "license": "ISC", - "dependencies": { - "asap": "^2.0.0", - "wrappy": "1" - } - }, - "node_modules/diff-sequences": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", - "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dotenv-cli": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/dotenv-cli/-/dotenv-cli-10.0.0.tgz", - "integrity": "sha512-lnOnttzfrzkRx2echxJHQRB6vOAMSCzzZg79IxpC00tU42wZPuZkQxNNrrwVAxaQZIIh001l4PxVlCrBxngBzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.6", - "dotenv": "^17.1.0", - "dotenv-expand": "^11.0.0", - "minimist": "^1.2.6" - }, - "bin": { - "dotenv": "cli.js" - } - }, - "node_modules/dotenv-cli/node_modules/dotenv": { - "version": "17.2.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.1.tgz", - "integrity": "sha512-kQhDYKZecqnM0fCnzI5eIv5L4cAe/iRI+HqMbO/hbRdTAeXDG+M9FjipUxNfbARuEg4iHIbhnhs78BCHNbSxEQ==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dotenv-expand": { - "version": "11.0.7", - "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", - "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "dotenv": "^16.4.5" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/ecdsa-sig-formatter": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", - "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "license": "Apache-2.0", - "dependencies": { - "safe-buffer": "^5.0.1" - } - }, - "node_modules/electron-to-chromium": { - "version": "1.5.203", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.203.tgz", - "integrity": "sha512-uz4i0vLhfm6dLZWbz/iH88KNDV+ivj5+2SA+utpgjKaj9Q0iDLuwk6Idhe9BTxciHudyx6IvTvijhkPvFGUQ0g==", - "dev": true, - "license": "ISC" - }, - "node_modules/emittery": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", - "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sindresorhus/emittery?sponsor=1" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, - "node_modules/enabled": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", - "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "license": "MIT" - }, - "node_modules/end-of-stream": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", - "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", - "license": "MIT", - "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/engine.io": { - "version": "6.6.4", - "resolved": "https://registry.npmjs.org/engine.io/-/engine.io-6.6.4.tgz", - "integrity": "sha512-ZCkIjSYNDyGn0R6ewHDtXgns/Zre/NT6Agvq1/WobF7JXgFff4SeDroKiCO3fNJreU9YG429Sc81o4w5ok/W5g==", - "license": "MIT", - "dependencies": { - "@types/cors": "^2.8.12", - "@types/node": ">=10.0.0", - "accepts": "~1.3.4", - "base64id": "2.0.0", - "cookie": "~0.7.2", - "cors": "~2.8.5", - "debug": "~4.3.1", - "engine.io-parser": "~5.2.1", - "ws": "~8.17.1" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/engine.io-parser": { - "version": "5.2.3", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", - "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/engine.io/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.2.1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.9", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.9.tgz", - "integrity": "sha512-CRbODhYyQx3qp7ZEwzxOk4JBqmD/seJrzPa/cGjY1VtIn5E09Oi9/dB4JwctnfZ8Q8iT7rioVv5k/FNT/uf54g==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.9", - "@esbuild/android-arm": "0.25.9", - "@esbuild/android-arm64": "0.25.9", - "@esbuild/android-x64": "0.25.9", - "@esbuild/darwin-arm64": "0.25.9", - "@esbuild/darwin-x64": "0.25.9", - "@esbuild/freebsd-arm64": "0.25.9", - "@esbuild/freebsd-x64": "0.25.9", - "@esbuild/linux-arm": "0.25.9", - "@esbuild/linux-arm64": "0.25.9", - "@esbuild/linux-ia32": "0.25.9", - "@esbuild/linux-loong64": "0.25.9", - "@esbuild/linux-mips64el": "0.25.9", - "@esbuild/linux-ppc64": "0.25.9", - "@esbuild/linux-riscv64": "0.25.9", - "@esbuild/linux-s390x": "0.25.9", - "@esbuild/linux-x64": "0.25.9", - "@esbuild/netbsd-arm64": "0.25.9", - "@esbuild/netbsd-x64": "0.25.9", - "@esbuild/openbsd-arm64": "0.25.9", - "@esbuild/openbsd-x64": "0.25.9", - "@esbuild/openharmony-arm64": "0.25.9", - "@esbuild/sunos-x64": "0.25.9", - "@esbuild/win32-arm64": "0.25.9", - "@esbuild/win32-ia32": "0.25.9", - "@esbuild/win32-x64": "0.25.9" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-html": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", - "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT" - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.57.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", - "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", - "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.4", - "@eslint/js": "8.57.1", - "@humanwhocodes/config-array": "^0.13.0", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "@ungap/structured-clone": "^1.2.0", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "graphemer": "^1.4.0", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.2", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", - "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "9.6.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", - "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.9.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "license": "BSD-2-Clause", - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/events": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", - "integrity": "sha512-kEcvvCBByWXGnZy6JUlgAp2gBIUjfCAV6P6TgT1/aaQKcmuAEC4OZTV1I4EWQLz2gxZw76atuVyvHhTxvi0Flw==", - "license": "MIT", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/execa": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", - "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/expect": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", - "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/expect-utils": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/fast-content-type-parse": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", - "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==", - "license": "MIT" - }, - "node_modules/fast-copy": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", - "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-decode-uri-component": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", - "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-json-stringify": { - "version": "5.16.1", - "resolved": "https://registry.npmjs.org/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz", - "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==", - "license": "MIT", - "dependencies": { - "@fastify/merge-json-schemas": "^0.1.0", - "ajv": "^8.10.0", - "ajv-formats": "^3.0.1", - "fast-deep-equal": "^3.1.3", - "fast-uri": "^2.1.0", - "json-schema-ref-resolver": "^1.0.1", - "rfdc": "^1.2.0" - } - }, - "node_modules/fast-json-stringify/node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, - "node_modules/fast-json-stringify/node_modules/fast-uri": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-2.4.0.tgz", - "integrity": "sha512-ypuAmmMKInk5q7XcepxlnUWDLWv4GFtaJqAzWKqn62IpQ3pejtr5dTVbt3vwqVaMKmkNR55sTT+CqUKIaT21BA==", - "license": "MIT" - }, - "node_modules/fast-jwt": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/fast-jwt/-/fast-jwt-3.3.3.tgz", - "integrity": "sha512-oS3P8bRI24oPLJUePt2OgF64FBQib5TlgHLFQxYNoHYEEZe0gU3cKjJAVqpB5XKV/zjxmq4Hzbk3fgfW/wRz8Q==", - "license": "Apache-2.0", - "dependencies": { - "@lukeed/ms": "^2.0.1", - "asn1.js": "^5.4.1", - "ecdsa-sig-formatter": "^1.0.11", - "mnemonist": "^0.39.5" - }, - "engines": { - "node": ">=16 <22" - } - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-querystring": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", - "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", - "license": "MIT", - "dependencies": { - "fast-decode-uri-component": "^1.0.1" - } - }, - "node_modules/fast-redact": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", - "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/fast-safe-stringify": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", - "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz", - "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/fast-xml-parser": { - "version": "5.2.5", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.5.tgz", - "integrity": "sha512-pfX9uG9Ki0yekDHx2SiuRIyFdyAr1kMIMitPvb0YBo8SUfKvia7w7FIyd/l6av85pFYRhZscS75MwMnbvY+hcQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT", - "dependencies": { - "strnum": "^2.1.0" - }, - "bin": { - "fxparser": "src/cli/cli.js" - } - }, - "node_modules/fastfall": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/fastfall/-/fastfall-1.5.1.tgz", - "integrity": "sha512-KH6p+Z8AKPXnmA7+Iz2Lh8ARCMr+8WNPVludm1LGkZoD2MjY6LVnRMtTKhkdzI+jr0RzQWXKzKyBJm1zoHEL4Q==", - "license": "MIT", - "dependencies": { - "reusify": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fastify": { - "version": "4.29.1", - "resolved": "https://registry.npmjs.org/fastify/-/fastify-4.29.1.tgz", - "integrity": "sha512-m2kMNHIG92tSNWv+Z3UeTR9AWLLuo7KctC7mlFPtMEVrfjIhmQhkQnT9v15qA/BfVq3vvj134Y0jl9SBje3jXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT", - "dependencies": { - "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.4.0", - "@fastify/fast-json-stringify-compiler": "^4.3.0", - "abstract-logging": "^2.0.1", - "avvio": "^8.3.0", - "fast-content-type-parse": "^1.1.0", - "fast-json-stringify": "^5.8.0", - "find-my-way": "^8.0.0", - "light-my-request": "^5.11.0", - "pino": "^9.0.0", - "process-warning": "^3.0.0", - "proxy-addr": "^2.0.7", - "rfdc": "^1.3.0", - "secure-json-parse": "^2.7.0", - "semver": "^7.5.4", - "toad-cache": "^3.3.0" - } - }, - "node_modules/fastify-plugin": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-4.5.1.tgz", - "integrity": "sha512-stRHYGeuqpEZTL1Ef0Ovr2ltazUT9g844X5z/zEBFLG8RYlpDiOCIG+ATvYEp+/zmc7sN29mcIMp8gvYplYPIQ==", - "license": "MIT" - }, - "node_modules/fastify/node_modules/pino": { - "version": "9.9.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-9.9.0.tgz", - "integrity": "sha512-zxsRIQG9HzG+jEljmvmZupOMDUQ0Jpj0yAgE28jQvvrdYTlEaiGwelJpdndMl/MBuRr70heIj83QyqJUWaU8mQ==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^2.0.0", - "pino-std-serializers": "^7.0.0", - "process-warning": "^5.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^4.0.1", - "thread-stream": "^3.0.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/fastify/node_modules/pino-abstract-transport": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", - "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", - "license": "MIT", - "dependencies": { - "split2": "^4.0.0" - } - }, - "node_modules/fastify/node_modules/pino-std-serializers": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", - "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", - "license": "MIT" - }, - "node_modules/fastify/node_modules/pino/node_modules/process-warning": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", - "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/fastify/node_modules/sonic-boom": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", - "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/fastify/node_modules/thread-stream": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", - "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", - "license": "MIT", - "dependencies": { - "real-require": "^0.2.0" - } - }, - "node_modules/fastparallel": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/fastparallel/-/fastparallel-2.4.1.tgz", - "integrity": "sha512-qUmhxPgNHmvRjZKBFUNI0oZuuH9OlSIOXmJ98lhKPxMZZ7zS/Fi0wRHOihDSz0R1YiIOjxzOY4bq65YTcdBi2Q==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4", - "xtend": "^4.0.2" - } - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fastseries": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/fastseries/-/fastseries-1.7.2.tgz", - "integrity": "sha512-dTPFrPGS8SNSzAt7u/CbMKCJ3s01N04s4JFbORHcmyvVfVKmbhMD1VtRbh5enGHxkaQDqWyLefiKOGGmohGDDQ==", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.0", - "xtend": "^4.0.0" - } - }, - "node_modules/fb-watchman": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", - "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "bser": "2.1.1" - } - }, - "node_modules/fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT" - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-my-way": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-8.2.2.tgz", - "integrity": "sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3", - "fast-querystring": "^1.0.0", - "safe-regex2": "^3.1.0" - }, - "engines": { - "node": ">=14" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", - "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/flat-cache/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flat-cache/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/fn.name": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", - "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "license": "MIT" - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", - "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", - "license": "MIT", - "dependencies": { - "is-callable": "^1.2.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/foreground-child/node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/form-data-encoder": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-1.7.2.tgz", - "integrity": "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==", - "license": "MIT" - }, - "node_modules/formdata-node": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/formdata-node/-/formdata-node-4.4.1.tgz", - "integrity": "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==", - "license": "MIT", - "dependencies": { - "node-domexception": "1.0.0", - "web-streams-polyfill": "4.0.0-beta.3" - }, - "engines": { - "node": ">= 12.20" - } - }, - "node_modules/formidable": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-2.1.5.tgz", - "integrity": "sha512-Oz5Hwvwak/DCaXVVUtPn4oLMLLy1CdclLKO1LFgU7XzDpVMUU5UjlSLpGMocyQNNk8F6IJW9M/YdooSn2MRI+Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@paralleldrive/cuid2": "^2.2.2", - "dezalgo": "^1.0.4", - "once": "^1.4.0", - "qs": "^6.11.0" - }, - "funding": { - "url": "https://ko-fi.com/tunnckoCore/commissions" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/from2": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", - "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.1", - "readable-stream": "^2.0.0" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "license": "ISC" - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/generic-pool": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", - "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/get-port": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", - "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", - "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-tsconfig": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", - "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-pkg-maps": "^1.0.0" - }, - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/glob/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/globals": { - "version": "13.24.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", - "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", - "dev": true, - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", - "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/handlebars": { - "version": "4.7.8", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", - "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "minimist": "^1.2.5", - "neo-async": "^2.6.2", - "source-map": "^0.6.1", - "wordwrap": "^1.0.0" - }, - "bin": { - "handlebars": "bin/handlebars" - }, - "engines": { - "node": ">=0.4.7" - }, - "optionalDependencies": { - "uglify-js": "^3.1.4" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "license": "MIT", - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/helmet": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/helmet/-/helmet-7.2.0.tgz", - "integrity": "sha512-ZRiwvN089JfMXokizgqEPXsl2Guk094yExfoDXR0cBYWxtBbaSww/w+vT4WEJsBW2iTUi1GgZ6swmoug3Oy4Xw==", - "license": "MIT", - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/help-me": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", - "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", - "dev": true, - "license": "MIT" - }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true, - "license": "MIT" - }, - "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", - "license": "MIT", - "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/human-signals": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", - "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "dependencies": { - "ms": "^2.0.0" - } - }, - "node_modules/ieee754": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", - "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==", - "license": "BSD-3-Clause" - }, - "node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/ignore-by-default": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", - "dev": true, - "license": "ISC" - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/import-local": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", - "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - }, - "bin": { - "import-local-fixture": "fixtures/cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC" - }, - "node_modules/into-stream": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/into-stream/-/into-stream-6.0.0.tgz", - "integrity": "sha512-XHbaOAvP+uFKUFsOgoNPRjLkwB+I22JFPFe5OjTkQ0nwgj6+pSjb4NmB6VMxaPshLiOf+zcpOCBQuLwC1KHhZA==", - "license": "MIT", - "dependencies": { - "from2": "^2.3.0", - "p-is-promise": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/ioredis": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/ioredis/-/ioredis-5.7.0.tgz", - "integrity": "sha512-NUcA93i1lukyXU+riqEyPtSEkyFq8tX90uL659J+qpCZ3rEdViB/APC58oAhIh3+bJln2hzdlZbBZsGNrlsR8g==", - "license": "MIT", - "dependencies": { - "@ioredis/commands": "^1.3.0", - "cluster-key-slot": "^1.1.0", - "debug": "^4.3.4", - "denque": "^2.1.0", - "lodash.defaults": "^4.2.0", - "lodash.isarguments": "^3.1.0", - "redis-errors": "^1.2.0", - "redis-parser": "^3.0.0", - "standard-as-callback": "^2.1.0" - }, - "engines": { - "node": ">=12.22.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ioredis" - } - }, - "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", - "license": "MIT", - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/is-arguments": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", - "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "dev": true, - "license": "MIT" - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", - "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.3", - "get-proto": "^1.0.0", - "has-tostringtag": "^1.0.2", - "safe-regex-test": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", - "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-regex": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", - "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", - "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", - "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", - "license": "MIT", - "dependencies": { - "which-typed-array": "^1.1.16" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", - "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", - "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "@babel/core": "^7.23.9", - "@babel/parser": "^7.23.9", - "@istanbuljs/schema": "^0.1.3", - "istanbul-lib-coverage": "^3.2.0", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", - "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^4.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/istanbul-reports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", - "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jest": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", - "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/types": "^29.6.3", - "import-local": "^3.0.2", - "jest-cli": "^29.7.0" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-changed-files": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", - "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", - "dev": true, - "license": "MIT", - "dependencies": { - "execa": "^5.0.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-circus": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", - "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/expect": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "co": "^4.6.0", - "dedent": "^1.0.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^29.7.0", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "p-limit": "^3.1.0", - "pretty-format": "^29.7.0", - "pure-rand": "^6.0.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-cli": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", - "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/core": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "create-jest": "^29.7.0", - "exit": "^0.1.2", - "import-local": "^3.0.2", - "jest-config": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "yargs": "^17.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" - }, - "peerDependenciesMeta": { - "node-notifier": { - "optional": true - } - } - }, - "node_modules/jest-config": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", - "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@jest/test-sequencer": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-jest": "^29.7.0", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-circus": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-runner": "^29.7.0", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "micromatch": "^4.0.4", - "parse-json": "^5.2.0", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "peerDependencies": { - "@types/node": "*", - "ts-node": ">=9.0.0" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "ts-node": { - "optional": true - } - } - }, - "node_modules/jest-config/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jest-config/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-config/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jest-diff": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-each": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-environment-node": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "license": "MIT", - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "license": "MIT", - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/jest-runtime/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/jest-runtime/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", - "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/types": "^29.6.3", - "camelcase": "^6.2.0", - "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", - "leven": "^3.1.0", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", - "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", - "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/node": "*", - "jest-util": "^29.7.0", - "merge-stream": "^2.0.0", - "supports-color": "^8.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-worker/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/jmespath": { - "version": "0.16.0", - "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", - "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", - "license": "Apache-2.0", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/joycon": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", - "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-ref-resolver": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", - "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - } - }, - "node_modules/json-schema-resolver": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/json-schema-resolver/-/json-schema-resolver-2.0.0.tgz", - "integrity": "sha512-pJ4XLQP4Q9HTxl6RVDLJ8Cyh1uitSs0CzDBAz1uoJ4sRD/Bk7cFSXL1FUXDW3zJ7YnfliJx6eu8Jn283bpZ4Yg==", - "license": "MIT", - "dependencies": { - "debug": "^4.1.1", - "rfdc": "^1.1.4", - "uri-js": "^4.2.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/Eomm/json-schema-resolver?sponsor=1" - } - }, - "node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonwebtoken": { - "version": "9.0.2", - "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", - "integrity": "sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ==", - "license": "MIT", - "dependencies": { - "jws": "^3.2.2", - "lodash.includes": "^4.3.0", - "lodash.isboolean": "^3.0.3", - "lodash.isinteger": "^4.0.4", - "lodash.isnumber": "^3.0.3", - "lodash.isplainobject": "^4.0.6", - "lodash.isstring": "^4.0.1", - "lodash.once": "^4.0.0", - "ms": "^2.1.1", - "semver": "^7.5.4" - }, - "engines": { - "node": ">=12", - "npm": ">=6" - } - }, - "node_modules/jwa": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", - "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", - "license": "MIT", - "dependencies": { - "buffer-equal-constant-time": "^1.0.1", - "ecdsa-sig-formatter": "1.0.11", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "license": "MIT", - "dependencies": { - "jwa": "^1.4.1", - "safe-buffer": "^5.0.1" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/kuler": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", - "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "license": "MIT" - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/light-my-request": { - "version": "5.14.0", - "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.14.0.tgz", - "integrity": "sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==", - "license": "BSD-3-Clause", - "dependencies": { - "cookie": "^0.7.0", - "process-warning": "^3.0.0", - "set-cookie-parser": "^2.4.1" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "license": "MIT" - }, - "node_modules/lodash.defaults": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", - "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", - "license": "MIT" - }, - "node_modules/lodash.includes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", - "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", - "license": "MIT" - }, - "node_modules/lodash.isarguments": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz", - "integrity": "sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg==", - "license": "MIT" - }, - "node_modules/lodash.isboolean": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", - "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", - "license": "MIT" - }, - "node_modules/lodash.isinteger": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", - "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", - "license": "MIT" - }, - "node_modules/lodash.isnumber": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", - "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", - "license": "MIT" - }, - "node_modules/lodash.isplainobject": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", - "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", - "license": "MIT" - }, - "node_modules/lodash.isstring": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", - "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", - "license": "MIT" - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/lodash.once": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", - "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", - "license": "MIT" - }, - "node_modules/logform": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", - "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", - "license": "MIT", - "dependencies": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/luxon": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.1.tgz", - "integrity": "sha512-RkRWjA926cTvz5rAb1BqyWkKbbjzCGchDUIKMCUvNi17j6f6j8uHGDV82Aqcqtzd+icoYpELmG3ksgGiFNNcNg==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true, - "license": "ISC" - }, - "node_modules/makeerror": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", - "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "tmpl": "1.0.5" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true, - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/methods": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", - "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", - "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/mime-db": { - "version": "1.54.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", - "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types/node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/minimalistic-assert": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", - "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", - "license": "ISC" - }, - "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/mkdirp": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", - "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.6" - }, - "bin": { - "mkdirp": "bin/cmd.js" - } - }, - "node_modules/mnemonist": { - "version": "0.39.6", - "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.39.6.tgz", - "integrity": "sha512-A/0v5Z59y63US00cRSLiloEIw3t5G+MiKz4BhX21FI+YBJXBOGW0ohFxTxO08dsOYlzxo87T7vGfZKYp2bcAWA==", - "license": "MIT", - "dependencies": { - "obliterator": "^2.0.1" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/msgpackr": { - "version": "1.11.5", - "resolved": "https://registry.npmjs.org/msgpackr/-/msgpackr-1.11.5.tgz", - "integrity": "sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==", - "license": "MIT", - "optionalDependencies": { - "msgpackr-extract": "^3.0.2" - } - }, - "node_modules/msgpackr-extract": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/msgpackr-extract/-/msgpackr-extract-3.0.3.tgz", - "integrity": "sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "dependencies": { - "node-gyp-build-optional-packages": "5.2.2" - }, - "bin": { - "download-msgpackr-prebuilds": "bin/download-prebuilds.js" - }, - "optionalDependencies": { - "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.3", - "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.3" - } - }, - "node_modules/multer": { - "version": "1.4.5-lts.2", - "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz", - "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==", - "deprecated": "Multer 1.x is impacted by a number of vulnerabilities, which have been patched in 2.x. You should upgrade to the latest 2.x version.", - "license": "MIT", - "dependencies": { - "append-field": "^1.0.0", - "busboy": "^1.0.0", - "concat-stream": "^1.5.2", - "mkdirp": "^0.5.4", - "object-assign": "^4.1.1", - "type-is": "^1.6.4", - "xtend": "^4.0.0" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/neo-async": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", - "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-cron": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/node-cron/-/node-cron-3.0.3.tgz", - "integrity": "sha512-dOal67//nohNgYWb+nWmg5dkFdIwDm8EpeGYMekPMrngV3637lqnX0lbUcCtgibHTz6SEz7DAIjKvKDFYCnO1A==", - "license": "ISC", - "dependencies": { - "uuid": "8.3.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/node-cron/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/node-domexception": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", - "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", - "deprecated": "Use your platform's native DOMException instead", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/jimmywarting" - }, - { - "type": "github", - "url": "https://paypal.me/jimmywarting" - } - ], - "license": "MIT", - "engines": { - "node": ">=10.5.0" - } - }, - "node_modules/node-fetch": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", - "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, - "node_modules/node-gyp-build-optional-packages": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/node-gyp-build-optional-packages/-/node-gyp-build-optional-packages-5.2.2.tgz", - "integrity": "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==", - "license": "MIT", - "optional": true, - "dependencies": { - "detect-libc": "^2.0.1" - }, - "bin": { - "node-gyp-build-optional-packages": "bin.js", - "node-gyp-build-optional-packages-optional": "optional.js", - "node-gyp-build-optional-packages-test": "build-test.js" - } - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-pg-migrate": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/node-pg-migrate/-/node-pg-migrate-6.2.2.tgz", - "integrity": "sha512-0WYLTXpWu2doeZhiwJUW/1u21OqAFU2CMQ8YZ8VBcJ0xrdqYAjtd8GGFe5A5DM4NJdIZsqJcLPDFqY0FQsmivw==", - "license": "MIT", - "dependencies": { - "@types/pg": "^8.0.0", - "decamelize": "^5.0.0", - "mkdirp": "~1.0.0", - "yargs": "~17.3.0" - }, - "bin": { - "node-pg-migrate": "bin/node-pg-migrate" - }, - "engines": { - "node": ">=12.20.0" - }, - "peerDependencies": { - "pg": ">=4.3.0 <9.0.0" - } - }, - "node_modules/node-pg-migrate/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" - } - }, - "node_modules/node-pg-migrate/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-pg-migrate/node_modules/yargs": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.3.1.tgz", - "integrity": "sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA==", - "license": "MIT", - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", - "dev": true, - "license": "MIT" - }, - "node_modules/nodemailer": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-7.0.5.tgz", - "integrity": "sha512-nsrh2lO3j4GkLLXoeEksAMgAOqxOv6QumNRVQTJwKH4nuiww6iC2y7GyANs9kRAxCexg3+lTWM3PZ91iLlVjfg==", - "license": "MIT-0", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/nodemon": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.10.tgz", - "integrity": "sha512-WDjw3pJ0/0jMFmyNDp3gvY2YizjLmmOUQo6DEBY+JgdvW/yQ9mEeSw6H5ythl5Ny2ytb7f9C2nIbjSxMNzbJXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "chokidar": "^3.5.2", - "debug": "^4", - "ignore-by-default": "^1.0.1", - "minimatch": "^3.1.2", - "pstree.remy": "^1.1.8", - "semver": "^7.5.3", - "simple-update-notifier": "^2.0.0", - "supports-color": "^5.5.0", - "touch": "^3.1.0", - "undefsafe": "^2.0.5" - }, - "bin": { - "nodemon": "bin/nodemon.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/nodemon" - } - }, - "node_modules/nodemon/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/nodemon/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/nodemon/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/nodemon/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/notepack.io": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/notepack.io/-/notepack.io-3.0.1.tgz", - "integrity": "sha512-TKC/8zH5pXIAMVQio2TvVDTtPRX+DJPHDqjRbxogtFiByHyzKmy96RA0JtCQJ+WouyyL4A10xomQzgbUT+1jCg==", - "license": "MIT" - }, - "node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.13.4", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", - "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/obliterator": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.5.tgz", - "integrity": "sha512-42CPE9AhahZRsMNslczq0ctAEtqk8Eka26QofnqC346BZdHDySk3LWka23LI7ULIw11NmltpiLagIq8gBozxTw==", - "license": "MIT" - }, - "node_modules/on-exit-leak-free": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", - "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/on-headers": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", - "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/one-time": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", - "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", - "license": "MIT", - "dependencies": { - "fn.name": "1.x.x" - } - }, - "node_modules/onetime": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", - "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/openai": { - "version": "4.104.0", - "resolved": "https://registry.npmjs.org/openai/-/openai-4.104.0.tgz", - "integrity": "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==", - "license": "Apache-2.0", - "dependencies": { - "@types/node": "^18.11.18", - "@types/node-fetch": "^2.6.4", - "abort-controller": "^3.0.0", - "agentkeepalive": "^4.2.1", - "form-data-encoder": "1.7.2", - "formdata-node": "^4.3.2", - "node-fetch": "^2.6.7" - }, - "bin": { - "openai": "bin/cli" - }, - "peerDependencies": { - "ws": "^8.18.0", - "zod": "^3.23.8" - }, - "peerDependenciesMeta": { - "ws": { - "optional": true - }, - "zod": { - "optional": true - } - } - }, - "node_modules/openai/node_modules/@types/node": { - "version": "18.19.123", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.123.tgz", - "integrity": "sha512-K7DIaHnh0mzVxreCR9qwgNxp3MH9dltPNIEddW9MYUlcKAzm+3grKNSTe2vCJHI1FaLpvpL5JGJrz1UZDKYvDg==", - "license": "MIT", - "dependencies": { - "undici-types": "~5.26.4" - } - }, - "node_modules/openai/node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "license": "MIT" - }, - "node_modules/openapi-types": { - "version": "12.1.3", - "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", - "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", - "license": "MIT" - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-is-promise": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-3.0.0.tgz", - "integrity": "sha512-Wo8VsW4IRQSKVXsJCn7TomUaVtyfjVDn3nUP7kE967BQk0CwFpdbZs0X0uk5sW9mkBa9eNM7hCMaG93WUAwxYQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/peek-stream": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz", - "integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==", - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "duplexify": "^3.5.0", - "through2": "^2.0.3" - } - }, - "node_modules/pg": { - "version": "8.16.3", - "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", - "integrity": "sha512-enxc1h0jA/aq5oSDMvqyW3q89ra6XIIDZgCX9vkMrnz5DFTw/Ny3Li2lFQ+pt3L6MCgm/5o2o8HW9hiJji+xvw==", - "license": "MIT", - "dependencies": { - "pg-connection-string": "^2.9.1", - "pg-pool": "^3.10.1", - "pg-protocol": "^1.10.3", - "pg-types": "2.2.0", - "pgpass": "1.0.5" - }, - "engines": { - "node": ">= 16.0.0" - }, - "optionalDependencies": { - "pg-cloudflare": "^1.2.7" - }, - "peerDependencies": { - "pg-native": ">=3.0.1" - }, - "peerDependenciesMeta": { - "pg-native": { - "optional": true - } - } - }, - "node_modules/pg-cloudflare": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.2.7.tgz", - "integrity": "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg==", - "license": "MIT", - "optional": true - }, - "node_modules/pg-connection-string": { - "version": "2.9.1", - "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.9.1.tgz", - "integrity": "sha512-nkc6NpDcvPVpZXxrreI/FOtX3XemeLl8E0qFr6F2Lrm/I8WOnaWNhIPK2Z7OHpw7gh5XJThi6j6ppgNoaT1w4w==", - "license": "MIT" - }, - "node_modules/pg-int8": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", - "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", - "license": "ISC", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/pg-pool": { - "version": "3.10.1", - "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.10.1.tgz", - "integrity": "sha512-Tu8jMlcX+9d8+QVzKIvM/uJtp07PKr82IUOYEphaWcoBhIYkoHpLXN3qO59nAI11ripznDsEzEv8nUxBVWajGg==", - "license": "MIT", - "peerDependencies": { - "pg": ">=8.0" - } - }, - "node_modules/pg-protocol": { - "version": "1.10.3", - "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.10.3.tgz", - "integrity": "sha512-6DIBgBQaTKDJyxnXaLiLR8wBpQQcGWuAESkRBX/t6OwA8YsqP+iVSiond2EDy6Y/dsGk8rh/jtax3js5NeV7JQ==", - "license": "MIT" - }, - "node_modules/pg-types": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", - "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", - "license": "MIT", - "dependencies": { - "pg-int8": "1.0.1", - "postgres-array": "~2.0.0", - "postgres-bytea": "~1.0.0", - "postgres-date": "~1.0.4", - "postgres-interval": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/pgpass": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", - "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", - "license": "MIT", - "dependencies": { - "split2": "^4.1.0" - } - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pino": { - "version": "8.21.0", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.21.0.tgz", - "integrity": "sha512-ip4qdzjkAyDDZklUaZkcRFb2iA118H9SgRh8yzTkSQK8HilsOJF7rSY8HoW5+I0M46AZgX/pxbprf2vvzQCE0Q==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0", - "fast-redact": "^3.1.1", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^1.2.0", - "pino-std-serializers": "^6.0.0", - "process-warning": "^3.0.0", - "quick-format-unescaped": "^4.0.3", - "real-require": "^0.2.0", - "safe-stable-stringify": "^2.3.1", - "sonic-boom": "^3.7.0", - "thread-stream": "^2.6.0" - }, - "bin": { - "pino": "bin.js" - } - }, - "node_modules/pino-abstract-transport": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-1.2.0.tgz", - "integrity": "sha512-Guhh8EZfPCfH+PMXAb6rKOjGQEoy0xlAIn+irODG5kgfYV+BQ0rGYYWTIel3P5mmyXqkYkPmdIkywsn6QKUR1Q==", - "license": "MIT", - "dependencies": { - "readable-stream": "^4.0.0", - "split2": "^4.0.0" - } - }, - "node_modules/pino-abstract-transport/node_modules/buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "node_modules/pino-abstract-transport/node_modules/events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", - "license": "MIT", - "engines": { - "node": ">=0.8.x" - } - }, - "node_modules/pino-abstract-transport/node_modules/ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/pino-abstract-transport/node_modules/readable-stream": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", - "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", - "license": "MIT", - "dependencies": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/pino-abstract-transport/node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/pino-pretty": { - "version": "13.1.1", - "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.1.tgz", - "integrity": "sha512-TNNEOg0eA0u+/WuqH0MH0Xui7uqVk9D74ESOpjtebSQYbNWJk/dIxCXIxFsNfeN53JmtWqYHP2OrIZjT/CBEnA==", - "dev": true, - "license": "MIT", - "dependencies": { - "colorette": "^2.0.7", - "dateformat": "^4.6.3", - "fast-copy": "^3.0.2", - "fast-safe-stringify": "^2.1.1", - "help-me": "^5.0.0", - "joycon": "^3.1.1", - "minimist": "^1.2.6", - "on-exit-leak-free": "^2.1.0", - "pino-abstract-transport": "^2.0.0", - "pump": "^3.0.0", - "secure-json-parse": "^4.0.0", - "sonic-boom": "^4.0.1", - "strip-json-comments": "^5.0.2" - }, - "bin": { - "pino-pretty": "bin.js" - } - }, - "node_modules/pino-pretty/node_modules/pino-abstract-transport": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", - "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", - "dev": true, - "license": "MIT", - "dependencies": { - "split2": "^4.0.0" - } - }, - "node_modules/pino-pretty/node_modules/secure-json-parse": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", - "integrity": "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "BSD-3-Clause" - }, - "node_modules/pino-pretty/node_modules/sonic-boom": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", - "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", - "dev": true, - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/pino-pretty/node_modules/strip-json-comments": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", - "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pino-std-serializers": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-6.2.2.tgz", - "integrity": "sha512-cHjPPsE+vhj/tnhCy/wiMh3M3z3h/j15zHQX+S9GkTBgqJuTuJzYJ4gUyACLhDaJ7kk9ba9iRDmbH2tJU03OiA==", - "license": "MIT" - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/pkg-dir/node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/pkg-dir/node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/possible-typed-array-names": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", - "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/postgres-array": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", - "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/postgres-bytea": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", - "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-date": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", - "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/postgres-interval": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", - "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", - "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", - "dev": true, - "license": "MIT", - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/pretty-format": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", - "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jest/schemas": "^29.6.3", - "ansi-styles": "^5.0.0", - "react-is": "^18.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/pretty-format/node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT" - }, - "node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", - "license": "MIT" - }, - "node_modules/prompts": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", - "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.5" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true, - "license": "MIT" - }, - "node_modules/pump": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", - "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/pumpify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz", - "integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==", - "license": "MIT", - "dependencies": { - "duplexify": "^4.1.1", - "inherits": "^2.0.3", - "pump": "^3.0.0" - } - }, - "node_modules/pumpify/node_modules/duplexify": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", - "integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==", - "license": "MIT", - "dependencies": { - "end-of-stream": "^1.4.1", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1", - "stream-shift": "^1.0.2" - } - }, - "node_modules/pumpify/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/pure-rand": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", - "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", - "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/dubzzz" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fast-check" - } - ], - "license": "MIT" - }, - "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "side-channel": "^1.1.0" - }, - "engines": { - "node": ">=0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/querystring": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", - "integrity": "sha512-X/xY82scca2tau62i9mDyU9K+I+djTMUsvwf7xnUX5GLvVzgJybOJf4Y6o9Zx3oJK/LSXg5tTZBjwzqVPaPO2g==", - "deprecated": "The querystring API is considered Legacy. new code should use the URLSearchParams API instead.", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/quick-format-unescaped": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", - "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", - "license": "MIT" - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "dev": true, - "license": "MIT" - }, - "node_modules/readable-stream": { - "version": "2.3.8", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", - "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/readable-stream/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/real-require": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", - "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", - "license": "MIT", - "engines": { - "node": ">= 12.13.0" - } - }, - "node_modules/redis": { - "version": "4.7.1", - "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.1.tgz", - "integrity": "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==", - "license": "MIT", - "workspaces": [ - "./packages/*" - ], - "dependencies": { - "@redis/bloom": "1.2.0", - "@redis/client": "1.6.1", - "@redis/graph": "1.1.1", - "@redis/json": "1.0.7", - "@redis/search": "1.2.0", - "@redis/time-series": "1.1.0" - } - }, - "node_modules/redis-errors": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/redis-errors/-/redis-errors-1.2.0.tgz", - "integrity": "sha512-1qny3OExCf0UvUV/5wpYKf2YwPcOqXzkwKKSmKHiE6ZMQs5heeE/c8eXK+PNllPvmjgAbfnsbpkGZWy8cBpn9w==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/redis-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-3.0.0.tgz", - "integrity": "sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==", - "license": "MIT", - "dependencies": { - "redis-errors": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-cwd/node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/resolve-pkg-maps": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", - "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" - } - }, - "node_modules/resolve.exports": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", - "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/ret": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.4.3.tgz", - "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rfdc": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", - "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", - "license": "MIT" - }, - "node_modules/rimraf": { - "version": "5.0.10", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", - "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/rimraf/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/safe-regex-test": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", - "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "is-regex": "^1.2.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/safe-regex2": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-3.1.0.tgz", - "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", - "license": "MIT", - "dependencies": { - "ret": "~0.4.0" - } - }, - "node_modules/safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT" - }, - "node_modules/sax": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", - "integrity": "sha512-8I2a3LovHTOpm7NV5yOyO8IHqgVsfK4+UuySrXU8YXkSRX7k6hCV9b3HrkKCr3nMpgj+0bmocaJJWpvp1oc7ZA==", - "license": "ISC" - }, - "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", - "license": "BSD-3-Clause" - }, - "node_modules/semver": { - "version": "7.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", - "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/set-cookie-parser": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz", - "integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==", - "license": "MIT" - }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "license": "MIT", - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/setprototypeof": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", - "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC" - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", - "side-channel-map": "^1.0.1", - "side-channel-weakmap": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", - "dev": true, - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", - "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/side-channel-weakmap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", - "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", - "dev": true, - "license": "MIT", - "dependencies": { - "call-bound": "^1.0.2", - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.5", - "object-inspect": "^1.13.3", - "side-channel-map": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", - "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/simple-swizzle": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", - "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "license": "MIT" - }, - "node_modules/simple-update-notifier": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", - "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true, - "license": "MIT" - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/socket.io": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/socket.io/-/socket.io-4.8.1.tgz", - "integrity": "sha512-oZ7iUCxph8WYRHHcjBEc9unw3adt5CmSNlppj/5Q4k2RIrhl8Z5yY2Xr4j9zj0+wzVZ0bxmYoGSzKJnRl6A4yg==", - "license": "MIT", - "dependencies": { - "accepts": "~1.3.4", - "base64id": "~2.0.0", - "cors": "~2.8.5", - "debug": "~4.3.2", - "engine.io": "~6.6.0", - "socket.io-adapter": "~2.5.2", - "socket.io-parser": "~4.2.4" - }, - "engines": { - "node": ">=10.2.0" - } - }, - "node_modules/socket.io-adapter": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/socket.io-adapter/-/socket.io-adapter-2.5.5.tgz", - "integrity": "sha512-eLDQas5dzPgOWCk9GuuJC2lBqItuhKI4uxGgo9aIV7MYbk2h9Q6uULEh8WBzThoI7l+qU9Ast9fVUmkqPP9wYg==", - "license": "MIT", - "dependencies": { - "debug": "~4.3.4", - "ws": "~8.17.1" - } - }, - "node_modules/socket.io-adapter/node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", - "license": "MIT", - "engines": { - "node": ">=10.0.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": ">=5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/socket.io-parser": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", - "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", - "license": "MIT", - "dependencies": { - "@socket.io/component-emitter": "~3.1.0", - "debug": "~4.3.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/sonic-boom": { - "version": "3.8.1", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.8.1.tgz", - "integrity": "sha512-y4Z8LCDBuum+PBP3lSV7RHrXscqksve/bi0as7mhwVnBW+/wUqKT/2Kb7um8yqcFy0duYbbPxzt89Zy2nOCaxg==", - "license": "MIT", - "dependencies": { - "atomic-sleep": "^1.0.0" - } - }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.13", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", - "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", - "dev": true, - "license": "MIT", - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/split2": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", - "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", - "license": "ISC", - "engines": { - "node": ">= 10.x" - } - }, - "node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "license": "MIT", - "engines": { - "node": "*" - } - }, - "node_modules/stack-utils": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", - "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/standard-as-callback": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/standard-as-callback/-/standard-as-callback-2.1.0.tgz", - "integrity": "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A==", - "license": "MIT" - }, - "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/steed": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/steed/-/steed-1.1.3.tgz", - "integrity": "sha512-EUkci0FAUiE4IvGTSKcDJIQ/eRUP2JJb56+fvZ4sdnguLTqIdKjSxUe138poW8mkvKWXW2sFPrgTsxqoISnmoA==", - "license": "MIT", - "dependencies": { - "fastfall": "^1.5.0", - "fastparallel": "^2.2.0", - "fastq": "^1.3.0", - "fastseries": "^1.7.0", - "reusify": "^1.0.0" - } - }, - "node_modules/stream-browserify": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz", - "integrity": "sha512-H73RAHsVBapbim0tU2JwwOiXUj+fikfiaoYAKHF3VJfA0pe2BCzkhAHBlLG6REzE+2WNZcxOXjK7lkso+9euLA==", - "license": "MIT", - "dependencies": { - "inherits": "~2.0.4", - "readable-stream": "^3.5.0" - } - }, - "node_modules/stream-browserify/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/stream-buffers": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-3.0.3.tgz", - "integrity": "sha512-pqMqwQCso0PBJt2PQmDO0cFj0lyqmiwOMiMSkVtRokl7e+ZTRYgDHKnuZNbqjiJXgsg4nuqtD/zxuo9KqTp0Yw==", - "license": "Unlicense", - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/stream-parser": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/stream-parser/-/stream-parser-0.3.1.tgz", - "integrity": "sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ==", - "license": "MIT", - "dependencies": { - "debug": "2" - } - }, - "node_modules/stream-parser/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/stream-parser/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/stream-shift": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz", - "integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==", - "license": "MIT" - }, - "node_modules/stream-wormhole": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stream-wormhole/-/stream-wormhole-1.1.0.tgz", - "integrity": "sha512-gHFfL3px0Kctd6Po0M8TzEvt3De/xu6cnRrjlfYNhwbhLPLwigI2t1nc6jrzNuaYg5C4YF78PPFuQPzRiqn9ew==", - "license": "MIT", - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "license": "MIT", - "dependencies": { - "safe-buffer": "~5.1.0" - } - }, - "node_modules/string_decoder/node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT" - }, - "node_modules/string-length": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", - "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strnum": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz", - "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" - } - ], - "license": "MIT" - }, - "node_modules/superagent": { - "version": "8.1.2", - "resolved": "https://registry.npmjs.org/superagent/-/superagent-8.1.2.tgz", - "integrity": "sha512-6WTxW1EB6yCxV5VFOIPQruWGHqc3yI7hEmZK6h+pyk69Lk/Ut7rLUY6W/ONF2MjBuGjvmMiIpsrVJ2vjrHlslA==", - "deprecated": "Please upgrade to superagent v10.2.2+, see release notes at https://github.com/forwardemail/superagent/releases/tag/v10.2.2 - maintenance is supported by Forward Email @ https://forwardemail.net", - "dev": true, - "license": "MIT", - "dependencies": { - "component-emitter": "^1.3.0", - "cookiejar": "^2.1.4", - "debug": "^4.3.4", - "fast-safe-stringify": "^2.1.1", - "form-data": "^4.0.0", - "formidable": "^2.1.2", - "methods": "^1.1.2", - "mime": "2.6.0", - "qs": "^6.11.0", - "semver": "^7.3.8" - }, - "engines": { - "node": ">=6.4.0 <13 || >=14" - } - }, - "node_modules/superagent/node_modules/mime": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", - "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", - "dev": true, - "license": "MIT", - "bin": { - "mime": "cli.js" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/supertest": { - "version": "6.3.4", - "resolved": "https://registry.npmjs.org/supertest/-/supertest-6.3.4.tgz", - "integrity": "sha512-erY3HFDG0dPnhw4U+udPfrzXa4xhSG+n4rxfRuZWCUvjFWwKl+OxWf/7zk50s84/fAAs7vf5QAb9uRa0cCykxw==", - "deprecated": "Please upgrade to supertest v7.1.3+, see release notes at https://github.com/forwardemail/supertest/releases/tag/v7.1.3 - maintenance is supported by Forward Email @ https://forwardemail.net", - "dev": true, - "license": "MIT", - "dependencies": { - "methods": "^1.1.2", - "superagent": "^8.1.2" - }, - "engines": { - "node": ">=6.4.0" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "license": "ISC", - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/test-exclude/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/text-hex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", - "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "license": "MIT" - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true, - "license": "MIT" - }, - "node_modules/thread-stream": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-2.7.0.tgz", - "integrity": "sha512-qQiRWsU/wvNolI6tbbCKd9iKaTnCXsTwVxhhKM6nctPdujTyztjlbUkUTUymidWcMnZ5pWR0ej4a0tjsW021vw==", - "license": "MIT", - "dependencies": { - "real-require": "^0.2.0" - } - }, - "node_modules/through2": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", - "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", - "license": "MIT", - "dependencies": { - "readable-stream": "~2.3.6", - "xtend": "~4.0.1" - } - }, - "node_modules/tmpl": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", - "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true, - "license": "BSD-3-Clause" - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toad-cache": { - "version": "3.7.0", - "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", - "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", - "license": "MIT", - "engines": { - "node": ">=12" - } - }, - "node_modules/toidentifier": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", - "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", - "license": "MIT", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/touch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", - "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", - "dev": true, - "license": "ISC", - "bin": { - "nodetouch": "bin/nodetouch.js" - } - }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT" - }, - "node_modules/triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", - "license": "MIT", - "engines": { - "node": ">= 14.0.0" - } - }, - "node_modules/ts-api-utils": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", - "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16" - }, - "peerDependencies": { - "typescript": ">=4.2.0" - } - }, - "node_modules/ts-jest": { - "version": "29.4.1", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.1.tgz", - "integrity": "sha512-SaeUtjfpg9Uqu8IbeDKtdaS0g8lS6FT6OzM3ezrDfErPJPHNDo/Ey+VFGP1bQIDfagYDLyRpd7O15XpG1Es2Uw==", - "dev": true, - "license": "MIT", - "dependencies": { - "bs-logger": "^0.2.6", - "fast-json-stable-stringify": "^2.1.0", - "handlebars": "^4.7.8", - "json5": "^2.2.3", - "lodash.memoize": "^4.1.2", - "make-error": "^1.3.6", - "semver": "^7.7.2", - "type-fest": "^4.41.0", - "yargs-parser": "^21.1.1" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" - }, - "peerDependencies": { - "@babel/core": ">=7.0.0-beta.0 <8", - "@jest/transform": "^29.0.0 || ^30.0.0", - "@jest/types": "^29.0.0 || ^30.0.0", - "babel-jest": "^29.0.0 || ^30.0.0", - "jest": "^29.0.0 || ^30.0.0", - "jest-util": "^29.0.0 || ^30.0.0", - "typescript": ">=4.3 <6" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "@jest/transform": { - "optional": true - }, - "@jest/types": { - "optional": true - }, - "babel-jest": { - "optional": true - }, - "esbuild": { - "optional": true - }, - "jest-util": { - "optional": true - } - } - }, - "node_modules/ts-jest/node_modules/type-fest": { - "version": "4.41.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", - "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/tslib": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", - "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "license": "0BSD" - }, - "node_modules/tsx": { - "version": "4.20.4", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.20.4.tgz", - "integrity": "sha512-yyxBKfORQ7LuRt/BQKBXrpcq59ZvSW0XxwfjAt3w2/8PmdxaFzijtMhTawprSHhpzeM5BgU2hXHG3lklIERZXg==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "~0.25.0", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" - }, - "engines": { - "node": ">=18.0.0" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - } - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true, - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", - "license": "MIT", - "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "license": "MIT" - }, - "node_modules/typescript": { - "version": "5.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", - "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/uglify-js": { - "version": "3.19.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", - "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", - "dev": true, - "license": "BSD-2-Clause", - "optional": true, - "bin": { - "uglifyjs": "bin/uglifyjs" - }, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/uid2": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/uid2/-/uid2-1.0.0.tgz", - "integrity": "sha512-+I6aJUv63YAcY9n4mQreLUt0d4lvwkkopDNmpomkAUz0fAkEMV9pRWxN0EjhW1YfRhcuyHg2v3mwddCDW1+LFQ==", - "license": "MIT", - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/undefsafe": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", - "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", - "dev": true, - "license": "MIT" - }, - "node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/url": { - "version": "0.10.3", - "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", - "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==", - "license": "MIT", - "dependencies": { - "punycode": "1.3.2", - "querystring": "0.2.0" - } - }, - "node_modules/url/node_modules/punycode": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", - "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==", - "license": "MIT" - }, - "node_modules/util": { - "version": "0.12.5", - "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", - "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "is-arguments": "^1.0.4", - "is-generator-function": "^1.0.7", - "is-typed-array": "^1.1.3", - "which-typed-array": "^1.1.2" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT" - }, - "node_modules/uuid": { - "version": "9.0.1", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", - "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", - "funding": [ - "https://github.com/sponsors/broofa", - "https://github.com/sponsors/ctavan" - ], - "license": "MIT", - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-to-istanbul": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", - "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", - "dev": true, - "license": "ISC", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.12", - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^2.0.0" - }, - "engines": { - "node": ">=10.12.0" - } - }, - "node_modules/vary": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", - "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", - "license": "MIT", - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/walker": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", - "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "makeerror": "1.0.12" - } - }, - "node_modules/wav": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wav/-/wav-1.0.2.tgz", - "integrity": "sha512-viHtz3cDd/Tcr/HbNqzQCofKdF6kWUymH9LGDdskfWFoIy/HJ+RTihgjEcHfnsy1PO4e9B+y4HwgTwMrByquhg==", - "license": "MIT", - "dependencies": { - "buffer-alloc": "^1.1.0", - "buffer-from": "^1.0.0", - "debug": "^2.2.0", - "readable-stream": "^1.1.14", - "stream-parser": "^0.3.1" - } - }, - "node_modules/wav/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "license": "MIT", - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/wav/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "license": "MIT" - }, - "node_modules/wav/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT" - }, - "node_modules/wav/node_modules/readable-stream": { - "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", - "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", - "license": "MIT", - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.1", - "isarray": "0.0.1", - "string_decoder": "~0.10.x" - } - }, - "node_modules/wav/node_modules/string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "license": "MIT" - }, - "node_modules/web-streams-polyfill": { - "version": "4.0.0-beta.3", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz", - "integrity": "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==", - "license": "MIT", - "engines": { - "node": ">= 14" - } - }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "license": "MIT", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.19", - "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", - "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.7", - "call-bind": "^1.0.8", - "call-bound": "^1.0.4", - "for-each": "^0.3.5", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-tostringtag": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/winston": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/winston/-/winston-3.17.0.tgz", - "integrity": "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw==", - "license": "MIT", - "dependencies": { - "@colors/colors": "^1.6.0", - "@dabh/diagnostics": "^2.0.2", - "async": "^3.2.3", - "is-stream": "^2.0.0", - "logform": "^2.7.0", - "one-time": "^1.0.0", - "readable-stream": "^3.4.0", - "safe-stable-stringify": "^2.3.1", - "stack-trace": "0.0.x", - "triple-beam": "^1.3.0", - "winston-transport": "^4.9.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport": { - "version": "4.9.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", - "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", - "license": "MIT", - "dependencies": { - "logform": "^2.7.0", - "readable-stream": "^3.6.2", - "triple-beam": "^1.3.0" - }, - "engines": { - "node": ">= 12.0.0" - } - }, - "node_modules/winston-transport/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/winston/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC" - }, - "node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", - "dev": true, - "license": "ISC", - "dependencies": { - "imurmurhash": "^0.1.4", - "signal-exit": "^3.0.7" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/xml2js": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", - "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", - "license": "MIT", - "dependencies": { - "sax": ">=0.6.0", - "xmlbuilder": "~11.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "license": "MIT", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yaml": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.1.tgz", - "integrity": "sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==", - "license": "ISC", - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14.6" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "dev": true, - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.25.76", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", - "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/backend/package.json b/backend/package.json deleted file mode 100644 index fbfbace..0000000 --- a/backend/package.json +++ /dev/null @@ -1,172 +0,0 @@ -{ - "name": "ceo-communication-backend", - "version": "1.0.0", - "description": "Enterprise-grade backend for CEO communication platform with voice processing and real-time collaboration", - "main": "dist/server.js", - "scripts": { - "build": "tsc", - "dev": "tsx watch src/server.ts", - "start": "node dist/server.js", - "test": "jest", - "test:watch": "jest --watch", - "test:coverage": "jest --coverage", - "lint": "eslint src/**/*.ts", - "lint:fix": "eslint src/**/*.ts --fix", - "format": "prettier --write src/**/*.ts", - "db:migrate": "dotenv -e .env -- node-pg-migrate up", - "db:rollback": "dotenv -e .env -- node-pg-migrate down", - "db:seed": "tsx src/scripts/seed.ts", - "db:reset": "npm run db:rollback && npm run db:migrate && npm run db:seed", - "docker:dev": "docker-compose -f docker/docker-compose.dev.yml up", - "docker:test": "docker-compose -f docker/docker-compose.test.yml up", - "clean": "rimraf dist coverage" - }, - "keywords": [ - "typescript", - "nodejs", - "postgresql", - "redis", - "websocket", - "voice-processing", - "real-time", - "enterprise" - ], - "author": "CEO Communication Platform Team", - "license": "PRIVATE", - "engines": { - "node": ">=20.0.0", - "npm": ">=10.0.0" - }, - "dependencies": { - "@aws-sdk/client-s3": "^3.478.0", - "@aws-sdk/lib-storage": "^3.478.0", - "@aws-sdk/s3-request-presigner": "^3.478.0", - "@fastify/compress": "^6.5.0", - "@fastify/cors": "^8.4.2", - "@fastify/helmet": "^11.1.1", - "@fastify/jwt": "^7.2.4", - "@fastify/multipart": "^8.0.0", - "@fastify/rate-limit": "^9.1.0", - "@fastify/static": "^6.12.0", - "@fastify/swagger": "^8.12.0", - "@fastify/swagger-ui": "^2.1.0", - "@sinclair/typebox": "^0.31.28", - "@socket.io/redis-adapter": "^8.2.1", - "@types/nodemailer": "^7.0.0", - "ajv": "^8.12.0", - "ajv-formats": "^2.1.1", - "aws-sdk": "^2.1489.0", - "axios": "^1.6.2", - "bcryptjs": "^2.4.3", - "bull": "^4.12.2", - "compression": "^1.7.4", - "dotenv": "^16.3.1", - "fastify": "^4.24.3", - "form-data": "^4.0.0", - "helmet": "^7.1.0", - "ioredis": "^5.3.2", - "jsonwebtoken": "^9.0.2", - "mime-types": "^2.1.35", - "multer": "^1.4.5-lts.1", - "node-cron": "^3.0.3", - "node-pg-migrate": "^6.2.2", - "nodemailer": "^7.0.5", - "openai": "^4.20.1", - "pg": "^8.11.3", - "pino": "^8.16.2", - "redis": "^4.6.10", - "socket.io": "^4.7.4", - "stream-buffers": "^3.0.2", - "uuid": "^9.0.1", - "wav": "^1.0.2", - "winston": "^3.11.0", - "zod": "^3.22.4" - }, - "devDependencies": { - "@faker-js/faker": "^8.3.1", - "@types/bcryptjs": "^2.4.6", - "@types/compression": "^1.7.5", - "@types/jest": "^29.5.8", - "@types/jsonwebtoken": "^9.0.5", - "@types/mime-types": "^2.1.4", - "@types/multer": "^1.4.11", - "@types/node": "^20.9.0", - "@types/pg": "^8.10.7", - "@types/stream-buffers": "^3.0.7", - "@types/supertest": "^2.0.16", - "@types/uuid": "^9.0.7", - "@typescript-eslint/eslint-plugin": "^6.12.0", - "@typescript-eslint/parser": "^6.12.0", - "dotenv-cli": "^10.0.0", - "eslint": "^8.54.0", - "jest": "^29.7.0", - "nodemon": "^3.0.1", - "pino-pretty": "^13.1.1", - "prettier": "^3.1.0", - "rimraf": "^5.0.5", - "supertest": "^6.3.3", - "ts-jest": "^29.1.1", - "tsx": "^4.6.0", - "typescript": "^5.2.2" - }, - "jest": { - "preset": "ts-jest", - "testEnvironment": "node", - "roots": [ - "/src", - "/tests" - ], - "testMatch": [ - "**/__tests__/**/*.ts", - "**/?(*.)+(spec|test).ts" - ], - "transform": { - "^.+\\.ts$": "ts-jest" - }, - "collectCoverageFrom": [ - "src/**/*.ts", - "!src/**/*.d.ts", - "!src/**/*.test.ts", - "!src/scripts/**" - ], - "coverageThreshold": { - "global": { - "branches": 90, - "functions": 90, - "lines": 90, - "statements": 90 - } - } - }, - "eslintConfig": { - "parser": "@typescript-eslint/parser", - "plugins": [ - "@typescript-eslint" - ], - "extends": [ - "eslint:recommended", - "@typescript-eslint/recommended" - ], - "parserOptions": { - "ecmaVersion": 2022, - "sourceType": "module" - }, - "env": { - "node": true, - "jest": true - }, - "rules": { - "@typescript-eslint/no-unused-vars": "error", - "@typescript-eslint/no-explicit-any": "warn", - "prefer-const": "error", - "no-var": "error" - } - }, - "prettier": { - "semi": true, - "trailingComma": "es5", - "singleQuote": true, - "printWidth": 100, - "tabWidth": 2 - } -} diff --git a/backend/run-seed.js b/backend/run-seed.js deleted file mode 100644 index 65450b3..0000000 --- a/backend/run-seed.js +++ /dev/null @@ -1,21 +0,0 @@ -const { exec } = require('child_process'); -const path = require('path'); - -console.log('🌱 Starting database seeding...'); - -// Change to backend directory and run seed -const command = 'cd backend && npx tsx src/scripts/seed.ts'; - -exec(command, { cwd: path.join(__dirname, '..') }, (error, stdout, stderr) => { - if (error) { - console.error('❌ Seed error:', error); - return; - } - - if (stderr) { - console.error('⚠️ Seed stderr:', stderr); - } - - console.log('✅ Seed output:', stdout); - console.log('🎉 Database seeding completed!'); -}); \ No newline at end of file diff --git a/backend/server.log b/backend/server.log deleted file mode 100644 index 696f3df..0000000 --- a/backend/server.log +++ /dev/null @@ -1,621 +0,0 @@ - -> ceo-communication-backend@1.0.0 dev -> tsx watch src/server.ts - -Starting server directly... -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.666Z [info] : 📋 API Server Startup... -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.680Z [info] : Database initialized - poolSize: 1 - currentTime: "2025-09-11T06:58:48.679Z" - postgresVersion: "PostgreSQL 14.19 (Homebrew) on aarch64-apple-darwin24.4.0, compiled by Apple clang version 17.0.0 (clang-1700.0.13.3), 64-bit" -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.680Z [info] : Initializing Redis connections... -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.682Z [info] cache: Redis client connected - component: "cache" -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.684Z [info] cache: Redis client ready for operations - component: "cache" -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.684Z [info] : Redis initialized -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.684Z [info] : Starting database migrations... -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.686Z [info] : Migration tracking table initialized -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.688Z [info] : Loaded migration files - count: 9 -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.689Z [info] : No pending migrations found -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.689Z [info] : Initializing API server... -[06:58:48 UTC] INFO: 2025-09-11T06:58:48.778Z [info] : API server initialized successfully -[06:58:48 UTC] ERROR: 2025-09-11T06:58:48.780Z [error] : Failed to start API server - error: { - "code": "EADDRINUSE", - "errno": -48, - "syscall": "listen", - "address": "0.0.0.0", - "port": 3001 - } -[06:58:48 UTC] FATAL: 2025-09-11T06:58:48.780Z [fatal] : Failed to start server - error: { - "code": "EADDRINUSE", - "errno": -48, - "syscall": "listen", - "address": "0.0.0.0", - "port": 3001 - } -8:28:01 AM [tsx] change in ./src/auth/routes.ts Rerunning... -cStarting server directly... -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.932Z [info] : 📋 API Server Startup... -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.947Z [info] : Database initialized - poolSize: 1 - currentTime: "2025-09-11T07:28:01.946Z" - postgresVersion: "PostgreSQL 14.19 (Homebrew) on aarch64-apple-darwin24.4.0, compiled by Apple clang version 17.0.0 (clang-1700.0.13.3), 64-bit" -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.947Z [info] : Initializing Redis connections... -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.949Z [info] cache: Redis client connected - component: "cache" -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.950Z [info] cache: Redis client ready for operations - component: "cache" -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.950Z [info] : Redis initialized -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.950Z [info] : Starting database migrations... -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.951Z [info] : Migration tracking table initialized -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.954Z [info] : Loaded migration files - count: 10 -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.957Z [info] : Migration status - total: 10 - executed: 10 - pending: 1 -[07:28:01 UTC] INFO: 2025-09-11T07:28:01.957Z [info] : Executing migration - migration: "20250101000002_create_activities_table.sql" -[07:28:01 UTC] ERROR: 2025-09-11T07:28:01.960Z [error] : Operation failed - operation: "database-transaction" - duration: 3 - error: { - "length": 147, - "name": "error", - "severity": "ERROR", - "code": "42710", - "file": "trigger.c", - "line": "760", - "routine": "CreateTriggerFiringOn" - } -[07:28:01 UTC] ERROR: 2025-09-11T07:28:01.961Z [error] : Database transaction rolled back - error: { - "length": 147, - "name": "error", - "severity": "ERROR", - "code": "42710", - "file": "trigger.c", - "line": "760", - "routine": "CreateTriggerFiringOn" - } -[07:28:01 UTC] ERROR: 2025-09-11T07:28:01.961Z [error] : Migration failed - error: { - "name": "DatabaseError", - "message": "Transaction failed: trigger \"trigger_activities_updated_at\" for relation \"activities\" already exists", - "statusCode": 500, - "code": "DATABASE_ERROR", - "context": { - "originalError": { - "length": 147, - "name": "error", - "severity": "ERROR", - "code": "42710", - "file": "trigger.c", - "line": "760", - "routine": "CreateTriggerFiringOn" - } - }, - "stack": - DatabaseError: Transaction failed: trigger "trigger_activities_updated_at" for relation "activities" already exists - at transaction (/Users/mac/Documents/Elvis/broken-app/backend/src/config/database.ts:192:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async executeMigration (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:106:3) - at async runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:168:7) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:28:01 UTC] ERROR: 2025-09-11T07:28:01.962Z [error] : Failed to start API server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:28:01 UTC] FATAL: 2025-09-11T07:28:01.962Z [fatal] : Failed to start server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -8:28:07 AM [tsx] change in ./src/auth/routes.ts Rerunning... -cStarting server directly... -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.592Z [info] : 📋 API Server Startup... -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.602Z [info] : Database initialized - poolSize: 1 - currentTime: "2025-09-11T07:28:07.601Z" - postgresVersion: "PostgreSQL 14.19 (Homebrew) on aarch64-apple-darwin24.4.0, compiled by Apple clang version 17.0.0 (clang-1700.0.13.3), 64-bit" -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.602Z [info] : Initializing Redis connections... -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.604Z [info] cache: Redis client connected - component: "cache" -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.604Z [info] cache: Redis client ready for operations - component: "cache" -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.605Z [info] : Redis initialized -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.605Z [info] : Starting database migrations... -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.606Z [info] : Migration tracking table initialized -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.607Z [info] : Loaded migration files - count: 10 -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.608Z [info] : Migration status - total: 10 - executed: 10 - pending: 1 -[07:28:07 UTC] INFO: 2025-09-11T07:28:07.608Z [info] : Executing migration - migration: "20250101000002_create_activities_table.sql" -[07:28:07 UTC] ERROR: 2025-09-11T07:28:07.609Z [error] : Operation failed - operation: "database-transaction" - duration: 1 - error: { - "length": 147, - "name": "error", - "severity": "ERROR", - "code": "42710", - "file": "trigger.c", - "line": "760", - "routine": "CreateTriggerFiringOn" - } -[07:28:07 UTC] ERROR: 2025-09-11T07:28:07.610Z [error] : Database transaction rolled back - error: { - "length": 147, - "name": "error", - "severity": "ERROR", - "code": "42710", - "file": "trigger.c", - "line": "760", - "routine": "CreateTriggerFiringOn" - } -[07:28:07 UTC] ERROR: 2025-09-11T07:28:07.610Z [error] : Migration failed - error: { - "name": "DatabaseError", - "message": "Transaction failed: trigger \"trigger_activities_updated_at\" for relation \"activities\" already exists", - "statusCode": 500, - "code": "DATABASE_ERROR", - "context": { - "originalError": { - "length": 147, - "name": "error", - "severity": "ERROR", - "code": "42710", - "file": "trigger.c", - "line": "760", - "routine": "CreateTriggerFiringOn" - } - }, - "stack": - DatabaseError: Transaction failed: trigger "trigger_activities_updated_at" for relation "activities" already exists - at transaction (/Users/mac/Documents/Elvis/broken-app/backend/src/config/database.ts:192:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async executeMigration (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:106:3) - at async runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:168:7) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:28:07 UTC] ERROR: 2025-09-11T07:28:07.611Z [error] : Failed to start API server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:28:07 UTC] FATAL: 2025-09-11T07:28:07.611Z [fatal] : Failed to start server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -8:28:14 AM [tsx] change in ./src/auth/routes.ts Rerunning... -cStarting server directly... -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.338Z [info] : 📋 API Server Startup... -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.355Z [info] : Database initialized - poolSize: 1 - currentTime: "2025-09-11T07:28:14.354Z" - postgresVersion: "PostgreSQL 14.19 (Homebrew) on aarch64-apple-darwin24.4.0, compiled by Apple clang version 17.0.0 (clang-1700.0.13.3), 64-bit" -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.355Z [info] : Initializing Redis connections... -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.356Z [info] cache: Redis client connected - component: "cache" -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.357Z [info] cache: Redis client ready for operations - component: "cache" -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.357Z [info] : Redis initialized -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.358Z [info] : Starting database migrations... -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.358Z [info] : Migration tracking table initialized -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.360Z [info] : Loaded migration files - count: 10 -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.362Z [info] : Migration status - total: 10 - executed: 10 - pending: 1 -[07:28:14 UTC] INFO: 2025-09-11T07:28:14.362Z [info] : Executing migration - migration: "20250101000002_create_activities_table.sql" -[07:28:14 UTC] ERROR: 2025-09-11T07:28:14.364Z [error] : Operation failed - operation: "database-transaction" - duration: 2 - error: { - "length": 333, - "name": "error", - "severity": "ERROR", - "code": "40P01", - "detail": "Process 23806 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 23805.\nProcess 23805 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 23806.", - "hint": "See server log for query details.", - "file": "deadlock.c", - "line": "1152", - "routine": "DeadLockReport" - } -[07:28:14 UTC] ERROR: 2025-09-11T07:28:14.364Z [error] : Database transaction rolled back - error: { - "length": 333, - "name": "error", - "severity": "ERROR", - "code": "40P01", - "detail": "Process 23806 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 23805.\nProcess 23805 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 23806.", - "hint": "See server log for query details.", - "file": "deadlock.c", - "line": "1152", - "routine": "DeadLockReport" - } -[07:28:14 UTC] ERROR: 2025-09-11T07:28:14.364Z [error] : Migration failed - error: { - "name": "DatabaseError", - "message": "Transaction failed: deadlock detected", - "statusCode": 500, - "code": "DATABASE_ERROR", - "context": { - "originalError": { - "length": 333, - "name": "error", - "severity": "ERROR", - "code": "40P01", - "detail": "Process 23806 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 23805.\nProcess 23805 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 23806.", - "hint": "See server log for query details.", - "file": "deadlock.c", - "line": "1152", - "routine": "DeadLockReport" - } - }, - "stack": - DatabaseError: Transaction failed: deadlock detected - at transaction (/Users/mac/Documents/Elvis/broken-app/backend/src/config/database.ts:192:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async executeMigration (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:106:3) - at async runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:168:7) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:28:14 UTC] ERROR: 2025-09-11T07:28:14.366Z [error] : Failed to start API server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:28:14 UTC] FATAL: 2025-09-11T07:28:14.366Z [fatal] : Failed to start server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -8:34:30 AM [tsx] change in ./src/auth/routes.ts Rerunning... -cStarting server directly... -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.745Z [info] : 📋 API Server Startup... -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.759Z [info] : Database initialized - poolSize: 1 - currentTime: "2025-09-11T07:34:30.758Z" - postgresVersion: "PostgreSQL 14.19 (Homebrew) on aarch64-apple-darwin24.4.0, compiled by Apple clang version 17.0.0 (clang-1700.0.13.3), 64-bit" -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.759Z [info] : Initializing Redis connections... -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.762Z [info] cache: Redis client connected - component: "cache" -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.763Z [info] cache: Redis client ready for operations - component: "cache" -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.763Z [info] : Redis initialized -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.763Z [info] : Starting database migrations... -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.765Z [info] : Migration tracking table initialized -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.767Z [info] : Loaded migration files - count: 10 -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.769Z [info] : Migration status - total: 10 - executed: 10 - pending: 1 -[07:34:30 UTC] INFO: 2025-09-11T07:34:30.769Z [info] : Executing migration - migration: "20250101000002_create_activities_table.sql" -[07:34:30 UTC] ERROR: 2025-09-11T07:34:30.772Z [error] : Operation failed - operation: "database-transaction" - duration: 3 - error: { - "length": 333, - "name": "error", - "severity": "ERROR", - "code": "40P01", - "detail": "Process 26806 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 26805.\nProcess 26805 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 26806.", - "hint": "See server log for query details.", - "file": "deadlock.c", - "line": "1152", - "routine": "DeadLockReport" - } -[07:34:30 UTC] ERROR: 2025-09-11T07:34:30.772Z [error] : Database transaction rolled back - error: { - "length": 333, - "name": "error", - "severity": "ERROR", - "code": "40P01", - "detail": "Process 26806 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 26805.\nProcess 26805 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 26806.", - "hint": "See server log for query details.", - "file": "deadlock.c", - "line": "1152", - "routine": "DeadLockReport" - } -[07:34:30 UTC] ERROR: 2025-09-11T07:34:30.772Z [error] : Migration failed - error: { - "name": "DatabaseError", - "message": "Transaction failed: deadlock detected", - "statusCode": 500, - "code": "DATABASE_ERROR", - "context": { - "originalError": { - "length": 333, - "name": "error", - "severity": "ERROR", - "code": "40P01", - "detail": "Process 26806 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 26805.\nProcess 26805 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 26806.", - "hint": "See server log for query details.", - "file": "deadlock.c", - "line": "1152", - "routine": "DeadLockReport" - } - }, - "stack": - DatabaseError: Transaction failed: deadlock detected - at transaction (/Users/mac/Documents/Elvis/broken-app/backend/src/config/database.ts:192:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async executeMigration (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:106:3) - at async runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:168:7) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:34:30 UTC] ERROR: 2025-09-11T07:34:30.773Z [error] : Failed to start API server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:34:30 UTC] FATAL: 2025-09-11T07:34:30.773Z [fatal] : Failed to start server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -8:34:34 AM [tsx] change in ./src/auth/routes.ts Rerunning... -cStarting server directly... -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.165Z [info] : 📋 API Server Startup... -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.176Z [info] : Database initialized - poolSize: 1 - currentTime: "2025-09-11T07:34:35.175Z" - postgresVersion: "PostgreSQL 14.19 (Homebrew) on aarch64-apple-darwin24.4.0, compiled by Apple clang version 17.0.0 (clang-1700.0.13.3), 64-bit" -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.176Z [info] : Initializing Redis connections... -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.178Z [info] cache: Redis client connected - component: "cache" -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.179Z [info] cache: Redis client ready for operations - component: "cache" -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.179Z [info] : Redis initialized -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.179Z [info] : Starting database migrations... -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.180Z [info] : Migration tracking table initialized -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.182Z [info] : Loaded migration files - count: 10 -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.183Z [info] : Migration status - total: 10 - executed: 10 - pending: 1 -[07:34:35 UTC] INFO: 2025-09-11T07:34:35.183Z [info] : Executing migration - migration: "20250101000002_create_activities_table.sql" -[07:34:35 UTC] ERROR: 2025-09-11T07:34:35.184Z [error] : Operation failed - operation: "database-transaction" - duration: 1 - error: { - "length": 147, - "name": "error", - "severity": "ERROR", - "code": "42710", - "file": "trigger.c", - "line": "760", - "routine": "CreateTriggerFiringOn" - } -[07:34:35 UTC] ERROR: 2025-09-11T07:34:35.184Z [error] : Database transaction rolled back - error: { - "length": 147, - "name": "error", - "severity": "ERROR", - "code": "42710", - "file": "trigger.c", - "line": "760", - "routine": "CreateTriggerFiringOn" - } -[07:34:35 UTC] ERROR: 2025-09-11T07:34:35.184Z [error] : Migration failed - error: { - "name": "DatabaseError", - "message": "Transaction failed: trigger \"trigger_activities_updated_at\" for relation \"activities\" already exists", - "statusCode": 500, - "code": "DATABASE_ERROR", - "context": { - "originalError": { - "length": 147, - "name": "error", - "severity": "ERROR", - "code": "42710", - "file": "trigger.c", - "line": "760", - "routine": "CreateTriggerFiringOn" - } - }, - "stack": - DatabaseError: Transaction failed: trigger "trigger_activities_updated_at" for relation "activities" already exists - at transaction (/Users/mac/Documents/Elvis/broken-app/backend/src/config/database.ts:192:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async executeMigration (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:106:3) - at async runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:168:7) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:34:35 UTC] ERROR: 2025-09-11T07:34:35.186Z [error] : Failed to start API server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:34:35 UTC] FATAL: 2025-09-11T07:34:35.186Z [fatal] : Failed to start server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -8:34:49 AM [tsx] change in ./src/auth/routes.ts Rerunning... -cStarting server directly... -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.266Z [info] : 📋 API Server Startup... -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.278Z [info] : Database initialized - poolSize: 1 - currentTime: "2025-09-11T07:34:50.278Z" - postgresVersion: "PostgreSQL 14.19 (Homebrew) on aarch64-apple-darwin24.4.0, compiled by Apple clang version 17.0.0 (clang-1700.0.13.3), 64-bit" -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.278Z [info] : Initializing Redis connections... -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.280Z [info] cache: Redis client connected - component: "cache" -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.281Z [info] cache: Redis client ready for operations - component: "cache" -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.281Z [info] : Redis initialized -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.281Z [info] : Starting database migrations... -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.282Z [info] : Migration tracking table initialized -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.283Z [info] : Loaded migration files - count: 10 -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.285Z [info] : Migration status - total: 10 - executed: 10 - pending: 1 -[07:34:50 UTC] INFO: 2025-09-11T07:34:50.285Z [info] : Executing migration - migration: "20250101000002_create_activities_table.sql" -[07:34:50 UTC] ERROR: 2025-09-11T07:34:50.287Z [error] : Operation failed - operation: "database-transaction" - duration: 2 - error: { - "length": 333, - "name": "error", - "severity": "ERROR", - "code": "40P01", - "detail": "Process 27038 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 27037.\nProcess 27037 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 27038.", - "hint": "See server log for query details.", - "file": "deadlock.c", - "line": "1152", - "routine": "DeadLockReport" - } -[07:34:50 UTC] ERROR: 2025-09-11T07:34:50.288Z [error] : Database transaction rolled back - error: { - "length": 333, - "name": "error", - "severity": "ERROR", - "code": "40P01", - "detail": "Process 27038 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 27037.\nProcess 27037 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 27038.", - "hint": "See server log for query details.", - "file": "deadlock.c", - "line": "1152", - "routine": "DeadLockReport" - } -[07:34:50 UTC] ERROR: 2025-09-11T07:34:50.288Z [error] : Migration failed - error: { - "name": "DatabaseError", - "message": "Transaction failed: deadlock detected", - "statusCode": 500, - "code": "DATABASE_ERROR", - "context": { - "originalError": { - "length": 333, - "name": "error", - "severity": "ERROR", - "code": "40P01", - "detail": "Process 27038 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 27037.\nProcess 27037 waits for ShareRowExclusiveLock on relation 67615 of database 64310; blocked by process 27038.", - "hint": "See server log for query details.", - "file": "deadlock.c", - "line": "1152", - "routine": "DeadLockReport" - } - }, - "stack": - DatabaseError: Transaction failed: deadlock detected - at transaction (/Users/mac/Documents/Elvis/broken-app/backend/src/config/database.ts:192:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async executeMigration (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:106:3) - at async runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:168:7) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:34:50 UTC] ERROR: 2025-09-11T07:34:50.289Z [error] : Failed to start API server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } -[07:34:50 UTC] FATAL: 2025-09-11T07:34:50.289Z [fatal] : Failed to start server - error: { - "name": "DatabaseError", - "message": "Migration execution failed", - "statusCode": 500, - "code": "DATABASE_ERROR", - "stack": - DatabaseError: Migration execution failed - at runMigrations (/Users/mac/Documents/Elvis/broken-app/backend/src/db/migrator.ts:179:11) - at process.processTicksAndRejections (node:internal/process/task_queues:105:5) - at async APIServer.start (/Users/mac/Documents/Elvis/broken-app/backend/src/server.ts:597:9) - } diff --git a/backend/src/ai/commands/AICommandParser.ts b/backend/src/ai/commands/AICommandParser.ts deleted file mode 100644 index 21d2517..0000000 --- a/backend/src/ai/commands/AICommandParser.ts +++ /dev/null @@ -1,565 +0,0 @@ -/** - * AI Command Parser - Phase 2 Voice Processing - * GPT-4 Turbo integration for intelligent command parsing - * - * Success Criteria: - * - Parses single and multi-action commands with >95% accuracy - * - Extracts entities (users, channels, dates, etc.) correctly - * - Confidence scoring for each parsed element - * - Processing time <1 second per command - */ - -import OpenAI from 'openai'; -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { - ParsedCommand, - UserContext, - ContextData, - CommandAction, - ResolvedEntities, - AIProcessingError, - ActionType -} from '../../voice/types'; -import { ContextManager } from '../context/ContextManager'; - -export interface ParseCommandOptions { - maxRetries?: number; - timeout?: number; - temperature?: number; - enhanceWithContext?: boolean; -} - -export class AICommandParser { - private openai: OpenAI; - private contextManager: ContextManager; - private promptCache: Map = new Map(); - private performanceMetrics: Map = new Map(); - private requestCount = 0; - - // System prompt template cache - private systemPromptTemplate: string | null = null; - - constructor() { - this.openai = new OpenAI({ - apiKey: process.env.OPENAI_API_KEY, - timeout: 15000, // 15 second timeout - maxRetries: 2 - }); - - this.contextManager = new ContextManager(); - - logger.info('AI Command Parser initialized', { - model: 'gpt-4-turbo', - timeout: 15000, - maxRetries: 2 - }); - } - - /** - * Parse voice command using GPT-4 - * Target: <1 second processing time - */ - async parseVoiceCommand( - transcript: string, - userContext: UserContext, - options: ParseCommandOptions = {} - ): Promise { - const startTime = performance.now(); - const requestId = `parse-${++this.requestCount}-${Date.now()}`; - - try { - // Validate input - this.validateInput(transcript, userContext); - - // Build comprehensive context - const context = await this.contextManager.buildContext(userContext); - - // Generate system and user prompts - const systemPrompt = await this.buildSystemPrompt(context); - const userPrompt = this.buildUserPrompt(transcript, context); - - // Make GPT-4 request with retry logic - const response = await this.makeGPTRequest( - systemPrompt, - userPrompt, - options, - requestId - ); - - const processingTime = performance.now() - startTime; - - // Parse and validate response - const parsed = await this.parseAndValidateResponse( - response, - transcript, - context, - requestId - ); - - // Record performance metrics - this.recordMetric('parsing_time', processingTime); - this.recordMetric('parsing_success', 1); - - // Log success - logger.info('Command parsing successful', { - requestId, - processingTime: `${processingTime.toFixed(2)}ms`, - transcript: transcript.substring(0, 100) + '...', - intent: parsed.intent, - actionCount: parsed.actions.length, - confidence: parsed.confidence - }); - - return { - ...parsed, - id: requestId, - userId: userContext.userId, - transcript, - processingTime, - timestamp: new Date().toISOString() - }; - - } catch (error) { - const processingTime = performance.now() - startTime; - - this.recordMetric('parsing_time', processingTime); - this.recordMetric('parsing_error', 1); - - logger.error('Command parsing failed', { - requestId, - error: (error as any).message, - processingTime: `${processingTime.toFixed(2)}ms`, - transcript: transcript.substring(0, 100) + '...', - userId: userContext.userId - }); - - throw new AIProcessingError('Command parsing failed', { - requestId, - transcript, - userId: userContext.userId, - processingTime, - originalError: (error as any).message - }); - } - } - - /** - * Batch parse multiple commands for efficiency - */ - async parseMultipleCommands( - commands: Array<{ transcript: string; userContext: UserContext }>, - options: ParseCommandOptions = {} - ): Promise { - const startTime = performance.now(); - const batchId = `batch-${Date.now()}`; - - logger.info('Starting batch command parsing', { - batchId, - commandCount: commands.length - }); - - try { - // Process commands in parallel (max 3 concurrent to avoid rate limiting) - const concurrencyLimit = 3; - const results: ParsedCommand[] = []; - - for (let i = 0; i < commands.length; i += concurrencyLimit) { - const batch = commands.slice(i, i + concurrencyLimit); - const batchPromises = batch.map(({ transcript, userContext }) => - this.parseVoiceCommand(transcript, userContext, options) - .catch((error: any) => ({ - id: `error-${Date.now()}`, - userId: userContext.userId, - transcript, - originalTranscript: transcript, - intent: 'PARSE_ERROR', - confidence: 0, - actions: [], - entities: { users: [], channels: [], tasks: [], dates: [], files: [] }, - processingTime: 0, - timestamp: new Date().toISOString() - } as ParsedCommand)) - ); - - const batchResults = await Promise.all(batchPromises); - results.push(...batchResults); - - // Small delay between batches - if (i + concurrencyLimit < commands.length) { - await new Promise(resolve => setTimeout(resolve, 200)); - } - } - - const totalTime = performance.now() - startTime; - const successfulResults = results.filter(r => !('error' in r)); - - logger.info('Batch command parsing completed', { - batchId, - totalTime: `${totalTime.toFixed(2)}ms`, - totalCommands: commands.length, - successfulParses: successfulResults.length, - failedParses: results.length - successfulResults.length - }); - - return results; - - } catch (error) { - const totalTime = performance.now() - startTime; - - logger.error('Batch command parsing failed', { - batchId, - error: (error as any).message, - totalTime: `${totalTime.toFixed(2)}ms`, - commandCount: commands.length - }); - - throw new AIProcessingError('Batch command parsing failed', { - batchId, - totalTime, - commandCount: commands.length, - originalError: (error as any).message - }); - } - } - - /** - * Get parsing performance statistics - */ - getPerformanceStats(): Record { - const stats: Record = {}; - - for (const [metric, values] of this.performanceMetrics.entries()) { - if (values.length === 0) continue; - - const sorted = [...values].sort((a, b) => a - b); - const average = values.reduce((sum, val) => sum + val, 0) / values.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - - stats[metric] = { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: values.length - }; - } - - return stats; - } - - /** - * Clear performance metrics - */ - clearMetrics(): void { - this.performanceMetrics.clear(); - logger.debug('Performance metrics cleared'); - } - - private validateInput(transcript: string, userContext: UserContext): void { - if (!transcript || transcript.trim().length === 0) { - throw new Error('Transcript cannot be empty'); - } - - if (transcript.length > 5000) { - throw new Error('Transcript too long (>5000 characters)'); - } - - if (!userContext.userId || !userContext.organizationId) { - throw new Error('User context must include userId and organizationId'); - } - } - - private async buildSystemPrompt(context: ContextData): Promise { - // Check cache first (cache for 5 minutes) - const cacheKey = `system_prompt_${context.user.id}_${context.organization.id}`; - const cached = this.promptCache.get(cacheKey); - - if (cached && Date.now() - cached.timestamp < 300000) { - return cached.prompt; - } - - // Build system prompt with current context - const systemPrompt = `You are an advanced AI assistant for "${context.organization.name}", a CEO communication platform. - -CURRENT CONTEXT: -- User: ${context.user.name} (${context.user.role}) -- Active Channels: ${context.activeChannels.map(c => `"${c.name}"`).join(', ')} -- Recent Tasks: ${context.recentTasks.slice(0, 5).map(t => `"${t.title}"`).join(', ')} -- Team Members: ${context.teamMembers.map(u => `${u.name} (${u.role})`).join(', ')} -- Current Time: ${context.temporal.currentTime} - -AVAILABLE ACTIONS: -1. CREATE_CHANNEL - Create new communication channels - Parameters: name (required), description, category_id, channel_type, privacy_level - -2. CREATE_TASK - Create tasks with assignments and deadlines - Parameters: title (required), description, channel_id, assigned_to[], priority, due_date - -3. ASSIGN_USERS - Assign users to tasks or channels - Parameters: entity_type, entity_id, user_ids[], role - -4. SEND_MESSAGE - Send messages to channels or users - Parameters: target_type, target_id, content, message_type - -5. UPLOAD_FILE - Upload and share files - Parameters: file_name, description, target_channels[], target_tasks[] - -6. SET_DEADLINE - Set or update task deadlines - Parameters: task_id, due_date, priority - -7. CREATE_DEPENDENCY - Create task dependencies - Parameters: task_id, depends_on_task_id, dependency_type - -8. UPDATE_STATUS - Update task or channel status - Parameters: entity_type, entity_id, status, notes - -9. SCHEDULE_MEETING - Schedule meetings and events - Parameters: title, date_time, duration, attendees[], location - -10. GENERATE_REPORT - Generate reports and summaries - Parameters: report_type, filters, output_format - -ENTITY RESOLUTION RULES: -- Use context to resolve ambiguous references -- "marketing team" → users with marketing role or marketing channel members -- "this project" → most recently mentioned channel or task -- "next Friday" → calculate actual date based on current time -- User names: Match against team members, use fuzzy matching if needed - -OUTPUT FORMAT (JSON): -{ - "intent": "Primary intention of the command", - "confidence": 0.95, - "actions": [ - { - "type": "ACTION_TYPE", - "parameters": { - "param1": "value1", - "param2": "value2" - }, - "priority": 1, - "dependencies": [], - "estimated_duration": "2 seconds" - } - ], - "entities": { - "users": [{"name": "John Smith", "resolved_id": "user-123", "confidence": 0.95}], - "channels": [{"name": "Marketing Q1", "resolved_id": "channel-456", "confidence": 1.0}], - "tasks": [{"title": "Campaign Launch", "resolved_id": "task-789", "confidence": 0.9}], - "dates": [{"text": "next Friday", "resolved_date": "2024-01-19T17:00:00Z", "confidence": 0.9}] - }, - "context_references": { - "pronouns": ["this", "that"], - "temporal_context": ["next week", "by Friday"], - "implicit_entities": ["the team", "the project"] - } -} - -IMPORTANT RULES: -1. Always resolve entity references using provided context -2. Be conservative with confidence scores - use lower scores for ambiguous references -3. Create logical action sequences for complex commands -4. Validate that all required parameters are present or can be inferred -5. Use dependency relationships to ensure proper execution order`; - - // Cache the prompt - this.promptCache.set(cacheKey, { - prompt: systemPrompt, - timestamp: Date.now() - }); - - // Clean old cache entries - this.cleanPromptCache(); - - return systemPrompt; - } - - private buildUserPrompt(transcript: string, context: ContextData): string { - return `Please parse this voice command: "${transcript}" - -Consider the current context and resolve all entity references. If any information is ambiguous, use the most likely interpretation based on the context and indicate lower confidence scores. - -Respond with a valid JSON object following the specified format.`; - } - - private async makeGPTRequest( - systemPrompt: string, - userPrompt: string, - options: ParseCommandOptions, - requestId: string - ): Promise { - const maxRetries = options.maxRetries || 2; - let lastError: Error | null = null; - - for (let attempt = 1; attempt <= maxRetries; attempt++) { - try { - const response = await this.openai.chat.completions.create({ - model: 'gpt-4-turbo', - messages: [ - { role: 'system', content: systemPrompt }, - { role: 'user', content: userPrompt } - ], - temperature: options.temperature || 0.1, - max_tokens: 2000, - response_format: { type: 'json_object' } - }); - - if (!response.choices[0]?.message?.content) { - throw new Error('Empty response from GPT-4'); - } - - return response.choices[0].message.content; - - } catch (error) { - lastError = error as Error; - - logger.warn('GPT-4 request failed', { - requestId, - attempt, - maxRetries, - error: (error as any).message - }); - - // Don't retry on certain types of errors - if ((error as any).message.includes('rate limit') && attempt < maxRetries) { - // Wait before retrying rate limit errors - await new Promise(resolve => setTimeout(resolve, 1000 * attempt)); - } else if ((error as any).message.includes('timeout') && attempt < maxRetries) { - // Wait before retrying timeout errors - await new Promise(resolve => setTimeout(resolve, 500)); - } else if (attempt === maxRetries) { - throw error; - } - } - } - - throw lastError || new Error('All GPT-4 request attempts failed'); - } - - private async parseAndValidateResponse( - responseContent: string, - originalTranscript: string, - context: ContextData, - requestId: string - ): Promise> { - try { - const parsed = JSON.parse(responseContent); - - // Validate required fields - this.validateParsedResponse(parsed); - - // Enhanced validation and processing - const processedActions = await this.processActions(parsed.actions, context); - const validatedEntities = await this.validateEntities(parsed.entities, context); - - return { - intent: parsed.intent, - confidence: Math.max(0, Math.min(1, parsed.confidence)), - actions: processedActions, - entities: validatedEntities, - contextReferences: parsed.context_references || {}, - originalTranscript - }; - - } catch (error: any) { - logger.error('Response parsing failed', { - requestId, - error: error.message, - responseContent: responseContent.substring(0, 500) + '...' - }); - - throw new AIProcessingError('Invalid response format from GPT-4', { - responseContent: responseContent.substring(0, 500), - originalTranscript, - parseError: error.message, - requestId - }); - } - } - - private validateParsedResponse(parsed: any): void { - const requiredFields = ['intent', 'confidence', 'actions']; - - for (const field of requiredFields) { - if (parsed[field] === undefined) { - throw new Error(`Missing required field: ${field}`); - } - } - - if (!Array.isArray(parsed.actions) || parsed.actions.length === 0) { - throw new Error('Actions array is required and must not be empty'); - } - - // Validate each action - parsed.actions.forEach((action: any, index: number) => { - if (!action.type || !action.parameters) { - throw new Error(`Invalid action at index ${index}: missing type or parameters`); - } - - // Validate action type - const validActionTypes: ActionType[] = [ - 'CREATE_CHANNEL', 'CREATE_TASK', 'ASSIGN_USERS', 'SEND_MESSAGE', - 'UPLOAD_FILE', 'SET_DEADLINE', 'CREATE_DEPENDENCY', 'UPDATE_STATUS', - 'SCHEDULE_MEETING', 'GENERATE_REPORT' - ]; - - if (!validActionTypes.includes(action.type)) { - throw new Error(`Invalid action type: ${action.type}`); - } - }); - } - - private async processActions(actions: any[], context: ContextData): Promise { - return actions.map((action: any, index: number) => ({ - id: `action-${index + 1}-${Date.now()}`, - type: action.type, - parameters: action.parameters, - priority: action.priority || index + 1, - dependencies: action.dependencies || [], - estimatedDuration: action.estimated_duration || '2 seconds', - critical: this.isActionCritical(action.type), - order: index + 1, - validated: true - })); - } - - private async validateEntities(entities: any, context: ContextData): Promise { - // Use context manager to validate and enhance entities - return this.contextManager.entityResolver.validateAndEnhanceEntities(entities, context); - } - - private isActionCritical(actionType: ActionType): boolean { - // Mark certain action types as critical (failure should rollback transaction) - const criticalActions: ActionType[] = [ - 'CREATE_CHANNEL', 'CREATE_TASK', 'ASSIGN_USERS', 'CREATE_DEPENDENCY' - ]; - - return criticalActions.includes(actionType); - } - - private recordMetric(key: string, value: number): void { - if (!this.performanceMetrics.has(key)) { - this.performanceMetrics.set(key, []); - } - - const metrics = this.performanceMetrics.get(key)!; - metrics.push(value); - - // Keep only last 1000 measurements - if (metrics.length > 1000) { - metrics.shift(); - } - } - - private cleanPromptCache(): void { - const now = Date.now(); - const maxAge = 300000; // 5 minutes - - for (const [key, value] of this.promptCache.entries()) { - if (now - value.timestamp > maxAge) { - this.promptCache.delete(key); - } - } - } -} \ No newline at end of file diff --git a/backend/src/ai/context/ContextManager.ts b/backend/src/ai/context/ContextManager.ts deleted file mode 100644 index 017cda6..0000000 --- a/backend/src/ai/context/ContextManager.ts +++ /dev/null @@ -1,413 +0,0 @@ -/** - * Context Manager - Phase 2 AI Intelligence - * Builds comprehensive context for AI command processing - * - * Success Criteria: - * - Aggregates user, organization, and conversation data - * - Includes recent tasks, channels, and team members - * - Context building time <500ms - * - Caches context data for 5-minute TTL - */ - -import { performance } from 'perf_hooks'; -import Redis from 'ioredis'; -import { logger } from '../../utils/logger'; -import { DatabaseManager } from '../../db/index'; -import { - UserContext, - ContextData, - ResolvedEntities, - AIProcessingError -} from '../../voice/types'; -import { EntityResolver } from './EntityResolver'; -import { TemporalProcessor } from './TemporalProcessor'; - -export interface ContextManagerConfig { - cacheEnabled: boolean; - cacheTTL: number; - maxRecentTasks: number; - maxRecentChannels: number; - maxTeamMembers: number; -} - -export class ContextManager { - public entityResolver: EntityResolver; - public temporalProcessor: TemporalProcessor; - - private redis: Redis; - private db: DatabaseManager; - private config: ContextManagerConfig; - private performanceMetrics: number[] = []; - - constructor(config: Partial = {}) { - this.config = { - cacheEnabled: true, - cacheTTL: 300, // 5 minutes - maxRecentTasks: 20, - maxRecentChannels: 15, - maxTeamMembers: 50, - ...config - }; - - this.db = new DatabaseManager(); - - this.redis = new Redis({ - host: process.env.REDIS_HOST || 'localhost', - port: parseInt(process.env.REDIS_PORT || '6379'), - db: parseInt(process.env.REDIS_CONTEXT_DB || '2'), - keyPrefix: 'context:', - maxRetriesPerRequest: 3, - lazyConnect: true - }); - - this.entityResolver = new EntityResolver(this.db); - this.temporalProcessor = new TemporalProcessor(); - - logger.info('Context Manager initialized', { config: this.config }); - } - - /** - * Build comprehensive context for command processing - * Target: <500ms processing time - */ - async buildContext(userContext: UserContext): Promise { - const startTime = performance.now(); - const cacheKey = this.generateCacheKey(userContext); - - try { - // Check cache first if enabled - if (this.config.cacheEnabled) { - const cached = await this.getCachedContext(cacheKey); - if (cached) { - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - - logger.debug('Context cache hit', { - cacheKey, - processingTime: `${processingTime.toFixed(2)}ms` - }); - - return cached; - } - } - - // Build context from database - const [ - userInfo, - organizationInfo, - activeChannels, - recentTasks, - teamMembers, - temporalContext - ] = await Promise.all([ - this.getUserInfo(userContext.userId), - this.getOrganizationInfo(userContext.organizationId), - this.getActiveChannels(userContext.userId), - this.getRecentTasks(userContext.userId), - this.getTeamMembers(userContext.organizationId), - this.temporalProcessor.getCurrentTemporalContext(userContext.timezone) - ]); - - const contextData: ContextData = { - user: userInfo, - organization: organizationInfo, - activeChannels, - recentTasks, - teamMembers, - temporal: temporalContext - }; - - // Cache the result if caching is enabled - if (this.config.cacheEnabled) { - await this.cacheContext(cacheKey, contextData); - } - - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - - // Log slow context building - if (processingTime > 500) { - logger.warn('Slow context building', { - processingTime: `${processingTime.toFixed(2)}ms`, - userId: userContext.userId, - channelCount: activeChannels.length, - taskCount: recentTasks.length - }); - } - - logger.debug('Context built successfully', { - processingTime: `${processingTime.toFixed(2)}ms`, - channelCount: activeChannels.length, - taskCount: recentTasks.length, - teamMemberCount: teamMembers.length - }); - - return contextData; - - } catch (error) { - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - - logger.error('Context building failed', { - error: (error as any).message, - processingTime: `${processingTime.toFixed(2)}ms`, - userId: userContext.userId, - organizationId: userContext.organizationId - }); - - throw new AIProcessingError('Failed to build context', { - userId: userContext.userId, - organizationId: userContext.organizationId, - processingTime, - originalError: (error as any).message - }); - } - } - - /** - * Get conversation history for a user - */ - async getConversationHistory( - userId: string, - limit: number = 10 - ): Promise<{ recentCommands: any[]; context: string[] }> { - try { - const recentCommands = await this.db.query(` - SELECT - id, - transcript, - processed_transcript, - intent_analysis, - execution_status, - created_at - FROM voice_commands - WHERE user_id = $1 - ORDER BY created_at DESC - LIMIT $2 - `, [userId, limit]); - - // Extract context hints from recent commands - const context = recentCommands - .filter((cmd: any) => cmd.execution_status === 'completed') - .map((cmd: any) => cmd.processed_transcript || cmd.transcript) - .slice(0, 5); // Last 5 successful commands - - return { - recentCommands: recentCommands.rows || [], - context - }; - - } catch (error) { - logger.error('Failed to get conversation history', { - userId, - error: (error as any).message - }); - - return { - recentCommands: [], - context: [] - }; - } - } - - /** - * Invalidate cached context for a user - */ - async invalidateUserContext(userId: string, organizationId?: string): Promise { - try { - if (organizationId) { - const cacheKey = this.generateCacheKey({ userId, organizationId }); - await this.redis.del(cacheKey); - } else { - // Delete all contexts for this user - const pattern = `*:${userId}:*`; - const keys = await this.redis.keys(pattern); - if (keys.length > 0) { - await this.redis.del(...keys); - } - } - - logger.debug('User context invalidated', { userId, organizationId }); - - } catch (error) { - logger.error('Failed to invalidate user context', { - userId, - organizationId, - error: (error as any).message - }); - } - } - - /** - * Get performance statistics - */ - getPerformanceStats(): { average: number; p95: number; p99: number; count: number } { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - - return { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: this.performanceMetrics.length - }; - } - - private generateCacheKey(userContext: UserContext): string { - return `ctx:${userContext.organizationId}:${userContext.userId}:${userContext.sessionId || 'default'}`; - } - - private async getCachedContext(key: string): Promise { - try { - const cached = await this.redis.get(key); - if (cached) { - return JSON.parse(cached); - } - } catch (error) { - logger.warn('Cache lookup failed', { key, error: (error as any).message }); - } - - return null; - } - - private async cacheContext(key: string, context: ContextData): Promise { - try { - await this.redis.setex(key, this.config.cacheTTL, JSON.stringify(context)); - } catch (error) { - logger.warn('Cache set failed', { key, error: (error as any).message }); - } - } - - private async getUserInfo(userId: string): Promise { - const result = await this.db.query(` - SELECT id, name, email, role - FROM users - WHERE id = $1 - `, [userId]); - - if (!result.rows[0]) { - throw new Error(`User not found: ${userId}`); - } - - const user = result.rows[0]; - return { - id: user.id, - name: user.name, - role: user.role, - email: user.email - }; - } - - private async getOrganizationInfo(organizationId: string): Promise { - // For now, return mock organization info - // In a real implementation, this would query an organizations table - return { - id: organizationId, - name: 'CEO Communication Platform', - timezone: 'UTC' - }; - } - - private async getActiveChannels(userId: string): Promise { - const result = await this.db.query(` - SELECT DISTINCT - c.id, - c.name, - c.channel_type as type, - COUNT(cm.user_id) as member_count - FROM channels c - LEFT JOIN channel_members cm ON c.id = cm.channel_id - WHERE c.status = 'active' - AND c.id IN ( - SELECT channel_id - FROM channel_members - WHERE user_id = $1 - ) - GROUP BY c.id, c.name, c.channel_type - ORDER BY c.created_at DESC - LIMIT $2 - `, [userId, this.config.maxRecentChannels]); - - return result.rows.map((row: any) => ({ - id: row.id, - name: row.name, - type: row.type, - memberCount: parseInt(row.member_count) || 0 - })); - } - - private async getRecentTasks(userId: string): Promise { - const result = await this.db.query(` - SELECT - id, - title, - status, - assigned_to, - due_date - FROM tasks - WHERE (created_by = $1 OR $1 = ANY(assigned_to)) - AND status IN ('pending', 'in_progress', 'review') - ORDER BY - CASE - WHEN due_date IS NOT NULL THEN due_date - ELSE created_at - END DESC - LIMIT $2 - `, [userId, this.config.maxRecentTasks]); - - return result.rows.map((row: any) => ({ - id: row.id, - title: row.title, - status: row.status, - assignedTo: row.assigned_to || [], - dueDate: row.due_date ? row.due_date.toISOString() : undefined - })); - } - - private async getTeamMembers(organizationId: string): Promise { - const result = await this.db.query(` - SELECT - id, - name, - role, - CASE - WHEN last_active > NOW() - INTERVAL '5 minutes' THEN 'online' - WHEN last_active > NOW() - INTERVAL '30 minutes' THEN 'busy' - ELSE 'offline' - END as status - FROM users - WHERE organization_id = $1 - OR $1 IS NULL -- Handle case where organization_id is not set - ORDER BY - CASE - WHEN last_active > NOW() - INTERVAL '5 minutes' THEN 1 - WHEN last_active > NOW() - INTERVAL '30 minutes' THEN 2 - ELSE 3 - END, - name - LIMIT $2 - `, [organizationId, this.config.maxTeamMembers]); - - return result.rows.map((row: any) => ({ - id: row.id, - name: row.name, - role: row.role, - status: row.status - })); - } - - private recordPerformance(time: number): void { - this.performanceMetrics.push(time); - - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} \ No newline at end of file diff --git a/backend/src/ai/context/EntityResolver.ts b/backend/src/ai/context/EntityResolver.ts deleted file mode 100644 index 631f5f1..0000000 --- a/backend/src/ai/context/EntityResolver.ts +++ /dev/null @@ -1,588 +0,0 @@ -/** - * Entity Resolver - Phase 2 AI Intelligence - * Smart entity resolution with fuzzy matching and context awareness - * - * Success Criteria: - * - Fuzzy matching with 85%+ accuracy - * - Handles pronouns (this, that, it) - * - Context-aware disambiguation - * - <200ms resolution time per entity - */ - -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { DatabaseManager } from '../../db'; -import { ContextData, ResolvedEntities } from '../../voice/types'; - -export interface ResolvedUser { - id: string; - name: string; - email: string; - role: string; - confidence: number; -} - -export interface ResolvedChannel { - id: string; - name: string; - type: string; - memberCount: number; - confidence: number; -} - -export interface ResolvedTask { - id: string; - title: string; - status: string; - assignedTo: string[]; - confidence: number; -} - -export class EntityResolver { - private db: DatabaseManager; - private performanceMetrics: number[] = []; - - // Simple fuzzy matching cache - private fuzzyCache: Map = new Map(); - - constructor(db: DatabaseManager) { - this.db = db; - - logger.debug('Entity Resolver initialized'); - } - - /** - * Resolve user entity with fuzzy matching and context - * Target: <200ms resolution time - */ - async resolveUser(name: string, context: ContextData): Promise { - const startTime = performance.now(); - - try { - // Exact match first (highest confidence) - let user = await this.findUserExactMatch(name, context); - if (user) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - return { ...user, confidence: 1.0 }; - } - - // Try fuzzy matching on team members - user = await this.findUserFuzzyMatch(name, context); - if (user) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - return user; - } - - // Try role-based resolution - user = await this.resolveUserByRole(name, context); - if (user) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - return user; - } - - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - - logger.debug('User resolution failed', { - name, - resolutionTime: `${resolutionTime.toFixed(2)}ms` - }); - - return null; - - } catch (error) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - - logger.error('User resolution error', { - name, - error: (error as any).message, - resolutionTime: `${resolutionTime.toFixed(2)}ms` - }); - - return null; - } - } - - /** - * Resolve channel entity with context awareness - */ - async resolveChannel(name: string, context: ContextData): Promise { - const startTime = performance.now(); - - try { - // Exact match within user's active channels - let channel = this.findChannelExactMatch(name, context); - if (channel) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - return { ...channel, confidence: 1.0 }; - } - - // Fuzzy match within user's channels - channel = this.findChannelFuzzyMatch(name, context); - if (channel) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - return channel; - } - - // Search all accessible channels - channel = await this.findChannelInDatabase(name, context); - if (channel) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - return channel; - } - - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - - logger.debug('Channel resolution failed', { - name, - resolutionTime: `${resolutionTime.toFixed(2)}ms` - }); - - return null; - - } catch (error) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - - logger.error('Channel resolution error', { - name, - error: (error as any).message, - resolutionTime: `${resolutionTime.toFixed(2)}ms` - }); - - return null; - } - } - - /** - * Resolve task entity with smart matching - */ - async resolveTask(reference: string, context: ContextData): Promise { - const startTime = performance.now(); - - try { - // Try exact title match - let task = this.findTaskExactMatch(reference, context); - if (task) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - return { ...task, confidence: 1.0 }; - } - - // Try fuzzy title match - task = this.findTaskFuzzyMatch(reference, context); - if (task) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - return task; - } - - // Try contextual resolution (e.g., "this task", "the current task") - task = await this.resolveTaskContextually(reference, context); - if (task) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - return task; - } - - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - - return null; - - } catch (error) { - const resolutionTime = performance.now() - startTime; - this.recordPerformance(resolutionTime); - - logger.error('Task resolution error', { - reference, - error: (error as any).message, - resolutionTime: `${resolutionTime.toFixed(2)}ms` - }); - - return null; - } - } - - /** - * Validate and enhance entities from AI parsing - */ - async validateAndEnhanceEntities( - entities: any, - context: ContextData - ): Promise { - const enhanced: ResolvedEntities = { - users: [], - channels: [], - tasks: [], - dates: [], - files: [] - }; - - // Validate and enhance user entities - if (entities.users && Array.isArray(entities.users)) { - for (const user of entities.users) { - const resolved = await this.resolveUser(user.name, context); - if (resolved) { - enhanced.users.push({ - name: user.name, - resolved_id: resolved.id, - confidence: Math.min(user.confidence, resolved.confidence) - }); - } - } - } - - // Validate and enhance channel entities - if (entities.channels && Array.isArray(entities.channels)) { - for (const channel of entities.channels) { - const resolved = await this.resolveChannel(channel.name, context); - if (resolved) { - enhanced.channels.push({ - name: channel.name, - resolved_id: resolved.id, - confidence: Math.min(channel.confidence, resolved.confidence) - }); - } - } - } - - // Validate and enhance task entities - if (entities.tasks && Array.isArray(entities.tasks)) { - for (const task of entities.tasks) { - const resolved = await this.resolveTask(task.title, context); - if (resolved) { - enhanced.tasks.push({ - title: task.title, - resolved_id: resolved.id, - confidence: Math.min(task.confidence, resolved.confidence) - }); - } - } - } - - // Pass through date entities (handled by TemporalProcessor) - if (entities.dates && Array.isArray(entities.dates)) { - enhanced.dates = entities.dates; - } - - // Pass through file entities - if (entities.files && Array.isArray(entities.files)) { - enhanced.files = entities.files; - } - - return enhanced; - } - - /** - * Get performance statistics - */ - getPerformanceStats(): { average: number; p95: number; p99: number; count: number } { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)]; - const p99 = sorted[Math.floor(sorted.length * 0.99)]; - - return { - average: Math.round(average * 100) / 100, - p95: Math.round((p95 || 0) * 100) / 100, - p99: Math.round((p99 || 0) * 100) / 100, - count: this.performanceMetrics.length - }; - } - - private async findUserExactMatch(name: string, context: ContextData): Promise { - // Check team members first (most common case) - const exactMatch = context.teamMembers.find(member => - member.name.toLowerCase() === name.toLowerCase() - ); - - if (exactMatch) { - return { - id: exactMatch.id, - name: exactMatch.name, - role: exactMatch.role, - email: '', // Would be filled from database if needed - confidence: 1.0 - }; - } - - return null; - } - - private async findUserFuzzyMatch(name: string, context: ContextData): Promise { - const matches = this.fuzzyMatchUsers(name, context.teamMembers); - - if (matches.length > 0 && matches[0]?.score && matches[0].score > 0.7) { - const match = matches[0]; - if (match) { - return { - id: match.user.id, - name: match.user.name, - role: match.user.role, - email: '', - confidence: match.score - }; - } - } - - return null; - } - - private async resolveUserByRole(name: string, context: ContextData): Promise { - // Handle role-based references like "the manager", "marketing team lead" - const roleKeywords = { - 'manager': ['manager', 'lead', 'supervisor'], - 'developer': ['developer', 'engineer', 'programmer'], - 'designer': ['designer', 'creative'], - 'marketing': ['marketing', 'marketer'], - 'sales': ['sales', 'salesperson'] - }; - - const nameLower = name.toLowerCase(); - - for (const [role, keywords] of Object.entries(roleKeywords)) { - if (keywords.some(keyword => nameLower.includes(keyword))) { - const roleMatch = context.teamMembers.find(member => - member.role.toLowerCase().includes(role) - ); - - if (roleMatch) { - return { - id: roleMatch.id, - name: roleMatch.name, - role: roleMatch.role, - email: '', - confidence: 0.8 // Lower confidence for role-based matches - }; - } - } - } - - return null; - } - - private findChannelExactMatch(name: string, context: ContextData): ResolvedChannel | null { - const exactMatch = context.activeChannels.find(channel => - channel.name.toLowerCase() === name.toLowerCase() - ); - - if (exactMatch) { - return { - id: exactMatch.id, - name: exactMatch.name, - type: exactMatch.type, - memberCount: exactMatch.memberCount, - confidence: 1.0 - }; - } - - return null; - } - - private findChannelFuzzyMatch(name: string, context: ContextData): ResolvedChannel | null { - const matches = this.fuzzyMatchChannels(name, context.activeChannels); - - if (matches.length > 0 && matches[0]?.score && matches[0].score > 0.7) { - const match = matches[0]; - if (match) { - return { - id: match.channel.id, - name: match.channel.name, - type: match.channel.type, - memberCount: match.channel.memberCount, - confidence: match.score - }; - } - } - - return null; - } - - private async findChannelInDatabase(name: string, context: ContextData): Promise { - try { - const result = await this.db.query(` - SELECT c.id, c.name, c.channel_type as type, - COUNT(cm.user_id) as member_count - FROM channels c - LEFT JOIN channel_members cm ON c.id = cm.channel_id - WHERE c.name ILIKE $1 - AND c.status = 'active' - AND (c.privacy_level = 'public' - OR c.id IN ( - SELECT channel_id - FROM channel_members - WHERE user_id = $2 - )) - GROUP BY c.id, c.name, c.channel_type - LIMIT 5 - `, [`%${name}%`, context.user.id]); - - if (result.rows.length > 0) { - const channel = result.rows[0]; - return { - id: channel.id, - name: channel.name, - type: channel.type, - memberCount: parseInt(channel.member_count) || 0, - confidence: 0.6 // Lower confidence for database searches - }; - } - - } catch (error) { - logger.error('Database channel search failed', { - name, - error: (error as any).message - }); - } - - return null; - } - - private findTaskExactMatch(reference: string, context: ContextData): ResolvedTask | null { - const exactMatch = context.recentTasks.find(task => - task.title.toLowerCase() === reference.toLowerCase() - ); - - if (exactMatch) { - return { - id: exactMatch.id, - title: exactMatch.title, - status: exactMatch.status, - assignedTo: exactMatch.assignedTo, - confidence: 1.0 - }; - } - - return null; - } - - private findTaskFuzzyMatch(reference: string, context: ContextData): ResolvedTask | null { - const matches = this.fuzzyMatchTasks(reference, context.recentTasks); - - if (matches.length > 0 && matches[0]?.score && matches[0].score > 0.7) { - const match = matches[0]; - if (match) { - return { - id: match.task.id, - title: match.task.title, - status: match.task.status, - assignedTo: match.task.assignedTo, - confidence: match.score - }; - } - } - - return null; - } - - private async resolveTaskContextually(reference: string, context: ContextData): Promise { - const refLower = reference.toLowerCase(); - - // Handle contextual references - if (refLower.includes('this task') || refLower.includes('current task')) { - // Return most recent task - const recentTask = context.recentTasks[0]; - if (recentTask) { - return { - id: recentTask.id, - title: recentTask.title, - status: recentTask.status, - assignedTo: recentTask.assignedTo, - confidence: 0.8 - }; - } - } - - if (refLower.includes('urgent') || refLower.includes('priority')) { - // Find urgent tasks - const urgentTask = context.recentTasks.find(task => - task.dueDate && new Date(task.dueDate) <= new Date(Date.now() + 86400000) // Due within 24 hours - ); - - if (urgentTask) { - return { - id: urgentTask.id, - title: urgentTask.title, - status: urgentTask.status, - assignedTo: urgentTask.assignedTo, - confidence: 0.7 - }; - } - } - - return null; - } - - // Simple fuzzy matching implementation - private fuzzyMatchUsers(query: string, users: ContextData['teamMembers']): Array<{user: any, score: number}> { - return users - .map(user => ({ - user, - score: this.calculateSimilarity(query.toLowerCase(), user.name.toLowerCase()) - })) - .filter(match => match.score > 0.5) - .sort((a, b) => b.score - a.score); - } - - private fuzzyMatchChannels(query: string, channels: ContextData['activeChannels']): Array<{channel: any, score: number}> { - return channels - .map(channel => ({ - channel, - score: this.calculateSimilarity(query.toLowerCase(), channel.name.toLowerCase()) - })) - .filter(match => match.score > 0.5) - .sort((a, b) => b.score - a.score); - } - - private fuzzyMatchTasks(query: string, tasks: ContextData['recentTasks']): Array<{task: any, score: number}> { - return tasks - .map(task => ({ - task, - score: this.calculateSimilarity(query.toLowerCase(), task.title.toLowerCase()) - })) - .filter(match => match.score > 0.5) - .sort((a, b) => b.score - a.score); - } - - // Simple similarity calculation (Jaccard similarity) - private calculateSimilarity(str1: string, str2: string): number { - if (str1 === str2) return 1.0; - - const words1 = new Set(str1.split(/\s+/)); - const words2 = new Set(str2.split(/\s+/)); - - const intersection = new Set([...words1].filter(word => words2.has(word))); - const union = new Set([...words1, ...words2]); - - if (union.size === 0) return 0; - - const jaccardSimilarity = intersection.size / union.size; - - // Boost for substring matches - const substringBoost = str2.includes(str1) || str1.includes(str2) ? 0.3 : 0; - - return Math.min(1.0, jaccardSimilarity + substringBoost); - } - - private recordPerformance(time: number): void { - this.performanceMetrics.push(time); - - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} \ No newline at end of file diff --git a/backend/src/ai/context/TemporalProcessor.ts b/backend/src/ai/context/TemporalProcessor.ts deleted file mode 100644 index 50d7464..0000000 --- a/backend/src/ai/context/TemporalProcessor.ts +++ /dev/null @@ -1,633 +0,0 @@ -/** - * Temporal Processor - Phase 2 AI Intelligence - * Processes temporal references in voice commands - * - * Success Criteria: - * - Handles "next Friday", "in 2 weeks", "tomorrow" - * - Timezone-aware date resolution - * - Business day calculations - * - 90%+ accuracy for common date expressions - */ - -import { logger } from '../../utils/logger'; - -export interface ResolvedDate { - date: Date; - confidence: number; - originalText: string; - interpretation: string; -} - -export interface TemporalContext { - currentTime: string; - timezone: string; - businessHours: { - start: string; - end: string; - }; - workingDays: number[]; // 1-7, Monday=1, Sunday=7 -} - -export class TemporalProcessor { - private readonly WEEKDAYS = ['sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday']; - private readonly MONTHS = [ - 'january', 'february', 'march', 'april', 'may', 'june', - 'july', 'august', 'september', 'october', 'november', 'december' - ]; - - // Common time patterns - private readonly TIME_PATTERNS = [ - // Relative days - { pattern: /\b(today|this day)\b/i, handler: this.handleToday.bind(this) }, - { pattern: /\b(tomorrow|next day)\b/i, handler: this.handleTomorrow.bind(this) }, - { pattern: /\b(yesterday|last day)\b/i, handler: this.handleYesterday.bind(this) }, - - // Relative weeks - { pattern: /\bnext (monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i, handler: this.handleNextWeekday.bind(this) }, - { pattern: /\bthis (monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i, handler: this.handleThisWeekday.bind(this) }, - { pattern: /\blast (monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i, handler: this.handleLastWeekday.bind(this) }, - - // Specific weekdays - { pattern: /\b(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i, handler: this.handleWeekday.bind(this) }, - - // Week references - { pattern: /\bnext week\b/i, handler: this.handleNextWeek.bind(this) }, - { pattern: /\bthis week\b/i, handler: this.handleThisWeek.bind(this) }, - { pattern: /\blast week\b/i, handler: this.handleLastWeek.bind(this) }, - - // Month references - { pattern: /\bnext month\b/i, handler: this.handleNextMonth.bind(this) }, - { pattern: /\bthis month\b/i, handler: this.handleThisMonth.bind(this) }, - { pattern: /\blast month\b/i, handler: this.handleLastMonth.bind(this) }, - - // Relative time periods - { pattern: /\bin (\d+) (day|days)\b/i, handler: this.handleInDays.bind(this) }, - { pattern: /\bin (\d+) (week|weeks)\b/i, handler: this.handleInWeeks.bind(this) }, - { pattern: /\bin (\d+) (month|months)\b/i, handler: this.handleInMonths.bind(this) }, - - // By/before/after patterns - { pattern: /\bby (next )?(monday|tuesday|wednesday|thursday|friday|saturday|sunday)\b/i, handler: this.handleByWeekday.bind(this) }, - { pattern: /\bby (tomorrow|next week|next month)\b/i, handler: this.handleByRelative.bind(this) }, - { pattern: /\bby the end of (this|next)? ?(week|month)\b/i, handler: this.handleByEndOf.bind(this) }, - - // Specific dates (basic) - { pattern: /\b(\d{1,2})\/(\d{1,2})\/(\d{4}|\d{2})\b/, handler: this.handleNumericDate.bind(this) }, - { pattern: /\b(january|february|march|april|may|june|july|august|september|october|november|december) (\d{1,2})\b/i, handler: this.handleMonthDay.bind(this) }, - - // Time specifications - { pattern: /\bat (\d{1,2}):(\d{2})\s?(am|pm|AM|PM)?\b/i, handler: this.handleTimeSpec.bind(this) }, - { pattern: /\bat (\d{1,2})\s?(am|pm|AM|PM)\b/i, handler: this.handleSimpleTime.bind(this) }, - ]; - - constructor() { - logger.debug('Temporal Processor initialized'); - } - - /** - * Get current temporal context for a timezone - */ - getCurrentTemporalContext(timezone: string = 'UTC'): TemporalContext { - const now = new Date(); - - // Convert to specified timezone (simplified - in production use proper timezone library) - const currentTime = now.toISOString(); - - return { - currentTime, - timezone, - businessHours: { - start: '09:00', - end: '17:00' - }, - workingDays: [1, 2, 3, 4, 5] // Monday to Friday - }; - } - - /** - * Resolve a date expression to actual date - */ - async resolveDate( - dateText: string, - context: TemporalContext - ): Promise { - try { - const baseDate = new Date(context.currentTime); - const originalText = dateText.trim(); - const lowerText = originalText.toLowerCase(); - - // Try each pattern - for (const { pattern, handler } of this.TIME_PATTERNS) { - const match = lowerText.match(pattern); - if (match) { - const result = handler(match, baseDate); - if (result) { - logger.debug('Date resolved', { - originalText, - resolvedDate: result.date.toISOString(), - confidence: result.confidence, - interpretation: result.interpretation - }); - - return { - ...result, - originalText - }; - } - } - } - - // Try natural language parsing as fallback - const fallbackResult = this.parseNaturalLanguageDate(lowerText, baseDate); - if (fallbackResult) { - return { - ...fallbackResult, - originalText - }; - } - - logger.debug('Date resolution failed', { originalText }); - return null; - - } catch (error) { - logger.error('Date resolution error', { - dateText, - error: (error as any).message - }); - - return null; - } - } - - /** - * Parse multiple date references in text - */ - async resolveDatesInText( - text: string, - context: TemporalContext - ): Promise { - const results: ResolvedDate[] = []; - const processedRanges: Array<{start: number, end: number}> = []; - - // Find all potential date references - for (const { pattern } of this.TIME_PATTERNS) { - let match; - const regex = new RegExp(pattern.source, pattern.flags + 'g'); - - while ((match = regex.exec(text)) !== null) { - const start = match.index; - const end = start + match[0].length; - - // Check if this range overlaps with already processed ranges - const overlaps = processedRanges.some(range => - (start >= range.start && start <= range.end) || - (end >= range.start && end <= range.end) - ); - - if (!overlaps) { - const dateText = match[0]; - const resolved = await this.resolveDate(dateText, context); - - if (resolved) { - results.push(resolved); - processedRanges.push({ start, end }); - } - } - } - } - - return results; - } - - /** - * Check if a date is a business day - */ - isBusinessDay(date: Date, context: TemporalContext): boolean { - const dayOfWeek = date.getDay(); // 0 = Sunday, 6 = Saturday - const adjustedDay = dayOfWeek === 0 ? 7 : dayOfWeek; // Convert to 1-7 scale - - return context.workingDays.includes(adjustedDay); - } - - /** - * Get next business day from a given date - */ - getNextBusinessDay(date: Date, context: TemporalContext): Date { - const result = new Date(date); - - do { - result.setDate(result.getDate() + 1); - } while (!this.isBusinessDay(result, context)); - - return result; - } - - /** - * Add business days to a date - */ - addBusinessDays(date: Date, days: number, context: TemporalContext): Date { - const result = new Date(date); - let addedDays = 0; - - while (addedDays < days) { - result.setDate(result.getDate() + 1); - if (this.isBusinessDay(result, context)) { - addedDays++; - } - } - - return result; - } - - // Handler methods for different date patterns - private handleToday(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - return { - date: new Date(baseDate), - confidence: 1.0, - originalText: match[0], - interpretation: 'Today' - }; - } - - private handleTomorrow(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const date = new Date(baseDate); - date.setDate(date.getDate() + 1); - - return { - date, - confidence: 1.0, - originalText: match[0], - interpretation: 'Tomorrow' - }; - } - - private handleYesterday(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const date = new Date(baseDate); - date.setDate(date.getDate() - 1); - - return { - date, - confidence: 1.0, - originalText: match[0], - interpretation: 'Yesterday' - }; - } - - private handleNextWeekday(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const weekday = (match[1] || '').toLowerCase(); - const targetDay = this.WEEKDAYS.indexOf(weekday); - const currentDay = baseDate.getDay(); - - const date = new Date(baseDate); - let daysToAdd = targetDay - currentDay; - - if (daysToAdd <= 0) { - daysToAdd += 7; // Next week - } - - date.setDate(date.getDate() + daysToAdd); - - return { - date, - confidence: 0.95, - originalText: match[0], - interpretation: `Next ${weekday}` - }; - } - - private handleThisWeekday(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const weekday = (match[1] || '').toLowerCase(); - const targetDay = this.WEEKDAYS.indexOf(weekday); - const currentDay = baseDate.getDay(); - - const date = new Date(baseDate); - const daysToAdd = targetDay - currentDay; - - date.setDate(date.getDate() + daysToAdd); - - return { - date, - confidence: 0.9, - originalText: match[0], - interpretation: `This ${weekday}` - }; - } - - private handleLastWeekday(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const weekday = (match[1] || '').toLowerCase(); - const targetDay = this.WEEKDAYS.indexOf(weekday); - const currentDay = baseDate.getDay(); - - const date = new Date(baseDate); - let daysToSubtract = currentDay - targetDay; - - if (daysToSubtract <= 0) { - daysToSubtract += 7; // Last week - } - - date.setDate(date.getDate() - daysToSubtract); - - return { - date, - confidence: 0.9, - originalText: match[0], - interpretation: `Last ${weekday}` - }; - } - - private handleWeekday(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - // Default to next occurrence of this weekday - return this.handleNextWeekday(['next ' + match[0], match[0]], baseDate); - } - - private handleNextWeek(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const date = new Date(baseDate); - date.setDate(date.getDate() + 7); - - return { - date, - confidence: 0.8, - originalText: match[0], - interpretation: 'Next week (same day)' - }; - } - - private handleThisWeek(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - // Return Friday of this week as default - const date = new Date(baseDate); - const currentDay = baseDate.getDay(); - const daysToFriday = 5 - currentDay; - - date.setDate(date.getDate() + daysToFriday); - - return { - date, - confidence: 0.7, - originalText: match[0], - interpretation: 'This week (Friday)' - }; - } - - private handleLastWeek(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const date = new Date(baseDate); - date.setDate(date.getDate() - 7); - - return { - date, - confidence: 0.8, - originalText: match[0], - interpretation: 'Last week (same day)' - }; - } - - private handleNextMonth(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const date = new Date(baseDate); - date.setMonth(date.getMonth() + 1); - - return { - date, - confidence: 0.8, - originalText: match[0], - interpretation: 'Next month (same day)' - }; - } - - private handleThisMonth(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - // Return end of this month - const date = new Date(baseDate); - date.setMonth(date.getMonth() + 1, 0); // Last day of current month - - return { - date, - confidence: 0.7, - originalText: match[0], - interpretation: 'End of this month' - }; - } - - private handleLastMonth(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const date = new Date(baseDate); - date.setMonth(date.getMonth() - 1); - - return { - date, - confidence: 0.8, - originalText: match[0], - interpretation: 'Last month (same day)' - }; - } - - private handleInDays(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const days = parseInt(match[1] || '0'); - const date = new Date(baseDate); - date.setDate(date.getDate() + days); - - return { - date, - confidence: 0.95, - originalText: match[0], - interpretation: `In ${days} day${days > 1 ? 's' : ''}` - }; - } - - private handleInWeeks(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const weeks = parseInt(match[1] || '0'); - const date = new Date(baseDate); - date.setDate(date.getDate() + (weeks * 7)); - - return { - date, - confidence: 0.9, - originalText: match[0], - interpretation: `In ${weeks} week${weeks > 1 ? 's' : ''}` - }; - } - - private handleInMonths(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const months = parseInt(match[1] || '0'); - const date = new Date(baseDate); - date.setMonth(date.getMonth() + months); - - return { - date, - confidence: 0.85, - originalText: match[0], - interpretation: `In ${months} month${months > 1 ? 's' : ''}` - }; - } - - private handleByWeekday(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const weekday = (match[2] || '').toLowerCase(); - const isNext = match[1] && match[1].toLowerCase().includes('next'); - - if (isNext) { - return this.handleNextWeekday(['', weekday], baseDate); - } else { - return this.handleThisWeekday(['', weekday], baseDate); - } - } - - private handleByRelative(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const timeRef = (match[1] || '').toLowerCase(); - - if (timeRef === 'tomorrow') { - return this.handleTomorrow(['tomorrow'], baseDate); - } else if (timeRef === 'next week') { - return this.handleNextWeek(['next week'], baseDate); - } else if (timeRef === 'next month') { - return this.handleNextMonth(['next month'], baseDate); - } - - // Default fallback - return tomorrow - return this.handleTomorrow(['tomorrow'], baseDate); - } - - private handleByEndOf(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const period = (match[2] || '').toLowerCase(); - const isNext = match[1] && match[1].toLowerCase() === 'next'; - - const date = new Date(baseDate); - - if (period === 'week') { - // End of week (Sunday) - const daysToSunday = 7 - date.getDay(); - date.setDate(date.getDate() + daysToSunday); - if (isNext) { - date.setDate(date.getDate() + 7); - } - } else if (period === 'month') { - // End of month - if (isNext) { - date.setMonth(date.getMonth() + 1); - } - date.setMonth(date.getMonth() + 1, 0); // Last day of month - } - - return { - date, - confidence: 0.85, - originalText: match[0], - interpretation: `By end of ${isNext ? 'next' : 'this'} ${period}` - }; - } - - private handleNumericDate(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const month = parseInt(match[1] || '1') - 1; // Month is 0-indexed - const day = parseInt(match[2] || '1'); - let year = parseInt(match[3] || new Date().getFullYear().toString()); - - // Handle 2-digit years - if (year < 100) { - year += year < 50 ? 2000 : 1900; - } - - const date = new Date(year, month, day); - - return { - date, - confidence: 0.95, - originalText: match[0], - interpretation: `Specific date: ${match[0]}` - }; - } - - private handleMonthDay(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const monthName = (match[1] || '').toLowerCase(); - const day = parseInt(match[2] || '1'); - const month = this.MONTHS.indexOf(monthName); - - const date = new Date(baseDate.getFullYear(), month, day); - - // If the date has passed this year, assume next year - if (date < baseDate) { - date.setFullYear(date.getFullYear() + 1); - } - - return { - date, - confidence: 0.9, - originalText: match[0], - interpretation: `${monthName} ${day}` - }; - } - - private handleTimeSpec(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const hour = parseInt(match[1] || '0'); - const minute = parseInt(match[2] || '0'); - const period = match[3]?.toLowerCase(); - - let adjustedHour = hour; - if (period === 'pm' && hour < 12) { - adjustedHour += 12; - } else if (period === 'am' && hour === 12) { - adjustedHour = 0; - } - - const date = new Date(baseDate); - date.setHours(adjustedHour, minute, 0, 0); - - return { - date, - confidence: 0.9, - originalText: match[0], - interpretation: `Time: ${hour}:${minute.toString().padStart(2, '0')} ${period || ''}` - }; - } - - private handleSimpleTime(match: RegExpMatchArray, baseDate: Date): ResolvedDate { - const hour = parseInt(match[1] || '0'); - const period = match[2]?.toLowerCase(); - - let adjustedHour = hour; - if (period === 'pm' && hour < 12) { - adjustedHour += 12; - } else if (period === 'am' && hour === 12) { - adjustedHour = 0; - } - - const date = new Date(baseDate); - date.setHours(adjustedHour, 0, 0, 0); - - return { - date, - confidence: 0.85, - originalText: match[0], - interpretation: `Time: ${hour} ${period || ''}` - }; - } - - private parseNaturalLanguageDate(text: string, baseDate: Date): ResolvedDate | null { - // Simple fallback parsing for common phrases - const phrases = { - 'end of week': () => { - const date = new Date(baseDate); - const daysToSunday = 7 - date.getDay(); - date.setDate(date.getDate() + daysToSunday); - return date; - }, - 'start of week': () => { - const date = new Date(baseDate); - const daysToMonday = date.getDay() === 0 ? 1 : (8 - date.getDay()); - date.setDate(date.getDate() + daysToMonday); - return date; - }, - 'end of day': () => { - const date = new Date(baseDate); - date.setHours(23, 59, 59, 999); - return date; - } - }; - - for (const [phrase, dateCalculator] of Object.entries(phrases)) { - if (text.includes(phrase)) { - return { - date: dateCalculator(), - confidence: 0.7, - originalText: phrase, - interpretation: phrase - }; - } - } - - return null; - } -} \ No newline at end of file diff --git a/backend/src/ai/execution/DependencyResolver.ts b/backend/src/ai/execution/DependencyResolver.ts deleted file mode 100644 index 94c0580..0000000 --- a/backend/src/ai/execution/DependencyResolver.ts +++ /dev/null @@ -1,782 +0,0 @@ -/** - * Dependency Resolver - Phase 2 Multi-Action Execution - * Resolves dependencies between actions in complex voice commands - * - * Success Criteria: - * - Identifies implicit and explicit dependencies between actions - * - Creates optimal execution order with parallel execution where possible - * - Handles circular dependency detection and prevention - * - Supports dependency resolution in <100ms - */ - -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { AIProcessingError } from '../../voice/types'; - -export interface Action { - id: string; - type: string; - parameters: Record; - dependencies?: string[]; - metadata?: { - priority: number; - estimatedDuration: number; - requiresInput?: boolean; - producesOutput?: boolean; - }; -} - -export interface DependencyGraph { - nodes: Map; - edges: Set; - levels: DependencyLevel[]; -} - -export interface DependencyNode { - action: Action; - dependsOn: Set; - dependents: Set; - level: number; - canRunInParallel: boolean; -} - -export interface DependencyLevel { - level: number; - actions: Action[]; - parallelizable: boolean; - estimatedDuration: number; -} - -export interface ExecutionPlan { - totalEstimatedTime: number; - parallelStages: ExecutionStage[]; - dependencyChain: string[]; - riskAssessment: RiskAssessment; -} - -export interface ExecutionStage { - stageId: string; - level: number; - actions: Action[]; - parallelExecution: boolean; - estimatedDuration: number; - prerequisites: string[]; -} - -export interface OptimizedPlan extends ExecutionPlan { - optimizations: Optimization[]; - performanceGains: PerformanceGains; -} - -export interface Optimization { - type: 'parallelization' | 'reordering' | 'batching' | 'caching'; - description: string; - impact: number; // Estimated time savings in ms - appliedTo: string[]; -} - -export interface PerformanceGains { - originalEstimatedTime: number; - optimizedEstimatedTime: number; - timeSavings: number; - parallelizationGains: number; -} - -export interface RiskAssessment { - riskLevel: 'low' | 'medium' | 'high'; - potentialIssues: string[]; - mitigations: string[]; - failureRecovery: string[]; -} - -export class DependencyResolver { - private performanceMetrics: number[] = []; - - constructor() { - logger.info('Dependency Resolver initialized'); - } - - /** - * Analyze dependencies between actions and create dependency graph - * Target: <100ms processing time - */ - async analyzeDependencies(actions: Action[]): Promise { - const startTime = performance.now(); - - if (!actions || actions.length === 0) { - throw new AIProcessingError('No actions provided for dependency analysis'); - } - - try { - logger.debug('Analyzing dependencies for actions', { - actionCount: actions.length, - actionTypes: actions.map(a => a.type) - }); - - // Create dependency graph - const graph: DependencyGraph = { - nodes: new Map(), - edges: new Set(), - levels: [] - }; - - // Initialize nodes - for (const action of actions) { - const node: DependencyNode = { - action, - dependsOn: new Set(), - dependents: new Set(), - level: 0, - canRunInParallel: true - }; - graph.nodes.set(action.id, node); - } - - // Analyze explicit dependencies - this.analyzeExplicitDependencies(actions, graph); - - // Analyze implicit dependencies - await this.analyzeImplicitDependencies(actions, graph); - - // Calculate dependency levels - this.calculateDependencyLevels(graph); - - // Check for circular dependencies - this.detectCircularDependencies(graph); - - // Create execution levels - this.createExecutionLevels(graph); - - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - - logger.debug('Dependency analysis completed', { - processingTime: `${processingTime.toFixed(2)}ms`, - nodeCount: graph.nodes.size, - edgeCount: graph.edges.size, - levelCount: graph.levels.length - }); - - return graph; - - } catch (error: unknown) { - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - const errorMessage = error instanceof Error ? error.message : String(error); - - logger.error('Dependency analysis failed', { - error: errorMessage, - processingTime: `${processingTime.toFixed(2)}ms`, - actionCount: actions.length - }); - - throw new AIProcessingError('Failed to analyze action dependencies', { - actionCount: actions.length, - processingTime, - originalError: errorMessage - }); - } - } - - /** - * Create execution plan from dependency graph - */ - async createExecutionPlan(graph: DependencyGraph): Promise { - const startTime = performance.now(); - - try { - const stages: ExecutionStage[] = []; - let totalEstimatedTime = 0; - const dependencyChain: string[] = []; - - // Create execution stages from dependency levels - for (let i = 0; i < graph.levels.length; i++) { - const level = graph.levels[i]; - if (!level) { - throw new AIProcessingError('Invalid dependency level in graph', { - levelIndex: i, - totalLevels: graph.levels.length - }); - } - - const stageId = `stage-${i}`; - - const stage: ExecutionStage = { - stageId, - level: i, - actions: level.actions, - parallelExecution: level.parallelizable && level.actions.length > 1, - estimatedDuration: level.estimatedDuration, - prerequisites: this.getStagePrerequisites(level, graph) - }; - - stages.push(stage); - - // For parallel stages, use the max duration. For sequential, add durations - if (stage.parallelExecution) { - totalEstimatedTime += Math.max(...level.actions.map(a => a.metadata?.estimatedDuration || 1000)); - } else { - totalEstimatedTime += level.estimatedDuration; - } - - // Build dependency chain - dependencyChain.push(...level.actions.map(a => a.id)); - } - - // Assess risks - const riskAssessment = this.assessExecutionRisks(stages, graph); - - const plan: ExecutionPlan = { - totalEstimatedTime, - parallelStages: stages, - dependencyChain, - riskAssessment - }; - - const processingTime = performance.now() - startTime; - - logger.debug('Execution plan created', { - processingTime: `${processingTime.toFixed(2)}ms`, - stageCount: stages.length, - totalEstimatedTime: `${totalEstimatedTime}ms`, - parallelizableStages: stages.filter(s => s.parallelExecution).length - }); - - return plan; - - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); - logger.error('Failed to create execution plan', { - error: errorMessage, - graphNodes: graph.nodes.size - }); - - throw new AIProcessingError('Failed to create execution plan', { - graphSize: graph.nodes.size, - originalError: errorMessage - }); - } - } - - /** - * Optimize execution plan for better performance - */ - async optimizeExecutionOrder(plan: ExecutionPlan): Promise { - const startTime = performance.now(); - - try { - const optimizations: Optimization[] = []; - let optimizedStages = [...plan.parallelStages]; - - // Optimization 1: Maximize parallelization - const parallelizationGains = this.optimizeParallelization(optimizedStages); - if (parallelizationGains.impact > 0) { - optimizations.push(parallelizationGains); - } - - // Optimization 2: Reorder actions within stages for efficiency - const reorderingGains = this.optimizeActionOrdering(optimizedStages); - if (reorderingGains.impact > 0) { - optimizations.push(reorderingGains); - } - - // Optimization 3: Batch similar actions - const batchingGains = this.optimizeBatching(optimizedStages); - if (batchingGains.impact > 0) { - optimizations.push(batchingGains); - } - - // Optimization 4: Cache repeated operations - const cachingGains = this.optimizeCaching(optimizedStages); - if (cachingGains.impact > 0) { - optimizations.push(cachingGains); - } - - // Calculate performance gains - const totalTimeSavings = optimizations.reduce((sum, opt) => sum + opt.impact, 0); - const originalTime = plan.totalEstimatedTime; - const optimizedTime = Math.max(originalTime - totalTimeSavings, originalTime * 0.3); // Don't optimize beyond 70% improvement - - const performanceGains: PerformanceGains = { - originalEstimatedTime: originalTime, - optimizedEstimatedTime: optimizedTime, - timeSavings: totalTimeSavings, - parallelizationGains: parallelizationGains.impact - }; - - const optimizedPlan: OptimizedPlan = { - ...plan, - totalEstimatedTime: optimizedTime, - parallelStages: optimizedStages, - optimizations, - performanceGains - }; - - const processingTime = performance.now() - startTime; - - logger.info('Execution plan optimized', { - processingTime: `${processingTime.toFixed(2)}ms`, - originalTime: `${originalTime}ms`, - optimizedTime: `${optimizedTime}ms`, - timeSavings: `${totalTimeSavings}ms`, - optimizationCount: optimizations.length - }); - - return optimizedPlan; - - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); - logger.error('Failed to optimize execution plan', { - error: errorMessage - }); - - // Return original plan if optimization fails - return { - ...plan, - optimizations: [], - performanceGains: { - originalEstimatedTime: plan.totalEstimatedTime, - optimizedEstimatedTime: plan.totalEstimatedTime, - timeSavings: 0, - parallelizationGains: 0 - } - }; - } - } - - /** - * Get performance statistics - */ - getPerformanceStats(): { average: number; p95: number; p99: number; count: number } { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] ?? 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] ?? 0; - - return { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: this.performanceMetrics.length - }; - } - - private analyzeExplicitDependencies(actions: Action[], graph: DependencyGraph): void { - for (const action of actions) { - if (action.dependencies && action.dependencies.length > 0) { - const node = graph.nodes.get(action.id); - if (node) { - for (const depId of action.dependencies) { - const depNode = graph.nodes.get(depId); - if (depNode) { - node.dependsOn.add(depId); - depNode.dependents.add(action.id); - graph.edges.add(`${depId}->${action.id}`); - } - } - } - } - } - } - - private async analyzeImplicitDependencies(actions: Action[], graph: DependencyGraph): Promise { - // Analyze action types and parameters to infer dependencies - const actionsByType = new Map(); - - for (const action of actions) { - if (!actionsByType.has(action.type)) { - actionsByType.set(action.type, []); - } - actionsByType.get(action.type)?.push(action); - } - - // Common dependency patterns - const dependencyRules = [ - // Create before assign/update - { prerequisite: 'create_task', dependent: 'assign_task' }, - { prerequisite: 'create_channel', dependent: 'send_message' }, - { prerequisite: 'create_task', dependent: 'update_task' }, - - // User operations must come after user-related creates - { prerequisite: 'invite_user', dependent: 'assign_task' }, - { prerequisite: 'invite_user', dependent: 'add_to_channel' }, - - // File operations - { prerequisite: 'upload_file', dependent: 'share_file' }, - { prerequisite: 'create_task', dependent: 'attach_file' }, - - // Notification dependencies - { prerequisite: 'create_task', dependent: 'send_notification' }, - { prerequisite: 'assign_task', dependent: 'send_notification' } - ]; - - // Apply dependency rules - for (const rule of dependencyRules) { - const prerequisites = actionsByType.get(rule.prerequisite) || []; - const dependents = actionsByType.get(rule.dependent) || []; - - for (const prereq of prerequisites) { - for (const dependent of dependents) { - if (this.shouldCreateImplicitDependency(prereq, dependent)) { - const prereqNode = graph.nodes.get(prereq.id); - const depNode = graph.nodes.get(dependent.id); - - if (prereqNode && depNode) { - depNode.dependsOn.add(prereq.id); - prereqNode.dependents.add(dependent.id); - graph.edges.add(`${prereq.id}->${dependent.id}`); - } - } - } - } - } - } - - private shouldCreateImplicitDependency(prereq: Action, dependent: Action): boolean { - // Check if actions operate on the same entity - if (prereq.parameters?.entityId && dependent.parameters?.entityId) { - return prereq.parameters.entityId === dependent.parameters.entityId; - } - - // Check if dependent action needs output from prerequisite - if (prereq.metadata?.producesOutput && dependent.metadata?.requiresInput) { - return true; - } - - // Default to no dependency - return false; - } - - private calculateDependencyLevels(graph: DependencyGraph): void { - const visited = new Set(); - const visiting = new Set(); - - const calculateLevel = (nodeId: string): number => { - if (visiting.has(nodeId)) { - throw new Error(`Circular dependency detected: ${nodeId}`); - } - - if (visited.has(nodeId)) { - return graph.nodes.get(nodeId)?.level || 0; - } - - visiting.add(nodeId); - - const node = graph.nodes.get(nodeId); - if (!node) { - return 0; - } - - let maxDepLevel = -1; - for (const depId of node.dependsOn) { - maxDepLevel = Math.max(maxDepLevel, calculateLevel(depId)); - } - - node.level = maxDepLevel + 1; - visited.add(nodeId); - visiting.delete(nodeId); - - return node.level; - }; - - // Calculate levels for all nodes - for (const nodeId of graph.nodes.keys()) { - calculateLevel(nodeId); - } - } - - private detectCircularDependencies(graph: DependencyGraph): void { - const visited = new Set(); - const recursionStack = new Set(); - - const hasCycle = (nodeId: string): boolean => { - if (recursionStack.has(nodeId)) { - return true; - } - - if (visited.has(nodeId)) { - return false; - } - - visited.add(nodeId); - recursionStack.add(nodeId); - - const node = graph.nodes.get(nodeId); - if (node) { - for (const depId of node.dependsOn) { - if (hasCycle(depId)) { - return true; - } - } - } - - recursionStack.delete(nodeId); - return false; - }; - - for (const nodeId of graph.nodes.keys()) { - if (hasCycle(nodeId)) { - throw new AIProcessingError(`Circular dependency detected involving: ${nodeId}`); - } - } - } - - private createExecutionLevels(graph: DependencyGraph): void { - const levelMap = new Map(); - - // Group actions by level - for (const node of graph.nodes.values()) { - if (!levelMap.has(node.level)) { - levelMap.set(node.level, []); - } - levelMap.get(node.level)?.push(node.action); - } - - // Create dependency levels - graph.levels = []; - for (const [level, actions] of levelMap.entries()) { - const estimatedDuration = actions.reduce((sum, action) => - sum + (action.metadata?.estimatedDuration || 1000), 0 - ); - - const parallelizable = actions.length > 1 && - actions.every(action => !action.metadata?.requiresInput); - - graph.levels.push({ - level, - actions, - parallelizable, - estimatedDuration: parallelizable ? - Math.max(...actions.map(a => a.metadata?.estimatedDuration || 1000)) : - estimatedDuration - }); - } - - // Sort levels by level number - graph.levels.sort((a, b) => a.level - b.level); - } - - private getStagePrerequisites(level: DependencyLevel, graph: DependencyGraph): string[] { - const prerequisites = new Set(); - - for (const action of level.actions) { - const node = graph.nodes.get(action.id); - if (node) { - for (const depId of node.dependsOn) { - prerequisites.add(depId); - } - } - } - - return Array.from(prerequisites); - } - - private assessExecutionRisks(stages: ExecutionStage[], graph: DependencyGraph): RiskAssessment { - const potentialIssues: string[] = []; - const mitigations: string[] = []; - const failureRecovery: string[] = []; - - let riskLevel: 'low' | 'medium' | 'high' = 'low'; - - // Assess complexity risks - if (stages.length > 5) { - potentialIssues.push('Complex multi-stage execution with many dependencies'); - mitigations.push('Implement comprehensive progress tracking and rollback capabilities'); - riskLevel = 'medium'; - } - - // Assess parallel execution risks - const parallelStages = stages.filter(s => s.parallelExecution); - if (parallelStages.length > 2) { - potentialIssues.push('Multiple parallel execution stages increase failure probability'); - mitigations.push('Implement stage-level transaction management'); - riskLevel = 'medium'; - } - - // Assess external dependency risks - const hasExternalDependencies = Array.from(graph.nodes.values()) - .some(node => node.action.type.includes('external') || node.action.type.includes('api')); - - if (hasExternalDependencies) { - potentialIssues.push('External API dependencies may cause failures'); - mitigations.push('Implement retry mechanisms and fallback strategies'); - if (riskLevel === 'low') riskLevel = 'medium'; - } - - // General failure recovery strategies - failureRecovery.push('Database transaction rollback for data consistency'); - failureRecovery.push('Detailed error reporting for manual intervention'); - failureRecovery.push('Automatic retry for transient failures'); - failureRecovery.push('User notification of partial completion status'); - - return { - riskLevel, - potentialIssues, - mitigations, - failureRecovery - }; - } - - private optimizeParallelization(stages: ExecutionStage[]): Optimization { - let impact = 0; - const appliedTo: string[] = []; - - for (const stage of stages) { - if (!stage.parallelExecution && stage.actions.length > 1) { - // Check if actions can be parallelized - const canParallelize = stage.actions.every(action => - !action.metadata?.requiresInput && - !stage.actions.some(other => - other.id !== action.id && - other.dependencies?.includes(action.id) - ) - ); - - if (canParallelize) { - const originalDuration = stage.estimatedDuration; - const parallelDuration = Math.max(...stage.actions.map(a => a.metadata?.estimatedDuration || 1000)); - impact += originalDuration - parallelDuration; - appliedTo.push(stage.stageId); - - // Update stage - stage.parallelExecution = true; - stage.estimatedDuration = parallelDuration; - } - } - } - - return { - type: 'parallelization', - description: `Enabled parallel execution for ${appliedTo.length} stages`, - impact, - appliedTo - }; - } - - private optimizeActionOrdering(stages: ExecutionStage[]): Optimization { - let impact = 0; - const appliedTo: string[] = []; - - for (const stage of stages) { - if (stage.actions.length > 2 && !stage.parallelExecution) { - // Sort by estimated duration (shortest first for better perceived performance) - const originalOrder = [...stage.actions]; - stage.actions.sort((a, b) => { - const durationA = a.metadata?.estimatedDuration || 1000; - const durationB = b.metadata?.estimatedDuration || 1000; - return durationA - durationB; - }); - - // Estimate impact (faster feedback to user) - const hasOrderChange = originalOrder.some((action, index) => - action.id !== (stage.actions[index]?.id || '') - ); - - if (hasOrderChange) { - impact += 200; // Estimated user experience improvement - appliedTo.push(stage.stageId); - } - } - } - - return { - type: 'reordering', - description: `Optimized action ordering for ${appliedTo.length} stages`, - impact, - appliedTo - }; - } - - private optimizeBatching(stages: ExecutionStage[]): Optimization { - let impact = 0; - const appliedTo: string[] = []; - - for (const stage of stages) { - // Group similar actions that can be batched - const actionGroups = new Map(); - - for (const action of stage.actions) { - if (!actionGroups.has(action.type)) { - actionGroups.set(action.type, []); - } - actionGroups.get(action.type)?.push(action); - } - - // Find batchable groups - for (const [type, actions] of actionGroups.entries()) { - if (actions.length > 1 && this.isBatchableActionType(type)) { - const originalTime = actions.reduce((sum, a) => sum + (a.metadata?.estimatedDuration || 1000), 0); - const batchTime = Math.max(actions.length * 100, 500); // Batch overhead - - if (batchTime < originalTime) { - impact += originalTime - batchTime; - appliedTo.push(stage.stageId); - } - } - } - } - - return { - type: 'batching', - description: `Applied batching optimizations to ${appliedTo.length} stages`, - impact, - appliedTo - }; - } - - private optimizeCaching(stages: ExecutionStage[]): Optimization { - let impact = 0; - const appliedTo: string[] = []; - - // Look for repeated operations that can be cached - const operationCounts = new Map(); - - for (const stage of stages) { - for (const action of stage.actions) { - const key = `${action.type}:${JSON.stringify(action.parameters)}`; - operationCounts.set(key, (operationCounts.get(key) || 0) + 1); - } - } - - // Calculate caching benefits for repeated operations - for (const [operation, count] of operationCounts.entries()) { - if (count > 1) { - const estimatedSavings = (count - 1) * 200; // Cache hit savings - impact += estimatedSavings; - appliedTo.push(operation); - } - } - - return { - type: 'caching', - description: `Identified ${appliedTo.length} cacheable operations`, - impact, - appliedTo - }; - } - - private isBatchableActionType(actionType: string): boolean { - const batchableTypes = [ - 'send_notification', - 'assign_task', - 'update_task', - 'create_task', - 'send_message' - ]; - - return batchableTypes.includes(actionType); - } - - private recordPerformance(time: number): void { - this.performanceMetrics.push(time); - - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} \ No newline at end of file diff --git a/backend/src/ai/execution/MultiActionExecutor.ts b/backend/src/ai/execution/MultiActionExecutor.ts deleted file mode 100644 index b061dd7..0000000 --- a/backend/src/ai/execution/MultiActionExecutor.ts +++ /dev/null @@ -1,1171 +0,0 @@ -/** - * Multi-Action Executor - Phase 2 Complex Command Execution - * Executes complex voice commands with multiple actions, dependencies, and transactions - * - * Success Criteria: - * - Supports all 10 action types from command parsing - * - ACID transaction compliance for multi-action commands - * - Automatic rollback on any action failure - * - Permission validation per action with detailed audit logging - * - Transaction timeout handling (30 seconds maximum) - */ - -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { repositories, DatabaseManager } from '../../db'; -import { VoiceFileUploadService } from '../../files/upload/VoiceFileUploadService'; -import { - UserContext, - ParsedCommand, - AIProcessingError, - ActionResult, - ExecutionSummary, - AuditEntry, -} from '../../voice/types'; -import { - DependencyResolver, - ExecutionPlan, - OptimizedPlan, - ExecutionStage, - Action, -} from './DependencyResolver'; - -export interface MultiActionExecutionOptions { - transactionTimeout?: number; - parallelExecutionLimit?: number; - rollbackOnAnyFailure?: boolean; - auditLogging?: boolean; - progressTracking?: boolean; -} - -export interface ExecutionContext { - userId: string; - organizationId: string; - sessionId: string; - commandId: string; - timestamp: string; - permissions: UserPermissions; - transaction?: any; // Database transaction -} - -export interface UserPermissions { - canCreateTasks: boolean; - canAssignTasks: boolean; - canCreateChannels: boolean; - canInviteUsers: boolean; - canUploadFiles: boolean; - canSendMessages: boolean; - canUpdateTasks: boolean; - canDeleteTasks: boolean; - canManageChannels: boolean; - canSendNotifications: boolean; -} - -export interface ActionExecutionResult { - actionId: string; - actionType: string; - success: boolean; - result?: any; - error?: string; - executionTime: number; - affectedEntities: string[]; - rollbackData?: any; -} - -export interface MultiActionResult { - commandId: string; - success: boolean; - executedActions: ActionExecutionResult[]; - failedActions: ActionExecutionResult[]; - totalExecutionTime: number; - rollbackRequired: boolean; - rollbackCompleted: boolean; - summary: ExecutionSummary; - auditEntry: AuditEntry; -} - -export interface TransactionManager { - transaction: any; - startTime: number; - timeout: NodeJS.Timeout; - isActive: boolean; - rollbackData: Map; -} - -export class MultiActionExecutor extends EventEmitter { - private dependencyResolver: DependencyResolver; - private db: DatabaseManager; - private repositories: typeof repositories; - private voiceFileUploadService: VoiceFileUploadService; - private activeTransactions: Map = new Map(); - private performanceMetrics: number[] = []; - - constructor() { - super(); - - this.dependencyResolver = new DependencyResolver(); - this.db = new DatabaseManager(); - this.repositories = repositories; - this.voiceFileUploadService = new VoiceFileUploadService(); - - // Setup cleanup interval for stale transactions - setInterval(() => this.cleanupStaleTransactions(), 60000); // Every minute - - logger.info('Multi-Action Executor initialized'); - } - - /** - * Execute a complex multi-action voice command - * Main entry point for command execution with full dependency resolution and transaction management - */ - async executeMultiActionCommand( - command: ParsedCommand, - userContext: UserContext, - options: MultiActionExecutionOptions = {} - ): Promise { - const startTime = performance.now(); - const executionId = `exec-${Date.now()}-${Math.random().toString(36).substring(7)}`; - - const executionContext: ExecutionContext = { - userId: userContext.userId, - organizationId: userContext.organizationId, - sessionId: userContext.sessionId || 'default', - commandId: command.id, - timestamp: new Date().toISOString(), - permissions: await this.getUserPermissions(userContext.userId), - }; - - const executionOptions = { - transactionTimeout: 30000, // 30 seconds - parallelExecutionLimit: 3, - rollbackOnAnyFailure: true, - auditLogging: true, - progressTracking: true, - ...options, - }; - - logger.info('Starting multi-action command execution', { - executionId, - commandId: command.id, - actionCount: command.actions.length, - userId: userContext.userId, - options: executionOptions, - }); - - let transactionManager: TransactionManager | undefined; - const executedActions: ActionExecutionResult[] = []; - const failedActions: ActionExecutionResult[] = []; - let rollbackCompleted = false; - - try { - // Step 1: Convert parsed command actions to dependency actions - const actions = this.convertParsedActionsToActions(command.actions); - - // Step 2: Analyze dependencies and create execution plan - this.emit('dependency_analysis_start', { executionId, actionCount: actions.length }); - - const dependencyGraph = await this.dependencyResolver.analyzeDependencies(actions); - const executionPlan = await this.dependencyResolver.createExecutionPlan(dependencyGraph); - const optimizedPlan = await this.dependencyResolver.optimizeExecutionOrder(executionPlan); - - this.emit('execution_plan_ready', { - executionId, - stageCount: optimizedPlan.parallelStages.length, - estimatedTime: optimizedPlan.totalEstimatedTime, - optimizations: optimizedPlan.optimizations.length, - }); - - // Step 3: Start database transaction - transactionManager = await this.beginTransaction( - executionId, - executionOptions.transactionTimeout - ); - executionContext.transaction = transactionManager.transaction; - - // Step 4: Execute plan stages - for (let i = 0; i < optimizedPlan.parallelStages.length; i++) { - const stage = optimizedPlan.parallelStages[i]; - - this.emit('stage_execution_start', { - executionId, - stageId: stage?.stageId ?? 'unknown', - actionCount: stage?.actions?.length ?? 0, - parallelExecution: stage?.parallelExecution ?? false, - }); - - if (stage) { - const stageResults = await this.executeStage(stage, executionContext, executionOptions); - - // Process results - for (const result of stageResults) { - if (result.success) { - executedActions.push(result); - } else { - failedActions.push(result); - - if (executionOptions.rollbackOnAnyFailure) { - throw new AIProcessingError( - `Action failed: ${result.actionType} - ${result.error}` - ); - } - } - } - - this.emit('stage_execution_complete', { - executionId, - stageId: stage.stageId, - successCount: stageResults.filter((r) => r.success).length, - failureCount: stageResults.filter((r) => !r.success).length, - }); - } else { - logger.warn('Skipped undefined stage during execution', { executionId, stageIndex: i }); - } - } - - // Step 5: Commit transaction - await this.commitTransaction(transactionManager); - - const totalExecutionTime = performance.now() - startTime; - this.recordPerformance(totalExecutionTime); - - // Step 6: Create execution summary and audit entry - const summary = this.createExecutionSummary( - executedActions, - failedActions, - totalExecutionTime - ); - const auditEntry = this.createAuditEntry(command, executionContext, summary); - - // Log audit entry if enabled - if (executionOptions.auditLogging) { - await this.logAuditEntry(auditEntry); - } - - const result: MultiActionResult = { - commandId: command.id, - success: failedActions.length === 0, - executedActions, - failedActions, - totalExecutionTime, - rollbackRequired: false, - rollbackCompleted: false, - summary, - auditEntry, - }; - - this.emit('execution_complete', { - executionId, - success: result.success, - totalTime: totalExecutionTime, - actionCount: executedActions.length, - }); - - logger.info('Multi-action command execution completed successfully', { - executionId, - commandId: command.id, - totalTime: `${totalExecutionTime.toFixed(2)}ms`, - successfulActions: executedActions.length, - failedActions: failedActions.length, - }); - - return result; - } catch (error) { - const totalExecutionTime = performance.now() - startTime; - this.recordPerformance(totalExecutionTime); - - logger.error('Multi-action command execution failed', { - executionId, - commandId: command.id, - error: error instanceof Error ? error.message : String(error), - totalTime: `${totalExecutionTime.toFixed(2)}ms`, - executedActions: executedActions.length, - }); - - // Attempt rollback - let rollbackSuccess = false; - if (transactionManager) { - try { - rollbackSuccess = await this.rollbackTransaction(transactionManager, executedActions); - rollbackCompleted = true; - } catch (rollbackError: unknown) { - const rollbackErrorMessage = rollbackError instanceof Error ? rollbackError.message : String(rollbackError); - logger.error('Transaction rollback failed', { - executionId, - rollbackError: rollbackErrorMessage, - }); - } - } - - const summary = this.createExecutionSummary( - executedActions, - failedActions, - totalExecutionTime - ); - const errorMessage = error instanceof Error ? error.message : String(error); - const auditEntry = this.createAuditEntry(command, executionContext, summary, errorMessage); - - // Log audit entry even for failures - if (executionOptions.auditLogging) { - await this.logAuditEntry(auditEntry); - } - - this.emit('execution_failed', { - executionId, - error: errorMessage, - rollbackCompleted: rollbackSuccess, - }); - - return { - commandId: command.id, - success: false, - executedActions, - failedActions, - totalExecutionTime, - rollbackRequired: true, - rollbackCompleted: rollbackSuccess, - summary, - auditEntry, - }; - } finally { - // Cleanup transaction - if (transactionManager) { - this.activeTransactions.delete(executionId); - if (transactionManager.timeout) { - clearTimeout(transactionManager.timeout); - } - } - } - } - - /** - * Execute a single stage of the execution plan - */ - private async executeStage( - stage: ExecutionStage, - context: ExecutionContext, - options: MultiActionExecutionOptions - ): Promise { - const results: ActionExecutionResult[] = []; - - if (stage.parallelExecution && stage.actions.length > 1) { - // Execute actions in parallel - const parallelLimit = Math.min(stage.actions.length, options.parallelExecutionLimit || 3); - const chunks: Action[][] = []; - - // Split actions into chunks for parallel execution - for (let i = 0; i < stage.actions.length; i += parallelLimit) { - chunks.push(stage.actions.slice(i, i + parallelLimit)); - } - - for (const chunk of chunks) { - const chunkResults = await Promise.all( - chunk.map((action) => this.executeAction(action, context)) - ); - results.push(...chunkResults); - } - } else { - // Execute actions sequentially - for (const action of stage.actions) { - const result = await this.executeAction(action, context); - results.push(result); - - // Stop on first failure if configured - if (!result.success && options.rollbackOnAnyFailure) { - break; - } - } - } - - return results; - } - - /** - * Execute a single action - */ - private async executeAction( - action: Action, - context: ExecutionContext - ): Promise { - const startTime = performance.now(); - - try { - // Validate permissions - if (!this.validateActionPermissions(action, context.permissions)) { - throw new Error(`Insufficient permissions for action: ${action.type}`); - } - - // Execute the action based on type - let result: any; - const affectedEntities: string[] = []; - let rollbackData: any = null; - - switch (action.type) { - case 'create_task': - result = await this.executeCreateTask(action, context); - affectedEntities.push(result.id); - rollbackData = { taskId: result.id }; - break; - - case 'assign_task': - result = await this.executeAssignTask(action, context); - affectedEntities.push(action.parameters.taskId); - rollbackData = { - taskId: action.parameters.taskId, - previousAssignees: result.previousAssignees, - }; - break; - - case 'update_task': - result = await this.executeUpdateTask(action, context); - affectedEntities.push(action.parameters.taskId); - rollbackData = { - taskId: action.parameters.taskId, - previousValues: result.previousValues, - }; - break; - - case 'create_channel': - result = await this.executeCreateChannel(action, context); - affectedEntities.push(result.id); - rollbackData = { channelId: result.id }; - break; - - case 'send_message': - result = await this.executeSendMessage(action, context); - affectedEntities.push(action.parameters.channelId); - rollbackData = { messageId: result.id }; - break; - - case 'invite_user': - result = await this.executeInviteUser(action, context); - affectedEntities.push(result.userId); - rollbackData = { invitationId: result.id }; - break; - - case 'upload_file': - result = await this.executeUploadFile(action, context); - affectedEntities.push(result.fileId); - rollbackData = { fileId: result.fileId }; - break; - - case 'send_notification': - result = await this.executeSendNotification(action, context); - affectedEntities.push(...(action.parameters.userIds || [])); - rollbackData = { notificationId: result.id }; - break; - - case 'schedule_meeting': - result = await this.executeScheduleMeeting(action, context); - affectedEntities.push(result.meetingId); - rollbackData = { meetingId: result.meetingId }; - break; - - case 'generate_report': - result = await this.executeGenerateReport(action, context); - affectedEntities.push(result.reportId); - rollbackData = { reportId: result.reportId }; - break; - - default: - throw new Error(`Unsupported action type: ${action.type}`); - } - - const executionTime = performance.now() - startTime; - - return { - actionId: action.id, - actionType: action.type, - success: true, - result, - executionTime, - affectedEntities, - rollbackData, - }; - } catch (error: unknown) { - const executionTime = performance.now() - startTime; - const errorMessage = error instanceof Error ? error.message : String(error); - - return { - actionId: action.id, - actionType: action.type, - success: false, - error: errorMessage, - executionTime, - affectedEntities: [], - }; - } - } - - // Action execution implementations - private async executeCreateTask(action: Action, context: ExecutionContext): Promise { - const { title, description, priority, dueDate, assignedTo } = action.parameters; - - const task = await this.repositories.tasks.query( - ` - INSERT INTO tasks (title, description, priority, due_date, assigned_to, created_by, status) - VALUES ($1, $2, $3, $4, $5, $6, 'pending') - RETURNING id, title, status, created_at - `, - [title, description, priority || 'medium', dueDate, assignedTo || [], context.userId], - context.transaction - ); - - return task.rows[0]; - } - - private async executeAssignTask(action: Action, context: ExecutionContext): Promise { - const { taskId, assignedTo } = action.parameters; - - // Get current assignees for rollback - const currentTask = await this.db.query( - ` - SELECT assigned_to FROM tasks WHERE id = $1 - `, - [taskId], - context.transaction - ); - - const previousAssignees = currentTask.rows[0]?.assigned_to || []; - - const result = await this.db.query( - ` - UPDATE tasks - SET assigned_to = $2, updated_at = NOW() - WHERE id = $1 - RETURNING id, assigned_to - `, - [taskId, assignedTo], - context.transaction - ); - - return { - taskId, - assignedTo, - previousAssignees, - updated: result.rowCount > 0, - }; - } - - private async executeUpdateTask(action: Action, context: ExecutionContext): Promise { - const { taskId, updates } = action.parameters; - - // Get current values for rollback - const currentTask = await this.db.query( - ` - SELECT title, description, status, priority, due_date - FROM tasks WHERE id = $1 - `, - [taskId], - context.transaction - ); - - const previousValues = currentTask.rows[0] || {}; - - // Build dynamic update query - const updateFields: string[] = []; - const updateValues: any[] = [taskId]; - let paramIndex = 2; - - for (const [field, value] of Object.entries(updates)) { - if (['title', 'description', 'status', 'priority', 'due_date'].includes(field)) { - updateFields.push(`${field} = $${paramIndex}`); - updateValues.push(value); - paramIndex++; - } - } - - updateFields.push(`updated_at = NOW()`); - - const result = await this.db.query( - ` - UPDATE tasks - SET ${updateFields.join(', ')} - WHERE id = $1 - RETURNING id, title, status, updated_at - `, - updateValues, - context.transaction - ); - - return { - taskId, - updates: result.rows[0], - previousValues, - }; - } - - private async executeCreateChannel(action: Action, context: ExecutionContext): Promise { - const { name, description, channelType, members } = action.parameters; - - // Create channel - const channel = await this.db.query( - ` - INSERT INTO channels (name, description, channel_type, created_by, status) - VALUES ($1, $2, $3, $4, 'active') - RETURNING id, name, channel_type, created_at - `, - [name, description, channelType || 'general', context.userId], - context.transaction - ); - - const channelId = channel.rows[0].id; - - // Add creator as member - await this.db.query( - ` - INSERT INTO channel_members (channel_id, user_id, role, joined_at) - VALUES ($1, $2, 'admin', NOW()) - `, - [channelId, context.userId], - context.transaction - ); - - // Add additional members if specified - if (members && members.length > 0) { - for (const memberId of members) { - await this.db.query( - ` - INSERT INTO channel_members (channel_id, user_id, role, joined_at) - VALUES ($1, $2, 'member', NOW()) - ON CONFLICT (channel_id, user_id) DO NOTHING - `, - [channelId, memberId], - context.transaction - ); - } - } - - return channel.rows[0]; - } - - private async executeSendMessage(action: Action, context: ExecutionContext): Promise { - const { channelId, content, messageType } = action.parameters; - - const message = await this.db.query( - ` - INSERT INTO messages (channel_id, user_id, content, message_type, sent_at) - VALUES ($1, $2, $3, $4, NOW()) - RETURNING id, content, sent_at - `, - [channelId, context.userId, content, messageType || 'text'], - context.transaction - ); - - return message.rows[0]; - } - - private async executeInviteUser(action: Action, context: ExecutionContext): Promise { - const { email, name, role } = action.parameters; - - const invitation = await this.db.query( - ` - INSERT INTO user_invitations (email, name, role, invited_by, organization_id, status) - VALUES ($1, $2, $3, $4, $5, 'pending') - RETURNING id, email, created_at - `, - [email, name, role || 'member', context.userId, context.organizationId], - context.transaction - ); - - return { - id: invitation.rows[0].id, - userId: email, // Use email as user identifier for now - status: 'invited', - }; - } - - private async executeUploadFile(action: Action, context: ExecutionContext): Promise { - const { fileName, fileSize, contentType, description, targetChannels, targetTasks, tags } = - action.parameters; - - // Use the voice file upload service for complete workflow - const uploadRequest = { - fileName, - contentType: contentType || 'application/octet-stream', - fileSize: fileSize || 1024, // Default 1KB if not specified - userContext: { - userId: context.userId, - organizationId: context.organizationId, - sessionId: context.sessionId, - language: 'en', - timezone: 'UTC', - }, - description, - targetChannels: targetChannels || [], - targetTasks: targetTasks || [], - tags: tags || [], - }; - - const uploadResult = await this.voiceFileUploadService.initiateVoiceUpload(uploadRequest); - - if (!uploadResult.success) { - throw new Error(uploadResult.error || 'File upload initiation failed'); - } - - return { - fileId: uploadResult.fileId, - uploadUrl: uploadResult.uploadUrl, - downloadUrl: uploadResult.downloadUrl, - expiresAt: uploadResult.expiresAt, - linkedEntities: uploadResult.linkedEntities, - status: 'upload_initiated', - }; - } - - private async executeSendNotification(action: Action, context: ExecutionContext): Promise { - const { userIds, title, message, notificationType } = action.parameters; - - const notification = await this.db.query( - ` - INSERT INTO notifications (title, message, notification_type, sender_id, organization_id, created_at) - VALUES ($1, $2, $3, $4, $5, NOW()) - RETURNING id, title, created_at - `, - [title, message, notificationType || 'info', context.userId, context.organizationId], - context.transaction - ); - - const notificationId = notification.rows[0].id; - - // Send to specified users - if (userIds && userIds.length > 0) { - for (const userId of userIds) { - await this.db.query( - ` - INSERT INTO user_notifications (user_id, notification_id, status) - VALUES ($1, $2, 'unread') - `, - [userId, notificationId], - context.transaction - ); - } - } - - return notification.rows[0]; - } - - private async executeScheduleMeeting(action: Action, context: ExecutionContext): Promise { - const { title, startTime, endTime, attendees, description } = action.parameters; - - // For now, return a mock meeting ID - // In a real implementation, this would integrate with calendar systems - return { - meetingId: `meeting-${Date.now()}`, - title, - scheduled: true, - attendeeCount: attendees?.length || 0, - }; - } - - private async executeGenerateReport(action: Action, context: ExecutionContext): Promise { - const { reportType, parameters, format } = action.parameters; - - // For now, return a mock report ID - // In a real implementation, this would generate actual reports - return { - reportId: `report-${Date.now()}`, - type: reportType, - status: 'generated', - format: format || 'pdf', - }; - } - - private validateActionPermissions(action: Action, permissions: UserPermissions): boolean { - const permissionMap: Record = { - create_task: 'canCreateTasks', - assign_task: 'canAssignTasks', - update_task: 'canUpdateTasks', - create_channel: 'canCreateChannels', - send_message: 'canSendMessages', - invite_user: 'canInviteUsers', - upload_file: 'canUploadFiles', - send_notification: 'canSendNotifications', - }; - - const requiredPermission = permissionMap[action.type]; - return !requiredPermission || permissions[requiredPermission]; - } - - private async getUserPermissions(userId: string): Promise { - // Get user role and organization permissions - const user = await this.db.query( - ` - SELECT role FROM users WHERE id = $1 - `, - [userId] - ); - - const userRole = user.rows[0]?.role || 'member'; - - // Define permissions based on role - const rolePermissions: Record = { - admin: { - canCreateTasks: true, - canAssignTasks: true, - canCreateChannels: true, - canInviteUsers: true, - canUploadFiles: true, - canSendMessages: true, - canUpdateTasks: true, - canDeleteTasks: true, - canManageChannels: true, - canSendNotifications: true, - }, - manager: { - canCreateTasks: true, - canAssignTasks: true, - canCreateChannels: true, - canInviteUsers: false, - canUploadFiles: true, - canSendMessages: true, - canUpdateTasks: true, - canDeleteTasks: false, - canManageChannels: false, - canSendNotifications: true, - }, - member: { - canCreateTasks: true, - canAssignTasks: false, - canCreateChannels: false, - canInviteUsers: false, - canUploadFiles: true, - canSendMessages: true, - canUpdateTasks: true, - canDeleteTasks: false, - canManageChannels: false, - canSendNotifications: false, - }, - }; - - const permissions = rolePermissions[userRole]; - if (permissions) { - return permissions; - } - const memberPermissions = rolePermissions.member; - if (memberPermissions) { - return memberPermissions; - } - - // Fallback to minimal permissions if member role isn't defined - return { - canCreateTasks: false, - canAssignTasks: false, - canCreateChannels: false, - canInviteUsers: false, - canUploadFiles: false, - canSendMessages: false, - canUpdateTasks: false, - canDeleteTasks: false, - canManageChannels: false, - canSendNotifications: false, - }; - } - - private async beginTransaction( - executionId: string, - timeout: number - ): Promise { - const transaction = await this.db.transaction(async (client) => client); - - const timeoutHandler = setTimeout(() => { - this.handleTransactionTimeout(executionId); - }, timeout); - - const transactionManager: TransactionManager = { - transaction, - startTime: Date.now(), - timeout: timeoutHandler, - isActive: true, - rollbackData: new Map(), - }; - - this.activeTransactions.set(executionId, transactionManager); - - logger.debug('Transaction started', { - executionId, - timeout: `${timeout}ms`, - }); - - return transactionManager; - } - - private async commitTransaction(transactionManager: TransactionManager): Promise { - if (transactionManager.isActive) { - // For DatabaseManager, commit happens automatically at end of transaction callback - transactionManager.isActive = false; - - if (transactionManager.timeout) { - clearTimeout(transactionManager.timeout); - } - - logger.debug('Transaction committed successfully'); - } - } - - private async rollbackTransaction( - transactionManager: TransactionManager, - executedActions: ActionExecutionResult[] - ): Promise { - try { - if (transactionManager.isActive) { - // For DatabaseManager, rollback happens by throwing error in transaction callback - transactionManager.isActive = false; - - if (transactionManager.timeout) { - clearTimeout(transactionManager.timeout); - } - - logger.info('Transaction rolled back successfully', { - executedActionCount: executedActions.length, - }); - - return true; - } - - return false; - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); - logger.error('Transaction rollback failed', { - error: errorMessage, - executedActionCount: executedActions.length, - }); - - return false; - } - } - - private handleTransactionTimeout(executionId: string): void { - const transactionManager = this.activeTransactions.get(executionId); - - if (transactionManager?.isActive) { - logger.warn('Transaction timeout, forcing rollback', { - executionId, - duration: Date.now() - transactionManager.startTime, - }); - - // Force rollback on timeout - this.rollbackTransaction(transactionManager, []); - this.activeTransactions.delete(executionId); - - this.emit('transaction_timeout', { executionId }); - } - } - - private cleanupStaleTransactions(): void { - const staleTransactions: string[] = []; - - for (const [executionId, transactionManager] of this.activeTransactions.entries()) { - const age = Date.now() - transactionManager.startTime; - - if (age > 60000) { - // 1 minute - staleTransactions.push(executionId); - } - } - - for (const executionId of staleTransactions) { - const transactionManager = this.activeTransactions.get(executionId); - if (transactionManager) { - this.rollbackTransaction(transactionManager, []); - this.activeTransactions.delete(executionId); - } - } - - if (staleTransactions.length > 0) { - logger.warn('Cleaned up stale transactions', { - count: staleTransactions.length, - }); - } - } - - private convertParsedActionsToActions(parsedActions: any[]): Action[] { - return parsedActions.map((action, index) => ({ - id: action.id || `action-${index}`, - type: action.type, - parameters: action.parameters, - dependencies: action.dependencies || [], - metadata: { - priority: action.priority || 1, - estimatedDuration: this.getEstimatedDuration(action.type), - requiresInput: this.actionRequiresInput(action.type), - producesOutput: this.actionProducesOutput(action.type), - }, - })); - } - - private getEstimatedDuration(actionType: string): number { - const durations: Record = { - create_task: 500, - assign_task: 300, - update_task: 400, - create_channel: 600, - send_message: 200, - invite_user: 800, - upload_file: 2000, - send_notification: 300, - schedule_meeting: 1000, - generate_report: 3000, - }; - - return durations[actionType] || 1000; - } - - private actionRequiresInput(actionType: string): boolean { - return ['assign_task', 'update_task', 'send_message'].includes(actionType); - } - - private actionProducesOutput(actionType: string): boolean { - return ['create_task', 'create_channel', 'upload_file', 'generate_report'].includes(actionType); - } - - private createExecutionSummary( - executed: ActionExecutionResult[], - failed: ActionExecutionResult[], - totalTime: number - ): ExecutionSummary { - return { - totalActions: executed.length + failed.length, - successfulActions: executed.length, - failedActions: failed.length, - totalExecutionTime: totalTime, - averageActionTime: - executed.length > 0 - ? executed.reduce((sum, a) => sum + a.executionTime, 0) / executed.length - : 0, - affectedEntities: Array.from(new Set(executed.flatMap((a) => a.affectedEntities))), - actionBreakdown: this.createActionBreakdown(executed, failed), - }; - } - - private createActionBreakdown( - executed: ActionExecutionResult[], - failed: ActionExecutionResult[] - ): Record { - const breakdown: Record = {}; - - // Process executed actions - for (const action of executed) { - if (!breakdown[action.actionType]) { - breakdown[action.actionType] = { success: 0, failed: 0, times: [] }; - } - breakdown[action.actionType]!.success++; - breakdown[action.actionType]!.times.push(action.executionTime); - } - - // Process failed actions - for (const action of failed) { - if (!breakdown[action.actionType]) { - breakdown[action.actionType] = { success: 0, failed: 0, times: [] }; - } - breakdown[action.actionType]!.failed++; - if (action.executionTime) { - breakdown[action.actionType]!.times.push(action.executionTime); - } - } - - // Calculate averages - const result: Record = {}; - for (const [type, data] of Object.entries(breakdown)) { - result[type] = { - success: data.success, - failed: data.failed, - avgTime: - data.times.length > 0 - ? data.times.reduce((sum, time) => sum + time, 0) / data.times.length - : 0, - }; - } - - return result; - } - - private createAuditEntry( - command: ParsedCommand, - context: ExecutionContext, - summary: ExecutionSummary, - error?: string - ): AuditEntry { - return { - id: `audit-${Date.now()}`, - commandId: command.id, - userId: context.userId, - organizationId: context.organizationId, - sessionId: context.sessionId, - timestamp: context.timestamp, - originalTranscript: command.originalTranscript, - parsedIntent: command.intent, - executionSummary: summary, - success: summary.failedActions === 0 && !error, - error: error ?? undefined, - metadata: { - userAgent: 'voice-processing-service', - clientVersion: '2.0.0', - processingTime: summary.totalExecutionTime, - }, - }; - } - - private async logAuditEntry(auditEntry: AuditEntry): Promise { - try { - await this.db.query( - ` - INSERT INTO audit_log ( - id, command_id, user_id, organization_id, session_id, timestamp, - original_transcript, parsed_intent, execution_summary, success, error_message - ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) - `, - [ - auditEntry.id, - auditEntry.commandId, - auditEntry.userId, - auditEntry.organizationId, - auditEntry.sessionId, - auditEntry.timestamp, - auditEntry.originalTranscript, - auditEntry.parsedIntent, - JSON.stringify(auditEntry.executionSummary), - auditEntry.success, - auditEntry.error, - ] - ); - } catch (error: unknown) { - const errorMessage = error instanceof Error ? error.message : String(error); - logger.error('Failed to log audit entry', { - error: errorMessage, - auditEntryId: auditEntry.id, - }); - } - } - - /** - * Get performance statistics - */ - getPerformanceStats(): { average: number; p95: number; p99: number; count: number } { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = - this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] ?? 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] ?? 0; - - return { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: this.performanceMetrics.length, - }; - } - - private recordPerformance(time: number): void { - this.performanceMetrics.push(time); - - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} diff --git a/backend/src/api/index.ts b/backend/src/api/index.ts deleted file mode 100644 index 52671b5..0000000 --- a/backend/src/api/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -/** - * API Routes Index - * Centralized registration of all API endpoints - */ - -import { FastifyInstance } from 'fastify'; -import { registerUserRoutes } from './routes/UserRoutes'; -import { registerChannelRoutes } from './routes/ChannelRoutes'; -import { registerTaskRoutes } from './routes/TaskRoutes'; -import { registerDocsRoutes } from './routes/DocsRoutes'; -import { registerMessageRoutes } from './routes/MessageRoutes'; -import { registerActivityRoutes } from './routes/ActivityRoutes'; -import { notificationRoutes } from './routes/NotificationRoutes'; -import { announcementRoutes } from './routes/AnnouncementRoutes'; -import { logger } from '../utils/logger'; - -/** - * Register all API routes - */ -export const registerAPIRoutes = async (fastify: FastifyInstance): Promise => { - try { - // Register route modules - await fastify.register(registerUserRoutes); - await fastify.register(registerChannelRoutes); - await fastify.register(registerTaskRoutes); - await fastify.register(registerMessageRoutes); - await fastify.register(registerActivityRoutes); - await fastify.register(registerDocsRoutes); - await fastify.register(notificationRoutes); - await fastify.register(announcementRoutes); - - logger.debug('All API routes registered'); - } catch (error) { - console.error('API Registration Error Details:', error); - logger.error({ error: error instanceof Error ? error.message : String(error) }, 'Failed to register API routes'); - throw error; - } -}; - -export default registerAPIRoutes; \ No newline at end of file diff --git a/backend/src/api/routes/ActivityRoutes.ts b/backend/src/api/routes/ActivityRoutes.ts deleted file mode 100644 index f2c057a..0000000 --- a/backend/src/api/routes/ActivityRoutes.ts +++ /dev/null @@ -1,290 +0,0 @@ -import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; -import ActivityRepository from '../../db/ActivityRepository'; -import UserRepository from '../../db/UserRepository'; -import { authenticate } from '../../auth/middleware'; -import { logger } from '../../utils/logger'; - -interface ActivityFilters { - type?: string[]; - channel_id?: string; - user_id?: string; - limit?: number; - offset?: number; - from_date?: string; - to_date?: string; -} - -interface CreateActivityBody { - type: string; - title: string; - description: string; - metadata?: any; - related_id?: string; - channel_id?: string; -} - -/** - * Activity API Routes - * Handles activity feed, notifications, and activity logging - */ -export async function registerActivityRoutes(fastify: FastifyInstance) { - const activityRepo = new ActivityRepository(); - const userRepo = new UserRepository(); - - // All routes require authentication - await fastify.register(async function (fastify) { - fastify.addHook('onRequest', authenticate); - - /** - * GET /activities - Get activities with filters and pagination - */ - fastify.get('/', async (request: FastifyRequest<{ - Querystring: ActivityFilters; - }>, reply: FastifyReply) => { - try { - const { - type, - channel_id, - user_id, - limit = 50, - offset = 0, - from_date, - to_date - } = request.query; - - // Validate pagination parameters - if (limit > 100) { - return reply.code(400).send({ - error: { - message: 'Limit cannot exceed 100', - code: 'INVALID_LIMIT', - } - }); - } - - // Build filters - const filters: any = {}; - if (type) filters.type = Array.isArray(type) ? type : [type]; - if (channel_id) filters.channel_id = channel_id; - if (user_id) filters.user_id = user_id; - if (from_date) filters.from_date = from_date; - if (to_date) filters.to_date = to_date; - - // Get activities with user information - const activities = await activityRepo.findMany({ - ...filters, - limit, - offset, - includeUser: true, - }); - - const total = await activityRepo.count(filters); - const hasMore = offset + limit < total; - - reply.send({ - success: true, - data: activities, - pagination: { - total, - limit, - offset, - hasMore, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - logger.error({ error, userId: request.user?.userId }, 'Error fetching activities'); - reply.code(500).send({ - error: { - message: 'Failed to fetch activities', - code: 'ACTIVITY_FETCH_ERROR', - } - }); - } - }); - - /** - * GET /activities/feed - Get personalized activity feed - */ - fastify.get('/feed', async (request: FastifyRequest<{ - Querystring: { - limit?: number; - offset?: number; - }; - }>, reply: FastifyReply) => { - try { - const { limit = 20, offset = 0 } = request.query; - const userId = request.user!.userId; - - // Get activities for channels the user is a member of - // and activities directly related to the user - const activities = await activityRepo.getUserFeed(userId, limit, offset); - const total = await activityRepo.getUserFeedCount(userId); - const hasMore = offset + limit < total; - - reply.send({ - success: true, - data: activities, - pagination: { - total, - limit, - offset, - hasMore, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - logger.error({ error, userId: request.user?.userId }, 'Error fetching activity feed'); - reply.code(500).send({ - error: { - message: 'Failed to fetch activity feed', - code: 'ACTIVITY_FEED_ERROR', - } - }); - } - }); - - /** - * POST /activities - Create new activity - */ - fastify.post('/', async (request: FastifyRequest<{ - Body: CreateActivityBody; - }>, reply: FastifyReply) => { - try { - const { - type, - title, - description, - metadata = {}, - related_id, - channel_id - } = request.body; - const userId = request.user!.userId; - - // Validate required fields - if (!type || !title || !description) { - return reply.code(400).send({ - error: { - message: 'Type, title, and description are required', - code: 'MISSING_REQUIRED_FIELDS', - } - }); - } - - // Validate activity type - const validTypes = [ - 'task_created', 'task_updated', 'task_completed', 'task_assigned', - 'channel_created', 'user_joined', 'file_uploaded', 'message_sent', - 'announcement', 'notification' - ]; - - if (!validTypes.includes(type)) { - return reply.code(400).send({ - error: { - message: `Invalid activity type. Must be one of: ${validTypes.join(', ')}`, - code: 'INVALID_ACTIVITY_TYPE', - } - }); - } - - const activityData = { - user_id: userId, - type, - title, - description, - metadata, - related_id, - channel_id, - }; - - const activity = await activityRepo.create(activityData); - - reply.code(201).send({ - success: true, - data: activity, - timestamp: new Date().toISOString(), - }); - } catch (error) { - logger.error({ error, userId: request.user?.userId }, 'Error creating activity'); - reply.code(500).send({ - error: { - message: 'Failed to create activity', - code: 'ACTIVITY_CREATE_ERROR', - } - }); - } - }); - - /** - * GET /activities/stats - Get activity statistics - */ - fastify.get('/stats', async (request: FastifyRequest<{ - Querystring: { - period?: 'day' | 'week' | 'month'; - channel_id?: string; - }; - }>, reply: FastifyReply) => { - try { - const { period = 'week', channel_id } = request.query; - const userId = request.user!.userId; - - const stats = await activityRepo.getActivityStats({ - userId, - period, - channelId: channel_id, - }); - - reply.send({ - success: true, - data: stats, - timestamp: new Date().toISOString(), - }); - } catch (error) { - logger.error({ error, userId: request.user?.userId }, 'Error fetching activity stats'); - reply.code(500).send({ - error: { - message: 'Failed to fetch activity statistics', - code: 'ACTIVITY_STATS_ERROR', - } - }); - } - }); - - /** - * GET /activities/:id - Get single activity - */ - fastify.get('/:id', async (request: FastifyRequest<{ - Params: { id: string }; - }>, reply: FastifyReply) => { - try { - const { id } = request.params; - - const activity = await activityRepo.findById(id); - if (!activity) { - return reply.code(404).send({ - error: { - message: 'Activity not found', - code: 'ACTIVITY_NOT_FOUND', - } - }); - } - - reply.send({ - success: true, - data: activity, - timestamp: new Date().toISOString(), - }); - } catch (error) { - logger.error({ error, userId: request.user?.userId }, 'Error fetching activity'); - reply.code(500).send({ - error: { - message: 'Failed to fetch activity', - code: 'ACTIVITY_FETCH_ERROR', - } - }); - } - }); - }, { prefix: '/activities' }); - - logger.debug('Activity routes registered'); -} \ No newline at end of file diff --git a/backend/src/api/routes/AnnouncementRoutes.ts b/backend/src/api/routes/AnnouncementRoutes.ts deleted file mode 100644 index ee223d4..0000000 --- a/backend/src/api/routes/AnnouncementRoutes.ts +++ /dev/null @@ -1,689 +0,0 @@ -import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; -import { Type, Static } from '@sinclair/typebox'; -import AnnouncementRepository from '@db/AnnouncementRepository'; -import { loggers } from '@utils/logger'; -import { - formatErrorResponse, - createErrorContext, - NotFoundError, - ValidationError, - AuthorizationError, - BaseError, -} from '@utils/errors'; -import { CreateAnnouncementData, UpdateAnnouncementData, AnnouncementFilter } from '@db/AnnouncementRepository'; - -// Schema definitions -const AnnouncementTypeSchema = Type.Union([ - Type.Literal('info'), - Type.Literal('warning'), - Type.Literal('success'), - Type.Literal('error'), - Type.Literal('feature'), - Type.Literal('maintenance'), -]); - -const AnnouncementPrioritySchema = Type.Union([ - Type.Literal('low'), - Type.Literal('medium'), - Type.Literal('high'), - Type.Literal('critical'), -]); - -const AnnouncementAudienceSchema = Type.Union([ - Type.Literal('all'), - Type.Literal('admins'), - Type.Literal('developers'), - Type.Literal('designers'), - Type.Literal('managers'), -]); - -const CreateAnnouncementSchema = Type.Object({ - title: Type.String({ minLength: 1, maxLength: 200 }), - content: Type.String({ minLength: 1, maxLength: 5000 }), - type: AnnouncementTypeSchema, - priority: AnnouncementPrioritySchema, - target_audience: AnnouncementAudienceSchema, - scheduled_for: Type.Optional(Type.String({ format: 'date-time' })), - expires_at: Type.Optional(Type.String({ format: 'date-time' })), - action_button_text: Type.Optional(Type.String({ maxLength: 50 })), - action_button_url: Type.Optional(Type.String({ format: 'uri' })), - image_url: Type.Optional(Type.String({ format: 'uri' })), - published: Type.Optional(Type.Boolean()), -}); - -const UpdateAnnouncementSchema = Type.Object({ - title: Type.Optional(Type.String({ minLength: 1, maxLength: 200 })), - content: Type.Optional(Type.String({ minLength: 1, maxLength: 5000 })), - type: Type.Optional(AnnouncementTypeSchema), - priority: Type.Optional(AnnouncementPrioritySchema), - target_audience: Type.Optional(AnnouncementAudienceSchema), - scheduled_for: Type.Optional(Type.Union([Type.String({ format: 'date-time' }), Type.Null()])), - expires_at: Type.Optional(Type.Union([Type.String({ format: 'date-time' }), Type.Null()])), - action_button_text: Type.Optional(Type.Union([Type.String({ maxLength: 50 }), Type.Null()])), - action_button_url: Type.Optional(Type.Union([Type.String({ format: 'uri' }), Type.Null()])), - image_url: Type.Optional(Type.Union([Type.String({ format: 'uri' }), Type.Null()])), - published: Type.Optional(Type.Boolean()), -}); - -const AnnouncementSchema = Type.Object({ - id: Type.String(), - title: Type.String(), - content: Type.String(), - type: AnnouncementTypeSchema, - priority: AnnouncementPrioritySchema, - target_audience: AnnouncementAudienceSchema, - scheduled_for: Type.Union([Type.String({ format: 'date-time' }), Type.Null()]), - expires_at: Type.Union([Type.String({ format: 'date-time' }), Type.Null()]), - action_button_text: Type.Union([Type.String(), Type.Null()]), - action_button_url: Type.Union([Type.String(), Type.Null()]), - image_url: Type.Union([Type.String(), Type.Null()]), - created_by: Type.String(), - published: Type.Boolean(), - read_by: Type.Array(Type.String()), - created_at: Type.String({ format: 'date-time' }), - updated_at: Type.String({ format: 'date-time' }), - version: Type.Number(), -}); - -type CreateAnnouncementRequest = Static; -type UpdateAnnouncementRequest = Static; - -export async function announcementRoutes(fastify: FastifyInstance) { - const announcementRepository = new AnnouncementRepository(); - - /** - * POST /announcements - Create new announcement (CEO only) - */ - fastify.post<{ Body: CreateAnnouncementRequest }>( - '/announcements', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Create Announcement', - description: 'Create a new announcement (CEO only)', - body: CreateAnnouncementSchema, - response: { - 201: Type.Object({ - success: Type.Boolean(), - data: AnnouncementSchema, - }), - 400: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - 401: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - 403: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request: FastifyRequest<{ Body: CreateAnnouncementRequest }>, reply: FastifyReply) => { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - - try { - // Only CEO can create announcements - if (request.user?.role !== 'ceo') { - throw new AuthorizationError('Only CEO users can create announcements'); - } - - const announcementData: CreateAnnouncementData = { - title: request.body.title, - content: request.body.content, - type: request.body.type, - priority: request.body.priority, - target_audience: request.body.target_audience, - created_by: request.user.userId, - published: request.body.published, - ...(request.body.scheduled_for && { scheduled_for: new Date(request.body.scheduled_for) }), - ...(request.body.expires_at && { expires_at: new Date(request.body.expires_at) }), - ...(request.body.action_button_text && { action_button_text: request.body.action_button_text }), - ...(request.body.action_button_url && { action_button_url: request.body.action_button_url }), - ...(request.body.image_url && { image_url: request.body.image_url }), - }; - - loggers.api.info( - { - userId: request.user.userId, - title: announcementData.title, - type: announcementData.type, - priority: announcementData.priority, - targetAudience: announcementData.target_audience, - published: announcementData.published, - }, - 'Creating announcement' - ); - - const announcement = await announcementRepository.create(announcementData); - - loggers.api.info( - { - userId: request.user.userId, - announcementId: announcement.id, - }, - 'Announcement created successfully' - ); - - reply.code(201).send({ - success: true, - data: announcement, - }); - - } catch (error) { - loggers.api.error({ error, context }, 'Failed to create announcement'); - - if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else if (error instanceof AuthorizationError) { - reply.code(403).send(formatErrorResponse(error)); - } else { - reply.code(500).send(formatErrorResponse(error as BaseError)); - } - } - } - ); - - /** - * GET /announcements - Get announcements with filters - */ - fastify.get<{ - Querystring: { - type?: string; - priority?: string; - target_audience?: string; - published?: string; - created_by?: string; - date_from?: string; - date_to?: string; - limit?: string; - offset?: string; - user_view?: string; // 'true' for user-specific view - }; - }>( - '/announcements', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Get Announcements', - description: 'Get announcements with optional filtering', - querystring: Type.Object({ - type: Type.Optional(Type.String()), - priority: Type.Optional(Type.String()), - target_audience: Type.Optional(Type.String()), - published: Type.Optional(Type.String()), - created_by: Type.Optional(Type.String()), - date_from: Type.Optional(Type.String({ format: 'date-time' })), - date_to: Type.Optional(Type.String({ format: 'date-time' })), - limit: Type.Optional(Type.String()), - offset: Type.Optional(Type.String()), - user_view: Type.Optional(Type.String()), - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Array(AnnouncementSchema), - total: Type.Number(), - limit: Type.Number(), - offset: Type.Number(), - }), - }, - }, - }, - async (request, reply) => { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - - try { - const { - type, - priority, - target_audience, - published, - created_by, - date_from, - date_to, - limit = '50', - offset = '0', - user_view = 'false', - } = request.query; - - const limitNum = Math.min(parseInt(limit) || 50, 100); - const offsetNum = parseInt(offset) || 0; - - let data, total; - - if (user_view === 'true') { - // User-specific view - only show announcements relevant to the user - data = await announcementRepository.findForUser( - request.user!.userId, - request.user!.role, - true - ); - total = data.length; - // Apply pagination manually for user view - data = data.slice(offsetNum, offsetNum + limitNum); - } else { - // Admin view - show all announcements with filters (CEO only) - if (request.user?.role !== 'ceo') { - throw new AuthorizationError('Only CEO users can view all announcements'); - } - - const filter: AnnouncementFilter = {}; - - if (type) filter.type = type.split(',') as any; - if (priority) filter.priority = priority.split(',') as any; - if (target_audience) filter.target_audience = target_audience.split(',') as any; - if (published) filter.published = published === 'true'; - if (created_by) filter.created_by = created_by.split(','); - if (date_from) filter.date_from = new Date(date_from); - if (date_to) filter.date_to = new Date(date_to); - - const result = await announcementRepository.findWithFilter(filter, limitNum, offsetNum); - data = result.data; - total = result.total; - } - - loggers.api.info( - { - userId: request.user?.userId, - userView: user_view === 'true', - resultsCount: data.length, - total, - }, - 'Retrieved announcements' - ); - - reply.send({ - success: true, - data, - total, - limit: limitNum, - offset: offsetNum, - }); - - } catch (error) { - loggers.api.error({ error, context }, 'Failed to get announcements'); - - if (error instanceof AuthorizationError) { - reply.code(403).send(formatErrorResponse(error)); - } else { - reply.code(500).send(formatErrorResponse(error as BaseError)); - } - } - } - ); - - /** - * GET /announcements/:id - Get announcement by ID - */ - fastify.get<{ - Params: { id: string }; - }>( - '/announcements/:id', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Get Announcement by ID', - description: 'Get a specific announcement by ID', - params: Type.Object({ - id: Type.String(), - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: AnnouncementSchema, - }), - 404: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request, reply) => { - try { - const announcement = await announcementRepository.findById(request.params.id); - - if (!announcement) { - throw new NotFoundError(`Announcement with ID ${request.params.id} not found`); - } - - reply.send({ - success: true, - data: announcement, - }); - - } catch (error) { - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else { - reply.code(500).send(formatErrorResponse(error as BaseError)); - } - } - } - ); - - /** - * PUT /announcements/:id - Update announcement (CEO only) - */ - fastify.put<{ - Params: { id: string }; - Body: UpdateAnnouncementRequest; - }>( - '/announcements/:id', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Update Announcement', - description: 'Update an existing announcement (CEO only)', - params: Type.Object({ - id: Type.String(), - }), - body: UpdateAnnouncementSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: AnnouncementSchema, - }), - 400: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - 403: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - 404: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request, reply) => { - try { - // Only CEO can update announcements - if (request.user?.role !== 'ceo') { - throw new AuthorizationError('Only CEO users can update announcements'); - } - - const updateData: UpdateAnnouncementData = {}; - - if (request.body.title !== undefined) updateData.title = request.body.title; - if (request.body.content !== undefined) updateData.content = request.body.content; - if (request.body.type !== undefined) updateData.type = request.body.type; - if (request.body.priority !== undefined) updateData.priority = request.body.priority; - if (request.body.target_audience !== undefined) updateData.target_audience = request.body.target_audience; - if (request.body.published !== undefined) updateData.published = request.body.published; - if (request.body.action_button_text !== undefined) updateData.action_button_text = request.body.action_button_text; - if (request.body.action_button_url !== undefined) updateData.action_button_url = request.body.action_button_url; - if (request.body.image_url !== undefined) updateData.image_url = request.body.image_url; - - if (request.body.scheduled_for !== undefined) { - updateData.scheduled_for = request.body.scheduled_for === null ? null : new Date(request.body.scheduled_for); - } - if (request.body.expires_at !== undefined) { - updateData.expires_at = request.body.expires_at === null ? null : new Date(request.body.expires_at); - } - - const announcement = await announcementRepository.update(request.params.id, updateData); - - loggers.api.info( - { - userId: request.user.userId, - announcementId: announcement.id, - }, - 'Announcement updated successfully' - ); - - reply.send({ - success: true, - data: announcement, - }); - - } catch (error) { - loggers.api.error({ error, announcementId: request.params.id }, 'Failed to update announcement'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else if (error instanceof AuthorizationError) { - reply.code(403).send(formatErrorResponse(error)); - } else { - reply.code(500).send(formatErrorResponse(error as BaseError)); - } - } - } - ); - - /** - * DELETE /announcements/:id - Delete announcement (CEO only) - */ - fastify.delete<{ - Params: { id: string }; - }>( - '/announcements/:id', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Delete Announcement', - description: 'Delete an announcement (CEO only)', - params: Type.Object({ - id: Type.String(), - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - message: Type.String(), - }), - 403: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - 404: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request, reply) => { - try { - // Only CEO can delete announcements - if (request.user?.role !== 'ceo') { - throw new AuthorizationError('Only CEO users can delete announcements'); - } - - await announcementRepository.softDelete(request.params.id, request.user.userId); - - loggers.api.info( - { - userId: request.user.userId, - announcementId: request.params.id, - }, - 'Announcement deleted successfully' - ); - - reply.send({ - success: true, - message: 'Announcement deleted successfully', - }); - - } catch (error) { - loggers.api.error({ error, announcementId: request.params.id }, 'Failed to delete announcement'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else if (error instanceof AuthorizationError) { - reply.code(403).send(formatErrorResponse(error)); - } else { - reply.code(500).send(formatErrorResponse(error as BaseError)); - } - } - } - ); - - /** - * POST /announcements/:id/read - Mark announcement as read - */ - fastify.post<{ - Params: { id: string }; - }>( - '/announcements/:id/read', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Mark Announcement as Read', - description: 'Mark an announcement as read by the current user', - params: Type.Object({ - id: Type.String(), - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - message: Type.String(), - }), - 404: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request, reply) => { - try { - await announcementRepository.markAsRead(request.params.id, request.user!.userId); - - loggers.api.info( - { - userId: request.user?.userId, - announcementId: request.params.id, - }, - 'Announcement marked as read' - ); - - reply.send({ - success: true, - message: 'Announcement marked as read', - }); - - } catch (error) { - loggers.api.error({ error, announcementId: request.params.id }, 'Failed to mark announcement as read'); - reply.code(500).send(formatErrorResponse(error as BaseError)); - } - } - ); - - /** - * GET /announcements/stats - Get announcement statistics (CEO only) - */ - fastify.get( - '/announcements/stats', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Announcements'], - summary: 'Get Announcement Statistics', - description: 'Get announcement statistics (CEO only)', - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Object({ - total: Type.Number(), - published: Type.Number(), - scheduled: Type.Number(), - expired: Type.Number(), - byType: Type.Record(Type.String(), Type.Number()), - byPriority: Type.Record(Type.String(), Type.Number()), - byAudience: Type.Record(Type.String(), Type.Number()), - }), - }), - 403: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request, reply) => { - try { - // Only CEO can view stats - if (request.user?.role !== 'ceo') { - throw new AuthorizationError('Only CEO users can view announcement statistics'); - } - - const stats = await announcementRepository.getStats(); - - reply.send({ - success: true, - data: stats, - }); - - } catch (error) { - loggers.api.error({ error }, 'Failed to get announcement stats'); - - if (error instanceof AuthorizationError) { - reply.code(403).send(formatErrorResponse(error)); - } else { - reply.code(500).send(formatErrorResponse(error as BaseError)); - } - } - } - ); -} \ No newline at end of file diff --git a/backend/src/api/routes/ChannelRoutes.ts b/backend/src/api/routes/ChannelRoutes.ts deleted file mode 100644 index f96a4f1..0000000 --- a/backend/src/api/routes/ChannelRoutes.ts +++ /dev/null @@ -1,1386 +0,0 @@ -import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; -import { Type } from '@sinclair/typebox'; -import { channelRepository, messageRepository, activityRepository, fileRepository } from '@db/index'; -import { Activity } from '../../db/ActivityRepository'; -import { logger, loggers } from '@utils/logger'; -import { - ValidationError, - NotFoundError, - AuthorizationError, - formatErrorResponse, - createErrorContext, -} from '@utils/errors'; -import { - authenticate, - authorize, - authorizeRoles, - requireChannelAccess, - apiRateLimit, - requireManagerOrCEO, -} from '@auth/middleware'; -import { cacheService } from '../../services/CacheService'; -import { Cacheable, CacheEvict, CacheKeyUtils } from '@utils/cache-decorators'; -import { WebSocketUtils } from '@websocket/utils'; -import { - UUIDSchema, - PaginationSchema, - ChannelTypeSchema, - ChannelPrivacySchema, - SuccessResponseSchema, -} from '@utils/validation'; - -/** - * Channel Management API Routes - * Enterprise-grade channel CRUD operations with real-time updates - */ - -// Request/Response Schemas -const CreateChannelSchema = Type.Object({ - name: Type.String({ minLength: 1, maxLength: 100 }), - description: Type.Optional(Type.String({ maxLength: 500 })), - type: ChannelTypeSchema, - privacy: ChannelPrivacySchema, - parent_id: Type.Optional(UUIDSchema), - settings: Type.Optional(Type.Record(Type.String(), Type.Any())), - tags: Type.Optional(Type.Array(Type.String({ maxLength: 50 }))), - color: Type.Optional(Type.String({ pattern: '^#[0-9A-Fa-f]{6}$' })), -}); - -const UpdateChannelSchema = Type.Object({ - name: Type.Optional(Type.String({ minLength: 1, maxLength: 100 })), - description: Type.Optional(Type.String({ maxLength: 500 })), - type: Type.Optional(ChannelTypeSchema), - privacy: Type.Optional(ChannelPrivacySchema), - settings: Type.Optional(Type.Record(Type.String(), Type.Any())), - tags: Type.Optional(Type.Array(Type.String({ maxLength: 50 }))), - color: Type.Optional(Type.String({ pattern: '^#[0-9A-Fa-f]{6}$' })), -}); - -const ChannelMemberSchema = Type.Object({ - user_id: UUIDSchema, - role: Type.Union([ - Type.Literal('owner'), - Type.Literal('admin'), - Type.Literal('member'), - Type.Literal('viewer'), - ]), - joined_at: Type.String({ format: 'date-time' }), - user_name: Type.String(), - user_avatar: Type.Optional(Type.String()), -}); - -const ChannelResponseSchema = Type.Object({ - id: UUIDSchema, - name: Type.String(), - description: Type.Optional(Type.String()), - type: ChannelTypeSchema, - privacy: ChannelPrivacySchema, - parent_id: Type.Optional(UUIDSchema), - created_by: UUIDSchema, - settings: Type.Optional(Type.Record(Type.String(), Type.Any())), - tags: Type.Array(Type.String()), - color: Type.Optional(Type.String()), - member_count: Type.Integer(), - message_count: Type.Integer(), - last_activity: Type.Optional(Type.String({ format: 'date-time' })), - created_at: Type.String({ format: 'date-time' }), - updated_at: Type.String({ format: 'date-time' }), -}); - -/** - * Channel service with caching - */ -class ChannelService { - @Cacheable({ - ttl: 1800, // 30 minutes - namespace: 'channels', - keyGenerator: (channelId: string) => CacheKeyUtils.channelKey(channelId), - }) - async getChannelById(channelId: string) { - return await channelRepository.findById(channelId); - } - - @CacheEvict({ - keys: (channelId: string) => [CacheKeyUtils.channelKey(channelId)], - namespace: 'channels', - }) - async updateChannel(channelId: string, updateData: any) { - // Map frontend fields to backend fields - const mappedData: any = {}; - - if (updateData.name !== undefined) mappedData.name = updateData.name; - if (updateData.description !== undefined) mappedData.description = updateData.description; - if (updateData.type !== undefined) mappedData.channel_type = updateData.type; - if (updateData.privacy !== undefined) mappedData.privacy_level = updateData.privacy; - if (updateData.settings !== undefined) mappedData.settings = updateData.settings; - - // Handle project_info updates - if (updateData.tags !== undefined || updateData.color !== undefined) { - const existingChannel = await channelRepository.findById(channelId); - const currentProjectInfo = existingChannel?.project_info || {}; - - mappedData.project_info = { - ...currentProjectInfo, - ...(updateData.tags !== undefined && { tags: updateData.tags }), - ...(updateData.color !== undefined && { color: updateData.color }), - }; - } - - return await channelRepository.update(channelId, mappedData); - } - - @CacheEvict({ - allEntries: true, - namespace: 'channels', - }) - async createChannel(channelData: any) { - return await channelRepository.createChannel(channelData); - } -} - -const channelService = new ChannelService(); - -/** - * Register channel routes - */ -export const registerChannelRoutes = async (fastify: FastifyInstance) => { - /** - * GET /channels - List channels accessible to user - */ - fastify.get<{ - Querystring: typeof PaginationSchema.static & { - type?: string; - privacy?: string; - parent_id?: string; - search?: string; - }; - }>( - '/channels', - { - preHandler: [authenticate, apiRateLimit], - schema: { - querystring: Type.Intersect([ - PaginationSchema, - Type.Object({ - type: Type.Optional(ChannelTypeSchema), - privacy: Type.Optional(ChannelPrivacySchema), - parent_id: Type.Optional(UUIDSchema), - search: Type.Optional(Type.String({ maxLength: 100 })), - }), - ]), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Array(ChannelResponseSchema), - pagination: Type.Object({ - total: Type.Integer(), - limit: Type.Integer(), - offset: Type.Integer(), - hasMore: Type.Boolean(), - }), - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { limit = 20, offset = 0, type, privacy, parent_id, search } = request.query; - - // Build filters - const filters: any = {}; - if (type) filters.type = type; - if (privacy) filters.privacy = privacy; - if (parent_id) filters.parent_id = parent_id; - if (search) filters.search = search; - - // Get channels user has access to based on their role - const result = await channelRepository.findUserChannels(request.user!.userId, request.user!.role); - - loggers.api.info( - { - userId: request.user?.userId, - filters, - resultCount: result.length, - }, - 'Channels list retrieved' - ); - - reply.send({ - success: true, - data: result, - pagination: { - total: result.length, - limit, - offset, - hasMore: false, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve channels'); - - reply.code(500).send({ - error: { - message: 'Failed to retrieve channels', - code: 'SERVER_ERROR', - }, - }); - } - } - ); - - /** - * GET /channels/categories - Get available channel categories - */ - fastify.get( - '/channels/categories', - { - preHandler: [authenticate], - schema: { - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Array(Type.Object({ - id: Type.String(), - name: Type.String(), - description: Type.String(), - icon: Type.Optional(Type.String()), - color: Type.Optional(Type.String()), - })), - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - // Define available channel categories based on ChannelTypeSchema - const categories = [ - { - id: 'general', - name: 'General', - description: 'General purpose discussions and communications', - icon: 'chatbubble-outline', - color: '#6B7280', - }, - { - id: 'project', - name: 'Project', - description: 'Project-specific discussions and collaboration', - icon: 'folder-outline', - color: '#3B82F6', - }, - { - id: 'department', - name: 'Department', - description: 'Department-wide communications and updates', - icon: 'business-outline', - color: '#10B981', - }, - { - id: 'announcement', - name: 'Announcement', - description: 'Official announcements and important updates', - icon: 'megaphone-outline', - color: '#F59E0B', - }, - { - id: 'private', - name: 'Private', - description: 'Private discussions and confidential matters', - icon: 'lock-closed-outline', - color: '#8B5CF6', - }, - ]; - - loggers.api.info( - { - userId: request.user?.userId, - categoriesCount: categories.length, - }, - 'Channel categories retrieved' - ); - - reply.send({ - success: true, - data: categories, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve channel categories'); - - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel categories', - code: 'SERVER_ERROR', - }, - }); - } - } - ); - - /** - * GET /channels/:id - Get channel details - */ - fastify.get<{ - Params: { id: string }; - }>( - '/channels/:id', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: ChannelResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - - const channel = await channelService.getChannelById(id); - if (!channel) { - throw new NotFoundError('Channel not found'); - } - - loggers.api.info( - { - userId: request.user?.userId, - channelId: id, - }, - 'Channel details retrieved' - ); - - reply.send({ - success: true, - data: channel, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve channel'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * POST /channels - Create new channel - */ - fastify.post<{ - Body: typeof CreateChannelSchema.static; - }>( - '/channels', - { - preHandler: [authenticate, requireManagerOrCEO], - schema: { - body: CreateChannelSchema, - response: { - 201: Type.Object({ - success: Type.Boolean(), - data: ChannelResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const channelData = { - name: request.body.name, - description: request.body.description, - channel_type: request.body.type, - privacy_level: request.body.privacy, - created_by: request.user!.userId, - settings: request.body.settings || {}, - project_info: { - tags: request.body.tags || [], - ...(request.body.color && { color: request.body.color }), - }, - ...(request.body.parent_id && { parent_id: request.body.parent_id }), - }; - - const channel = await channelService.createChannel(channelData); - - // Creator is already added as member in createChannel method - // await channelRepository.addMember(channel.id, request.user!.userId, request.user!.userId); - - // Create activity for channel creation - try { - await activityRepository.createActivity({ - channelId: channel.id, - userId: request.user!.userId, - activityType: 'channel_created', - title: `Channel Created: ${channel.name}`, - description: `New ${channel.channel_type} channel "${channel.name}" was created${channel.description ? ': ' + channel.description : ''}`, - category: 'channel' as any, - metadata: { - channelId: channel.id, - channelName: channel.name, - channelType: channel.channel_type, - channelPrivacy: channel.privacy_level, - parentId: (channel as any).parent_id, - createdBy: request.user!.userId, - createdByName: request.user!.name, - tags: request.body.tags || [], - settings: channel.settings - } - }); - } catch (error) { - loggers.api.warn?.({ error, channelId: channel.id }, 'Failed to create channel creation activity'); - } - - // Broadcast channel creation - await WebSocketUtils.broadcastChannelMessage({ - type: 'chat_message', - channelId: channel.id, - messageId: `system_${Date.now()}`, - message: `Channel "${channel.name}" created`, - messageType: 'system', - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - - loggers.api.info( - { - userId: request.user?.userId, - channelId: channel.id, - channelName: channel.name, - channelType: channel.channel_type, - }, - 'Channel created successfully' - ); - - reply.code(201).send({ - success: true, - data: channel, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to create channel'); - - if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to create channel', - code: 'SERVER_ERROR', - ...(process.env.NODE_ENV === 'development' && { - details: error instanceof Error ? error.message : String(error), - stack: error instanceof Error ? error.stack : undefined, - }), - }, - }); - } - } - } - ); - - /** - * PUT /channels/:id - Update channel - */ - fastify.put<{ - Params: { id: string }; - Body: typeof UpdateChannelSchema.static; - }>( - '/channels/:id', - { - preHandler: [authenticate, requireChannelAccess, authorize('channels:update')], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - body: UpdateChannelSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: ChannelResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - const updateData = request.body; - - const channel = await channelService.updateChannel(id, updateData); - - // Broadcast channel update - await WebSocketUtils.sendToChannel(id, 'channel_updated', { - type: 'channel_updated', - channelId: id, - updates: updateData, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - timestamp: new Date().toISOString(), - }); - - loggers.api.info( - { - userId: request.user?.userId, - channelId: id, - updatedFields: Object.keys(updateData), - }, - 'Channel updated successfully' - ); - - reply.send({ - success: true, - data: channel, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to update channel'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to update channel', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * DELETE /channels/:id - Delete channel - */ - fastify.delete<{ - Params: { id: string }; - }>( - '/channels/:id', - { - preHandler: [authenticate, requireChannelAccess, authorize('channels:delete')], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - response: { - 200: SuccessResponseSchema, - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - - const success = await channelRepository.softDelete(id, request.user!.userId); - if (!success) { - throw new NotFoundError('Channel not found'); - } - - // Clear channel cache - await cacheService.channels.delete(CacheKeyUtils.channelKey(id)); - - // Broadcast channel deletion - await WebSocketUtils.sendToChannel(id, 'channel_deleted', { - type: 'channel_deleted', - channelId: id, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - timestamp: new Date().toISOString(), - }); - - loggers.api.info( - { - userId: request.user?.userId, - channelId: id, - }, - 'Channel deleted successfully' - ); - - reply.send({ - success: true, - message: 'Channel deleted successfully', - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to delete channel'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to delete channel', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * GET /channels/:id/members - Get channel members - */ - fastify.get<{ - Params: { id: string }; - Querystring: typeof PaginationSchema.static; - }>( - '/channels/:id/members', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - querystring: PaginationSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Array(ChannelMemberSchema), - pagination: Type.Object({ - total: Type.Integer(), - limit: Type.Integer(), - offset: Type.Integer(), - hasMore: Type.Boolean(), - }), - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - const { limit = 50, offset = 0 } = request.query; - - const members = await channelRepository.getMembers(id); - - // Map the members data to match the expected schema - const mappedMembers = members.map((member: any) => ({ - user_id: member.id, - role: member.role, - joined_at: new Date().toISOString(), // TODO: Get actual joined_at from channel_member_history - user_name: member.name, - user_avatar: member.avatar_url, - })); - - const result = { - data: mappedMembers.slice(offset, offset + limit), - total: mappedMembers.length, - }; - - reply.send({ - success: true, - data: result.data, - pagination: { - total: result.total, - limit, - offset, - hasMore: offset + limit < result.total, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve channel members'); - - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel members', - code: 'SERVER_ERROR', - }, - }); - } - } - ); - - /** - * POST /channels/:id/members - Add member to channel - */ - fastify.post<{ - Params: { id: string }; - Body: { - user_id: string; - role?: 'admin' | 'member' | 'viewer'; - }; - }>( - '/channels/:id/members', - { - preHandler: [authenticate, requireChannelAccess, authorize('channels:manage_members')], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - body: Type.Object({ - user_id: UUIDSchema, - role: Type.Optional( - Type.Union([Type.Literal('admin'), Type.Literal('member'), Type.Literal('viewer')]) - ), - }), - response: { - 200: SuccessResponseSchema, - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - const { user_id, role = 'member' } = request.body; - - const success = await channelRepository.addMember(id, user_id, role); - if (!success) { - throw new ValidationError('Failed to add member to channel', []); - } - - // Broadcast member addition - await WebSocketUtils.sendToChannel(id, 'user_joined_channel', { - type: 'user_joined_channel', - channelId: id, - userId: user_id, - userName: '', // TODO: Get user name - userRole: request.user!.role, - memberCount: (await channelRepository.getMembers(id)).length, - timestamp: new Date().toISOString(), - }); - - loggers.api.info( - { - userId: request.user?.userId, - channelId: id, - addedUserId: user_id, - memberRole: role, - }, - 'Member added to channel' - ); - - reply.send({ - success: true, - message: 'Member added successfully', - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to add member to channel'); - - if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to add member', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * DELETE /channels/:id/members/:user_id - Remove member from channel - */ - fastify.delete<{ - Params: { id: string; user_id: string }; - }>( - '/channels/:id/members/:user_id', - { - preHandler: [authenticate, requireChannelAccess, authorize('channels:manage_members')], - schema: { - params: Type.Object({ - id: UUIDSchema, - user_id: UUIDSchema, - }), - response: { - 200: SuccessResponseSchema, - }, - }, - }, - async (request, reply) => { - try { - const { id, user_id } = request.params; - - const success = await channelRepository.removeMember(id, user_id, request.user!.userId); - if (!success) { - throw new NotFoundError('Member not found in channel'); - } - - // Broadcast member removal - await WebSocketUtils.sendToChannel(id, 'user_left_channel', { - type: 'user_left_channel', - channelId: id, - userId: user_id, - userName: '', // TODO: Get user name - userRole: request.user!.role, - memberCount: (await channelRepository.getMembers(id)).length, - timestamp: new Date().toISOString(), - }); - - loggers.api.info( - { - userId: request.user?.userId, - channelId: id, - removedUserId: user_id, - }, - 'Member removed from channel' - ); - - reply.send({ - success: true, - message: 'Member removed successfully', - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to remove member from channel'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to remove member', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * GET /channels/:id/files - Get channel file attachments - */ - fastify.get<{ - Params: { id: string }; - Querystring: typeof PaginationSchema.static & { - file_type?: string; - uploaded_by?: string; - search?: string; - }; - }>( - '/channels/:id/files', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - querystring: Type.Intersect([ - PaginationSchema, - Type.Object({ - file_type: Type.Optional(Type.String()), - uploaded_by: Type.Optional(UUIDSchema), - search: Type.Optional(Type.String({ maxLength: 100 })), - }), - ]), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Array(Type.Object({ - id: UUIDSchema, - filename: Type.String(), - originalName: Type.String(), - mimeType: Type.String(), - size: Type.Integer(), - url: Type.String(), - downloadUrl: Type.Optional(Type.String()), - thumbnailUrl: Type.Optional(Type.String()), - uploadedBy: UUIDSchema, - uploadedByName: Type.String(), - uploadedAt: Type.String({ format: 'date-time' }), - messageId: Type.Optional(UUIDSchema), - })), - pagination: Type.Object({ - total: Type.Integer(), - limit: Type.Integer(), - offset: Type.Integer(), - hasMore: Type.Boolean(), - }), - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - const { limit = 50, offset = 0, file_type, uploaded_by, search } = request.query; - - // Build filters - const filters: any = { - channelId: id, - fileType: file_type, - uploadedBy: uploaded_by, - search, - }; - - let files: any[] = []; - let total = 0; - - try { - // Try to fetch files, but handle database errors gracefully - files = await fileRepository.findChannelFiles(id, filters, limit, offset); - total = await fileRepository.getChannelFileCount(id, filters); - } catch (dbError: any) { - // Log database errors but return empty results instead of failing - loggers.api.warn( - { - userId: request.user?.userId, - channelId: id, - error: dbError.message, - filters, - }, - 'Channel files database query failed, returning empty results' - ); - - // Return empty results if database query fails - files = []; - total = 0; - } - - loggers.api.info( - { - userId: request.user?.userId, - channelId: id, - fileCount: files.length, - filters, - }, - 'Channel files retrieved' - ); - - reply.send({ - success: true, - data: files, - pagination: { - total, - limit, - offset, - hasMore: offset + limit < total, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.warn({ error, context }, 'Failed to retrieve channel files, returning empty results'); - - // Return empty results instead of error to prevent frontend issues - reply.send({ - success: true, - data: [], - pagination: { - total: 0, - limit: request.query.limit || 50, - offset: request.query.offset || 0, - hasMore: false, - }, - timestamp: new Date().toISOString(), - }); - } - } - ); - - /** - * GET /channels/:id/activity - Get channel activity log - */ - fastify.get<{ - Params: { id: string }; - Querystring: typeof PaginationSchema.static & { - activity_type?: string; - user_id?: string; - after?: string; - }; - }>( - '/channels/:id/activity', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - querystring: Type.Intersect([ - PaginationSchema, - Type.Object({ - activity_type: Type.Optional( - Type.Union([ - Type.Literal('message'), - Type.Literal('task_created'), - Type.Literal('task_updated'), - Type.Literal('task_completed'), - Type.Literal('member_joined'), - Type.Literal('member_left'), - Type.Literal('file_uploaded'), - Type.Literal('channel_updated'), - ]) - ), - user_id: Type.Optional(UUIDSchema), - after: Type.Optional(Type.String({ format: 'date-time' })), - }), - ]), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Array(Type.Object({ - id: UUIDSchema, - channelId: UUIDSchema, - activityType: Type.String(), - userId: UUIDSchema, - userName: Type.String(), - userAvatar: Type.Optional(Type.String()), - title: Type.String(), - description: Type.Optional(Type.String()), - metadata: Type.Record(Type.String(), Type.Any()), - createdAt: Type.String({ format: 'date-time' }), - })), - pagination: Type.Object({ - total: Type.Integer(), - limit: Type.Integer(), - offset: Type.Integer(), - hasMore: Type.Boolean(), - }), - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - const { limit = 50, offset = 0, activity_type, user_id, after } = request.query; - - // Build filters - const filters: any = { - channelId: id, - activityType: activity_type, - userId: user_id, - after: after ? new Date(after) : undefined, - }; - - // This would require implementing activity repository methods - const activities = await activityRepository.findChannelActivities(id, filters, limit, offset); - const total = await activityRepository.getChannelActivityCount(id, filters); - - loggers.api.info( - { - userId: request.user?.userId, - channelId: id, - activityCount: activities.length, - filters, - }, - 'Channel activity retrieved' - ); - - reply.send({ - success: true, - data: activities, - pagination: { - total, - limit, - offset, - hasMore: offset + limit < total, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve channel activity'); - - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel activity', - code: 'SERVER_ERROR', - }, - }); - } - } - ); - - /** - * POST /channels/:id/activity - Log channel activity - */ - fastify.post<{ - Params: { id: string }; - Body: { - activity_type: string; - title: string; - description?: string; - metadata?: Record; - }; - }>( - '/channels/:id/activity', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - body: Type.Object({ - activity_type: Type.String({ minLength: 1, maxLength: 50 }), - title: Type.String({ minLength: 1, maxLength: 200 }), - description: Type.Optional(Type.String({ maxLength: 1000 })), - metadata: Type.Optional(Type.Record(Type.String(), Type.Any())), - }), - response: { - 201: Type.Object({ - success: Type.Boolean(), - data: Type.Object({ - id: UUIDSchema, - activityType: Type.String(), - title: Type.String(), - description: Type.Optional(Type.String()), - metadata: Type.Record(Type.String(), Type.Any()), - createdAt: Type.String({ format: 'date-time' }), - }), - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - const { activity_type, title, description, metadata } = request.body; - - const activityData = { - channelId: id, - activityType: activity_type as Activity['activity_type'], - userId: request.user!.userId, - title, - description: description || '', - metadata: metadata || {}, - }; - - const activity = await activityRepository.createActivity(activityData); - - // Broadcast activity to channel members - await WebSocketUtils.sendToChannel(id, 'channel_activity', { - type: 'channel_activity', - channelId: id, - activity: { - id: activity.id, - activityType: activity.activity_type, - title: activity.title, - description: activity.description, - metadata: activity.metadata, - userId: request.user!.userId, - userName: request.user!.name, - userAvatar: null, // Avatar not available in TokenPayload - createdAt: activity.created_at, - }, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - timestamp: new Date().toISOString(), - }); - - loggers.api.info( - { - userId: request.user?.userId, - channelId: id, - activityId: activity.id, - activityType: activity_type, - }, - 'Channel activity logged successfully' - ); - - reply.code(201).send({ - success: true, - data: { - id: activity.id, - activityType: activity.activity_type, - title: activity.title, - description: activity.description, - metadata: activity.metadata, - createdAt: activity.created_at, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to log channel activity'); - - if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to log channel activity', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); -}; diff --git a/backend/src/api/routes/DocsRoutes.ts b/backend/src/api/routes/DocsRoutes.ts deleted file mode 100644 index de8ea3a..0000000 --- a/backend/src/api/routes/DocsRoutes.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { FastifyInstance } from 'fastify'; - -/** - * Documentation Routes - Swagger/OpenAPI Integration - * Provides interactive API documentation via Swagger UI - */ - -export const registerDocsRoutes = async (fastify: FastifyInstance) => { - // The Swagger UI will be available at /docs - // All route schemas are automatically collected by @fastify/swagger - - /** - * Redirect root docs to Swagger UI - */ - fastify.get('/docs', async (request, reply) => { - reply.redirect('/docs/'); - }); - - /** - * API Health Status endpoint with OpenAPI schema - */ - fastify.get('/health/api', { - schema: { - tags: ['Health'], - summary: 'API Health Status', - description: 'Get detailed API health status with features and endpoints', - response: { - 200: { - type: 'object', - properties: { - status: { type: 'string', example: 'healthy' }, - version: { type: 'string', example: '1.0.0' }, - environment: { type: 'string', example: 'development' }, - features: { - type: 'object', - properties: { - authentication: { type: 'string', example: 'enabled' }, - websockets: { type: 'string', example: 'enabled' }, - caching: { type: 'string', example: 'enabled' }, - rateLimit: { type: 'string', example: 'enabled' }, - voiceCommands: { type: 'string', example: 'ready' }, - analytics: { type: 'string', example: 'ready' }, - } - }, - endpoints: { - type: 'object', - properties: { - auth: { type: 'string', example: '/api/v1/auth/*' }, - users: { type: 'string', example: '/api/v1/users/*' }, - channels: { type: 'string', example: '/api/v1/channels/*' }, - tasks: { type: 'string', example: '/api/v1/tasks/*' }, - docs: { type: 'string', example: '/docs' }, - } - }, - timestamp: { type: 'string', format: 'date-time' }, - } - } - } - } - }, async (request, reply) => { - reply.send({ - status: 'healthy', - version: '1.0.0', - environment: process.env.NODE_ENV || 'development', - features: { - authentication: 'enabled', - websockets: 'enabled', - caching: 'enabled', - rateLimit: 'enabled', - voiceCommands: 'ready', - analytics: 'ready', - }, - endpoints: { - auth: '/api/v1/auth/*', - users: '/api/v1/users/*', - channels: '/api/v1/channels/*', - tasks: '/api/v1/tasks/*', - docs: '/docs', - }, - timestamp: new Date().toISOString(), - }); - }); -}; \ No newline at end of file diff --git a/backend/src/api/routes/MessageRoutes.ts b/backend/src/api/routes/MessageRoutes.ts deleted file mode 100644 index 35a6228..0000000 --- a/backend/src/api/routes/MessageRoutes.ts +++ /dev/null @@ -1,966 +0,0 @@ -import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; -import { Type } from '@sinclair/typebox'; -import { messageRepository, channelRepository } from '@db/index'; -import { logger, loggers } from '@utils/logger'; -import { - ValidationError, - NotFoundError, - AuthorizationError, - formatErrorResponse, - createErrorContext, -} from '@utils/errors'; -import { authenticate, requireChannelAccess, apiRateLimit } from '@auth/middleware'; -import { cacheService } from '../../services/CacheService'; -import { Cacheable, CacheEvict, CacheKeyUtils } from '@utils/cache-decorators'; -import { WebSocketUtils } from '@websocket/utils'; -import { - UUIDSchema, - PaginationSchema, - SuccessResponseSchema, -} from '@utils/validation'; - -/** - * Channel Message Management API Routes - * Enterprise-grade messaging system with real-time updates - */ - -// Request/Response Schemas -const SendMessageSchema = Type.Object({ - content: Type.String({ minLength: 1, maxLength: 4000 }), - message_type: Type.Optional( - Type.Union([ - Type.Literal('text'), - Type.Literal('voice'), - Type.Literal('file'), - Type.Literal('system'), - ]) - ), - reply_to: Type.Optional(UUIDSchema), - thread_root: Type.Optional(UUIDSchema), - mentions: Type.Optional(Type.Array(UUIDSchema)), - attachments: Type.Optional(Type.Array(Type.Object({ - file_id: UUIDSchema, - filename: Type.String(), - file_type: Type.String(), - file_size: Type.Integer(), - }))), - voice_data: Type.Optional(Type.Object({ - duration: Type.Number(), - transcript: Type.Optional(Type.String()), - voice_file_id: Type.Optional(UUIDSchema), - })), - metadata: Type.Optional(Type.Record(Type.String(), Type.Any())), -}); - -const UpdateMessageSchema = Type.Object({ - content: Type.String({ minLength: 1, maxLength: 4000 }), -}); - -const MessageResponseSchema = Type.Object({ - id: UUIDSchema, - channel_id: UUIDSchema, - task_id: Type.Optional(UUIDSchema), - user_id: UUIDSchema, - user_name: Type.String(), - user_avatar: Type.Optional(Type.String()), - content: Type.String(), - message_type: Type.String(), - voice_data: Type.Optional(Type.Any()), - transcription: Type.Optional(Type.String()), - attachments: Type.Array(Type.Any()), - reply_to: Type.Optional(UUIDSchema), - thread_root: Type.Optional(UUIDSchema), - is_edited: Type.Boolean(), - is_pinned: Type.Boolean(), - is_announcement: Type.Boolean(), - reactions: Type.Record(Type.String(), Type.Any()), - mentions: Type.Array(UUIDSchema), - ai_generated: Type.Boolean(), - ai_context: Type.Optional(Type.Any()), - command_execution_id: Type.Optional(UUIDSchema), - metadata: Type.Record(Type.String(), Type.Any()), - reply_count: Type.Optional(Type.Integer()), - last_reply_timestamp: Type.Optional(Type.String({ format: 'date-time' })), - created_at: Type.String({ format: 'date-time' }), - updated_at: Type.String({ format: 'date-time' }), - edited_at: Type.Optional(Type.String({ format: 'date-time' })), -}); - -/** - * Message service with caching - */ -class MessageService { - @Cacheable({ - ttl: 300, // 5 minutes - namespace: 'messages', - keyGenerator: (messageId: string) => CacheKeyUtils.messageKey(messageId), - }) - async getMessageById(messageId: string) { - return await messageRepository.findById(messageId); - } - - @CacheEvict({ - keys: (channelId: string) => [CacheKeyUtils.channelMessagesKey(channelId)], - namespace: 'messages', - }) - async createMessage(messageData: any) { - return await messageRepository.createMessage(messageData); - } - - @CacheEvict({ - keys: (messageId: string, channelId: string) => [ - CacheKeyUtils.messageKey(messageId), - CacheKeyUtils.channelMessagesKey(channelId), - ], - namespace: 'messages', - }) - async updateMessage(messageId: string, updateData: any) { - return await messageRepository.updateMessage(messageId, updateData); - } -} - -const messageService = new MessageService(); - -/** - * Register message routes - */ -export const registerMessageRoutes = async (fastify: FastifyInstance) => { - /** - * GET /channels/:channelId/messages - Get channel messages - */ - fastify.get<{ - Params: { channelId: string }; - Querystring: typeof PaginationSchema.static & { - thread_root?: string; - search?: string; - message_type?: string; - before?: string; - after?: string; - }; - }>( - '/channels/:channelId/messages', - { - preHandler: [authenticate, requireChannelAccess, apiRateLimit], - schema: { - params: Type.Object({ - channelId: UUIDSchema, - }), - querystring: Type.Intersect([ - PaginationSchema, - Type.Object({ - thread_root: Type.Optional(UUIDSchema), - search: Type.Optional(Type.String({ maxLength: 200 })), - message_type: Type.Optional(Type.String()), - before: Type.Optional(Type.String({ format: 'date-time' })), - after: Type.Optional(Type.String({ format: 'date-time' })), - }), - ]), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Array(MessageResponseSchema), - pagination: Type.Object({ - total: Type.Integer(), - limit: Type.Integer(), - offset: Type.Integer(), - hasMore: Type.Boolean(), - }), - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { channelId } = request.params; - const { - limit = 50, - offset = 0, - thread_root, - search, - message_type, - before, - after, - } = request.query; - - // Build filters - const filters: any = { - channelId, - threadRoot: thread_root, - messageType: message_type, - before: before ? new Date(before) : undefined, - after: after ? new Date(after) : undefined, - }; - - let messages: any[] = []; - let total = 0; - - if (search) { - // Search messages - messages = await messageRepository.searchMessages( - channelId, - search, - Math.min(limit, 100), - offset - ); - total = messages.length; // Approximation - } else { - // Get messages with filters - messages = await messageRepository.findChannelMessages( - channelId, - filters, - Math.min(limit, 100), - offset - ); - total = await messageRepository.getChannelMessageCount(channelId, filters); - } - - // Update last read for user - await messageRepository.updateLastRead(channelId, request.user!.userId); - - loggers.api.info( - { - userId: request.user?.userId, - channelId, - messageCount: messages.length, - filters, - }, - 'Channel messages retrieved' - ); - - reply.send({ - success: true, - data: messages, - pagination: { - total, - limit, - offset, - hasMore: offset + limit < total, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve channel messages'); - - reply.code(500).send({ - error: { - message: 'Failed to retrieve messages', - code: 'SERVER_ERROR', - }, - }); - } - } - ); - - /** - * POST /channels/:channelId/messages - Send message - */ - fastify.post<{ - Params: { channelId: string }; - Body: typeof SendMessageSchema.static; - }>( - '/channels/:channelId/messages', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - channelId: UUIDSchema, - }), - body: SendMessageSchema, - response: { - 201: Type.Object({ - success: Type.Boolean(), - data: MessageResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { channelId } = request.params; - const messageData = { - ...request.body, - channel_id: channelId, - user_id: request.user!.userId, - message_type: request.body.message_type || 'text', - attachments: request.body.attachments || [], - mentions: request.body.mentions || [], - metadata: request.body.metadata || {}, - ai_generated: false, - }; - - const message = await messageService.createMessage(messageData); - - // Update channel activity - await channelRepository.updateActivity(channelId); - - // Determine if this is a thread message - const isThreadReply = message.reply_to && message.thread_root; - - // Broadcast message to channel members with thread context - await WebSocketUtils.sendToChannel(channelId, 'message_sent', { - type: 'message_sent', - channelId, - messageId: message.id, - isThreadReply, - threadRoot: message.thread_root, - replyTo: message.reply_to, - message: { - id: message.id, - channel_id: message.channel_id, - user_id: message.user_id, - user_name: request.user!.name, - user_email: request.user!.email, - user_avatar: request.user!.avatar_url, - user_role: request.user!.role, - content: message.content, - message_type: message.message_type, - voice_data: message.voice_data, - transcription: message.transcription, - attachments: message.attachments, - reply_to: message.reply_to, - thread_root: message.thread_root, - is_edited: message.is_edited, - is_pinned: message.is_pinned, - is_announcement: message.is_announcement, - reactions: message.reactions, - mentions: message.mentions, - ai_generated: message.ai_generated, - ai_context: message.ai_context, - command_execution_id: message.command_execution_id, - metadata: message.metadata, - formatting: message.formatting, - created_at: message.created_at, - updated_at: message.updated_at, - edited_at: message.edited_at, - }, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - timestamp: new Date().toISOString(), - }); - - // If this is a thread reply, also send a separate thread-specific event - if (isThreadReply) { - await WebSocketUtils.sendToChannel(channelId, 'thread_reply_sent', { - type: 'thread_reply_sent', - channelId, - threadRoot: message.thread_root, - parentMessageId: message.reply_to, - messageId: message.id, - message: { - id: message.id, - channel_id: message.channel_id, - user_id: message.user_id, - user_name: request.user!.name, - user_email: request.user!.email, - user_avatar: request.user!.avatar_url, - user_role: request.user!.role, - content: message.content, - message_type: message.message_type, - voice_data: message.voice_data, - transcription: message.transcription, - attachments: message.attachments, - reply_to: message.reply_to, - thread_root: message.thread_root, - is_edited: message.is_edited, - is_pinned: message.is_pinned, - is_announcement: message.is_announcement, - reactions: message.reactions, - mentions: message.mentions, - ai_generated: message.ai_generated, - ai_context: message.ai_context, - command_execution_id: message.command_execution_id, - metadata: message.metadata, - formatting: message.formatting, - created_at: message.created_at, - updated_at: message.updated_at, - edited_at: message.edited_at, - }, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - timestamp: new Date().toISOString(), - }); - } - - // Send mention notifications - if (message.mentions.length > 0) { - for (const mentionedUserId of message.mentions) { - if (mentionedUserId !== request.user!.userId) { - await WebSocketUtils.createAndSendNotification(mentionedUserId, { - title: 'You were mentioned', - message: `${request.user!.name} mentioned you in ${channelId}`, - category: 'mention', - priority: 'medium', - actionUrl: `/channels/${channelId}`, - actionText: 'View Message', - data: { - channelId, - messageId: message.id, - mentionedBy: request.user!.userId, - }, - }); - } - } - } - - loggers.api.info( - { - userId: request.user?.userId, - channelId, - messageId: message.id, - messageType: message.message_type, - mentionsCount: message.mentions.length, - attachmentsCount: message.attachments.length, - }, - 'Message sent successfully' - ); - - reply.code(201).send({ - success: true, - data: message, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to send message'); - - if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to send message', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * PUT /channels/:channelId/messages/:messageId - Edit message - */ - fastify.put<{ - Params: { channelId: string; messageId: string }; - Body: typeof UpdateMessageSchema.static; - }>( - '/channels/:channelId/messages/:messageId', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - channelId: UUIDSchema, - messageId: UUIDSchema, - }), - body: UpdateMessageSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: MessageResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { channelId, messageId } = request.params; - const { content } = request.body; - - // Check if user can edit this message - const existingMessage = await messageRepository.findById(messageId); - if (!existingMessage) { - throw new NotFoundError('Message not found'); - } - - if (existingMessage.user_id !== request.user!.userId && request.user!.role !== 'ceo') { - throw new AuthorizationError('You can only edit your own messages'); - } - - // Check if message is too old to edit (24 hours) - const messageAge = Date.now() - new Date(existingMessage.created_at).getTime(); - const twentyFourHours = 24 * 60 * 60 * 1000; - if (messageAge > twentyFourHours && request.user!.role !== 'ceo') { - throw new AuthorizationError('Messages older than 24 hours cannot be edited'); - } - - const message = await messageService.updateMessage(messageId, { - content, - is_edited: true, - edited_at: new Date(), - }); - - // Broadcast message edit - await WebSocketUtils.sendToChannel(channelId, 'message_updated', { - type: 'message_updated', - channelId, - messageId, - message: message, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - timestamp: new Date().toISOString(), - }); - - loggers.api.info( - { - userId: request.user?.userId, - channelId, - messageId, - }, - 'Message edited successfully' - ); - - reply.send({ - success: true, - data: message, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to edit message'); - - if (error instanceof NotFoundError || error instanceof AuthorizationError) { - reply.code(error.statusCode).send(formatErrorResponse(error)); - } else if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to edit message', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * DELETE /channels/:channelId/messages/:messageId - Delete message - */ - fastify.delete<{ - Params: { channelId: string; messageId: string }; - }>( - '/channels/:channelId/messages/:messageId', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - channelId: UUIDSchema, - messageId: UUIDSchema, - }), - response: { - 200: SuccessResponseSchema, - }, - }, - }, - async (request, reply) => { - try { - const { channelId, messageId } = request.params; - - // Check if user can delete this message - const existingMessage = await messageRepository.findById(messageId); - if (!existingMessage) { - throw new NotFoundError('Message not found'); - } - - const canDelete = - existingMessage.user_id === request.user!.userId || - request.user!.role === 'ceo'; - - if (!canDelete) { - throw new AuthorizationError('You can only delete your own messages'); - } - - const success = await messageRepository.softDelete(messageId, request.user!.userId); - if (!success) { - throw new NotFoundError('Message not found'); - } - - // Clear message cache - await cacheService.messages.delete(CacheKeyUtils.messageKey(messageId)); - - // Broadcast message deletion - await WebSocketUtils.sendToChannel(channelId, 'message_deleted', { - type: 'message_deleted', - channelId, - messageId, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - timestamp: new Date().toISOString(), - }); - - loggers.api.info( - { - userId: request.user?.userId, - channelId, - messageId, - }, - 'Message deleted successfully' - ); - - reply.send({ - success: true, - message: 'Message deleted successfully', - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to delete message'); - - if (error instanceof NotFoundError || error instanceof AuthorizationError) { - reply.code(error.statusCode).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to delete message', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * POST /channels/:channelId/messages/:messageId/reactions - Add reaction - */ - fastify.post<{ - Params: { channelId: string; messageId: string }; - Body: { emoji: string }; - }>( - '/channels/:channelId/messages/:messageId/reactions', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - channelId: UUIDSchema, - messageId: UUIDSchema, - }), - body: Type.Object({ - emoji: Type.String({ minLength: 1, maxLength: 10 }), - }), - response: { - 200: SuccessResponseSchema, - }, - }, - }, - async (request, reply) => { - try { - const { channelId, messageId } = request.params; - const { emoji } = request.body; - - const success = await messageRepository.addReaction( - messageId, - request.user!.userId, - emoji - ); - - if (!success) { - throw new NotFoundError('Message not found'); - } - - // Broadcast reaction - await WebSocketUtils.sendToChannel(channelId, 'message_reaction_added', { - type: 'message_reaction_added', - channelId, - messageId, - emoji, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - timestamp: new Date().toISOString(), - }); - - reply.send({ - success: true, - message: 'Reaction added successfully', - timestamp: new Date().toISOString(), - }); - } catch (error) { - loggers.api.error({ error }, 'Failed to add reaction'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to add reaction', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * GET /channels/:channelId/messages/:messageId/thread - Get thread messages - */ - fastify.get<{ - Params: { channelId: string; messageId: string }; - Querystring: typeof PaginationSchema.static; - }>( - '/channels/:channelId/messages/:messageId/thread', - { - preHandler: [authenticate, requireChannelAccess, apiRateLimit], - schema: { - params: Type.Object({ - channelId: UUIDSchema, - messageId: UUIDSchema, - }), - querystring: PaginationSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Object({ - parentMessage: MessageResponseSchema, - replies: Type.Array(MessageResponseSchema), - pagination: Type.Object({ - total: Type.Integer(), - limit: Type.Integer(), - offset: Type.Integer(), - hasMore: Type.Boolean(), - }), - }), - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { channelId, messageId } = request.params; - const { limit = 50, offset = 0 } = request.query; - - // Get the parent message - const parentMessage = await messageRepository.findById(messageId); - if (!parentMessage) { - throw new NotFoundError('Message not found'); - } - - // Get thread root - if this message is itself a reply, get its root - const threadRoot = parentMessage.thread_root || messageId; - - // Get all replies to this thread - const replies = await messageRepository.findChannelMessages( - channelId, - { threadRoot }, - Math.min(limit, 100), - offset - ); - - // Filter out the parent message from replies if it's included - const threadReplies = replies.filter(msg => msg.id !== threadRoot); - - const total = await messageRepository.getChannelMessageCount(channelId, { threadRoot }); - - loggers.api.info( - { - userId: request.user?.userId, - channelId, - messageId, - threadRoot, - repliesCount: threadReplies.length, - }, - 'Thread messages retrieved' - ); - - reply.send({ - success: true, - data: { - parentMessage, - replies: threadReplies, - pagination: { - total: total - 1, // Exclude parent message from count - limit, - offset, - hasMore: offset + limit < total - 1, - }, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve thread messages'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to retrieve thread messages', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * POST /channels/:channelId/messages/:messageId/pin - Pin/Unpin message - */ - fastify.post<{ - Params: { channelId: string; messageId: string }; - Body: { pinned: boolean }; - }>( - '/channels/:channelId/messages/:messageId/pin', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - channelId: UUIDSchema, - messageId: UUIDSchema, - }), - body: Type.Object({ - pinned: Type.Boolean(), - }), - response: { - 200: SuccessResponseSchema, - }, - }, - }, - async (request, reply) => { - try { - const { channelId, messageId } = request.params; - const { pinned } = request.body; - - // Only channel admins/owners and CEO can pin messages - // This would need channel member role checking - if (request.user!.role !== 'ceo') { - throw new AuthorizationError('Only channel administrators can pin messages'); - } - - const success = await messageRepository.updateMessage(messageId, { - is_pinned: pinned, - }); - - if (!success) { - throw new NotFoundError('Message not found'); - } - - // Broadcast pin status change - await WebSocketUtils.sendToChannel(channelId, 'message_pinned', { - type: pinned ? 'message_pinned' : 'message_unpinned', - channelId, - messageId, - pinned, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - timestamp: new Date().toISOString(), - }); - - loggers.api.info( - { - userId: request.user?.userId, - channelId, - messageId, - pinned, - }, - `Message ${pinned ? 'pinned' : 'unpinned'} successfully` - ); - - reply.send({ - success: true, - message: `Message ${pinned ? 'pinned' : 'unpinned'} successfully`, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to update message pin status'); - - if (error instanceof NotFoundError || error instanceof AuthorizationError) { - reply.code(error.statusCode).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to update pin status', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); -}; \ No newline at end of file diff --git a/backend/src/api/routes/NotificationRoutes.ts b/backend/src/api/routes/NotificationRoutes.ts deleted file mode 100644 index 4c7f02e..0000000 --- a/backend/src/api/routes/NotificationRoutes.ts +++ /dev/null @@ -1,354 +0,0 @@ -import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; -import { Type, Static } from '@sinclair/typebox'; -import { channelRepository } from '@db/index'; -import { loggers } from '@utils/logger'; -import { - formatErrorResponse, - createErrorContext, - ExternalServiceError, -} from '@utils/errors'; -import { authenticate } from '@auth/middleware'; -import { WebSocketUtils } from '@websocket/utils'; - -// Schema definitions -const ChannelNotificationSchema = Type.Object({ - channelId: Type.String(), - senderId: Type.String(), - senderName: Type.String(), - message: Type.String(), - type: Type.Union([Type.Literal('new_message'), Type.Literal('thread_reply')]), - parentMessageId: Type.Optional(Type.String()), -}); - -type ChannelNotificationRequest = Static; - -export async function notificationRoutes(fastify: FastifyInstance) { - /** - * POST /notifications/channel - Send push notifications to channel members - */ - fastify.post<{ Body: ChannelNotificationRequest }>( - '/notifications/channel', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Notifications'], - summary: 'Send Channel Notification', - description: 'Send push notifications to all channel members except the sender', - body: ChannelNotificationSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - message: Type.String(), - notificationsSent: Type.Number(), - }), - 400: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - 500: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request: FastifyRequest<{ Body: ChannelNotificationRequest }>, reply: FastifyReply) => { - const { channelId, senderId, senderName, message, type, parentMessageId } = request.body; - - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - - try { - loggers.api.info( - { - userId: request.user?.userId, - channelId, - type, - messageLength: message.length, - }, - 'Sending channel notification' - ); - - // Get channel members (excluding the sender) - const members = await channelRepository.getMembers(channelId); - const recipientMembers = members.filter(member => member.user_id !== senderId); - - if (recipientMembers.length === 0) { - return reply.send({ - success: true, - message: 'No recipients to notify', - notificationsSent: 0, - }); - } - - // Prepare notification data - const notificationTitle = type === 'thread_reply' - ? `${senderName} replied in thread` - : `New message in channel`; - - const notificationBody = message.length > 100 - ? `${message.substring(0, 97)}...` - : message; - - // Send notifications via WebSocket (which can trigger push notifications) - let notificationsSent = 0; - for (const member of recipientMembers) { - try { - // Send WebSocket notification - await WebSocketUtils.createAndSendNotification(member.user_id, { - title: notificationTitle, - message: notificationBody, - category: 'channel' as const, - priority: 'medium' as const, - data: { - channelId, - senderId, - senderName, - messageId: parentMessageId || 'new', - type, - }, - }); - - notificationsSent++; - } catch (error) { - loggers.api.warn( - { error, userId: member.user_id, channelId }, - 'Failed to send notification to user' - ); - } - } - - loggers.api.info( - { - userId: request.user?.userId, - channelId, - type, - notificationsSent, - totalRecipients: recipientMembers.length, - }, - 'Channel notifications sent successfully' - ); - - reply.send({ - success: true, - message: 'Notifications sent successfully', - notificationsSent, - }); - - } catch (error) { - loggers.api.error({ error, context }, 'Failed to send channel notifications'); - - const notificationError = new ExternalServiceError('Failed to send notifications', 'notification-service', error as Error); - reply.code(502).send(formatErrorResponse(notificationError)); - } - } - ); - - /** - * POST /notifications/register - Register device token for push notifications - */ - fastify.post<{ - Body: { - token: string; - platform: 'ios' | 'android'; - deviceInfo?: { - brand?: string; - modelName?: string; - osName?: string; - osVersion?: string; - }; - }; - }>( - '/notifications/register', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Notifications'], - summary: 'Register Device Token', - description: 'Register device token for push notifications', - body: Type.Object({ - token: Type.String(), - platform: Type.Union([Type.Literal('ios'), Type.Literal('android')]), - deviceInfo: Type.Optional(Type.Object({ - brand: Type.Optional(Type.String()), - modelName: Type.Optional(Type.String()), - osName: Type.Optional(Type.String()), - osVersion: Type.Optional(Type.String()), - })), - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - message: Type.String(), - }), - }, - }, - }, - async (request, reply) => { - const { token, platform, deviceInfo } = request.body; - - try { - loggers.api.info( - { - userId: request.user?.userId, - platform, - deviceInfo, - }, - 'Registering device token' - ); - - // Here you would typically save the token to your database - // associated with the user for later use in push notifications - - // For now, just log it as this would require a device_tokens table - loggers.api.info( - { - userId: request.user?.userId, - tokenLength: token.length, - platform, - }, - 'Device token registered (not persisted - requires device_tokens table)' - ); - - reply.send({ - success: true, - message: 'Device token registered successfully', - }); - } catch (error) { - loggers.api.error({ error, userId: request.user?.userId }, 'Failed to register device token'); - - const tokenError = new ExternalServiceError('Failed to register device token', 'notification-service', error as Error); - reply.code(502).send(formatErrorResponse(tokenError)); - } - } - ); - - /** - * PUT /notifications/preferences - Update notification preferences - */ - fastify.put<{ - Body: { - taskNotifications: boolean; - mentionNotifications: boolean; - channelNotifications: boolean; - quietHoursEnabled: boolean; - quietHoursStart?: string; - quietHoursEnd?: string; - }; - }>( - '/notifications/preferences', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Notifications'], - summary: 'Update Notification Preferences', - description: 'Update user notification preferences', - body: Type.Object({ - taskNotifications: Type.Boolean(), - mentionNotifications: Type.Boolean(), - channelNotifications: Type.Boolean(), - quietHoursEnabled: Type.Boolean(), - quietHoursStart: Type.Optional(Type.String()), - quietHoursEnd: Type.Optional(Type.String()), - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - message: Type.String(), - }), - }, - }, - }, - async (request, reply) => { - try { - loggers.api.info( - { - userId: request.user?.userId, - preferences: request.body, - }, - 'Updating notification preferences' - ); - - // Here you would update the user's notification preferences in the database - // This would require a user_notification_preferences table or additional columns - - reply.send({ - success: true, - message: 'Notification preferences updated successfully', - }); - } catch (error) { - loggers.api.error({ error, userId: request.user?.userId }, 'Failed to update notification preferences'); - - const prefsError = new ExternalServiceError('Failed to update notification preferences', 'preferences-service', error as Error); - reply.code(502).send(formatErrorResponse(prefsError)); - } - } - ); - - /** - * GET /notifications/preferences - Get notification preferences - */ - fastify.get( - '/notifications/preferences', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Notifications'], - summary: 'Get Notification Preferences', - description: 'Get user notification preferences', - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Object({ - taskNotifications: Type.Boolean(), - mentionNotifications: Type.Boolean(), - channelNotifications: Type.Boolean(), - quietHoursEnabled: Type.Boolean(), - quietHoursStart: Type.Optional(Type.String()), - quietHoursEnd: Type.Optional(Type.String()), - }), - }), - }, - }, - }, - async (request, reply) => { - try { - // Return default preferences for now - // In a real implementation, you'd fetch these from the database - const defaultPreferences = { - taskNotifications: true, - mentionNotifications: true, - channelNotifications: true, - quietHoursEnabled: false, - quietHoursStart: '22:00', - quietHoursEnd: '08:00', - }; - - reply.send({ - success: true, - data: defaultPreferences, - }); - } catch (error) { - loggers.api.error({ error, userId: request.user?.userId }, 'Failed to get notification preferences'); - - const getPrefsError = new ExternalServiceError('Failed to get notification preferences', 'preferences-service', error as Error); - reply.code(502).send(formatErrorResponse(getPrefsError)); - } - } - ); -} diff --git a/backend/src/api/routes/TaskRoutes.ts b/backend/src/api/routes/TaskRoutes.ts deleted file mode 100644 index 7fd97fe..0000000 --- a/backend/src/api/routes/TaskRoutes.ts +++ /dev/null @@ -1,1721 +0,0 @@ -import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; -import { Type } from '@sinclair/typebox'; -import { taskRepository, channelRepository, activityRepository } from '@db/index'; -import { logger, loggers } from '@utils/logger'; -import { - ValidationError, - NotFoundError, - AuthorizationError, - formatErrorResponse, - createErrorContext, -} from '@utils/errors'; -import { authenticate, authorize, authorizeRoles, requireChannelAccess, apiRateLimit, requireManagerOrCEO } from '@auth/middleware'; -import { cacheService } from '../../services/CacheService'; -import { Cacheable, CacheEvict, CacheKeyUtils } from '@utils/cache-decorators'; -import { WebSocketUtils } from '@websocket/utils'; -import { - UUIDSchema, - PaginationSchema, - TaskPrioritySchema, - TaskStatusSchema, - BusinessValueSchema, - SuccessResponseSchema, -} from '@utils/validation'; - -/** - * Task Management API Routes - * Enterprise-grade task CRUD operations with real-time updates - */ - -// Request/Response Schemas -const CreateTaskSchema = Type.Object({ - title: Type.String({ minLength: 1, maxLength: 255 }), - description: Type.Optional(Type.String({ maxLength: 2000 })), - channel_id: Type.Optional(UUIDSchema), - parent_task_id: Type.Optional(UUIDSchema), - assigned_to: Type.Optional(Type.Array(UUIDSchema)), - owned_by: Type.Optional(UUIDSchema), - priority: Type.Optional(TaskPrioritySchema), - task_type: Type.Optional( - Type.Union([ - Type.Literal('general'), - Type.Literal('project'), - Type.Literal('maintenance'), - Type.Literal('emergency'), - Type.Literal('research'), - Type.Literal('approval'), - ]) - ), - complexity: Type.Optional(Type.Integer({ minimum: 1, maximum: 10 })), - estimated_hours: Type.Optional(Type.Number({ minimum: 0 })), - due_date: Type.Optional(Type.String({ format: 'date-time' })), - start_date: Type.Optional(Type.String({ format: 'date-time' })), - tags: Type.Optional(Type.Array(Type.String({ maxLength: 50 }))), - labels: Type.Optional(Type.Record(Type.String(), Type.Any())), - voice_created: Type.Optional(Type.Boolean()), - voice_command_id: Type.Optional(Type.String()), - voice_instructions: Type.Optional(Type.String()), - business_value: Type.Optional(BusinessValueSchema), - acceptance_criteria: Type.Optional(Type.String({ maxLength: 2000 })), -}); - -const UpdateTaskSchema = Type.Object({ - title: Type.Optional(Type.String({ minLength: 1, maxLength: 255 })), - description: Type.Optional(Type.String({ maxLength: 2000 })), - priority: Type.Optional(TaskPrioritySchema), - status: Type.Optional(TaskStatusSchema), - complexity: Type.Optional(Type.Integer({ minimum: 1, maximum: 10 })), - estimated_hours: Type.Optional(Type.Number({ minimum: 0 })), - due_date: Type.Optional(Type.String({ format: 'date-time' })), - start_date: Type.Optional(Type.String({ format: 'date-time' })), - tags: Type.Optional(Type.Array(Type.String({ maxLength: 50 }))), - labels: Type.Optional(Type.Record(Type.String(), Type.Any())), - business_value: Type.Optional(BusinessValueSchema), - acceptance_criteria: Type.Optional(Type.String({ maxLength: 2000 })), -}); - -const TaskResponseSchema = Type.Object({ - id: UUIDSchema, - title: Type.String(), - description: Type.Optional(Type.String()), - channel_id: Type.Optional(UUIDSchema), - parent_task_id: Type.Optional(UUIDSchema), - created_by: UUIDSchema, - assigned_to: Type.Array(UUIDSchema), - owned_by: Type.Optional(UUIDSchema), - priority: TaskPrioritySchema, - status: TaskStatusSchema, - task_type: Type.String(), - complexity: Type.Integer(), - estimated_hours: Type.Optional(Type.Number()), - actual_hours: Type.Number(), - story_points: Type.Optional(Type.Integer()), - due_date: Type.Optional(Type.String({ format: 'date-time' })), - start_date: Type.Optional(Type.String({ format: 'date-time' })), - completed_at: Type.Optional(Type.String({ format: 'date-time' })), - progress_percentage: Type.Integer(), - tags: Type.Array(Type.String()), - labels: Type.Record(Type.String(), Type.Any()), - voice_created: Type.Boolean(), - voice_command_id: Type.Optional(Type.String()), - voice_instructions: Type.Optional(Type.String()), - business_value: BusinessValueSchema, - acceptance_criteria: Type.Optional(Type.String()), - watchers: Type.Array(UUIDSchema), - comments_count: Type.Integer(), - attachments_count: Type.Integer(), - created_at: Type.String({ format: 'date-time' }), - updated_at: Type.String({ format: 'date-time' }), - last_activity_at: Type.String({ format: 'date-time' }), -}); - -const TaskStatsSchema = Type.Object({ - totalTasks: Type.Integer(), - tasksByStatus: Type.Record(Type.String(), Type.Integer()), - tasksByPriority: Type.Record(Type.String(), Type.Integer()), - overdueTasks: Type.Integer(), - completedThisWeek: Type.Integer(), - averageCompletionTime: Type.Number(), -}); - -/** - * Task service with caching - */ -class TaskService { - @Cacheable({ - ttl: 900, // 15 minutes - namespace: 'tasks', - keyGenerator: (taskId: string) => CacheKeyUtils.taskKey(taskId), - }) - async getTaskById(taskId: string) { - return await taskRepository.findById(taskId); - } - - @CacheEvict({ - keys: (taskId: string) => [CacheKeyUtils.taskKey(taskId)], - namespace: 'tasks', - tags: ['tasks'], - }) - async updateTask(taskId: string, updateData: any) { - return await taskRepository.update(taskId, updateData); - } - - @CacheEvict({ - allEntries: true, - namespace: 'tasks', - }) - async createTask(taskData: any) { - return await taskRepository.createTask(taskData); - } -} - -const taskService = new TaskService(); - -/** - * Register task routes - */ -export const registerTaskRoutes = async (fastify: FastifyInstance) => { - /** - * GET /tasks - List tasks with filters - */ - fastify.get<{ - Querystring: typeof PaginationSchema.static & { - status?: string[]; - priority?: string[]; - assigned_to?: string; - channel_id?: string; - created_by?: string; - due_after?: string; - due_before?: string; - tags?: string[]; - overdue?: boolean; - voice_created?: boolean; - search?: string; - }; - }>( - '/tasks', - { - preHandler: [authenticate, apiRateLimit], - schema: { - querystring: Type.Intersect([ - PaginationSchema, - Type.Object({ - status: Type.Optional(Type.Array(TaskStatusSchema)), - priority: Type.Optional(Type.Array(TaskPrioritySchema)), - assigned_to: Type.Optional(UUIDSchema), - channel_id: Type.Optional(UUIDSchema), - created_by: Type.Optional(UUIDSchema), - due_after: Type.Optional(Type.String({ format: 'date-time' })), - due_before: Type.Optional(Type.String({ format: 'date-time' })), - tags: Type.Optional(Type.Array(Type.String())), - overdue: Type.Optional(Type.Boolean()), - voice_created: Type.Optional(Type.Boolean()), - search: Type.Optional(Type.String({ maxLength: 200 })), - }), - ]), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Array(TaskResponseSchema), - pagination: Type.Object({ - total: Type.Integer(), - limit: Type.Integer(), - offset: Type.Integer(), - hasMore: Type.Boolean(), - }), - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { - limit = 20, - offset = 0, - status, - priority, - assigned_to, - channel_id, - created_by, - due_after, - due_before, - tags, - overdue, - voice_created, - search, - } = request.query; - - // Debug logging for filter parameters - loggers.api.info({ - userId: request.user?.userId, - queryParams: { - limit, - offset, - status: Array.isArray(status) ? status : [status], - priority: Array.isArray(priority) ? priority : [priority], - assigned_to, - channel_id, - created_by, - due_after, - due_before, - tags, - overdue, - voice_created, - search, - } - }, 'Task filter request received'); - - // Build task filters - const filters: any = {}; - if (status) { - filters.status = Array.isArray(status) ? status : [status]; - } - if (priority) { - filters.priority = Array.isArray(priority) ? priority : [priority]; - } - if (assigned_to) filters.assignedTo = [assigned_to]; - if (channel_id) filters.channelId = channel_id; - if (due_after) filters.dueAfter = new Date(due_after); - if (due_before) filters.dueBefore = new Date(due_before); - if (tags) { - filters.tags = Array.isArray(tags) ? tags : [tags]; - } - if (overdue !== undefined) filters.overdue = overdue; - if (voice_created !== undefined) filters.voiceCreated = voice_created; - - // Filter based on user permissions (non-CEO users only see their tasks unless specified) - if (request.user!.role !== 'ceo' && !assigned_to && !created_by) { - filters.assignedTo = [request.user!.userId]; - } - - let tasks: any[] = []; - let total = 0; - - if (search) { - // Use search functionality - tasks = await taskRepository.searchTasks( - search, - request.user!.userId, - Math.min(limit, 100), - offset - ); - total = tasks.length; // Approximation for search results - } else { - // Use filtered query - tasks = await taskRepository.findWithFilters(filters, Math.min(limit, 100), offset); - // TODO: Get total count for pagination - total = tasks.length; - } - - loggers.api.info( - { - userId: request.user?.userId, - filters, - search, - resultCount: tasks.length, - }, - 'Tasks list retrieved' - ); - - reply.send({ - success: true, - data: tasks, - pagination: { - total, - limit, - offset, - hasMore: offset + limit < total, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - - // Enhanced error logging - loggers.api.error({ - error: { - message: error instanceof Error ? error.message : String(error), - stack: error instanceof Error ? error.stack : undefined, - name: error instanceof Error ? error.name : undefined, - }, - context, - queryParams: request.query, - }, 'Failed to retrieve tasks - detailed error'); - - reply.code(500).send({ - error: { - message: 'Failed to retrieve tasks', - code: 'SERVER_ERROR', - details: error instanceof Error ? error.message : String(error), - }, - }); - } - } - ); - - /** - * GET /tasks/:id - Get task details - */ - fastify.get<{ - Params: { id: string }; - }>( - '/tasks/:id', - { - preHandler: [authenticate], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: TaskResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - - const task = await taskService.getTaskById(id); - if (!task) { - throw new NotFoundError('Task not found'); - } - - // Check if user has access to this task - const hasAccess = - request.user!.role === 'ceo' || - task.assigned_to.includes(request.user!.userId) || - task.created_by === request.user!.userId || - task.watchers.includes(request.user!.userId); - - if (!hasAccess) { - throw new AuthorizationError('You do not have access to this task'); - } - - loggers.api.info( - { - userId: request.user?.userId, - taskId: id, - }, - 'Task details retrieved' - ); - - reply.send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve task'); - - if (error instanceof NotFoundError || error instanceof AuthorizationError) { - reply.code(error.statusCode).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to retrieve task', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * POST /tasks - Create new task - */ - fastify.post<{ - Body: typeof CreateTaskSchema.static; - }>( - '/tasks', - { - preHandler: [authenticate, requireManagerOrCEO], - schema: { - body: CreateTaskSchema, - response: { - 201: Type.Object({ - success: Type.Boolean(), - data: TaskResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - // If channel_id is specified, verify user has access to the channel - if (request.body.channel_id) { - const hasChannelAccess = await channelRepository.canUserAccess( - request.body.channel_id, - request.user!.userId, - request.user!.role - ); - - if (!hasChannelAccess) { - throw new AuthorizationError('You do not have access to create tasks in this channel'); - } - } - - // Check if manager is trying to assign other managers - if (request.user!.role === 'manager' && request.body.assigned_to) { - const { userRepository } = await import('@db/index'); - const usersToAssign = await Promise.all( - request.body.assigned_to.map(userId => userRepository.findById(userId)) - ); - - const otherManagersBeingAssigned = usersToAssign.filter(user => - user && user.role === 'manager' && user.id !== request.user!.userId - ); - - if (otherManagersBeingAssigned.length > 0) { - throw new AuthorizationError('Managers cannot assign other managers to tasks'); - } - } - - const taskData = { - ...request.body, - created_by: request.user!.userId, - assigned_to: request.body.assigned_to || [request.user!.userId], - owned_by: request.body.owned_by || request.user!.userId, - tags: request.body.tags || [], - labels: request.body.labels || {}, - due_date: request.body.due_date ? new Date(request.body.due_date) : undefined, - start_date: request.body.start_date ? new Date(request.body.start_date) : undefined, - }; - - const task = await taskService.createTask(taskData); - - // Create activity for task creation - try { - await activityRepository.createActivity({ - channelId: task.channel_id, - taskId: task.id, - userId: request.user!.userId, - activityType: 'task_created', - title: `Task Created: ${task.title}`, - description: `New task "${task.title}" was created${task.description ? ': ' + task.description : ''}`, - priority: task.priority as any, - category: 'task' as any, - metadata: { - taskId: task.id, - taskTitle: task.title, - taskStatus: task.status, - taskPriority: task.priority, - assignedTo: task.assigned_to, - channelId: task.channel_id, - createdBy: request.user!.userId, - createdByName: request.user!.name - } - }); - } catch (error) { - loggers.api.warn?.({ error, taskId: task.id }, 'Failed to create task creation activity'); - } - - // Broadcast task creation - await WebSocketUtils.broadcastTaskUpdate({ - type: 'task_created', - taskId: task.id, - channelId: task.channel_id || '', - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'create', - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - - // Send notifications to assignees - if (task.assigned_to.length > 0) { - for (const assigneeId of task.assigned_to) { - if (assigneeId !== request.user!.userId) { - await WebSocketUtils.createAndSendNotification(assigneeId, { - title: 'New Task Assigned', - message: `You have been assigned to task: ${task.title}`, - category: 'task', - priority: - task.priority === 'critical' || task.priority === 'urgent' ? 'high' : 'medium', - actionUrl: `/tasks/${task.id}`, - actionText: 'View Task', - data: { taskId: task.id, taskTitle: task.title }, - }); - } - } - } - - loggers.api.info( - { - userId: request.user?.userId, - taskId: task.id, - taskTitle: task.title, - assignedTo: task.assigned_to, - priority: task.priority, - }, - 'Task created successfully' - ); - - reply.code(201).send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to create task'); - - if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to create task', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * PUT /tasks/:id - Update task - */ - fastify.put<{ - Params: { id: string }; - Body: typeof UpdateTaskSchema.static; - }>( - '/tasks/:id', - { - preHandler: [authenticate], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - body: UpdateTaskSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: TaskResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - const updateData = { - ...request.body, - due_date: request.body.due_date ? new Date(request.body.due_date) : undefined, - start_date: request.body.start_date ? new Date(request.body.start_date) : undefined, - }; - - // Check if user can update this task - const existingTask = await taskRepository.findById(id); - if (!existingTask) { - throw new NotFoundError('Task not found'); - } - - const canUpdate = - request.user!.role === 'ceo' || - existingTask.assigned_to.includes(request.user!.userId) || - existingTask.created_by === request.user!.userId || - existingTask.owned_by === request.user!.userId; - - if (!canUpdate) { - throw new AuthorizationError('You do not have permission to update this task'); - } - - const task = await taskService.updateTask(id, updateData); - - // Broadcast task update - await WebSocketUtils.broadcastTaskUpdate({ - type: 'task_updated', - taskId: id, - channelId: task.channel_id || '', - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'update', - changes: updateData, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - - loggers.api.info( - { - userId: request.user?.userId, - taskId: id, - updatedFields: Object.keys(updateData), - }, - 'Task updated successfully' - ); - - reply.send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to update task'); - - if (error instanceof NotFoundError || error instanceof AuthorizationError) { - reply.code(error.statusCode).send(formatErrorResponse(error)); - } else if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to update task', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * PATCH /tasks/:id/status - Update task status - */ - fastify.patch<{ - Params: { id: string }; - Body: { status: string }; - }>( - '/tasks/:id/status', - { - preHandler: [authenticate], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - body: Type.Object({ - status: TaskStatusSchema, - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: TaskResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - const { status } = request.body; - - const task = await taskRepository.updateStatus(id, status as any, request.user!.userId); - - // Create activity for task status update - try { - const activityType = status === 'completed' ? 'task_completed' : 'task_updated'; - const activityTitle = status === 'completed' - ? `Task Completed: ${task.title}` - : `Task Status Updated: ${task.title}`; - const activityDescription = status === 'completed' - ? `Task "${task.title}" was marked as completed` - : `Task "${task.title}" status was updated to ${status}`; - - await activityRepository.createActivity({ - channelId: task.channel_id, - taskId: task.id, - userId: request.user!.userId, - activityType, - title: activityTitle, - description: activityDescription, - priority: task.priority as any, - category: 'task' as any, - metadata: { - taskId: task.id, - taskTitle: task.title, - oldStatus: status, // Note: we don't have the old status here, but status is the new one - newStatus: status, - taskPriority: task.priority, - assignedTo: task.assigned_to, - channelId: task.channel_id, - updatedBy: request.user!.userId, - updatedByName: request.user!.name - } - }); - } catch (error) { - loggers.api.warn?.({ error, taskId: id }, 'Failed to create task status update activity'); - } - - // Broadcast status change - await WebSocketUtils.broadcastTaskUpdate({ - type: status === 'completed' ? 'task_completed' : 'task_updated', - taskId: id, - channelId: task.channel_id || '', - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: status === 'completed' ? 'complete' : 'update', - changes: { status }, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - - // Send completion notification - if (status === 'completed') { - for (const assigneeId of task.assigned_to) { - if (assigneeId !== request.user!.userId) { - await WebSocketUtils.createAndSendNotification(assigneeId, { - title: 'Task Completed', - message: `Task "${task.title}" has been completed`, - category: 'task', - priority: 'medium', - actionUrl: `/tasks/${task.id}`, - actionText: 'View Task', - data: { taskId: task.id, taskTitle: task.title }, - }); - } - } - } - - loggers.api.info( - { - userId: request.user?.userId, - taskId: id, - newStatus: status, - }, - 'Task status updated successfully' - ); - - reply.send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to update task status'); - - if (error instanceof NotFoundError || error instanceof AuthorizationError) { - reply.code(error.statusCode).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to update task status', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * POST /tasks/:id/assign - Assign users to task - */ - fastify.post<{ - Params: { id: string }; - Body: { user_ids: string[] }; - }>( - '/tasks/:id/assign', - { - preHandler: [authenticate], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - body: Type.Object({ - user_ids: Type.Array(UUIDSchema), - }), - response: { - 200: SuccessResponseSchema, - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - const { user_ids } = request.body; - - // Check if current user is a manager trying to assign other managers - if (request.user!.role === 'manager') { - // Get user roles for the users being assigned - const { userRepository } = await import('@db/index'); - const usersToAssign = await Promise.all( - user_ids.map(userId => userRepository.findById(userId)) - ); - - const otherManagersBeingAssigned = usersToAssign.filter(user => - user && user.role === 'manager' && user.id !== request.user!.userId - ); - - if (otherManagersBeingAssigned.length > 0) { - throw new AuthorizationError('Managers cannot assign other managers to tasks'); - } - } - - const success = await taskRepository.assignUsers(id, user_ids, request.user!.userId); - if (!success) { - throw new ValidationError('Failed to assign users to task', []); - } - - // Clear task cache - await cacheService.tasks.delete(CacheKeyUtils.taskKey(id)); - - // Get updated task for broadcast - const task = await taskRepository.findById(id); - if (task) { - // Create activity for task assignment - try { - await activityRepository.createActivity({ - channelId: task.channel_id, - taskId: task.id, - userId: request.user!.userId, - activityType: 'task_assigned', - title: `Task Assigned: ${task.title}`, - description: `Task "${task.title}" was assigned to ${user_ids.length} user(s)`, - priority: task.priority as any, - category: 'task' as any, - metadata: { - taskId: task.id, - taskTitle: task.title, - assignedUserIds: user_ids, - taskPriority: task.priority, - channelId: task.channel_id, - assignedBy: request.user!.userId, - assignedByName: request.user!.name, - totalAssignedUsers: task.assigned_to.length - } - }); - } catch (error) { - loggers.api.warn?.({ error, taskId: id }, 'Failed to create task assignment activity'); - } - - // Broadcast assignment - await WebSocketUtils.broadcastTaskUpdate({ - type: 'task_updated', - taskId: id, - channelId: task.channel_id || '', - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'assign', - changes: { assigned_to: user_ids }, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - - // Send notifications to newly assigned users - for (const userId of user_ids) { - await WebSocketUtils.createAndSendNotification(userId, { - title: 'Task Assigned', - message: `You have been assigned to task: ${task.title}`, - category: 'task', - priority: - task.priority === 'critical' || task.priority === 'urgent' ? 'high' : 'medium', - actionUrl: `/tasks/${task.id}`, - actionText: 'View Task', - data: { taskId: task.id, taskTitle: task.title }, - }); - } - } - - loggers.api.info( - { - userId: request.user?.userId, - taskId: id, - assignedUsers: user_ids, - }, - 'Users assigned to task successfully' - ); - - reply.send({ - success: true, - message: 'Users assigned successfully', - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to assign users to task'); - - if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to assign users', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * DELETE /tasks/:id - Delete task - */ - fastify.delete<{ - Params: { id: string }; - }>( - '/tasks/:id', - { - preHandler: [authenticate, requireManagerOrCEO], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - response: { - 200: SuccessResponseSchema, - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - - const success = await taskRepository.softDelete(id, request.user!.userId); - if (!success) { - throw new NotFoundError('Task not found'); - } - - // Clear task cache - await cacheService.tasks.delete(CacheKeyUtils.taskKey(id)); - - // Broadcast task deletion - await WebSocketUtils.broadcastTaskUpdate({ - type: 'task_deleted', - taskId: id, - task: {} as any, - action: 'delete', - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - - loggers.api.info( - { - userId: request.user?.userId, - taskId: id, - }, - 'Task deleted successfully' - ); - - reply.send({ - success: true, - message: 'Task deleted successfully', - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to delete task'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to delete task', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * GET /tasks/stats - Get task statistics - */ - fastify.get<{ - Querystring: { user_id?: string }; - }>( - '/tasks/stats', - { - preHandler: [authenticate], - schema: { - querystring: Type.Object({ - user_id: Type.Optional(UUIDSchema), - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: TaskStatsSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { user_id } = request.query; - - // Only CEO can view stats for other users - const targetUserId = - user_id && request.user!.role === 'ceo' ? user_id : request.user!.userId; - - const stats = await taskRepository.getTaskStats(targetUserId); - - loggers.api.info( - { - userId: request.user?.userId, - targetUserId, - stats, - }, - 'Task stats retrieved' - ); - - reply.send({ - success: true, - data: stats, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve task stats'); - - reply.code(500).send({ - error: { - message: 'Failed to retrieve task stats', - code: 'SERVER_ERROR', - }, - }); - } - } - ); - - /** - * GET /channels/:channelId/tasks - Get channel tasks - */ - fastify.get<{ - Params: { channelId: string }; - Querystring: typeof PaginationSchema.static & { - status?: string[]; - priority?: string[]; - assigned_to?: string; - }; - }>( - '/channels/:channelId/tasks', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - channelId: UUIDSchema, - }), - querystring: Type.Intersect([ - PaginationSchema, - Type.Object({ - status: Type.Optional(Type.Array(TaskStatusSchema)), - priority: Type.Optional(Type.Array(TaskPrioritySchema)), - assigned_to: Type.Optional(UUIDSchema), - }), - ]), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Array(TaskResponseSchema), - pagination: Type.Object({ - total: Type.Integer(), - limit: Type.Integer(), - offset: Type.Integer(), - hasMore: Type.Boolean(), - }), - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { channelId } = request.params; - const { limit = 20, offset = 0, status, priority, assigned_to } = request.query; - - // Build filters - const filters: any = { - channelId, - status, - priority, - assignedTo: assigned_to ? [assigned_to] : undefined, - }; - - const tasks = await taskRepository.findWithFilters(filters, Math.min(limit, 100), offset); - const total = tasks.length; // Simplified - in production, implement proper count query - - loggers.api.info( - { - userId: request.user?.userId, - channelId, - taskCount: tasks.length, - filters, - }, - 'Channel tasks retrieved' - ); - - reply.send({ - success: true, - data: tasks, - pagination: { - total, - limit, - offset, - hasMore: offset + limit < total, - }, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve channel tasks'); - - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel tasks', - code: 'SERVER_ERROR', - }, - }); - } - } - ); - - /** - * POST /channels/:channelId/tasks - Create task in channel - */ - fastify.post<{ - Params: { channelId: string }; - Body: Omit; - }>( - '/channels/:channelId/tasks', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - channelId: UUIDSchema, - }), - body: Type.Omit(CreateTaskSchema, ['channel_id']), - response: { - 201: Type.Object({ - success: Type.Boolean(), - data: TaskResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { channelId } = request.params; - - // Check if manager is trying to assign other managers - if (request.user!.role === 'manager' && request.body.assigned_to) { - const { userRepository } = await import('@db/index'); - const usersToAssign = await Promise.all( - request.body.assigned_to.map(userId => userRepository.findById(userId)) - ); - - const otherManagersBeingAssigned = usersToAssign.filter(user => - user && user.role === 'manager' && user.id !== request.user!.userId - ); - - if (otherManagersBeingAssigned.length > 0) { - throw new AuthorizationError('Managers cannot assign other managers to tasks'); - } - } - - const taskData = { - ...request.body, - channel_id: channelId, - created_by: request.user!.userId, - assigned_to: request.body.assigned_to || [request.user!.userId], - owned_by: request.body.owned_by || request.user!.userId, - tags: request.body.tags || [], - labels: request.body.labels || {}, - due_date: request.body.due_date ? new Date(request.body.due_date) : undefined, - start_date: request.body.start_date ? new Date(request.body.start_date) : undefined, - }; - - const task = await taskService.createTask(taskData); - - // Create activity for channel task creation - try { - await activityRepository.createActivity({ - channelId, - taskId: task.id, - userId: request.user!.userId, - activityType: 'task_created', - title: `Channel Task Created: ${task.title}`, - description: `New task "${task.title}" was created in channel${task.description ? ': ' + task.description : ''}`, - priority: task.priority as any, - category: 'task' as any, - metadata: { - taskId: task.id, - taskTitle: task.title, - taskStatus: task.status, - taskPriority: task.priority, - assignedTo: task.assigned_to, - channelId, - createdBy: request.user!.userId, - createdByName: request.user!.name, - isChannelTask: true - } - }); - } catch (error) { - loggers.api.warn?.({ error, taskId: task.id, channelId }, 'Failed to create channel task creation activity'); - } - - // Broadcast task creation to channel and task management system - await WebSocketUtils.broadcastTaskUpdate({ - type: 'task_created', - taskId: task.id, - channelId, - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'create', - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - - // Send channel notification message - if (channelId) { - await WebSocketUtils.broadcastChannelMessage({ - type: 'chat_message', - channelId, - messageId: `task_created_${task.id}`, - message: `Task "${task.title}" was created`, - messageType: 'system', - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - } - - // Send notifications to assignees - if (task.assigned_to.length > 0) { - for (const assigneeId of task.assigned_to) { - if (assigneeId !== request.user!.userId) { - await WebSocketUtils.createAndSendNotification(assigneeId, { - title: 'New Channel Task Assigned', - message: `You have been assigned to task: ${task.title} in channel`, - category: 'task', - priority: task.priority === 'critical' || task.priority === 'urgent' ? 'high' : 'medium', - actionUrl: `/channels/${channelId}?task=${task.id}`, - actionText: 'View Task', - data: { - taskId: task.id, - taskTitle: task.title, - channelId, - }, - }); - } - } - } - - loggers.api.info( - { - userId: request.user?.userId, - channelId, - taskId: task.id, - taskTitle: task.title, - assignedTo: task.assigned_to, - }, - 'Channel task created successfully' - ); - - reply.code(201).send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to create channel task'); - - if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to create channel task', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * PUT /tasks/:id/channel - Link task to channel - */ - fastify.put<{ - Params: { id: string }; - Body: { channel_id: string | null }; - }>( - '/tasks/:id/channel', - { - preHandler: [authenticate], - schema: { - params: Type.Object({ - id: UUIDSchema, - }), - body: Type.Object({ - channel_id: Type.Union([UUIDSchema, Type.Null()]), - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: TaskResponseSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { id } = request.params; - const { channel_id } = request.body; - - // Check if user can update this task - const existingTask = await taskRepository.findById(id); - if (!existingTask) { - throw new NotFoundError('Task not found'); - } - - const canUpdate = - request.user!.role === 'ceo' || - existingTask.assigned_to.includes(request.user!.userId) || - existingTask.created_by === request.user!.userId || - existingTask.owned_by === request.user!.userId; - - if (!canUpdate) { - throw new AuthorizationError('You do not have permission to update this task'); - } - - // If linking to a channel, verify user has access - if (channel_id) { - const hasChannelAccess = await channelRepository.canUserAccess( - channel_id, - request.user!.userId, - request.user!.role - ); - - if (!hasChannelAccess) { - throw new AuthorizationError('You do not have access to this channel'); - } - } - - const task = await taskService.updateTask(id, { channel_id }); - - // Broadcast to both old and new channels - if (existingTask.channel_id) { - await WebSocketUtils.broadcastChannelMessage({ - type: 'chat_message', - channelId: existingTask.channel_id, - messageId: `task_unlink_${task.id}`, - message: `Task "${task.title}" was unlinked from this channel`, - messageType: 'system', - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - } - - if (channel_id) { - await WebSocketUtils.broadcastChannelMessage({ - type: 'chat_message', - channelId: channel_id, - messageId: `task_link_${task.id}`, - message: `Task "${task.title}" was linked to this channel`, - messageType: 'system', - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - } - - // Also broadcast to task system - await WebSocketUtils.broadcastTaskUpdate({ - type: 'task_updated', - taskId: id, - channelId: channel_id || '', - task: { - id: task.id, - title: task.title, - ...(task.description ? { description: task.description } : {}), - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - ...(task.due_date ? { dueDate: task.due_date.toISOString() } : {}), - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'update', - changes: { channel_id }, - userId: request.user!.userId, - userName: request.user!.name, - userRole: request.user!.role, - }); - - loggers.api.info( - { - userId: request.user?.userId, - taskId: id, - oldChannelId: existingTask.channel_id, - newChannelId: channel_id, - }, - 'Task channel link updated successfully' - ); - - reply.send({ - success: true, - data: task, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to update task channel link'); - - if (error instanceof NotFoundError || error instanceof AuthorizationError) { - reply.code(error.statusCode).send(formatErrorResponse(error)); - } else if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to update task channel link', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * GET /channels/:channelId/tasks/stats - Get channel task statistics - */ - fastify.get<{ - Params: { channelId: string }; - }>( - '/channels/:channelId/tasks/stats', - { - preHandler: [authenticate, requireChannelAccess], - schema: { - params: Type.Object({ - channelId: UUIDSchema, - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: TaskStatsSchema, - timestamp: Type.String({ format: 'date-time' }), - }), - }, - }, - }, - async (request, reply) => { - try { - const { channelId } = request.params; - - // Get channel task statistics by using regular getTaskStats with channel filter - // This is a simplified implementation - in production, implement proper channel stats - const stats = await taskRepository.getTaskStats(request.user!.userId); - - loggers.api.info( - { - userId: request.user?.userId, - channelId, - stats, - }, - 'Channel task stats retrieved' - ); - - reply.send({ - success: true, - data: stats, - timestamp: new Date().toISOString(), - }); - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.userId, - email: request.user.email ?? '', - role: request.user.role, - }, - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - loggers.api.error({ error, context }, 'Failed to retrieve channel task stats'); - - reply.code(500).send({ - error: { - message: 'Failed to retrieve channel task stats', - code: 'SERVER_ERROR', - }, - }); - } - } - ); -}; diff --git a/backend/src/api/routes/UserRoutes.ts b/backend/src/api/routes/UserRoutes.ts deleted file mode 100644 index 480af43..0000000 --- a/backend/src/api/routes/UserRoutes.ts +++ /dev/null @@ -1,764 +0,0 @@ -import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; -import { Type } from '@sinclair/typebox'; -import { userRepository } from '@db/index'; -import { logger, loggers } from '@utils/logger'; -import { - ValidationError, - NotFoundError, - AuthorizationError, - formatErrorResponse, - createErrorContext -} from '@utils/errors'; -import { - authenticate, - authorize, - authorizeRoles, - requireResourceOwnership, - apiRateLimit -} from '@auth/middleware'; -import { cacheService } from '../../services/CacheService'; -import { Cacheable, CacheEvict, CacheKeyUtils } from '@utils/cache-decorators'; -import { - UUIDSchema, - EmailSchema, - UserRoleSchema, - PaginationSchema, - SuccessResponseSchema -} from '@utils/validation'; - -/** - * User Management API Routes - * Enterprise-grade user CRUD operations with caching and security - */ - -// Request/Response Schemas -const CreateUserSchema = Type.Object({ - email: EmailSchema, - password: Type.String({ minLength: 8, maxLength: 128 }), - name: Type.String({ minLength: 1, maxLength: 255 }), - role: UserRoleSchema, - department: Type.Optional(Type.String({ maxLength: 100 })), - job_title: Type.Optional(Type.String({ maxLength: 100 })), - phone: Type.Optional(Type.String({ maxLength: 20 })), - avatar_url: Type.Optional(Type.String({ format: 'uri' })), - timezone: Type.Optional(Type.String({ maxLength: 50 })), - language_preference: Type.Optional(Type.String({ maxLength: 10 })), -}); - -const UpdateUserSchema = Type.Object({ - name: Type.Optional(Type.String({ minLength: 1, maxLength: 255 })), - department: Type.Optional(Type.String({ maxLength: 100 })), - job_title: Type.Optional(Type.String({ maxLength: 100 })), - phone: Type.Optional(Type.String({ maxLength: 20 })), - avatar_url: Type.Optional(Type.String({ format: 'uri' })), - timezone: Type.Optional(Type.String({ maxLength: 50 })), - language_preference: Type.Optional(Type.String({ maxLength: 10 })), - notification_settings: Type.Optional(Type.Record(Type.String(), Type.Any())), - voice_settings: Type.Optional(Type.Record(Type.String(), Type.Any())), -}); - -const ChangePasswordSchema = Type.Object({ - currentPassword: Type.String(), - newPassword: Type.String({ minLength: 8, maxLength: 128 }), -}); - -const UserResponseSchema = Type.Object({ - id: UUIDSchema, - email: EmailSchema, - name: Type.String(), - role: UserRoleSchema, - department: Type.Optional(Type.String()), - job_title: Type.Optional(Type.String()), - phone: Type.Optional(Type.String()), - avatar_url: Type.Optional(Type.String()), - timezone: Type.Optional(Type.String()), - language_preference: Type.Optional(Type.String()), - notification_settings: Type.Optional(Type.Record(Type.String(), Type.Any())), - voice_settings: Type.Optional(Type.Record(Type.String(), Type.Any())), - email_verified: Type.Boolean(), - last_active: Type.Optional(Type.String({ format: 'date-time' })), - created_at: Type.String({ format: 'date-time' }), - updated_at: Type.String({ format: 'date-time' }), -}); - -const UserListResponseSchema = Type.Object({ - success: Type.Boolean(), - data: Type.Array(UserResponseSchema), - pagination: Type.Object({ - total: Type.Integer(), - limit: Type.Integer(), - offset: Type.Integer(), - hasMore: Type.Boolean(), - }), - timestamp: Type.String({ format: 'date-time' }), -}); - -/** - * User service class with caching decorators - */ -class UserService { - @Cacheable({ - ttl: 3600, - namespace: 'users', - keyGenerator: (userId: string) => CacheKeyUtils.userKey(userId) - }) - async getUserById(userId: string) { - return await userRepository.findById(userId); - } - - @CacheEvict({ - keys: (userId: string) => [CacheKeyUtils.userKey(userId), `${CacheKeyUtils.userKey(userId)}:profile`], - namespace: 'users' - }) - async updateUser(userId: string, updateData: any) { - return await userRepository.update(userId, updateData); - } - - @CacheEvict({ - allEntries: true, - namespace: 'users' - }) - async createUser(userData: any) { - return await userRepository.createUser(userData); - } -} - -const userService = new UserService(); - -/** - * Register user routes - */ -export const registerUserRoutes = async (fastify: FastifyInstance) => { - - /** - * GET /users - List all users (Admin/Manager only) - */ - fastify.get<{ - Querystring: typeof PaginationSchema.static & { - search?: string; - role?: string; - department?: string; - status?: string; - } - }>('/users', { - preHandler: [authenticate, authorizeRoles('ceo', 'manager'), apiRateLimit], - schema: { - querystring: Type.Intersect([ - PaginationSchema, - Type.Object({ - search: Type.Optional(Type.String({ maxLength: 100 })), - role: Type.Optional(UserRoleSchema), - department: Type.Optional(Type.String({ maxLength: 100 })), - status: Type.Optional(Type.Union([ - Type.Literal('active'), - Type.Literal('inactive'), - Type.Literal('suspended') - ])), - }) - ]), - response: { - 200: UserListResponseSchema, - 401: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String() - }) - }), - 403: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String() - }) - }) - } - } - }, async (request, reply) => { - try { - const { limit = 20, offset = 0, search, role, department, status } = request.query; - - // Build filters - const filters: any = {}; - if (search) { - filters.search = search; - } - if (role) { - filters.role = role; - } - if (department) { - filters.department = department; - } - if (status) { - filters.status = status; - } - - // Get users with pagination - const result = await userRepository.findMany({ - filters, - limit: Math.min(limit, 100), // Cap at 100 - offset, - orderBy: 'created_at', - orderDirection: 'DESC' - }); - - loggers.api.info({ - userId: request.user?.userId, - filters, - resultCount: result.data.length, - total: result.total - }, 'Users list retrieved'); - - reply.send({ - success: true, - data: result.data.map(user => ({ - ...user, - // Remove sensitive fields - password_hash: undefined, - reset_token: undefined, - verification_token: undefined, - })), - pagination: { - total: result.total, - limit, - offset, - hasMore: offset + limit < result.total - }, - timestamp: new Date().toISOString() - }); - - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record - }); - loggers.api.error({ error, context }, 'Failed to retrieve users list'); - - reply.code(500).send({ - error: { - message: 'Failed to retrieve users', - code: 'SERVER_ERROR' - } - }); - } - }); - - /** - * GET /users/:id - Get user by ID - */ - fastify.get<{ - Params: { id: string } - }>('/users/:id', { - preHandler: [authenticate, requireResourceOwnership('id')], - schema: { - params: Type.Object({ - id: UUIDSchema - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: UserResponseSchema, - timestamp: Type.String({ format: 'date-time' }) - }), - 404: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String() - }) - }) - } - } - }, async (request, reply) => { - try { - const { id } = request.params; - - // Use cached service method - const user = await userService.getUserById(id); - - if (!user) { - throw new NotFoundError('User not found'); - } - - loggers.api.info({ - userId: request.user?.userId, - requestedUserId: id - }, 'User profile retrieved'); - - reply.send({ - success: true, - data: { - ...user, - // Remove sensitive fields - password_hash: undefined, - reset_token: undefined, - verification_token: undefined, - }, - timestamp: new Date().toISOString() - }); - - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record - }); - loggers.api.error({ error, context }, 'Failed to retrieve user'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to retrieve user', - code: 'SERVER_ERROR' - } - }); - } - } - }); - - /** - * POST /users - Create new user (Admin only) - */ - fastify.post<{ - Body: typeof CreateUserSchema.static - }>('/users', { - preHandler: [authenticate, authorizeRoles('ceo'), apiRateLimit], - schema: { - body: CreateUserSchema, - response: { - 201: Type.Object({ - success: Type.Boolean(), - data: UserResponseSchema, - timestamp: Type.String({ format: 'date-time' }) - }), - 400: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - details: Type.Optional(Type.Array(Type.Object({ - field: Type.String(), - message: Type.String(), - value: Type.Optional(Type.Any()) - }))) - }) - }) - } - } - }, async (request, reply) => { - try { - const userData = { - ...request.body, - created_by: request.user?.userId, - }; - - // Create user using cached service - const user = await userService.createUser(userData); - - loggers.api.info({ - userId: request.user?.userId, - createdUserId: user.id, - userEmail: user.email, - userRole: user.role - }, 'User created successfully'); - - reply.code(201).send({ - success: true, - data: { - ...user, - password_hash: undefined, - reset_token: undefined, - verification_token: undefined, - }, - timestamp: new Date().toISOString() - }); - - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record - }); - loggers.api.error({ error, context }, 'Failed to create user'); - - if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to create user', - code: 'SERVER_ERROR' - } - }); - } - } - }); - - /** - * PUT /users/:id - Update user - */ - fastify.put<{ - Params: { id: string }; - Body: typeof UpdateUserSchema.static - }>('/users/:id', { - preHandler: [authenticate, requireResourceOwnership('id')], - schema: { - params: Type.Object({ - id: UUIDSchema - }), - body: UpdateUserSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: UserResponseSchema, - timestamp: Type.String({ format: 'date-time' }) - }) - } - } - }, async (request, reply) => { - try { - const { id } = request.params; - const updateData = request.body; - - // Update user using cached service (will evict cache) - const user = await userService.updateUser(id, updateData); - - loggers.api.info({ - userId: request.user?.userId, - updatedUserId: id, - updatedFields: Object.keys(updateData) - }, 'User updated successfully'); - - reply.send({ - success: true, - data: { - ...user, - password_hash: undefined, - reset_token: undefined, - verification_token: undefined, - }, - timestamp: new Date().toISOString() - }); - - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record - }); - loggers.api.error({ error, context }, 'Failed to update user'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to update user', - code: 'SERVER_ERROR' - } - }); - } - } - }); - - /** - * POST /users/:id/change-password - Change user password - */ - fastify.post<{ - Params: { id: string }; - Body: typeof ChangePasswordSchema.static - }>('/users/:id/change-password', { - preHandler: [authenticate, requireResourceOwnership('id')], - schema: { - params: Type.Object({ - id: UUIDSchema - }), - body: ChangePasswordSchema, - response: { - 200: SuccessResponseSchema - } - } - }, async (request, reply) => { - try { - const { id } = request.params; - const { currentPassword, newPassword } = request.body; - - // Verify current password - const user = await userRepository.findById(id); - if (!user) { - throw new NotFoundError('User not found'); - } - - const isCurrentPasswordValid = await userRepository.verifyPassword(user.email, currentPassword); - if (!isCurrentPasswordValid) { - throw new ValidationError('Current password is incorrect', [ - { field: 'currentPassword', message: 'Invalid password' } - ]); - } - - // Update password - const success = await userRepository.updatePassword(id, newPassword); - if (!success) { - throw new Error('Failed to update password'); - } - - // Clear user cache - await cacheService.users.delete(CacheKeyUtils.userKey(id)); - - loggers.api.info({ - userId: request.user?.userId, - targetUserId: id - }, 'Password changed successfully'); - - reply.send({ - success: true, - message: 'Password changed successfully', - timestamp: new Date().toISOString() - }); - - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record - }); - loggers.api.error({ error, context }, 'Failed to change password'); - - if (error instanceof NotFoundError || error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to change password', - code: 'SERVER_ERROR' - } - }); - } - } - }); - - /** - * DELETE /users/:id - Soft delete user (Admin only) - */ - fastify.delete<{ - Params: { id: string } - }>('/users/:id', { - preHandler: [authenticate, authorizeRoles('ceo')], - schema: { - params: Type.Object({ - id: UUIDSchema - }), - response: { - 200: SuccessResponseSchema - } - } - }, async (request, reply) => { - try { - const { id } = request.params; - - // Prevent self-deletion - if (id === request.user?.userId) { - throw new ValidationError('Cannot delete your own account', []); - } - - const success = await userRepository.softDelete(id, request.user!.userId); - if (!success) { - throw new NotFoundError('User not found'); - } - - // Clear user cache - await cacheService.users.delete(CacheKeyUtils.userKey(id)); - - loggers.api.info({ - userId: request.user?.userId, - deletedUserId: id - }, 'User deleted successfully'); - - reply.send({ - success: true, - message: 'User deleted successfully', - timestamp: new Date().toISOString() - }); - - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record - }); - loggers.api.error({ error, context }, 'Failed to delete user'); - - if (error instanceof NotFoundError || error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to delete user', - code: 'SERVER_ERROR' - } - }); - } - } - }); - - /** - * GET /users/:id/stats - Get user statistics (Admin/Manager only) - */ - fastify.get<{ - Params: { id: string } - }>('/users/:id/stats', { - preHandler: [authenticate, authorizeRoles('ceo', 'manager')], - schema: { - params: Type.Object({ - id: UUIDSchema - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Object({ - userId: UUIDSchema, - taskStats: Type.Object({ - total: Type.Integer(), - completed: Type.Integer(), - inProgress: Type.Integer(), - overdue: Type.Integer(), - }), - channelStats: Type.Object({ - memberships: Type.Integer(), - messagesCount: Type.Integer(), - }), - activityStats: Type.Object({ - lastActive: Type.Optional(Type.String({ format: 'date-time' })), - totalSessions: Type.Integer(), - avgSessionDuration: Type.Number(), - }), - }), - timestamp: Type.String({ format: 'date-time' }) - }) - } - } - }, async (request, reply) => { - try { - const { id } = request.params; - - // Get user stats from repositories - const [user, taskStats] = await Promise.all([ - userRepository.findById(id), - // taskRepository.getTaskStats(id), // TODO: Implement - Promise.resolve({ - total: 0, - completed: 0, - inProgress: 0, - overdue: 0, - }) - ]); - - if (!user) { - throw new NotFoundError('User not found'); - } - - // TODO: Get channel stats, activity stats from respective repositories - - const stats = { - userId: id, - taskStats, - channelStats: { - memberships: 0, - messagesCount: 0, - }, - activityStats: { - lastActive: user.last_active?.toISOString(), - totalSessions: 0, - avgSessionDuration: 0, - }, - }; - - loggers.api.info({ - userId: request.user?.userId, - requestedUserId: id - }, 'User stats retrieved'); - - reply.send({ - success: true, - data: stats, - timestamp: new Date().toISOString() - }); - - } catch (error) { - const context = createErrorContext({ - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role - } - }), - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record - }); - loggers.api.error({ error, context }, 'Failed to get user stats'); - - if (error instanceof NotFoundError) { - reply.code(404).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to get user stats', - code: 'SERVER_ERROR' - } - }); - } - } - }); -}; \ No newline at end of file diff --git a/backend/src/auth/jwt.ts b/backend/src/auth/jwt.ts deleted file mode 100644 index 82c23cd..0000000 --- a/backend/src/auth/jwt.ts +++ /dev/null @@ -1,449 +0,0 @@ -import jwt from 'jsonwebtoken'; -import { config } from '@config/index'; -import { logger, securityLogger } from '@utils/logger'; -import { AuthenticationError, TokenExpiredError, InvalidTokenError } from '@utils/errors'; -import crypto from 'crypto'; - -/** - * JWT Authentication system with refresh token support - * Enterprise-grade security with proper token management - */ - -export interface TokenPayload { - userId: string; - email: string; - role: 'ceo' | 'manager' | 'staff'; - name: string; - permissions: string[]; - sessionId: string; - type: 'access' | 'refresh'; - iat: number; - exp: number; -} - -export interface TokenPair { - accessToken: string; - refreshToken: string; - expiresIn: number; - refreshExpiresIn: number; -} - -export interface AccessTokenData { - token: string; - payload: TokenPayload; - expiresAt: Date; -} - -/** - * Role-based permissions mapping - */ -const ROLE_PERMISSIONS: Record = { - ceo: [ - // Wildcard permission - CEO has access to everything - '*' - ], - manager: [ - // Channel management within scope - 'channels:create', 'channels:read', 'channels:update', - 'channels:manage_members', - // Task management - 'tasks:create', 'tasks:read', 'tasks:update', - 'tasks:assign', 'tasks:manage_dependencies', - // Limited user management - 'users:read', 'users:update_profile', - // Voice features (limited) - 'voice:commands', - // Analytics (read-only) - 'analytics:read' - ], - staff: [ - // Basic channel access - 'channels:read', 'channels:participate', - // Task participation - 'tasks:read', 'tasks:update_own', 'tasks:comment', - // Profile management - 'users:read_own', 'users:update_own_profile', - // No voice commands (CEO-only feature) - ] -}; - -/** - * Generate cryptographically secure session ID - */ -const generateSessionId = (): string => { - return crypto.randomBytes(32).toString('hex'); -}; - -/** - * Convert time string to seconds - */ -const parseTimeToSeconds = (timeStr: string): number => { - const unit = timeStr.slice(-1); - const value = parseInt(timeStr.slice(0, -1)); - - switch (unit) { - case 's': return value; - case 'm': return value * 60; - case 'h': return value * 3600; - case 'd': return value * 86400; - default: return value; - } -}; - -/** - * JWT service class - */ -class JWTService { - private readonly accessTokenSecret: string; - private readonly refreshTokenSecret: string; - private readonly accessTokenExpiry: string; - private readonly refreshTokenExpiry: string; - - constructor() { - this.accessTokenSecret = config.jwt.secret; - this.refreshTokenSecret = config.jwt.refreshSecret; - this.accessTokenExpiry = config.jwt.expiresIn; - this.refreshTokenExpiry = config.jwt.refreshExpiresIn; - } - - /** - * Generate access and refresh token pair - */ - async generateTokens(user: { - id: string; - email: string; - role: 'ceo' | 'manager' | 'staff'; - name: string; - }): Promise { - const sessionId = generateSessionId(); - const permissions = ROLE_PERMISSIONS[user.role] || []; - - const basePayload = { - userId: user.id, - email: user.email, - role: user.role, - name: user.name, - permissions, - sessionId - }; - - // Generate access token - const accessToken = jwt.sign( - { ...basePayload, type: 'access' }, - this.accessTokenSecret, - { - expiresIn: this.accessTokenExpiry, - issuer: 'ceo-platform', - audience: 'ceo-platform-api', - subject: user.id - } as jwt.SignOptions - ); - - // Generate refresh token - const refreshToken = jwt.sign( - { ...basePayload, type: 'refresh' }, - this.refreshTokenSecret, - { - expiresIn: this.refreshTokenExpiry, - issuer: 'ceo-platform', - audience: 'ceo-platform-api', - subject: user.id - } as jwt.SignOptions - ); - - const expiresIn = parseTimeToSeconds(this.accessTokenExpiry); - const refreshExpiresIn = parseTimeToSeconds(this.refreshTokenExpiry); - - securityLogger.logAuthEvent('token_generated', { - userId: user.id, - email: user.email, - role: user.role, - sessionId, - expiresIn, - refreshExpiresIn - }); - - return { - accessToken, - refreshToken, - expiresIn, - refreshExpiresIn - }; - } - - /** - * Verify and decode access token - */ - async verifyAccessToken(token: string): Promise { - try { - const payload = jwt.verify(token, this.accessTokenSecret, { - issuer: 'ceo-platform', - audience: 'ceo-platform-api' - }) as TokenPayload; - - if (payload.type !== 'access') { - throw new InvalidTokenError('Token type mismatch - expected access token'); - } - - return payload; - } catch (error) { - if (error instanceof jwt.TokenExpiredError) { - throw new TokenExpiredError('Access token has expired'); - } else if (error instanceof jwt.JsonWebTokenError) { - throw new InvalidTokenError(`Invalid access token: ${error.message}`); - } else { - logger.error({ error }, 'Unexpected error verifying access token'); - throw new AuthenticationError('Token verification failed'); - } - } - } - - /** - * Verify and decode refresh token - */ - async verifyRefreshToken(token: string): Promise { - try { - const payload = jwt.verify(token, this.refreshTokenSecret, { - issuer: 'ceo-platform', - audience: 'ceo-platform-api' - }) as TokenPayload; - - if (payload.type !== 'refresh') { - throw new InvalidTokenError('Token type mismatch - expected refresh token'); - } - - return payload; - } catch (error) { - if (error instanceof jwt.TokenExpiredError) { - securityLogger.logAuthEvent('refresh_token_expired', { - error: error.message - }); - throw new TokenExpiredError('Refresh token has expired'); - } else if (error instanceof jwt.JsonWebTokenError) { - securityLogger.logSecurityViolation('invalid_refresh_token', { - error: error.message, - token: token.substring(0, 20) + '...' - }); - throw new InvalidTokenError(`Invalid refresh token: ${error.message}`); - } else { - logger.error({ error }, 'Unexpected error verifying refresh token'); - throw new AuthenticationError('Refresh token verification failed'); - } - } - } - - /** - * Refresh access token using refresh token - */ - async refreshTokens(refreshToken: string): Promise { - const payload = await this.verifyRefreshToken(refreshToken); - - // Generate new token pair with same user data - const user = { - id: payload.userId, - email: payload.email, - role: payload.role, - name: payload.name - }; - - const newTokens = await this.generateTokens(user); - - securityLogger.logAuthEvent('token_refreshed', { - userId: payload.userId, - email: payload.email, - oldSessionId: payload.sessionId, - newSessionId: 'generated' // New session ID is in the new tokens - }); - - return newTokens; - } - - /** - * Decode token without verification (for debugging) - */ - decodeToken(token: string): TokenPayload | null { - try { - const payload = jwt.decode(token) as TokenPayload; - return payload; - } catch (error) { - logger.warn({ error }, 'Failed to decode token'); - return null; - } - } - - /** - * Extract token from Authorization header - */ - extractTokenFromHeader(authHeader: string | undefined): string | null { - if (!authHeader || typeof authHeader !== 'string') { - return null; - } - - const parts = authHeader.split(' '); - if (parts.length !== 2 || parts[0] !== 'Bearer') { - return null; - } - - return parts[1] || null; - } - - /** - * Check if user has required permission - */ - hasPermission(payload: TokenPayload, requiredPermission: string): boolean { - // Check for wildcard permission first (CEO has all permissions) - if (payload.permissions.includes('*')) { - return true; - } - return payload.permissions.includes(requiredPermission); - } - - /** - * Check if user has any of the required permissions - */ - hasAnyPermission(payload: TokenPayload, requiredPermissions: string[]): boolean { - // Check for wildcard permission first (CEO has all permissions) - if (payload.permissions.includes('*')) { - return true; - } - return requiredPermissions.some(permission => - payload.permissions.includes(permission) - ); - } - - /** - * Check if user has all required permissions - */ - hasAllPermissions(payload: TokenPayload, requiredPermissions: string[]): boolean { - // Check for wildcard permission first (CEO has all permissions) - if (payload.permissions.includes('*')) { - return true; - } - return requiredPermissions.every(permission => - payload.permissions.includes(permission) - ); - } - - /** - * Get token expiration date - */ - getTokenExpiration(token: string): Date | null { - const payload = this.decodeToken(token); - if (!payload || !payload.exp) { - return null; - } - - return new Date(payload.exp * 1000); - } - - /** - * Check if token is expired - */ - isTokenExpired(token: string): boolean { - const expiration = this.getTokenExpiration(token); - if (!expiration) { - return true; - } - - return expiration.getTime() < Date.now(); - } - - /** - * Generate password reset token (separate from auth tokens) - */ - generatePasswordResetToken(userId: string): string { - return jwt.sign( - { userId, type: 'password_reset' }, - this.accessTokenSecret + userId, // Include user ID in secret for security - { expiresIn: '1h' } - ); - } - - /** - * Verify password reset token - */ - verifyPasswordResetToken(token: string, userId: string): boolean { - try { - const payload = jwt.verify( - token, - this.accessTokenSecret + userId - ) as any; - - return payload.userId === userId && payload.type === 'password_reset'; - } catch (error) { - return false; - } - } - - /** - * Generate email verification token - */ - generateEmailVerificationToken(userId: string, email: string): string { - return jwt.sign( - { userId, email, type: 'email_verification' }, - this.accessTokenSecret + email, - { expiresIn: '24h' } - ); - } - - /** - * Verify email verification token - */ - verifyEmailVerificationToken(token: string, email: string): { userId: string } | null { - try { - const payload = jwt.verify( - token, - this.accessTokenSecret + email - ) as any; - - if (payload.email === email && payload.type === 'email_verification') { - return { userId: payload.userId }; - } - - return null; - } catch (error) { - return null; - } - } -} - -// Export singleton instance -export const jwtService = new JWTService(); -export default jwtService; - -// Export permission constants -export const PERMISSIONS = { - // Channel permissions - CHANNELS_CREATE: 'channels:create', - CHANNELS_READ: 'channels:read', - CHANNELS_UPDATE: 'channels:update', - CHANNELS_DELETE: 'channels:delete', - CHANNELS_MANAGE_MEMBERS: 'channels:manage_members', - CHANNELS_ARCHIVE: 'channels:archive', - - // Task permissions - TASKS_CREATE: 'tasks:create', - TASKS_READ: 'tasks:read', - TASKS_UPDATE: 'tasks:update', - TASKS_DELETE: 'tasks:delete', - TASKS_ASSIGN: 'tasks:assign', - TASKS_MANAGE_DEPENDENCIES: 'tasks:manage_dependencies', - - // User permissions - USERS_CREATE: 'users:create', - USERS_READ: 'users:read', - USERS_UPDATE: 'users:update', - USERS_DELETE: 'users:delete', - USERS_MANAGE_ROLES: 'users:manage_roles', - - // Voice permissions - VOICE_COMMANDS: 'voice:commands', - VOICE_TRANSCRIBE: 'voice:transcribe', - VOICE_PROCESS: 'voice:process', - - // System permissions - SYSTEM_ADMIN: 'system:admin', - ANALYTICS_READ: 'analytics:read', -} as const; - -export { ROLE_PERMISSIONS }; \ No newline at end of file diff --git a/backend/src/auth/middleware.ts b/backend/src/auth/middleware.ts deleted file mode 100644 index 4f7034e..0000000 --- a/backend/src/auth/middleware.ts +++ /dev/null @@ -1,440 +0,0 @@ -import { FastifyRequest, FastifyReply } from 'fastify'; -import { jwtService, TokenPayload, PERMISSIONS } from './jwt'; -import { userRepository } from '@db/index'; -import { logger, securityLogger } from '@utils/logger'; -import { AuthenticationError, AuthorizationError, RateLimitError } from '@utils/errors'; - -/** - * Authentication and Authorization middleware for Fastify - * Enterprise-grade security with rate limiting and audit logging - */ - -// Extend Fastify request to include user context -declare module 'fastify' { - interface FastifyRequest { - user?: - | (TokenPayload & { - isAuthenticated: boolean; - id: string; // Alias for userId for compatibility - }) - | undefined; - } -} - -/** - * Rate limiting store (in production, use Redis) - */ -const rateLimitStore = new Map(); - -/** - * Clean up expired rate limit entries - */ -const cleanupRateLimit = () => { - const now = Date.now(); - for (const [key, value] of rateLimitStore.entries()) { - if (now > value.resetTime) { - rateLimitStore.delete(key); - } - } -}; - -// Cleanup every 5 minutes -setInterval(cleanupRateLimit, 5 * 60 * 1000); - -/** - * Authentication middleware - validates JWT tokens - */ -export const authenticate = async (request: FastifyRequest, reply: FastifyReply): Promise => { - try { - const authHeader = request.headers.authorization; - const token = jwtService.extractTokenFromHeader(authHeader); - - if (!token) { - securityLogger.logAuthEvent('missing_token', { - ip: request.ip, - userAgent: request.headers['user-agent'] ?? '', - path: request.url, - method: request.method, - }); - - throw new AuthenticationError('Access token required'); - } - - // Verify token - const payload = await jwtService.verifyAccessToken(token); - - // Additional security: verify user still exists and is active - const user = await userRepository.findById(payload.userId); - if (!user) { - securityLogger.logSecurityViolation('token_user_not_found', { - userId: payload.userId, - ip: request.ip, - }); - - throw new AuthenticationError('User account not found'); - } - - if (user.deleted_at) { - securityLogger.logSecurityViolation('deleted_user_token', { - userId: payload.userId, - ip: request.ip, - }); - - throw new AuthenticationError('User account has been deactivated'); - } - - // Update user's last active timestamp (async, don't wait) - userRepository.updateLastActive(payload.userId).catch((error) => { - logger.warn?.({ error, userId: payload.userId }, 'Failed to update last active timestamp'); - }); - - // Attach user to request - request.user = { - ...payload, - isAuthenticated: true, - id: payload.userId, // Add id alias for compatibility - }; - - securityLogger.logAuthEvent('authentication_success', { - userId: payload.userId, - email: payload.email, - role: payload.role, - ip: request.ip, - path: request.url, - method: request.method, - }); - } catch (error) { - if (error instanceof AuthenticationError) { - throw error; - } - - logger.error({ error }, 'Authentication middleware error'); - throw new AuthenticationError('Authentication failed'); - } -}; - -/** - * Optional authentication middleware - doesn't throw if no token - */ -export const optionalAuthenticate = async ( - request: FastifyRequest, - reply: FastifyReply -): Promise => { - try { - await authenticate(request, reply); - } catch (error) { - // Don't throw, just log the attempt - logger.debug( - { error: error instanceof Error ? error.message : String(error) }, - 'Optional authentication failed' - ); - request.user = undefined; - } -}; - -/** - * Authorization middleware factory - checks permissions - */ -export const authorize = (...requiredPermissions: string[]) => { - return async (request: FastifyRequest, reply: FastifyReply): Promise => { - if (!request.user || !request.user.isAuthenticated) { - throw new AuthenticationError('Authentication required for this resource'); - } - - const hasPermission = - requiredPermissions.length === 0 || - jwtService.hasAnyPermission(request.user, requiredPermissions); - - if (!hasPermission) { - securityLogger.logAuthzEvent('access_denied', { - userId: request.user.userId, - requiredPermissions, - userPermissions: request.user.permissions, - resource: request.url, - action: request.method, - ip: request.ip, - }); - - throw new AuthorizationError( - `Insufficient permissions. Required: ${requiredPermissions.join(', ')}` - ); - } - - securityLogger.logAuthzEvent('access_granted', { - userId: request.user.userId, - resource: request.url, - action: request.method, - ip: request.ip, - }); - }; -}; - -/** - * Role-based authorization middleware - */ -export const authorizeRoles = (...allowedRoles: Array<'ceo' | 'manager' | 'staff'>) => { - return async (request: FastifyRequest, reply: FastifyReply): Promise => { - if (!request.user || !request.user.isAuthenticated) { - throw new AuthenticationError('Authentication required for this resource'); - } - - if (!allowedRoles.includes(request.user.role)) { - securityLogger.logAuthzEvent('access_denied', { - userId: request.user.userId, - userRole: request.user.role, - allowedRoles, - resource: request.url, - action: request.method, - ip: request.ip, - }); - - throw new AuthorizationError(`Access denied. Required roles: ${allowedRoles.join(', ')}`); - } - - securityLogger.logAuthzEvent('access_granted', { - userId: request.user.userId, - resource: request.url, - action: request.method, - ip: request.ip, - }); - }; -}; - -/** - * CEO-only middleware - */ -export const requireCEO = authorizeRoles('ceo'); - -/** - * Manager or CEO middleware - */ -export const requireManagerOrCEO = authorizeRoles('ceo', 'manager'); - -/** - * Voice commands authorization (CEO only feature) - */ -export const authorizeVoiceCommands = authorize(PERMISSIONS.VOICE_COMMANDS); - -/** - * Rate limiting middleware - */ -export const rateLimit = (options: { - maxRequests: number; - windowMs: number; - keyGenerator?: (request: FastifyRequest) => string; - skipSuccessful?: boolean; - skipFailedRequests?: boolean; -}) => { - const { - maxRequests, - windowMs, - keyGenerator = (req) => req.ip, - skipSuccessful = false, - skipFailedRequests = false, - } = options; - - return async (request: FastifyRequest, reply: FastifyReply): Promise => { - const key = keyGenerator(request); - const now = Date.now(); - const resetTime = now + windowMs; - - let rateLimitInfo = rateLimitStore.get(key); - - if (!rateLimitInfo || now > rateLimitInfo.resetTime) { - rateLimitInfo = { count: 0, resetTime }; - rateLimitStore.set(key, rateLimitInfo); - } - - rateLimitInfo.count++; - - if (rateLimitInfo.count > maxRequests) { - securityLogger.logSecurityViolation('rate_limit_exceeded', { - key, - ip: request.ip, - path: request.url, - count: rateLimitInfo.count, - maxRequests, - userId: request.user?.userId, - }); - - const retryAfter = Math.ceil((rateLimitInfo.resetTime - now) / 1000); - - reply.header('Retry-After', retryAfter.toString()); - reply.header('X-RateLimit-Limit', maxRequests.toString()); - reply.header('X-RateLimit-Remaining', '0'); - reply.header('X-RateLimit-Reset', Math.ceil(rateLimitInfo.resetTime / 1000).toString()); - - throw new RateLimitError('Rate limit exceeded. Too many requests.', retryAfter, { - maxRequests, - windowMs, - currentCount: rateLimitInfo.count, - }); - } - - // Set rate limit headers - const remaining = Math.max(0, maxRequests - rateLimitInfo.count); - reply.header('X-RateLimit-Limit', maxRequests.toString()); - reply.header('X-RateLimit-Remaining', remaining.toString()); - reply.header('X-RateLimit-Reset', Math.ceil(rateLimitInfo.resetTime / 1000).toString()); - }; -}; - -/** - * Strict rate limiting for authentication endpoints - */ -export const authRateLimit = rateLimit({ - maxRequests: 5, // 5 attempts - windowMs: 15 * 60 * 1000, // 15 minutes - keyGenerator: (req) => `auth:${req.ip}`, -}); - -/** - * Voice processing rate limiting - */ -export const voiceRateLimit = rateLimit({ - maxRequests: 60, // 60 commands per hour - windowMs: 60 * 60 * 1000, // 1 hour - keyGenerator: (req) => `voice:${req.user?.userId || req.ip}`, -}); - -/** - * API rate limiting for general endpoints - */ -export const apiRateLimit = rateLimit({ - maxRequests: 1000, // 1000 requests per hour - windowMs: 60 * 60 * 1000, // 1 hour - keyGenerator: (req) => `api:${req.user?.userId || req.ip}`, -}); - -/** - * Resource ownership middleware - ensures user can only access their own resources - */ -export const requireResourceOwnership = (userIdParam: string = 'userId') => { - return async (request: FastifyRequest, reply: FastifyReply): Promise => { - if (!request.user || !request.user.isAuthenticated) { - throw new AuthenticationError('Authentication required'); - } - - // CEO can access any resource - if (request.user.role === 'ceo') { - return; - } - - const requestedUserId = (request.params as any)[userIdParam]; - - if (!requestedUserId) { - throw new AuthorizationError('Resource user ID not specified'); - } - - if (request.user.userId !== requestedUserId) { - securityLogger.logAuthzEvent('access_denied', { - userId: request.user.userId, - requestedUserId, - resource: request.url, - action: request.method, - ip: request.ip, - reason: 'resource_ownership_violation', - }); - - throw new AuthorizationError('You can only access your own resources'); - } - }; -}; - -/** - * Channel membership middleware - ensures user has access to channel - */ -export const requireChannelAccess = async ( - request: FastifyRequest, - reply: FastifyReply -): Promise => { - if (!request.user || !request.user.isAuthenticated) { - throw new AuthenticationError('Authentication required'); - } - - // Check for channel ID in different parameter names used in routes - const channelId = (request.params as any).channelId || - (request.params as any).id || - (request.body as any)?.channelId; - - if (!channelId) { - throw new AuthorizationError('Channel ID not specified'); - } - - // CEO can access any channel - if (request.user.role === 'ceo') { - return; - } - - try { - const { channelRepository } = await import('@db/index'); - const hasAccess = await channelRepository.canUserAccess( - channelId, - request.user.userId, - request.user.role - ); - - if (!hasAccess) { - securityLogger.logAuthzEvent('access_denied', { - userId: request.user.userId, - channelId, - resource: request.url, - action: request.method, - ip: request.ip, - reason: 'channel_access_denied', - }); - - throw new AuthorizationError('You do not have access to this channel'); - } - } catch (error) { - if (error instanceof AuthorizationError) { - throw error; - } - - logger.error( - { error, channelId, userId: request.user.userId }, - 'Error checking channel access' - ); - throw new AuthorizationError('Unable to verify channel access'); - } -}; - -/** - * Audit logging middleware - logs all requests for security audit - */ -export const auditLog = async (request: FastifyRequest, reply: FastifyReply): Promise => { - const startTime = Date.now(); - - // Log request - logger.info( - { - method: request.method, - url: request.url, - ip: request.ip, - userAgent: request.headers['user-agent'], - userId: request.user?.userId, - userRole: request.user?.role, - timestamp: new Date().toISOString(), - }, - 'API Request' - ); - - // Log response when request completes - request.server.addHook('onSend', async (request, reply, payload) => { - const duration = Date.now() - startTime; - - logger.info( - { - method: request.method, - url: request.url, - statusCode: reply.statusCode, - duration, - userId: request.user?.userId, - timestamp: new Date().toISOString(), - }, - 'API Response' - ); - - return payload; - }); -}; diff --git a/backend/src/auth/routes.ts b/backend/src/auth/routes.ts deleted file mode 100644 index 35a3b36..0000000 --- a/backend/src/auth/routes.ts +++ /dev/null @@ -1,813 +0,0 @@ -import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify'; -import { jwtService } from './jwt'; -import { userRepository } from '@db/index'; -import { logger, securityLogger } from '@utils/logger'; -import { - AuthenticationError, - ValidationError, - ConflictError, - createErrorContext, - formatErrorResponse, -} from '@utils/errors'; -import { authRateLimit } from './middleware'; -import { Type, Static } from '@sinclair/typebox'; -import crypto from 'crypto'; -import { emailService } from '@/services/EmailService'; - -/** - * Authentication routes for the CEO Communication Platform - * Includes login, registration, password reset, and token management - */ - -// Request/Response schemas -const LoginSchema = Type.Object({ - email: Type.String({ format: 'email' }), - password: Type.String({ minLength: 1 }), -}); - -const RegisterSchema = Type.Object({ - email: Type.String({ format: 'email' }), - password: Type.String({ minLength: 8 }), - name: Type.String({ minLength: 1, maxLength: 255 }), - role: Type.Union([Type.Literal('ceo'), Type.Literal('manager'), Type.Literal('staff')]), - department: Type.Optional(Type.String()), - job_title: Type.Optional(Type.String()), - phone: Type.Optional(Type.String()), -}); - -const RefreshTokenSchema = Type.Object({ - refreshToken: Type.String(), -}); - -const PasswordResetRequestSchema = Type.Object({ - email: Type.String({ format: 'email' }), -}); - -const PasswordResetSchema = Type.Object({ - token: Type.String(), - newPassword: Type.String({ minLength: 8 }), -}); - -const ChangePasswordSchema = Type.Object({ - currentPassword: Type.String(), - newPassword: Type.String({ minLength: 8 }), -}); - -type LoginRequest = Static; -type RegisterRequest = Static; -type RefreshTokenRequest = Static; -type PasswordResetRequestRequest = Static; -type PasswordResetRequest = Static; - -/** - * Register authentication routes - */ -export const registerAuthRoutes = async (fastify: FastifyInstance) => { - /** - * POST /auth/login - User login - */ - fastify.post<{ Body: LoginRequest }>( - '/auth/login', - { - preHandler: [authRateLimit], - schema: { - tags: ['Authentication'], - summary: 'User Login', - description: 'Authenticate user with email and password to receive JWT tokens', - body: LoginSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Object({ - accessToken: Type.String(), - refreshToken: Type.String(), - expiresIn: Type.Number(), - user: Type.Object({ - id: Type.String(), - email: Type.String(), - name: Type.String(), - role: Type.String(), - avatar_url: Type.Optional(Type.String()), - email_verified: Type.Boolean(), - permissions: Type.Array(Type.String()), - }), - }), - }), - 400: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - 401: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request: FastifyRequest<{ Body: LoginRequest }>, reply: FastifyReply) => { - const { email, password } = request.body; - const context = createErrorContext({ - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - }); - - try { - securityLogger.logAuthEvent('login_attempt', { - email, - ip: request.ip, - userAgent: request.headers['user-agent'] ?? '', - }); - - // Verify credentials - const user = await userRepository.verifyPassword(email, password); - - if (!user) { - securityLogger.logAuthEvent('failed_login', { - email, - ip: request.ip, - reason: 'invalid_credentials', - }); - - throw new AuthenticationError('Invalid email or password'); - } - - // Log if user is not verified (but don't prevent login) - if (!user.email_verified) { - securityLogger.logAuthEvent('login_attempt', { - email, - ip: request.ip, - reason: 'email_not_verified', - }); - } - - // Generate tokens - const tokens = await jwtService.generateTokens({ - id: user.id, - email: user.email, - role: user.role, - name: user.name, - }); - - securityLogger.logAuthEvent('login', { - userId: user.id, - email: user.email, - ip: request.ip, - userAgent: request.headers['user-agent'] ?? '', - }); - - const responseData = { - success: true, - data: { - ...tokens, - user: { - id: user.id, - email: user.email, - name: user.name, - role: user.role, - avatar_url: user.avatar_url, - email_verified: user.email_verified, - permissions: jwtService.decodeToken(tokens.accessToken)?.permissions || [], - }, - }, - }; - - logger.info({ - responseData: JSON.stringify(responseData).substring(0, 200) + '...', - contentLength: JSON.stringify(responseData).length, - userId: user.id - }, 'Sending login response'); - - reply.code(200).send(responseData); - } catch (error) { - logger.error({ error, context }, 'Login failed'); - - if (error instanceof AuthenticationError || error instanceof ValidationError) { - reply.code(401).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Login failed due to server error', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * POST /auth/register - User registration - */ - fastify.post<{ Body: RegisterRequest }>( - '/auth/register', - { - preHandler: [authRateLimit], - schema: { - tags: ['Authentication'], - summary: 'User Registration', - description: 'Register a new user account with email verification', - body: RegisterSchema, - response: { - 201: Type.Object({ - success: Type.Boolean(), - data: Type.Object({ - user: Type.Object({ - id: Type.String(), - email: Type.String(), - name: Type.String(), - role: Type.String(), - }), - message: Type.String(), - }), - }), - 400: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - 409: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request: FastifyRequest<{ Body: RegisterRequest }>, reply: FastifyReply) => { - const userData = request.body; - const context = createErrorContext({ - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - }); - - try { - securityLogger.logAuthEvent('registration_attempt', { - email: userData.email, - role: userData.role, - ip: request.ip, - }); - - // Create user - const user = await userRepository.createUser({ - ...userData, - // Self-registration: do not set created_by - }); - - // Generate email verification token - const verificationToken = jwtService.generateEmailVerificationToken(user.id, user.email); - await userRepository.setEmailVerificationToken(user.id, verificationToken); - - // Send verification email - const emailSent = await emailService.sendEmailVerification({ - userEmail: user.email, - userName: user.name, - verificationToken, - }); - - if (!emailSent) { - logger.warn({ userId: user.id, email: user.email }, 'Failed to send verification email'); - } - - securityLogger.logAuthEvent('registration_success', { - userId: user.id, - email: user.email, - role: user.role, - ip: request.ip, - emailSent, - }); - - reply.code(201).send({ - success: true, - data: { - user: { - id: user.id, - email: user.email, - name: user.name, - role: user.role, - }, - message: - 'Account created successfully. Please check your email for verification instructions.', - }, - }); - } catch (error) { - logger.error({ error, context }, 'Registration failed'); - - if (error instanceof ConflictError) { - reply.code(409).send(formatErrorResponse(error)); - } else if (error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Registration failed due to server error', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * POST /auth/refresh - Refresh access token - */ - fastify.post<{ Body: RefreshTokenRequest }>( - '/auth/refresh', - { - schema: { - body: RefreshTokenSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Object({ - accessToken: Type.String(), - refreshToken: Type.String(), - expiresIn: Type.Number(), - }), - }), - 401: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request: FastifyRequest<{ Body: RefreshTokenRequest }>, reply: FastifyReply) => { - const { refreshToken } = request.body; - const context = createErrorContext({ - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - ...(request.user && { - user: { - id: request.user.id, - email: request.user.email, - role: request.user.role, - }, - }), - }); - - try { - const tokens = await jwtService.refreshTokens(refreshToken); - - reply.code(200).send({ - success: true, - data: tokens, - }); - } catch (error) { - logger.error({ error, context }, 'Token refresh failed'); - - if (error instanceof AuthenticationError) { - reply.code(401).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Token refresh failed', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * POST /auth/logout - User logout (invalidate tokens - would need Redis blacklist in production) - */ - fastify.post('/auth/logout', async (request: FastifyRequest, reply: FastifyReply) => { - // In production, add token to blacklist in Redis - // For now, just log the logout - - if (request.user) { - securityLogger.logAuthEvent('logout', { - userId: request.user.userId, - ip: request.ip, - }); - } - - reply.code(200).send({ - success: true, - message: 'Logged out successfully', - }); - }); - - /** - * POST /auth/password-reset-request - Request password reset - */ - fastify.post<{ Body: PasswordResetRequestRequest }>( - '/auth/password-reset-request', - { - preHandler: [authRateLimit], - schema: { - body: PasswordResetRequestSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - message: Type.String(), - }), - }, - }, - }, - async (request: FastifyRequest<{ Body: PasswordResetRequestRequest }>, reply: FastifyReply) => { - const { email } = request.body; - - try { - // Generate reset token - const resetToken = crypto.randomBytes(32).toString('hex'); - - // Save token (expires in 1 hour) - const tokenSet = await userRepository.setPasswordResetToken(email, resetToken, '1 hour'); - - if (tokenSet) { - // Get user details for the email - const user = await userRepository.findByEmail(email); - - if (user) { - // Send password reset email - const emailSent = await emailService.sendPasswordReset({ - userEmail: user.email, - userName: user.name, - resetToken, - }); - - if (!emailSent) { - logger.warn({ email, userId: user.id }, 'Failed to send password reset email'); - } - - securityLogger.logAuthEvent('password_reset_requested', { - email, - userId: user.id, - ip: request.ip, - emailSent, - }); - } - - logger.info({ email, resetToken }, 'Password reset token generated'); - } - - // Always return success (don't reveal if email exists) - reply.code(200).send({ - success: true, - message: 'If the email address exists, you will receive password reset instructions.', - }); - } catch (error) { - logger.error({ error, email }, 'Password reset request failed'); - - // Still return success to prevent email enumeration - reply.code(200).send({ - success: true, - message: 'If the email address exists, you will receive password reset instructions.', - }); - } - } - ); - - /** - * POST /auth/password-reset - Reset password with token - */ - fastify.post<{ Body: PasswordResetRequest }>( - '/auth/password-reset', - { - preHandler: [authRateLimit], - schema: { - body: PasswordResetSchema, - response: { - 200: Type.Object({ - success: Type.Boolean(), - message: Type.String(), - }), - 400: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request: FastifyRequest<{ Body: PasswordResetRequest }>, reply: FastifyReply) => { - const { token, newPassword } = request.body; - const context = createErrorContext({ - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - ...(request.user && - typeof request.user === 'object' && - 'email' in request.user && - 'role' in request.user - ? { - user: { - id: request.user.id, - email: (request.user as any).email, - role: (request.user as any).role, - }, - } - : {}), - }); - - try { - // Verify token - const user = await userRepository.verifyPasswordResetToken(token); - - if (!user) { - throw new AuthenticationError('Invalid or expired password reset token'); - } - - // Update password - const success = await userRepository.updatePassword(user.id, newPassword); - - if (!success) { - throw new ValidationError('Failed to update password', []); - } - - securityLogger.logAuthEvent('password_reset', { - userId: user.id, - email: user.email, - ip: request.ip, - }); - - reply.code(200).send({ - success: true, - message: - 'Password has been reset successfully. You can now log in with your new password.', - }); - } catch (error) { - logger.error({ error, context }, 'Password reset failed'); - - if (error instanceof AuthenticationError || error instanceof ValidationError) { - reply.code(400).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Password reset failed', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * GET /auth/verify-email/:token - Verify email address - */ - fastify.get<{ Params: { token: string } }>( - '/auth/verify-email/:token', - async (request, reply) => { - const { token } = request.params; - - try { - const user = await userRepository.verifyEmail(token); - - if (!user) { - throw new AuthenticationError('Invalid or expired email verification token'); - } - - securityLogger.logAuthEvent('email_verified', { - userId: user.id, - email: user.email, - ip: request.ip, - }); - - reply.code(200).send({ - success: true, - message: 'Email address verified successfully. You can now log in.', - }); - } catch (error) { - logger.error({ error, token }, 'Email verification failed'); - - reply.code(400).send({ - error: { - message: 'Invalid or expired verification token', - code: 'INVALID_TOKEN', - }, - }); - } - } - ); - - /** - * GET /auth/me - Get current user profile - */ - fastify.get( - '/auth/me', - { - preHandler: [fastify.authenticate], - schema: { - tags: ['Authentication'], - summary: 'Get Current User', - description: 'Get the profile information of the currently authenticated user', - security: [{ BearerAuth: [] }], - response: { - 200: Type.Object({ - success: Type.Boolean(), - data: Type.Object({ - user: Type.Object({ - id: Type.String({ format: 'uuid' }), - email: Type.String({ format: 'email' }), - name: Type.String(), - role: Type.String({ enum: ['ceo', 'manager', 'staff'] }), - avatar_url: Type.Union([Type.String({ format: 'uri' }), Type.Null()]), - department: Type.Union([Type.String(), Type.Null()]), - job_title: Type.Union([Type.String(), Type.Null()]), - phone: Type.Union([Type.String(), Type.Null()]), - language_preference: Type.Union([Type.String(), Type.Null()]), - timezone: Type.Union([Type.String(), Type.Null()]), - notification_settings: Type.Any(), - voice_settings: Type.Any(), - email_verified: Type.Boolean(), - last_active: Type.Union([Type.String({ format: 'date-time' }), Type.Null()]), - created_at: Type.String({ format: 'date-time' }), - updated_at: Type.Union([Type.String({ format: 'date-time' }), Type.Null()]), - permissions: Type.Array(Type.String()), - }), - }), - }), - 401: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request: FastifyRequest, reply: FastifyReply) => { - if (!request.user) { - throw new AuthenticationError('Authentication required'); - } - - try { - const user = await userRepository.findById(request.user.userId); - - if (!user) { - throw new AuthenticationError('User not found'); - } - - reply.code(200).send({ - success: true, - data: { - user: { - id: user.id, - email: user.email, - name: user.name, - role: user.role, - avatar_url: user.avatar_url, - department: user.department, - job_title: user.job_title, - phone: user.phone, - language_preference: user.language_preference, - timezone: user.timezone, - notification_settings: user.notification_settings, - voice_settings: user.voice_settings, - email_verified: user.email_verified, - last_active: user.last_active, - created_at: user.created_at, - updated_at: user.updated_at, - permissions: request.user.permissions, - }, - }, - }); - } catch (error) { - logger.error({ error, userId: request.user.userId }, 'Get profile failed'); - - if (error instanceof AuthenticationError) { - reply.code(401).send(formatErrorResponse(error)); - } else { - reply.code(500).send({ - error: { - message: 'Failed to get user profile', - code: 'SERVER_ERROR', - }, - }); - } - } - } - ); - - /** - * POST /auth/resend-verification - Resend email verification - */ - fastify.post<{ Body: { email: string } }>( - '/auth/resend-verification', - { - preHandler: [authRateLimit], - schema: { - tags: ['Authentication'], - summary: 'Resend Email Verification', - description: 'Resend email verification link to the specified email address', - body: Type.Object({ - email: Type.String({ format: 'email' }), - }), - response: { - 200: Type.Object({ - success: Type.Boolean(), - message: Type.String(), - }), - 400: Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - }), - }), - }, - }, - }, - async (request: FastifyRequest<{ Body: { email: string } }>, reply: FastifyReply) => { - const { email } = request.body; - const context = createErrorContext({ - ip: request.ip, - method: request.method, - url: request.url, - headers: request.headers as Record, - }); - - try { - // Check if user exists with this email - const user = await userRepository.findByEmail(email); - - if (!user) { - // Don't reveal that the email doesn't exist for security reasons - reply.code(200).send({ - success: true, - message: 'If the email address exists, a new verification email will be sent.', - }); - return; - } - - // Check if user is already verified - if (user.email_verified) { - reply.code(200).send({ - success: true, - message: 'Email address is already verified.', - }); - return; - } - - // Generate new email verification token - const verificationToken = jwtService.generateEmailVerificationToken(user.id, user.email); - await userRepository.setEmailVerificationToken(user.id, verificationToken); - - // Send verification email - const emailSent = await emailService.sendEmailVerification({ - userEmail: user.email, - userName: user.name, - verificationToken, - }); - - if (!emailSent) { - logger.warn({ userId: user.id, email: user.email }, 'Failed to send verification email'); - } - - securityLogger.logAuthEvent('email_verified', { - userId: user.id, - email: user.email, - ip: request.ip, - emailSent, - }); - - reply.code(200).send({ - success: true, - message: 'Verification email sent successfully. Please check your inbox and spam folder.', - }); - } catch (error) { - logger.error({ error, context, email }, 'Resend verification failed'); - - reply.code(500).send({ - error: { - message: 'Failed to resend verification email', - code: 'SERVER_ERROR', - }, - }); - } - } - ); -}; diff --git a/backend/src/config/database.ts b/backend/src/config/database.ts deleted file mode 100644 index bd9c02e..0000000 --- a/backend/src/config/database.ts +++ /dev/null @@ -1,348 +0,0 @@ -import { Pool, PoolClient, PoolConfig } from 'pg'; -import { config } from './index'; -import { logger, performanceLogger } from '@utils/logger'; -import { DatabaseConnectionError, DatabaseError } from '@utils/errors'; - -/** - * Database configuration and connection management - * Optimized for high-performance concurrent operations with voice processing - */ - -// Enhanced pool configuration for CEO platform requirements -const poolConfig: PoolConfig = { - connectionString: config.database.url, - min: config.database.pool.min, - max: config.database.pool.max, - - // Connection lifecycle settings - idleTimeoutMillis: 30000, // 30 seconds idle timeout - connectionTimeoutMillis: 10000, // 10 seconds connection timeout - maxUses: 7500, // Maximum uses per connection before recycling - - // SSL configuration for production and AWS RDS - ssl: config.app.isProduction || config.database.url.includes('rds.amazonaws.com') - ? { - rejectUnauthorized: false, // Configure properly in production - } - : false, - - // Query timeout settings - query_timeout: 30000, // 30 seconds max query time - statement_timeout: 30000, // Statement timeout - - // Performance optimizations - application_name: 'ceo-communication-platform', - - // Error handling -}; - -// Global connection pool -let pool: Pool | null = null; - -/** - * Initialize the database connection pool - */ -export const initializeDatabase = async (): Promise => { - try { - pool = new Pool(poolConfig); - - // Set up connection pool event handlers - pool.on('connect', (client) => { - logger.debug( - { - totalCount: pool?.totalCount, - idleCount: pool?.idleCount, - waitingCount: pool?.waitingCount, - }, - 'Database client connected' - ); - }); - - pool.on('acquire', (client) => { - logger.debug('Database client acquired from pool'); - }); - - pool.on('remove', (client) => { - logger.debug('Database client removed from pool'); - }); - - pool.on('error', (err, client) => { - logger.error({ error: err }, 'Database pool error'); - }); - - // Test the connection - const client = await pool.connect(); - - // Optimize connection settings for performance - await client.query(` - SET timezone = 'UTC'; - SET statement_timeout = '30s'; - SET lock_timeout = '10s'; - SET idle_in_transaction_session_timeout = '60s'; - SET search_path = app, public; - `); - - // Test query to verify everything is working - const result = await client.query('SELECT NOW() as current_time, version() as pg_version'); - - client.release(); - - logger.info( - { - poolSize: pool.totalCount, - currentTime: result.rows[0].current_time, - postgresVersion: result.rows[0].pg_version, - }, - 'Database initialized' - ); - } catch (error) { - logger.error({ error }, 'Failed to initialize database connection pool'); - throw new DatabaseConnectionError('Failed to connect to database', { error }); - } -}; - -/** - * Get the database connection pool - */ -export const getPool = (): Pool => { - if (!pool) { - throw new DatabaseConnectionError('Database pool not initialized'); - } - return pool; -}; - -/** - * Execute a query with performance monitoring - */ -export const query = async ( - text: string, - params?: any[], - client?: PoolClient -): Promise<{ rows: T[]; rowCount: number }> => { - const queryClient = client || pool; - - if (!queryClient) { - throw new DatabaseConnectionError('No database connection available'); - } - - return performanceLogger.trackAsyncOperation( - async () => { - try { - const result = await queryClient.query(text, params); - - if (config.development.debugSql) { - logger.debug( - { - query: text, - params: params, - rowCount: result.rowCount, - }, - 'SQL Query executed' - ); - } - - return { - rows: result.rows, - rowCount: result.rowCount || 0, - }; - } catch (error) { - logger.error( - { - error, - query: text, - params: params, - }, - 'Database query failed' - ); - - throw new DatabaseError( - `Database query failed: ${error instanceof Error ? error.message : 'Unknown error'}`, - { query: text, params } - ); - } - }, - 'database-query', - { query: text.substring(0, 100) + (text.length > 100 ? '...' : '') } - ); -}; - -/** - * Execute a transaction with automatic rollback on errors - */ -export const transaction = async (callback: (client: PoolClient) => Promise): Promise => { - const client = await getPool().connect(); - - try { - await client.query('BEGIN'); - logger.debug('Database transaction started'); - - const result = await performanceLogger.trackAsyncOperation( - () => callback(client), - 'database-transaction' - ); - - await client.query('COMMIT'); - logger.debug('Database transaction committed'); - - return result; - } catch (error) { - await client.query('ROLLBACK'); - logger.error({ error }, 'Database transaction rolled back'); - - throw new DatabaseError( - `Transaction failed: ${error instanceof Error ? error.message : 'Unknown error'}`, - { originalError: error } - ); - } finally { - client.release(); - } -}; - -/** - * Check database health and connection status - */ -export const healthCheck = async (): Promise<{ - status: 'healthy' | 'unhealthy'; - details: { - connected: boolean; - poolStats: { - total: number; - idle: number; - waiting: number; - }; - queryTime?: number; - error?: string; - }; -}> => { - try { - const startTime = Date.now(); - - if (!pool) { - return { - status: 'unhealthy', - details: { - connected: false, - poolStats: { total: 0, idle: 0, waiting: 0 }, - error: 'Pool not initialized', - }, - }; - } - - // Test with a simple query - await pool.query('SELECT 1 as health_check'); - const queryTime = Date.now() - startTime; - - return { - status: 'healthy', - details: { - connected: true, - poolStats: { - total: pool.totalCount, - idle: pool.idleCount, - waiting: pool.waitingCount, - }, - queryTime, - }, - }; - } catch (error) { - return { - status: 'unhealthy', - details: { - connected: false, - poolStats: { - total: pool?.totalCount || 0, - idle: pool?.idleCount || 0, - waiting: pool?.waitingCount || 0, - }, - error: error instanceof Error ? error.message : 'Unknown error', - }, - }; - } -}; - -/** - * Gracefully close the database connection pool - */ -export const closeDatabase = async (): Promise => { - if (pool) { - logger.info('Closing database connection pool...'); - await pool.end(); - pool = null; - logger.info('Database connection pool closed'); - } -}; - -/** - * Get current pool statistics for monitoring - */ -export const getPoolStats = () => { - if (!pool) { - return { - total: 0, - idle: 0, - waiting: 0, - connected: false, - }; - } - - return { - total: pool.totalCount, - idle: pool.idleCount, - waiting: pool.waitingCount, - connected: true, - }; -}; - -// Performance monitoring utilities -export const databaseMetrics = { - /** - * Log slow queries for optimization - */ - logSlowQuery: (queryText: string, duration: number, params?: any[]) => { - if (duration > 1000) { - // Log queries taking more than 1 second - logger.warn( - { - query: queryText, - duration, - params, - threshold: '1000ms', - }, - 'Slow query detected' - ); - } - }, - - /** - * Monitor connection pool health - */ - monitorPoolHealth: () => { - if (!pool) return; - - const stats = getPoolStats(); - const utilizationRate = (stats.total - stats.idle) / stats.total; - - if (utilizationRate > 0.8) { - logger.warn( - { - stats, - utilizationRate, - }, - 'High database connection pool utilization' - ); - } - - if (stats.waiting > 0) { - logger.warn( - { - stats, - }, - 'Clients waiting for database connections' - ); - } - }, -}; - -// Export types for use throughout the application -export type DatabaseClient = PoolClient; -export type QueryResult = { rows: T[]; rowCount: number }; diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts deleted file mode 100644 index a05439d..0000000 --- a/backend/src/config/index.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { config as loadEnv } from 'dotenv'; -import { z } from 'zod'; - -// Load environment variables -loadEnv(); - -// Environment validation schema -const envSchema = z.object({ - // Application - NODE_ENV: z.enum(['development', 'test', 'production']).default('development'), - PORT: z.union([z.string().transform(Number), z.number()]).default(8080), - HOST: z.string().default('0.0.0.0'), - - // Database - DATABASE_URL: z.string(), - DB_POOL_MIN: z.string().transform(Number).default('2'), - DB_POOL_MAX: z.string().transform(Number).default('20'), - - // Redis - REDIS_URL: z.string().default('redis://localhost:6379'), - - // JWT - JWT_SECRET: z.string().min(32), - JWT_REFRESH_SECRET: z.string().min(32), - JWT_EXPIRES_IN: z.string().default('15m'), - JWT_REFRESH_EXPIRES_IN: z.string().default('7d'), - - // API - API_PREFIX: z.string().default('/api'), - API_VERSION: z.string().default('v1'), - CORS_ORIGIN: z.string().default('*'), - RATE_LIMIT_MAX: z.string().transform(Number).default('100'), - - // Security - BCRYPT_ROUNDS: z.string().transform(Number).default('12'), - MAX_LOGIN_ATTEMPTS: z.string().transform(Number).default('5'), - LOCKOUT_DURATION: z.string().default('15m'), - - // Redis - REDIS_HOST: z.string().default('localhost'), - REDIS_PORT: z.coerce.number().default(6379), - REDIS_PASSWORD: z.string().optional(), - REDIS_DB: z.coerce.number().default(0), - REDIS_PUBSUB_DB: z.coerce.number().default(1), - - // Performance - CACHE_TTL_SHORT: z.string().default('5m'), - CACHE_TTL_MEDIUM: z.string().default('1h'), - CACHE_TTL_LONG: z.string().default('24h'), - QUERY_TIMEOUT: z.string().default('30s'), - REQUEST_TIMEOUT: z.string().default('30s'), - - // Logging - LOG_LEVEL: z.enum(['error', 'warn', 'info', 'debug']).default('info'), - LOG_FORMAT: z.enum(['json', 'pretty']).default('json'), - - // Development - SEED_DATABASE: z.string().transform(Boolean).default('false'), - DEBUG_SQL: z.string().transform(Boolean).default('false'), - DEBUG_WEBSOCKET: z.string().transform(Boolean).default('false'), -}); - -// Validate and export configuration -const env = envSchema.parse(process.env); - -export const config = { - // Application settings - app: { - env: env.NODE_ENV, - port: env.PORT, - host: env.HOST, - isDevelopment: env.NODE_ENV === 'development', - isProduction: env.NODE_ENV === 'production', - isTest: env.NODE_ENV === 'test', - }, - - // Database configuration - database: { - url: env.DATABASE_URL, - pool: { - min: env.DB_POOL_MIN, - max: env.DB_POOL_MAX, - }, - options: { - parseInputDatesAsUTC: true, - ssl: env.NODE_ENV === 'production', - }, - }, - - // Redis configuration - redis: { - host: env.REDIS_HOST, - port: env.REDIS_PORT, - password: env.REDIS_PASSWORD, - db: env.REDIS_DB, - pubSubDb: env.REDIS_PUBSUB_DB, - options: { - retryDelayOnFailover: 100, - enableReadyCheck: false, - maxRetriesPerRequest: null, - lazyConnect: true, - }, - }, - - // JWT configuration - jwt: { - secret: env.JWT_SECRET, - refreshSecret: env.JWT_REFRESH_SECRET, - expiresIn: env.JWT_EXPIRES_IN, - refreshExpiresIn: env.JWT_REFRESH_EXPIRES_IN, - }, - - // API configuration - api: { - prefix: env.API_PREFIX, - version: env.API_VERSION, - cors: { - origin: env.CORS_ORIGIN.split(','), - credentials: true, - }, - rateLimit: { - max: env.RATE_LIMIT_MAX, - timeWindow: '15 minutes', - }, - }, - - // Security configuration - security: { - bcryptRounds: env.BCRYPT_ROUNDS, - maxLoginAttempts: env.MAX_LOGIN_ATTEMPTS, - lockoutDuration: env.LOCKOUT_DURATION, - }, - - // Performance configuration - performance: { - cache: { - ttl: { - short: env.CACHE_TTL_SHORT, - medium: env.CACHE_TTL_MEDIUM, - long: env.CACHE_TTL_LONG, - }, - }, - timeouts: { - query: env.QUERY_TIMEOUT, - request: env.REQUEST_TIMEOUT, - }, - }, - - // Logging configuration - logging: { - level: env.LOG_LEVEL, - format: env.LOG_FORMAT, - }, - - // Development configuration - development: { - seedDatabase: env.SEED_DATABASE, - debugSql: env.DEBUG_SQL, - debugWebSocket: env.DEBUG_WEBSOCKET, - }, -} as const; - -// Type exports for use throughout the application -export type Config = typeof config; -export type AppEnv = typeof config.app.env; diff --git a/backend/src/config/redis.ts b/backend/src/config/redis.ts deleted file mode 100644 index 2d2b864..0000000 --- a/backend/src/config/redis.ts +++ /dev/null @@ -1,308 +0,0 @@ -import Redis from 'ioredis'; -import { config } from './index'; -import { logger, loggers } from '@utils/logger'; -import { CacheError } from '@utils/errors'; - -/** - * Redis configuration and connection management - * Enterprise-grade caching with performance monitoring - */ - -export interface CacheMetrics { - hits: number; - misses: number; - sets: number; - deletes: number; - errors: number; - totalOperations: number; - hitRate: number; -} - -class RedisManager { - private client: Redis | null = null; - private subscriber: Redis | null = null; - private publisher: Redis | null = null; - private isConnected = false; - private reconnectAttempts = 0; - private maxReconnectAttempts = 5; - private metrics: CacheMetrics = { - hits: 0, - misses: 0, - sets: 0, - deletes: 0, - errors: 0, - totalOperations: 0, - hitRate: 0, - }; - - /** - * Initialize Redis connections - */ - async initialize(): Promise { - try { - logger.info('Initializing Redis connections...'); - - const redisOptions: any = { - host: config.redis?.host || 'localhost', - port: config.redis?.port || 6379, - db: config.redis?.db || 0, - retryDelayOnFailover: 100, - maxRetriesPerRequest: 3, - connectTimeout: 5000, - lazyConnect: true, - keepAlive: 30000, - }; - if (typeof config.redis?.password === 'string') { - redisOptions.password = config.redis.password; - } - - // Main client for general operations - this.client = new Redis(redisOptions); - - // Subscriber for pub/sub operations - this.subscriber = new Redis({ - ...redisOptions, - db: config.redis?.pubSubDb || 1, - }); - - // Publisher for pub/sub operations - this.publisher = new Redis({ - ...redisOptions, - db: config.redis?.pubSubDb || 1, - }); - - // Setup event handlers - this.setupEventHandlers(); - - // Connect all clients - await Promise.all([ - this.client.connect(), - this.subscriber.connect(), - this.publisher.connect(), - ]); - - this.isConnected = true; - this.reconnectAttempts = 0; - - logger.info('Redis initialized'); - - // Start metrics collection - this.startMetricsCollection(); - } catch (error) { - logger.error({ error }, 'Failed to initialize Redis connections'); - throw new CacheError('Redis initialization failed', { error }); - } - } - - /** - * Setup Redis event handlers - */ - private setupEventHandlers(): void { - if (!this.client) return; - - this.client.on('connect', () => { - loggers.cache.info('Redis client connected'); - this.isConnected = true; - this.reconnectAttempts = 0; - }); - - this.client.on('ready', () => { - loggers.cache.info('Redis client ready for operations'); - }); - - this.client.on('error', (error: Error) => { - loggers.cache.error({ error }, 'Redis client error'); - this.metrics.errors++; - this.isConnected = false; - }); - - this.client.on('close', () => { - loggers.cache.warn('Redis client connection closed'); - this.isConnected = false; - }); - - this.client.on('reconnecting', () => { - this.reconnectAttempts++; - loggers.cache.warn({ attempt: this.reconnectAttempts }, 'Redis client reconnecting'); - - if (this.reconnectAttempts >= this.maxReconnectAttempts) { - loggers.cache.error('Max reconnection attempts reached'); - this.client?.disconnect(); - } - }); - - // Setup similar handlers for subscriber and publisher - this.subscriber?.on('error', (error: Error) => { - loggers.cache.error({ error, type: 'subscriber' }, 'Redis subscriber error'); - }); - - this.publisher?.on('error', (error: Error) => { - loggers.cache.error({ error, type: 'publisher' }, 'Redis publisher error'); - }); - } - - /** - * Get the main Redis client - */ - getClient(): Redis { - if (!this.client) { - throw new CacheError('Redis client not initialized'); - } - return this.client; - } - - /** - * Get the subscriber client - */ - getSubscriber(): Redis { - if (!this.subscriber) { - throw new CacheError('Redis subscriber not initialized'); - } - return this.subscriber; - } - - /** - * Get the publisher client - */ - getPublisher(): Redis { - if (!this.publisher) { - throw new CacheError('Redis publisher not initialized'); - } - return this.publisher; - } - - /** - * Check if Redis is connected - */ - isRedisConnected(): boolean { - return this.isConnected && this.client?.status === 'ready'; - } - - /** - * Health check for Redis - */ - async healthCheck(): Promise { - try { - if (!this.client) return false; - - const result = await this.client.ping(); - return result === 'PONG'; - } catch (error) { - loggers.cache.error({ error }, 'Redis health check failed'); - return false; - } - } - - /** - * Get cache metrics - */ - getMetrics(): CacheMetrics { - const totalOperations = this.metrics.hits + this.metrics.misses; - return { - ...this.metrics, - totalOperations, - hitRate: totalOperations > 0 ? (this.metrics.hits / totalOperations) * 100 : 0, - }; - } - - /** - * Reset metrics - */ - resetMetrics(): void { - this.metrics = { - hits: 0, - misses: 0, - sets: 0, - deletes: 0, - errors: 0, - totalOperations: 0, - hitRate: 0, - }; - } - - /** - * Start metrics collection - */ - private startMetricsCollection(): void { - // Log metrics every 5 minutes - setInterval( - () => { - const metrics = this.getMetrics(); - loggers.cache.info( - { - metrics, - isConnected: this.isConnected, - status: this.client?.status, - }, - 'Redis cache metrics' - ); - - // Log warnings for poor performance - if (metrics.hitRate < 80 && metrics.totalOperations > 100) { - loggers.cache.warn({ hitRate: metrics.hitRate }, 'Low cache hit rate detected'); - } - - if (metrics.errors > 10) { - loggers.cache.warn({ errors: metrics.errors }, 'High error rate in Redis operations'); - } - }, - 5 * 60 * 1000 - ); - } - - /** - * Increment hit counter - */ - incrementHits(): void { - this.metrics.hits++; - } - - /** - * Increment miss counter - */ - incrementMisses(): void { - this.metrics.misses++; - } - - /** - * Increment sets counter - */ - incrementSets(): void { - this.metrics.sets++; - } - - /** - * Increment deletes counter - */ - incrementDeletes(): void { - this.metrics.deletes++; - } - - /** - * Close all Redis connections - */ - async close(): Promise { - try { - logger.info('Closing Redis connections...'); - - await Promise.allSettled([ - this.client?.quit(), - this.subscriber?.quit(), - this.publisher?.quit(), - ]); - - this.client = null; - this.subscriber = null; - this.publisher = null; - this.isConnected = false; - - logger.info('Redis connections closed successfully'); - } catch (error) { - logger.error({ error }, 'Error closing Redis connections'); - } - } -} - -// Export singleton instance -export const redisManager = new RedisManager(); -export default redisManager; diff --git a/backend/src/db/ActivityRepository.ts b/backend/src/db/ActivityRepository.ts deleted file mode 100644 index b6c4229..0000000 --- a/backend/src/db/ActivityRepository.ts +++ /dev/null @@ -1,831 +0,0 @@ -import { DatabaseClient } from '../config/database'; -import { logger } from '../utils/logger'; -import { ValidationError, NotFoundError } from '../utils/errors'; -import BaseRepository, { BaseEntity, FilterOptions } from './BaseRepository'; - -export interface Activity extends BaseEntity { - channel_id?: string; - task_id?: string; - user_id: string; - activity_type: - | 'message' - | 'task_created' - | 'task_updated' - | 'task_completed' - | 'task_assigned' - | 'member_joined' - | 'member_left' - | 'file_uploaded' - | 'channel_updated' - | 'channel_created' - | 'reaction_added' - | 'mention' - | 'voice_command' - | 'ai_response'; - title: string; - description?: string; - metadata: Record; - read_by: string[]; // User IDs who have read this activity - priority: 'low' | 'medium' | 'high'; - category: 'task' | 'channel' | 'system' | 'social'; - referenced_entity_id?: string; // ID of the entity this activity references - referenced_entity_type?: 'task' | 'message' | 'channel' | 'user' | 'file'; -} - -export interface CreateActivityData { - channelId?: string; - taskId?: string; - userId: string; - activityType: Activity['activity_type']; - title: string; - description?: string; - metadata?: Record; - priority?: Activity['priority']; - category?: Activity['category']; - referencedEntityId?: string; - referencedEntityType?: Activity['referenced_entity_type']; -} - -export interface ActivityWithUser extends Activity { - user_name: string; - user_email: string; - user_avatar?: string; - user_role: string; - channel_name?: string; - task_title?: string; -} - -class ActivityRepository extends BaseRepository { - constructor() { - super('activities'); - } - - /** - * Create new activity with validation - */ - // Add methods needed for the API routes - async findMany(filters: { - type?: string[]; - channel_id?: string; - user_id?: string; - from_date?: string; - to_date?: string; - limit?: number; - offset?: number; - includeUser?: boolean; - }): Promise { - let whereConditions = ['a.deleted_at IS NULL']; - let params: any[] = []; - let paramIndex = 1; - - if (filters.type) { - whereConditions.push(`a.activity_type = ANY($${paramIndex})`); - params.push(filters.type); - paramIndex++; - } - - if (filters.channel_id) { - whereConditions.push(`a.channel_id = $${paramIndex}`); - params.push(filters.channel_id); - paramIndex++; - } - - if (filters.user_id) { - whereConditions.push(`a.user_id = $${paramIndex}`); - params.push(filters.user_id); - paramIndex++; - } - - if (filters.from_date) { - whereConditions.push(`a.created_at >= $${paramIndex}`); - params.push(filters.from_date); - paramIndex++; - } - - if (filters.to_date) { - whereConditions.push(`a.created_at <= $${paramIndex}`); - params.push(filters.to_date); - paramIndex++; - } - - const userJoin = filters.includeUser ? ` - LEFT JOIN users u ON a.user_id = u.id AND u.deleted_at IS NULL - ` : ''; - - const userFields = filters.includeUser ? ` - u.name as user_name, - u.email as user_email, - u.avatar_url, - u.role as user_role - ` : ''; - - const sql = ` - SELECT - a.*, - ${userFields} - FROM ${this.tableName} a - ${userJoin} - WHERE ${whereConditions.join(' AND ')} - ORDER BY a.created_at DESC - ${filters.limit ? `LIMIT $${paramIndex}` : ''} - ${filters.offset && filters.limit ? `OFFSET $${paramIndex + 1}` : ''} - `; - - if (filters.limit) { - params.push(filters.limit); - if (filters.offset) { - params.push(filters.offset); - } - } - - const result = await this.executeRawQuery(sql, params); - return result.rows.map(row => ({ - ...row, - user: filters.includeUser && row.user_name ? { - id: row.user_id, - name: row.user_name, - email: row.user_email, - avatar_url: row.avatar_url, - } : undefined - })); - } - - async count(filters: any): Promise { - let whereConditions = ['deleted_at IS NULL']; - let params: any[] = []; - let paramIndex = 1; - - if (filters.type) { - whereConditions.push(`activity_type = ANY($${paramIndex})`); - params.push(filters.type); - paramIndex++; - } - - if (filters.channel_id) { - whereConditions.push(`channel_id = $${paramIndex}`); - params.push(filters.channel_id); - paramIndex++; - } - - if (filters.user_id) { - whereConditions.push(`user_id = $${paramIndex}`); - params.push(filters.user_id); - paramIndex++; - } - - const sql = ` - SELECT COUNT(*) as count - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - `; - - const result = await this.executeRawQuery<{ count: string }>(sql, params); - return parseInt(result.rows[0]?.count || '0', 10); - } - - async getUserFeed(userId: string, limit: number, offset: number): Promise { - return this.findMany({ - limit, - offset, - includeUser: true, - }); - } - - async getUserFeedCount(userId: string): Promise { - return this.count({}); - } - - async getActivityStats(options: { - userId?: string; - period?: string; - channelId?: string; - }): Promise { - return { - totalActivities: 0, - activitiesByType: {}, - activitiesByPeriod: {}, - }; - } - - async createActivity(activityData: CreateActivityData, client?: DatabaseClient): Promise { - // Set defaults - const activityToCreate = { - channel_id: activityData.channelId, - task_id: activityData.taskId, - user_id: activityData.userId, - activity_type: activityData.activityType, - title: activityData.title, - description: activityData.description || '', - metadata: activityData.metadata || {}, - read_by: [], - priority: activityData.priority || this.determinePriority(activityData.activityType), - category: activityData.category || this.determineCategory(activityData.activityType), - referenced_entity_id: activityData.referencedEntityId, - referenced_entity_type: activityData.referencedEntityType, - }; - - const activity = await this.create(activityToCreate, client); - - logger.info( - { - activityId: activity.id, - activityType: activity.activity_type, - userId: activity.user_id, - channelId: activity.channel_id, - taskId: activity.task_id, - }, - 'Activity created successfully' - ); - - return activity; - } - - /** - * Find activities for a specific user - */ - async findUserActivities( - userId: string, - filters?: { - activityType?: string; - category?: string; - unreadOnly?: boolean; - channelId?: string; - taskId?: string; - after?: Date; - before?: Date; - }, - limit: number = 50, - offset: number = 0, - client?: DatabaseClient - ): Promise { - let whereConditions = ['a.deleted_at IS NULL']; - let params: any[] = []; - let paramIndex = 1; - - // User should see activities where: - // 1. They are the actor (created the activity) - // 2. They are mentioned/assigned in tasks - // 3. Activities in channels they are members of - // 4. Activities on tasks they are assigned to - whereConditions.push(`( - a.user_id = $${paramIndex} - OR a.metadata::text LIKE '%"' || $${paramIndex} || '"%' - OR EXISTS ( - SELECT 1 FROM channels c - WHERE c.id = a.channel_id - AND $${paramIndex} = ANY(c.members) - AND c.deleted_at IS NULL - ) - OR EXISTS ( - SELECT 1 FROM tasks t - WHERE t.id = a.task_id - AND $${paramIndex} = ANY(t.assigned_to) - AND t.deleted_at IS NULL - ) - )`); - params.push(userId); - paramIndex++; - - // Add filters - if (filters?.activityType) { - whereConditions.push(`a.activity_type = $${paramIndex}`); - params.push(filters.activityType); - paramIndex++; - } - - if (filters?.category) { - whereConditions.push(`a.category = $${paramIndex}`); - params.push(filters.category); - paramIndex++; - } - - if (filters?.channelId) { - whereConditions.push(`a.channel_id = $${paramIndex}`); - params.push(filters.channelId); - paramIndex++; - } - - if (filters?.taskId) { - whereConditions.push(`a.task_id = $${paramIndex}`); - params.push(filters.taskId); - paramIndex++; - } - - if (filters?.unreadOnly) { - whereConditions.push(`NOT ($${paramIndex - params.length + 1} = ANY(a.read_by))`); - } - - if (filters?.after) { - whereConditions.push(`a.created_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - - if (filters?.before) { - whereConditions.push(`a.created_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - - const sql = ` - SELECT - a.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - c.name as channel_name, - t.title as task_title - FROM ${this.tableName} a - LEFT JOIN users u ON a.user_id = u.id - LEFT JOIN channels c ON a.channel_id = c.id - LEFT JOIN tasks t ON a.task_id = t.id - WHERE ${whereConditions.join(' AND ')} - ORDER BY a.created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - - params.push(limit, offset); - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Find activities for a channel - */ - async findChannelActivities( - channelId: string, - filters?: { - activityType?: string; - userId?: string; - after?: Date; - before?: Date; - }, - limit: number = 50, - offset: number = 0, - client?: DatabaseClient - ): Promise { - let whereConditions = ['a.channel_id = $1', 'a.deleted_at IS NULL']; - let params: any[] = [channelId]; - let paramIndex = 2; - - // Add filters - if (filters?.activityType) { - whereConditions.push(`a.activity_type = $${paramIndex}`); - params.push(filters.activityType); - paramIndex++; - } - - if (filters?.userId) { - whereConditions.push(`a.user_id = $${paramIndex}`); - params.push(filters.userId); - paramIndex++; - } - - if (filters?.after) { - whereConditions.push(`a.created_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - - if (filters?.before) { - whereConditions.push(`a.created_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - - const sql = ` - SELECT - a.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - c.name as channel_name, - t.title as task_title - FROM ${this.tableName} a - LEFT JOIN users u ON a.user_id = u.id - LEFT JOIN channels c ON a.channel_id = c.id - LEFT JOIN tasks t ON a.task_id = t.id - WHERE ${whereConditions.join(' AND ')} - ORDER BY a.created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - - params.push(limit, offset); - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Get channel activity count - */ - async getChannelActivityCount( - channelId: string, - filters?: { - activityType?: string; - userId?: string; - after?: Date; - before?: Date; - }, - client?: DatabaseClient - ): Promise { - let whereConditions = ['channel_id = $1', 'deleted_at IS NULL']; - let params: any[] = [channelId]; - let paramIndex = 2; - - // Add filters - if (filters?.activityType) { - whereConditions.push(`activity_type = $${paramIndex}`); - params.push(filters.activityType); - paramIndex++; - } - - if (filters?.userId) { - whereConditions.push(`user_id = $${paramIndex}`); - params.push(filters.userId); - paramIndex++; - } - - if (filters?.after) { - whereConditions.push(`created_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - - if (filters?.before) { - whereConditions.push(`created_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - - const sql = ` - SELECT COUNT(*) as count - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - `; - - const result = await this.executeRawQuery<{ count: string }>(sql, params, client); - return parseInt(result.rows[0]?.count || '0', 10); - } - - /** - * Get user activity count - */ - async getUserActivityCount( - userId: string, - unreadOnly: boolean = false, - client?: DatabaseClient - ): Promise { - let whereConditions = ['deleted_at IS NULL']; - let params: any[] = []; - let paramIndex = 1; - - // User should see activities where they are involved - whereConditions.push(`( - user_id = $${paramIndex} - OR metadata::text LIKE '%"' || $${paramIndex} || '"%' - OR EXISTS ( - SELECT 1 FROM channels c - WHERE c.id = channel_id - AND $${paramIndex} = ANY(c.members) - AND c.deleted_at IS NULL - ) - OR EXISTS ( - SELECT 1 FROM tasks t - WHERE t.id = task_id - AND $${paramIndex} = ANY(t.assigned_to) - AND t.deleted_at IS NULL - ) - )`); - params.push(userId); - paramIndex++; - - if (unreadOnly) { - whereConditions.push(`NOT ($${paramIndex - 1} = ANY(read_by))`); - } - - const sql = ` - SELECT COUNT(*) as count - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - `; - - const result = await this.executeRawQuery<{ count: string }>(sql, params, client); - return parseInt(result.rows[0]?.count || '0', 10); - } - - /** - * Mark activity as read by user - */ - async markAsRead( - activityId: string, - userId: string, - client?: DatabaseClient - ): Promise { - const sql = ` - UPDATE ${this.tableName} - SET read_by = CASE - WHEN $2 = ANY(read_by) THEN read_by - ELSE array_append(read_by, $2) - END - WHERE id = $1 AND deleted_at IS NULL - RETURNING id - `; - - try { - const result = await this.executeRawQuery(sql, [activityId, userId], client); - - if (result.rowCount > 0) { - logger.debug( - { - activityId, - userId, - }, - 'Activity marked as read' - ); - return true; - } - return false; - } catch (error) { - logger.error({ error, activityId, userId }, 'Failed to mark activity as read'); - return false; - } - } - - /** - * Mark multiple activities as read - */ - async markMultipleAsRead( - activityIds: string[], - userId: string, - client?: DatabaseClient - ): Promise { - if (activityIds.length === 0) return 0; - - const placeholders = activityIds.map((_, index) => `$${index + 2}`).join(','); - const sql = ` - UPDATE ${this.tableName} - SET read_by = CASE - WHEN $1 = ANY(read_by) THEN read_by - ELSE array_append(read_by, $1) - END - WHERE id = ANY(ARRAY[${placeholders}]) AND deleted_at IS NULL - `; - - try { - const result = await this.executeRawQuery(sql, [userId, ...activityIds], client); - - logger.info( - { - userId, - markedCount: result.rowCount, - totalIds: activityIds.length, - }, - 'Multiple activities marked as read' - ); - - return result.rowCount; - } catch (error) { - logger.error({ error, userId, activityIds }, 'Failed to mark multiple activities as read'); - return 0; - } - } - - /** - * Mark all user activities as read - */ - async markAllAsRead(userId: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET read_by = CASE - WHEN $1 = ANY(read_by) THEN read_by - ELSE array_append(read_by, $1) - END - WHERE deleted_at IS NULL - AND NOT ($1 = ANY(read_by)) - AND ( - user_id = $1 - OR metadata::text LIKE '%"' || $1 || '"%' - OR EXISTS ( - SELECT 1 FROM channels c - WHERE c.id = channel_id - AND $1 = ANY(c.members) - AND c.deleted_at IS NULL - ) - OR EXISTS ( - SELECT 1 FROM tasks t - WHERE t.id = task_id - AND $1 = ANY(t.assigned_to) - AND t.deleted_at IS NULL - ) - ) - `; - - try { - const result = await this.executeRawQuery(sql, [userId], client); - - logger.info( - { - userId, - markedCount: result.rowCount, - }, - 'All user activities marked as read' - ); - - return result.rowCount; - } catch (error) { - logger.error({ error, userId }, 'Failed to mark all activities as read'); - return 0; - } - } - - /** - * Get activity statistics for a user - */ - async getUserActivityStats( - userId: string, - daysSince: number = 30, - client?: DatabaseClient - ): Promise<{ - totalActivities: number; - unreadActivities: number; - activitiesByType: Record; - activitiesByCategory: Record; - recentActivity: Array<{ date: string; count: number }>; - }> { - const statsSql = ` - WITH user_activities AS ( - SELECT * - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND created_at >= NOW() - INTERVAL '${daysSince} days' - AND ( - user_id = $1 - OR metadata::text LIKE '%"' || $1 || '"%' - OR EXISTS ( - SELECT 1 FROM channels c - WHERE c.id = channel_id - AND $1 = ANY(c.members) - AND c.deleted_at IS NULL - ) - OR EXISTS ( - SELECT 1 FROM tasks t - WHERE t.id = task_id - AND $1 = ANY(t.assigned_to) - AND t.deleted_at IS NULL - ) - ) - ) - SELECT - COUNT(*) as total_activities, - COUNT(*) FILTER (WHERE NOT ($1 = ANY(read_by))) as unread_activities, - COUNT(*) FILTER (WHERE activity_type = 'message') as message_activities, - COUNT(*) FILTER (WHERE activity_type = 'task_created') as task_created_activities, - COUNT(*) FILTER (WHERE activity_type = 'task_updated') as task_updated_activities, - COUNT(*) FILTER (WHERE activity_type = 'task_completed') as task_completed_activities, - COUNT(*) FILTER (WHERE category = 'task') as task_category, - COUNT(*) FILTER (WHERE category = 'channel') as channel_category, - COUNT(*) FILTER (WHERE category = 'system') as system_category, - COUNT(*) FILTER (WHERE category = 'social') as social_category - FROM user_activities - `; - - const recentActivitySql = ` - SELECT - DATE(created_at) as date, - COUNT(*) as count - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND created_at >= NOW() - INTERVAL '${daysSince} days' - AND ( - user_id = $1 - OR metadata::text LIKE '%"' || $1 || '"%' - OR EXISTS ( - SELECT 1 FROM channels c - WHERE c.id = channel_id - AND $1 = ANY(c.members) - AND c.deleted_at IS NULL - ) - OR EXISTS ( - SELECT 1 FROM tasks t - WHERE t.id = task_id - AND $1 = ANY(t.assigned_to) - AND t.deleted_at IS NULL - ) - ) - GROUP BY DATE(created_at) - ORDER BY date DESC - `; - - const [statsResult, recentActivityResult] = await Promise.all([ - this.executeRawQuery(statsSql, [userId], client), - this.executeRawQuery(recentActivitySql, [userId], client), - ]); - - const stats = statsResult.rows[0]; - - return { - totalActivities: parseInt(stats.total_activities, 10), - unreadActivities: parseInt(stats.unread_activities, 10), - activitiesByType: { - message: parseInt(stats.message_activities, 10), - task_created: parseInt(stats.task_created_activities, 10), - task_updated: parseInt(stats.task_updated_activities, 10), - task_completed: parseInt(stats.task_completed_activities, 10), - }, - activitiesByCategory: { - task: parseInt(stats.task_category, 10), - channel: parseInt(stats.channel_category, 10), - system: parseInt(stats.system_category, 10), - social: parseInt(stats.social_category, 10), - }, - recentActivity: recentActivityResult.rows.map((row: any) => ({ - date: row.date, - count: parseInt(row.count, 10), - })), - }; - } - - /** - * Determine priority based on activity type - */ - private determinePriority(activityType: Activity['activity_type']): Activity['priority'] { - switch (activityType) { - case 'task_created': - case 'task_assigned': - return 'medium'; - case 'task_completed': - return 'low'; - case 'mention': - return 'high'; - case 'voice_command': - return 'high'; - default: - return 'low'; - } - } - - /** - * Determine category based on activity type - */ - private determineCategory(activityType: Activity['activity_type']): Activity['category'] { - switch (activityType) { - case 'task_created': - case 'task_updated': - case 'task_completed': - case 'task_assigned': - return 'task'; - case 'message': - case 'member_joined': - case 'member_left': - case 'channel_updated': - case 'channel_created': - return 'channel'; - case 'mention': - case 'reaction_added': - return 'social'; - case 'file_uploaded': - case 'voice_command': - case 'ai_response': - return 'system'; - default: - return 'system'; - } - } - - /** - * Clean up old activities - */ - async cleanupOldActivities( - daysToKeep: number = 90, - client?: DatabaseClient - ): Promise { - const sql = ` - DELETE FROM ${this.tableName} - WHERE created_at < NOW() - INTERVAL '${daysToKeep} days' - AND category != 'task' -- Keep task-related activities longer - `; - - try { - const result = await this.executeRawQuery(sql, [], client); - - logger.info( - { - deletedCount: result.rowCount, - daysToKeep, - }, - 'Old activities cleaned up' - ); - - return result.rowCount; - } catch (error) { - logger.error({ error, daysToKeep }, 'Failed to clean up old activities'); - return 0; - } - } -} - -export default ActivityRepository; \ No newline at end of file diff --git a/backend/src/db/AnnouncementRepository.ts b/backend/src/db/AnnouncementRepository.ts deleted file mode 100644 index 50b6526..0000000 --- a/backend/src/db/AnnouncementRepository.ts +++ /dev/null @@ -1,414 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import { BaseRepository, BaseEntity } from './BaseRepository'; -import { logger } from '@utils/logger'; -import { NotFoundError, ValidationError } from '@utils/errors'; - -export interface Announcement extends BaseEntity { - id: string; - title: string; - content: string; - type: 'info' | 'warning' | 'success' | 'error' | 'feature' | 'maintenance'; - priority: 'low' | 'medium' | 'high' | 'critical'; - target_audience: 'all' | 'admins' | 'developers' | 'designers' | 'managers'; - scheduled_for?: Date | null; - expires_at?: Date | null; - action_button_text?: string | null; - action_button_url?: string | null; - image_url?: string | null; - created_by: string; - published: boolean; - read_by: string[]; // Array of user IDs who have read this announcement -} - -export interface CreateAnnouncementData { - title: string; - content: string; - type: Announcement['type']; - priority: Announcement['priority']; - target_audience: Announcement['target_audience']; - scheduled_for?: Date | undefined; - expires_at?: Date | undefined; - action_button_text?: string | undefined; - action_button_url?: string | undefined; - image_url?: string | undefined; - created_by: string; - published?: boolean | undefined; -} - -export interface UpdateAnnouncementData { - title?: string | undefined; - content?: string | undefined; - type?: Announcement['type'] | undefined; - priority?: Announcement['priority'] | undefined; - target_audience?: Announcement['target_audience'] | undefined; - scheduled_for?: Date | null | undefined; - expires_at?: Date | null | undefined; - action_button_text?: string | null | undefined; - action_button_url?: string | null | undefined; - image_url?: string | null | undefined; - published?: boolean | undefined; -} - -export interface AnnouncementFilter { - type?: Announcement['type'][] | undefined; - priority?: Announcement['priority'][] | undefined; - target_audience?: Announcement['target_audience'][] | undefined; - published?: boolean | undefined; - created_by?: string[] | undefined; - date_from?: Date | undefined; - date_to?: Date | undefined; -} - -export default class AnnouncementRepository extends BaseRepository { - constructor() { - super('announcements'); - this.selectFields = [ - 'id', 'title', 'content', 'type', 'priority', 'target_audience', - 'scheduled_for', 'expires_at', 'action_button_text', 'action_button_url', - 'image_url', 'created_by', 'published', 'read_by', 'created_at', - 'updated_at', 'version', 'deleted_at', 'deleted_by' - ]; - } - - async create(data: CreateAnnouncementData, client?: DatabaseClient): Promise { - const now = new Date(); - - // Validation - if (!data.title?.trim()) { - throw new ValidationError('Title is required'); - } - if (!data.content?.trim()) { - throw new ValidationError('Content is required'); - } - if (!data.created_by) { - throw new ValidationError('Created by user ID is required'); - } - - const announcementData = { - ...data, - published: data.published ?? false, - read_by: [], - created_at: now, - updated_at: now, - version: 1 - }; - - const query = ` - INSERT INTO ${this.tableName} ( - title, content, type, priority, target_audience, scheduled_for, - expires_at, action_button_text, action_button_url, image_url, - created_by, published, read_by, created_at, updated_at, version - ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16 - ) RETURNING * - `; - - const values = [ - announcementData.title, - announcementData.content, - announcementData.type, - announcementData.priority, - announcementData.target_audience, - announcementData.scheduled_for || null, - announcementData.expires_at || null, - announcementData.action_button_text || null, - announcementData.action_button_url || null, - announcementData.image_url || null, - announcementData.created_by, - announcementData.published, - JSON.stringify(announcementData.read_by), - announcementData.created_at, - announcementData.updated_at, - announcementData.version - ]; - - try { - const result = await this.query(query, values, client); - logger.info(`Created announcement: ${result.rows[0].id}`); - return this.transformRow(result.rows[0]); - } catch (error) { - logger.error('Failed to create announcement:', error); - throw error; - } - } - - async update(id: string, data: UpdateAnnouncementData, client?: DatabaseClient): Promise { - const existing = await this.findById(id, client); - if (!existing) { - throw new NotFoundError(`Announcement with ID ${id} not found`); - } - - const now = new Date(); - const updateFields: string[] = []; - const updateValues: any[] = []; - let paramIndex = 1; - - // Build dynamic update query - Object.entries(data).forEach(([key, value]) => { - if (value !== undefined) { - updateFields.push(`${key} = $${paramIndex}`); - updateValues.push(value); - paramIndex++; - } - }); - - if (updateFields.length === 0) { - return existing; - } - - // Add updated_at and version - updateFields.push(`updated_at = $${paramIndex}`); - updateValues.push(now); - paramIndex++; - - updateFields.push(`version = $${paramIndex}`); - updateValues.push(existing.version + 1); - paramIndex++; - - // Add WHERE clause - updateValues.push(id); - updateValues.push(existing.version); - - const query = ` - UPDATE ${this.tableName} - SET ${updateFields.join(', ')} - WHERE id = $${paramIndex - 1} AND version = $${paramIndex} AND deleted_at IS NULL - RETURNING * - `; - - try { - const result = await this.query(query, updateValues, client); - if (result.rows.length === 0) { - throw new Error('Concurrent update detected - announcement was modified by another process'); - } - - logger.info(`Updated announcement: ${id}`); - return this.transformRow(result.rows[0]); - } catch (error) { - logger.error(`Failed to update announcement ${id}:`, error); - throw error; - } - } - - async findForUser(userId: string, userRole: string, includeRead: boolean = true, client?: DatabaseClient): Promise { - let whereConditions = ['deleted_at IS NULL', 'published = true']; - const queryParams: any[] = []; - let paramIndex = 1; - - // Filter by expiration - whereConditions.push('(expires_at IS NULL OR expires_at > NOW())'); - - // Filter by schedule - whereConditions.push('(scheduled_for IS NULL OR scheduled_for <= NOW())'); - - // Filter by target audience - if (userRole !== 'ceo') { - whereConditions.push(`(target_audience = 'all' OR target_audience = $${paramIndex})`); - queryParams.push(this.mapRoleToAudience(userRole)); - paramIndex++; - } - - // Filter by read status if requested - if (!includeRead) { - whereConditions.push(`NOT (read_by ? $${paramIndex})`); - queryParams.push(userId); - paramIndex++; - } - - const query = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - ORDER BY - CASE priority - WHEN 'critical' THEN 1 - WHEN 'high' THEN 2 - WHEN 'medium' THEN 3 - WHEN 'low' THEN 4 - END, - created_at DESC - `; - - try { - const result = await this.query(query, queryParams, client); - return result.rows.map(row => this.transformRow(row)); - } catch (error) { - logger.error(`Failed to find announcements for user ${userId}:`, error); - throw error; - } - } - - async markAsRead(announcementId: string, userId: string, client?: DatabaseClient): Promise { - const query = ` - UPDATE ${this.tableName} - SET read_by = read_by || $1::jsonb, - updated_at = NOW(), - version = version + 1 - WHERE id = $2 - AND deleted_at IS NULL - AND NOT (read_by ? $3) - `; - - const values = [JSON.stringify([userId]), announcementId, userId]; - - try { - await this.query(query, values, client); - logger.info(`Marked announcement ${announcementId} as read by user ${userId}`); - } catch (error) { - logger.error(`Failed to mark announcement ${announcementId} as read:`, error); - throw error; - } - } - - async findWithFilter(filter: AnnouncementFilter, limit: number = 50, offset: number = 0, client?: DatabaseClient): Promise<{ data: Announcement[]; total: number }> { - const whereConditions = ['deleted_at IS NULL']; - const queryParams: any[] = []; - let paramIndex = 1; - - // Apply filters - if (filter.type && filter.type.length > 0) { - whereConditions.push(`type = ANY($${paramIndex})`); - queryParams.push(filter.type); - paramIndex++; - } - - if (filter.priority && filter.priority.length > 0) { - whereConditions.push(`priority = ANY($${paramIndex})`); - queryParams.push(filter.priority); - paramIndex++; - } - - if (filter.target_audience && filter.target_audience.length > 0) { - whereConditions.push(`target_audience = ANY($${paramIndex})`); - queryParams.push(filter.target_audience); - paramIndex++; - } - - if (filter.published !== undefined) { - whereConditions.push(`published = $${paramIndex}`); - queryParams.push(filter.published); - paramIndex++; - } - - if (filter.created_by && filter.created_by.length > 0) { - whereConditions.push(`created_by = ANY($${paramIndex})`); - queryParams.push(filter.created_by); - paramIndex++; - } - - if (filter.date_from) { - whereConditions.push(`created_at >= $${paramIndex}`); - queryParams.push(filter.date_from); - paramIndex++; - } - - if (filter.date_to) { - whereConditions.push(`created_at <= $${paramIndex}`); - queryParams.push(filter.date_to); - paramIndex++; - } - - const whereClause = whereConditions.join(' AND '); - - // Count query - const countQuery = `SELECT COUNT(*) FROM ${this.tableName} WHERE ${whereClause}`; - const countResult = await this.query(countQuery, queryParams, client); - const total = parseInt(countResult.rows[0].count); - - // Data query - queryParams.push(limit, offset); - const dataQuery = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ${whereClause} - ORDER BY created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - - const dataResult = await this.query(dataQuery, queryParams, client); - const data = dataResult.rows.map(row => this.transformRow(row)); - - return { data, total }; - } - - async getStats(client?: DatabaseClient): Promise<{ - total: number; - published: number; - scheduled: number; - expired: number; - byType: Record; - byPriority: Record; - byAudience: Record; - }> { - const query = ` - SELECT - COUNT(*) as total, - COUNT(CASE WHEN published = true THEN 1 END) as published, - COUNT(CASE WHEN scheduled_for > NOW() THEN 1 END) as scheduled, - COUNT(CASE WHEN expires_at < NOW() THEN 1 END) as expired, - jsonb_object_agg(type, type_count) as by_type, - jsonb_object_agg(priority, priority_count) as by_priority, - jsonb_object_agg(target_audience, audience_count) as by_audience - FROM ${this.tableName} - CROSS JOIN LATERAL ( - SELECT type, COUNT(*) as type_count - FROM ${this.tableName} t2 - WHERE t2.type = ${this.tableName}.type AND deleted_at IS NULL - GROUP BY type - ) type_stats - CROSS JOIN LATERAL ( - SELECT priority, COUNT(*) as priority_count - FROM ${this.tableName} t3 - WHERE t3.priority = ${this.tableName}.priority AND deleted_at IS NULL - GROUP BY priority - ) priority_stats - CROSS JOIN LATERAL ( - SELECT target_audience, COUNT(*) as audience_count - FROM ${this.tableName} t4 - WHERE t4.target_audience = ${this.tableName}.target_audience AND deleted_at IS NULL - GROUP BY target_audience - ) audience_stats - WHERE deleted_at IS NULL - `; - - try { - const result = await this.query(query, [], client); - const row = result.rows[0]; - return { - total: parseInt(row.total) || 0, - published: parseInt(row.published) || 0, - scheduled: parseInt(row.scheduled) || 0, - expired: parseInt(row.expired) || 0, - byType: row.by_type || {}, - byPriority: row.by_priority || {}, - byAudience: row.by_audience || {}, - }; - } catch (error) { - logger.error('Failed to get announcement stats:', error); - throw error; - } - } - - private mapRoleToAudience(role: string): string { - const roleMapping: Record = { - 'admin': 'admins', - 'developer': 'developers', - 'designer': 'designers', - 'manager': 'managers', - 'staff': 'all' - }; - return roleMapping[role] || 'all'; - } - - private transformRow(row: any): Announcement { - return { - ...row, - read_by: Array.isArray(row.read_by) ? row.read_by : (row.read_by ? JSON.parse(row.read_by) : []), - created_at: new Date(row.created_at), - updated_at: new Date(row.updated_at), - scheduled_for: row.scheduled_for ? new Date(row.scheduled_for) : null, - expires_at: row.expires_at ? new Date(row.expires_at) : null, - deleted_at: row.deleted_at ? new Date(row.deleted_at) : null, - }; - } -} \ No newline at end of file diff --git a/backend/src/db/BaseRepository.ts b/backend/src/db/BaseRepository.ts deleted file mode 100644 index 581788c..0000000 --- a/backend/src/db/BaseRepository.ts +++ /dev/null @@ -1,581 +0,0 @@ -import { PoolClient } from 'pg'; -import { query, transaction, DatabaseClient } from '@config/database'; -import { logger, performanceLogger } from '@utils/logger'; -import { DatabaseError, NotFoundError, ValidationError } from '@utils/errors'; - -/** - * Base Repository class providing common CRUD operations - * Implements enterprise patterns: soft delete, audit trail, optimistic locking - */ - -export interface BaseEntity { - id: string; - created_at: Date; - updated_at: Date; - version: number; - deleted_at?: Date | null; - deleted_by?: string | null; -} - -export interface FilterOptions { - limit?: number; - offset?: number; - orderBy?: string; - orderDirection?: 'ASC' | 'DESC'; - includeDeleted?: boolean; - filters?: Record; -} - -export interface PaginatedResult { - data: T[]; - total: number; - limit: number; - offset: number; - hasMore: boolean; -} - -export abstract class BaseRepository { - protected tableName: string; - protected primaryKey: string = 'id'; - protected selectFields: string[] = ['*']; - - constructor(tableName: string) { - this.tableName = tableName; - } - - /** - * Create a new entity - */ - async create(data: Partial>, client?: DatabaseClient): Promise { - const fields = Object.keys(data); - const values = Object.values(data); - const placeholders = fields.map((_, index) => `$${index + 1}`).join(', '); - - const sql = ` - INSERT INTO ${this.tableName} (${fields.join(', ')}) - VALUES (${placeholders}) - RETURNING ${this.selectFields.join(', ')} - `; - - return performanceLogger.trackAsyncOperation( - async () => { - const result = await query(sql, values, client); - - if (result.rows.length === 0 || result.rows[0] === undefined) { - throw new DatabaseError('Failed to create entity - no rows returned'); - } - - logger.debug( - { - table: this.tableName, - operation: 'create', - entityId: result.rows[0].id, - }, - 'Entity created successfully' - ); - - // Ensure result.rows[0] is not undefined before returning - if (result.rows[0] === undefined) { - throw new DatabaseError('Failed to create entity - undefined row returned'); - } - return result.rows[0] as T; - }, - `repository-create-${this.tableName}`, - { entityType: this.tableName } - ); - } - - /** - * Find entity by ID - */ - async findById( - id: string, - includeDeleted: boolean = false, - client?: DatabaseClient - ): Promise { - const deletedCondition = includeDeleted ? '' : 'AND deleted_at IS NULL'; - - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ${this.primaryKey} = $1 ${deletedCondition} - `; - - return performanceLogger.trackAsyncOperation( - async () => { - const result = await query(sql, [id], client); - return result.rows[0] || null; - }, - `repository-findById-${this.tableName}`, - { entityId: id } - ); - } - - /** - * Find entity by ID or throw NotFoundError - */ - async findByIdOrThrow( - id: string, - includeDeleted: boolean = false, - client?: DatabaseClient - ): Promise { - const entity = await this.findById(id, includeDeleted, client); - - if (!entity) { - throw new NotFoundError(`${this.tableName} with id ${id} not found`); - } - - return entity; - } - - /** - * Update entity by ID with optimistic locking - */ - async update( - id: string, - data: Partial>, - expectedVersion?: number, - client?: DatabaseClient - ): Promise { - // Exclude audit fields from update data - const { created_at, updated_at, version, deleted_at, deleted_by, ...updateData } = data as any; - - const fields = Object.keys(updateData); - const values = Object.values(updateData); - - if (fields.length === 0) { - throw new ValidationError('No fields provided for update', []); - } - - const setClause = fields.map((field, index) => `${field} = $${index + 2}`).join(', '); - const versionCondition = expectedVersion ? 'AND version = $' + (fields.length + 3) : ''; - const versionParams = expectedVersion ? [expectedVersion] : []; - - const sql = ` - UPDATE ${this.tableName} - SET ${setClause} - WHERE ${this.primaryKey} = $1 - AND deleted_at IS NULL - ${versionCondition} - RETURNING ${this.selectFields.join(', ')} - `; - - return performanceLogger.trackAsyncOperation( - async () => { - const result = await query(sql, [id, ...values, ...versionParams], client); - - if (result.rows.length === 0) { - if (expectedVersion) { - throw new ValidationError( - 'Optimistic locking failure - entity was modified by another transaction', - [] - ); - } - throw new NotFoundError(`${this.tableName} with id ${id} not found or already deleted`); - } - - logger.debug( - { - table: this.tableName, - operation: 'update', - entityId: id, - fields: fields, - }, - 'Entity updated successfully' - ); - - if (result.rows[0] === undefined) { - throw new DatabaseError('Failed to update entity - undefined row returned'); - } - return result.rows[0] as T; - }, - `repository-update-${this.tableName}`, - { entityId: id, fieldsUpdated: fields.length } - ); - } - - /** - * Soft delete entity - */ - async softDelete(id: string, deletedBy: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET deleted_at = NOW(), deleted_by = $2 - WHERE ${this.primaryKey} = $1 AND deleted_at IS NULL - `; - - return performanceLogger.trackAsyncOperation( - async () => { - const result = await query(sql, [id, deletedBy], client); - - const wasDeleted = result.rowCount > 0; - - if (wasDeleted) { - logger.info( - { - table: this.tableName, - operation: 'softDelete', - entityId: id, - deletedBy: deletedBy, - }, - 'Entity soft deleted successfully' - ); - } - - return wasDeleted; - }, - `repository-softDelete-${this.tableName}`, - { entityId: id } - ); - } - - /** - * Restore soft deleted entity - */ - async restore(id: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET deleted_at = NULL, deleted_by = NULL - WHERE ${this.primaryKey} = $1 AND deleted_at IS NOT NULL - `; - - return performanceLogger.trackAsyncOperation( - async () => { - const result = await query(sql, [id], client); - - const wasRestored = result.rowCount > 0; - - if (wasRestored) { - logger.info( - { - table: this.tableName, - operation: 'restore', - entityId: id, - }, - 'Entity restored successfully' - ); - } - - return wasRestored; - }, - `repository-restore-${this.tableName}`, - { entityId: id } - ); - } - - /** - * Hard delete entity (use with caution) - */ - async hardDelete(id: string, client?: DatabaseClient): Promise { - const sql = `DELETE FROM ${this.tableName} WHERE ${this.primaryKey} = $1`; - - return performanceLogger.trackAsyncOperation( - async () => { - const result = await query(sql, [id], client); - - const wasDeleted = result.rowCount > 0; - - if (wasDeleted) { - logger.warn( - { - table: this.tableName, - operation: 'hardDelete', - entityId: id, - }, - 'Entity permanently deleted' - ); - } - - return wasDeleted; - }, - `repository-hardDelete-${this.tableName}`, - { entityId: id } - ); - } - - /** - * Find multiple entities with filtering and pagination - */ - async findMany( - options: FilterOptions = {}, - client?: DatabaseClient - ): Promise> { - const { - limit = 20, - offset = 0, - orderBy = 'created_at', - orderDirection = 'DESC', - includeDeleted = false, - filters = {}, - } = options; - - // Build WHERE conditions - const whereConditions = []; - const values: any[] = []; - let paramCounter = 1; - - // Soft delete condition - if (!includeDeleted) { - whereConditions.push('deleted_at IS NULL'); - } - - // Additional filters - Object.entries(filters).forEach(([field, value]) => { - if (value !== undefined && value !== null) { - if (Array.isArray(value)) { - whereConditions.push(`${field} = ANY($${paramCounter})`); - values.push(value); - } else { - whereConditions.push(`${field} = $${paramCounter}`); - values.push(value); - } - paramCounter++; - } - }); - - const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''; - - // Count query - const countSql = ` - SELECT COUNT(*) as total - FROM ${this.tableName} - ${whereClause} - `; - - // Data query - const dataSql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - ${whereClause} - ORDER BY ${orderBy} ${orderDirection} - LIMIT $${paramCounter} OFFSET $${paramCounter + 1} - `; - - return performanceLogger.trackAsyncOperation( - async () => { - // Execute both queries in parallel for better performance - const [countResult, dataResult] = await Promise.all([ - query<{ total: string }>(countSql, values, client), - query(dataSql, [...values, limit, offset], client), - ]); - - const total = countResult.rows[0]?.total ? parseInt(countResult.rows[0].total, 10) : 0; - const data = dataResult.rows; - const hasMore = offset + limit < total; - - return { - data, - total, - limit, - offset, - hasMore, - }; - }, - `repository-findMany-${this.tableName}`, - { limit, offset, filterCount: Object.keys(filters).length } - ); - } - - /** - * Count entities with optional filters - */ - async count( - filters: Record = {}, - includeDeleted: boolean = false, - client?: DatabaseClient - ): Promise { - const whereConditions = []; - const values: any[] = []; - let paramCounter = 1; - - // Soft delete condition - if (!includeDeleted) { - whereConditions.push('deleted_at IS NULL'); - } - - // Additional filters - Object.entries(filters).forEach(([field, value]) => { - if (value !== undefined && value !== null) { - whereConditions.push(`${field} = $${paramCounter}`); - values.push(value); - paramCounter++; - } - }); - - const whereClause = whereConditions.length > 0 ? `WHERE ${whereConditions.join(' AND ')}` : ''; - - const sql = ` - SELECT COUNT(*) as total - FROM ${this.tableName} - ${whereClause} - `; - - return performanceLogger.trackAsyncOperation( - async () => { - const result = await query<{ total: string }>(sql, values, client); - return result.rows[0]?.total ? parseInt(result.rows[0].total, 10) : 0; - }, - `repository-count-${this.tableName}`, - { filterCount: Object.keys(filters).length } - ); - } - - /** - * Check if entity exists - */ - async exists( - id: string, - includeDeleted: boolean = false, - client?: DatabaseClient - ): Promise { - const deletedCondition = includeDeleted ? '' : 'AND deleted_at IS NULL'; - - const sql = ` - SELECT 1 FROM ${this.tableName} - WHERE ${this.primaryKey} = $1 ${deletedCondition} - LIMIT 1 - `; - - const result = await query(sql, [id], client); - return result.rows.length > 0; - } - - /** - * Execute raw SQL query within repository context - */ - protected async executeRawQuery( - sql: string, - params: any[] = [], - client?: DatabaseClient - ): Promise<{ rows: R[]; rowCount: number }> { - return performanceLogger.trackAsyncOperation( - async () => { - const result = await query(sql, params, client); - - logger.debug( - { - table: this.tableName, - operation: 'rawQuery', - sql: sql.substring(0, 100) + (sql.length > 100 ? '...' : ''), - paramCount: params.length, - rowCount: result.rowCount, - }, - 'Raw query executed' - ); - - return result; - }, - `repository-rawQuery-${this.tableName}`, - { paramCount: params.length } - ); - } - - /** - * Execute multiple operations in a transaction - */ - async executeInTransaction(callback: (client: PoolClient) => Promise): Promise { - return transaction(callback); - } - - /** - * Bulk insert entities (for seeding and imports) - */ - async bulkCreate( - entities: Partial>[], - client?: DatabaseClient - ): Promise { - if (entities.length === 0) { - return []; - } - - // Get all unique field names - const allFields = new Set(); - entities.forEach((entity) => { - Object.keys(entity).forEach((field) => allFields.add(field)); - }); - - const fields = Array.from(allFields); - const values: any[] = []; - const valuePlaceholders: string[] = []; - - entities.forEach((entity, entityIndex) => { - const entityValues = fields.map((field) => entity[field as keyof typeof entity] ?? null); - values.push(...entityValues); - - const startParam = entityIndex * fields.length + 1; - const entityPlaceholders = fields.map((_, fieldIndex) => `$${startParam + fieldIndex}`); - valuePlaceholders.push(`(${entityPlaceholders.join(', ')})`); - }); - - const sql = ` - INSERT INTO ${this.tableName} (${fields.join(', ')}) - VALUES ${valuePlaceholders.join(', ')} - RETURNING ${this.selectFields.join(', ')} - `; - - return performanceLogger.trackAsyncOperation( - async () => { - const result = await query(sql, values, client); - - logger.info( - { - table: this.tableName, - operation: 'bulkCreate', - entityCount: entities.length, - fieldsCount: fields.length, - }, - 'Bulk create completed successfully' - ); - - return result.rows; - }, - `repository-bulkCreate-${this.tableName}`, - { entityCount: entities.length } - ); - } - - /** - * Get repository statistics - */ - async getStats(client?: DatabaseClient): Promise<{ - total: number; - active: number; - deleted: number; - createdToday: number; - updatedToday: number; - }> { - const sql = ` - SELECT - COUNT(*) as total, - COUNT(*) FILTER (WHERE deleted_at IS NULL) as active, - COUNT(*) FILTER (WHERE deleted_at IS NOT NULL) as deleted, - COUNT(*) FILTER (WHERE DATE(created_at) = CURRENT_DATE) as created_today, - COUNT(*) FILTER (WHERE DATE(updated_at) = CURRENT_DATE AND updated_at::date > created_at::date) as updated_today - FROM ${this.tableName} - `; - - const result = await query(sql, [], client); - const stats = result.rows[0]; - - return { - total: parseInt(stats.total, 10), - active: parseInt(stats.active, 10), - deleted: parseInt(stats.deleted, 10), - createdToday: parseInt(stats.created_today, 10), - updatedToday: parseInt(stats.updated_today, 10), - }; - } - - /** - * Direct query method for complex database operations - */ - async query( - sql: string, - params: any[] = [], - client?: DatabaseClient - ): Promise<{ rows: R[]; rowCount: number }> { - return this.executeRawQuery(sql, params, client); - } -} - -export default BaseRepository; diff --git a/backend/src/db/ChannelRepository.ts b/backend/src/db/ChannelRepository.ts deleted file mode 100644 index 6cb6be9..0000000 --- a/backend/src/db/ChannelRepository.ts +++ /dev/null @@ -1,721 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import { logger } from '@utils/logger'; -import { ValidationError, ConflictError, NotFoundError } from '@utils/errors'; -import BaseRepository, { BaseEntity, FilterOptions } from './BaseRepository'; - -export interface Channel extends BaseEntity { - name: string; - description?: string; - category_id?: string; - channel_type: - | 'project' - | 'department' - | 'initiative' - | 'temporary' - | 'emergency' - | 'announcement'; - privacy_level: 'public' | 'private' | 'restricted'; - status: 'active' | 'archived' | 'paused' | 'completed'; - created_by: string; - owned_by: string; - moderators: string[]; - members: string[]; - member_count: number; - max_members: number; - auto_join_roles: string[]; - settings: Record; - integrations: Record; - activity_stats: Record; - project_info: Record; - schedule: Record; - archived_at?: Date; - archived_by?: string; - archive_reason?: string; - retention_until?: Date; - last_activity_at: Date; -} - -export interface CreateChannelData { - name: string; - description?: string; - category_id?: string; - channel_type?: Channel['channel_type']; - privacy_level?: Channel['privacy_level']; - created_by: string; - owned_by?: string; - members?: string[]; - moderators?: string[]; - max_members?: number; - auto_join_roles?: string[]; - settings?: Record; - project_info?: Record; - integrations?: Record; -} - -export interface ChannelWithDetails extends Channel { - category_name?: string; - owner_name?: string; - member_details?: Array<{ - id: string; - name: string; - email: string; - role: string; - avatar_url?: string; - }>; -} - -class ChannelRepository extends BaseRepository { - constructor() { - super('channels'); - } - - /** - * Create new channel with validation - */ - async createChannel(channelData: CreateChannelData, client?: DatabaseClient): Promise { - // Check for duplicate name within category - if (channelData.category_id) { - const existingChannel = await this.findByCategoryAndName( - channelData.category_id, - channelData.name, - client - ); - if (existingChannel) { - throw new ConflictError('Channel name already exists in this category', { - name: channelData.name, - categoryId: channelData.category_id, - }); - } - } - - // Set defaults - const channelToCreate = { - ...channelData, - owned_by: channelData.owned_by || channelData.created_by, - channel_type: channelData.channel_type || 'project', - privacy_level: channelData.privacy_level || 'public', - status: 'active' as const, - members: channelData.members || [channelData.created_by], - moderators: channelData.moderators || [channelData.created_by], - member_count: channelData.members?.length || 1, - max_members: channelData.max_members || 100, - auto_join_roles: channelData.auto_join_roles || [], - settings: { - allow_voice_commands: true, - voice_command_roles: ['ceo', 'manager'], - allow_file_uploads: true, - ...channelData.settings, - }, - integrations: { - ai_assistant_enabled: true, - task_creation_enabled: true, - ...channelData.integrations, - }, - activity_stats: { - total_messages: 0, - total_files: 0, - total_tasks: 0, - last_activity: null, - }, - project_info: channelData.project_info || {}, - schedule: { - timezone: 'UTC', - working_hours: { - monday: { start: '09:00', end: '17:00' }, - tuesday: { start: '09:00', end: '17:00' }, - wednesday: { start: '09:00', end: '17:00' }, - thursday: { start: '09:00', end: '17:00' }, - friday: { start: '09:00', end: '17:00' }, - saturday: null, - sunday: null, - }, - }, - }; - - const channel = await this.create(channelToCreate, client); - - logger.info( - { - channelId: channel.id, - name: channel.name, - createdBy: channel.created_by, - memberCount: channel.member_count, - }, - 'Channel created successfully' - ); - - return channel; - } - - /** - * Find channel by category and name - */ - async findByCategoryAndName( - categoryId: string, - name: string, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE category_id = $1 - AND LOWER(name) = LOWER($2) - AND deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [categoryId, name], client); - return result.rows[0] || null; - } - - /** - * Find channels by category - */ - async findByCategory( - categoryId: string, - includeDeleted: boolean = false, - client?: DatabaseClient - ): Promise { - const options: FilterOptions = { - filters: { category_id: categoryId }, - includeDeleted, - orderBy: 'last_activity_at', - orderDirection: 'DESC', - }; - - const result = await this.findMany(options, client); - return result.data; - } - - /** - * Find channels where user is a member - */ - async findByMember( - userId: string, - includeDeleted: boolean = false, - client?: DatabaseClient - ): Promise { - const deletedCondition = includeDeleted ? '' : 'AND deleted_at IS NULL'; - - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE $1 = ANY(members) ${deletedCondition} - ORDER BY last_activity_at DESC - `; - - const result = await this.executeRawQuery(sql, [userId], client); - return result.rows; - } - - /** - * Find channels accessible by user (considering role and privacy) - */ - async findAccessibleByUser( - userId: string, - userRole: string, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND ( - -- Public channels - privacy_level = 'public' - -- User is a member - OR $1 = ANY(members) - -- CEO can access all channels - OR $2 = 'ceo' - -- Restricted channels with role access - OR (privacy_level = 'restricted' AND $2 = ANY(auto_join_roles)) - ) - ORDER BY - CASE WHEN $1 = ANY(members) THEN 1 ELSE 2 END, - last_activity_at DESC - `; - - const result = await this.executeRawQuery(sql, [userId, userRole], client); - return result.rows; - } - - /** - * Add member to channel - */ - async addMember( - channelId: string, - userId: string, - addedBy: string, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT add_channel_member($1, $2, $3) - `; - - try { - const result = await this.executeRawQuery<{ add_channel_member: boolean }>( - sql, - [channelId, userId, addedBy], - client - ); - - const success = result.rows[0]?.add_channel_member || false; - - if (success) { - logger.info( - { - channelId, - userId, - addedBy, - }, - 'Member added to channel' - ); - } - - return success; - } catch (error) { - logger.error({ error, channelId, userId }, 'Failed to add member to channel'); - throw error; - } - } - - /** - * Remove member from channel - */ - async removeMember( - channelId: string, - userId: string, - removedBy: string, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT remove_channel_member($1, $2, $3) - `; - - try { - const result = await this.executeRawQuery<{ remove_channel_member: boolean }>( - sql, - [channelId, userId, removedBy], - client - ); - - const success = result.rows[0]?.remove_channel_member || false; - - if (success) { - logger.info( - { - channelId, - userId, - removedBy, - }, - 'Member removed from channel' - ); - } - - return success; - } catch (error) { - logger.error({ error, channelId, userId }, 'Failed to remove member from channel'); - throw error; - } - } - - /** - * Add multiple members to channel - */ - async addMembers( - channelId: string, - userIds: string[], - addedBy: string, - client?: DatabaseClient - ): Promise { - const successfulAdds: string[] = []; - - for (const userId of userIds) { - try { - const success = await this.addMember(channelId, userId, addedBy, client); - if (success) { - successfulAdds.push(userId); - } - } catch (error) { - logger.warn({ error, channelId, userId }, 'Failed to add individual member'); - } - } - - return successfulAdds; - } - - /** - * Get channel with detailed member information - */ - async findWithMembers( - channelId: string, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT - c.*, - cat.name as category_name, - owner.name as owner_name, - ARRAY( - SELECT json_build_object( - 'id', u.id, - 'name', u.name, - 'email', u.email, - 'role', u.role, - 'avatar_url', u.avatar_url - ) - FROM users u - WHERE u.id = ANY(c.members) - AND u.deleted_at IS NULL - ORDER BY u.name - ) as member_details - FROM channels c - LEFT JOIN categories cat ON c.category_id = cat.id - LEFT JOIN users owner ON c.owned_by = owner.id - WHERE c.id = $1 AND c.deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [channelId], client); - return result.rows[0] || null; - } - - /** - * Search channels by name or description - */ - async searchChannels( - searchTerm: string, - userId?: string, - userRole?: string, - limit: number = 20, - offset: number = 0, - client?: DatabaseClient - ): Promise { - let accessCondition = ''; - let params = [`%${searchTerm}%`, limit, offset]; - - if (userId && userRole) { - accessCondition = ` - AND ( - privacy_level = 'public' - OR $4 = ANY(members) - OR $5 = 'ceo' - OR (privacy_level = 'restricted' AND $5 = ANY(auto_join_roles)) - ) - `; - params.push(userId, userRole); - } - - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ( - LOWER(name) LIKE LOWER($1) OR - LOWER(description) LIKE LOWER($1) - ) - AND deleted_at IS NULL - AND status = 'active' - ${accessCondition} - ORDER BY - CASE WHEN LOWER(name) LIKE LOWER($1) THEN 1 ELSE 2 END, - last_activity_at DESC - LIMIT $2 OFFSET $3 - `; - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Get channels by status - */ - async findByStatus( - status: Channel['status'], - limit?: number, - client?: DatabaseClient - ): Promise { - const options: FilterOptions = { - filters: { status }, - orderBy: 'last_activity_at', - orderDirection: 'DESC', - ...(limit !== undefined ? { limit } : {}), - }; - - const result = await this.findMany(options, client); - return result.data; - } - - /** - * Archive channel - */ - async archiveChannel( - channelId: string, - archivedBy: string, - reason?: string, - client?: DatabaseClient - ): Promise { - const sql = ` - UPDATE ${this.tableName} - SET - status = 'archived', - archived_at = NOW(), - archived_by = $2, - archive_reason = $3 - WHERE id = $1 AND deleted_at IS NULL - `; - - const result = await this.executeRawQuery( - sql, - [channelId, archivedBy, reason || 'Manual archive'], - client - ); - - if (result.rowCount > 0) { - logger.info( - { - channelId, - archivedBy, - reason, - }, - 'Channel archived successfully' - ); - } - - return result.rowCount > 0; - } - - /** - * Restore archived channel - */ - async restoreChannel(channelId: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET - status = 'active', - archived_at = NULL, - archived_by = NULL, - archive_reason = NULL - WHERE id = $1 AND deleted_at IS NULL AND status = 'archived' - `; - - const result = await this.executeRawQuery(sql, [channelId], client); - - if (result.rowCount > 0) { - logger.info({ channelId }, 'Channel restored successfully'); - } - - return result.rowCount > 0; - } - - /** - * Update channel activity timestamp - */ - async updateActivity(channelId: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET last_activity_at = NOW() - WHERE id = $1 AND deleted_at IS NULL - `; - - await this.executeRawQuery(sql, [channelId], client); - } - - /** - * Get channels requiring attention (no recent activity) - */ - async getInactiveChannels( - daysSinceActivity: number = 30, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE last_activity_at < NOW() - INTERVAL '${daysSinceActivity} days' - AND status = 'active' - AND deleted_at IS NULL - ORDER BY last_activity_at ASC - `; - - const result = await this.executeRawQuery(sql, [], client); - return result.rows; - } - - /** - * Get channel statistics - */ - async getChannelStats(client?: DatabaseClient): Promise<{ - totalChannels: number; - activeChannels: number; - channelsByType: Record; - channelsByPrivacy: Record; - averageMembers: number; - mostActiveChannels: Array<{ id: string; name: string; activity_count: number }>; - }> { - const statsSql = ` - SELECT - COUNT(*) as total_channels, - COUNT(*) FILTER (WHERE status = 'active') as active_channels, - COUNT(*) FILTER (WHERE channel_type = 'project') as project_channels, - COUNT(*) FILTER (WHERE channel_type = 'department') as department_channels, - COUNT(*) FILTER (WHERE channel_type = 'initiative') as initiative_channels, - COUNT(*) FILTER (WHERE channel_type = 'temporary') as temporary_channels, - COUNT(*) FILTER (WHERE privacy_level = 'public') as public_channels, - COUNT(*) FILTER (WHERE privacy_level = 'private') as private_channels, - COUNT(*) FILTER (WHERE privacy_level = 'restricted') as restricted_channels, - AVG(member_count) as average_members - FROM ${this.tableName} - WHERE deleted_at IS NULL - `; - - const mostActiveSql = ` - SELECT id, name, - COALESCE((activity_stats->>'total_messages')::int, 0) + - COALESCE((activity_stats->>'total_tasks')::int, 0) as activity_count - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND status = 'active' - ORDER BY activity_count DESC - LIMIT 10 - `; - - const [statsResult, activeResult] = await Promise.all([ - this.executeRawQuery(statsSql, [], client), - this.executeRawQuery(mostActiveSql, [], client), - ]); - - const stats = statsResult.rows[0]; - - return { - totalChannels: parseInt(stats.total_channels, 10), - activeChannels: parseInt(stats.active_channels, 10), - channelsByType: { - project: parseInt(stats.project_channels, 10), - department: parseInt(stats.department_channels, 10), - initiative: parseInt(stats.initiative_channels, 10), - temporary: parseInt(stats.temporary_channels, 10), - }, - channelsByPrivacy: { - public: parseInt(stats.public_channels, 10), - private: parseInt(stats.private_channels, 10), - restricted: parseInt(stats.restricted_channels, 10), - }, - averageMembers: parseFloat(stats.average_members || '0'), - mostActiveChannels: activeResult.rows.map((row: any) => ({ - id: row.id, - name: row.name, - activity_count: parseInt(row.activity_count, 10), - })), - }; - } - - /** - * Find channels for a specific user based on their role and permissions - */ - async findUserChannels(userId: string, userRole?: string, client?: DatabaseClient): Promise { - let sql: string; - let params: any[]; - - if (userRole === 'ceo') { - // CEO can see all active channels - sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND status = 'active' - ORDER BY last_activity_at DESC - `; - params = []; - } else if (userRole === 'manager') { - // Managers can see channels they created OR channels where they are moderators OR channels they are members of - sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ( - created_by = $1 OR - owned_by = $1 OR - $1 = ANY(moderators) OR - $1 = ANY(members) - ) - AND deleted_at IS NULL - AND status = 'active' - ORDER BY last_activity_at DESC - `; - params = [userId]; - } else { - // Staff can only see channels where they are members - sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE $1 = ANY(members) - AND deleted_at IS NULL - AND status = 'active' - ORDER BY last_activity_at DESC - `; - params = [userId]; - } - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Get channel members - */ - async getMembers(channelId: string, client?: DatabaseClient): Promise { - const sql = ` - SELECT u.id, u.name, u.email, u.role, u.avatar_url - FROM users u - JOIN ${this.tableName} c ON u.id = ANY(c.members) - WHERE c.id = $1 AND c.deleted_at IS NULL - AND u.deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [channelId], client); - return result.rows; - } - - /** - * Check if user can access channel - */ - async canUserAccess( - channelId: string, - userId: string, - userRole: string, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT id, privacy_level, members, created_by - FROM ${this.tableName} - WHERE id = $1 AND deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [channelId], client); - if (result.rows.length === 0) { - return false; - } - - const channel = result.rows[0]; - - // CEO can access any channel - if (userRole === 'ceo') { - return true; - } - - // Creator can always access - if (channel.created_by === userId) { - return true; - } - - // Check if user is a member - if (channel.members && channel.members.includes(userId)) { - return true; - } - - // Public channels are accessible to all - if (channel.privacy_level === 'public') { - return true; - } - - return false; - } -} - -export default ChannelRepository; diff --git a/backend/src/db/FileRepository.ts b/backend/src/db/FileRepository.ts deleted file mode 100644 index 6ee2a77..0000000 --- a/backend/src/db/FileRepository.ts +++ /dev/null @@ -1,699 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import { logger } from '@utils/logger'; -import { ValidationError, NotFoundError } from '@utils/errors'; -import BaseRepository, { BaseEntity, FilterOptions } from './BaseRepository'; - -export interface FileEntity extends BaseEntity { - filename: string; - original_name: string; - mime_type: string; - size: number; - url: string; - download_url?: string; - thumbnail_url?: string; - uploaded_by: string; - channel_id?: string; - task_id?: string; - message_id?: string; - file_path: string; - storage_provider: 'local' | 'aws_s3' | 'gcs' | 'azure'; - storage_key: string; - checksum: string; - metadata: Record; - access_count: number; - last_accessed_at?: Date; - virus_scan_status: 'pending' | 'clean' | 'infected' | 'error'; - virus_scan_result?: Record; - expires_at?: Date; - is_public: boolean; - download_count: number; -} - -export interface CreateFileData { - filename: string; - originalName: string; - mimeType: string; - size: number; - url: string; - downloadUrl?: string; - thumbnailUrl?: string; - uploadedBy: string; - channelId?: string; - taskId?: string; - messageId?: string; - filePath: string; - storageProvider?: FileEntity['storage_provider']; - storageKey: string; - checksum: string; - metadata?: Record; - isPublic?: boolean; - expiresAt?: Date; -} - -export interface FileWithUploader extends FileEntity { - uploader_name: string; - uploader_email: string; - uploader_avatar?: string; - channel_name?: string; - task_title?: string; -} - -class FileRepository extends BaseRepository { - constructor() { - super('files'); - } - - /** - * Create new file record with validation - */ - async createFile(fileData: CreateFileData, client?: DatabaseClient): Promise { - // Validate file size (100MB default max) - const maxSize = 100 * 1024 * 1024; // 100MB - if (fileData.size > maxSize) { - throw new ValidationError('File size exceeds maximum allowed size', [ - { field: 'size', message: `File size must be less than ${maxSize} bytes` }, - ]); - } - - // Validate mime type - const allowedTypes = this.getAllowedMimeTypes(); - if (!allowedTypes.includes(fileData.mimeType)) { - throw new ValidationError('Unsupported file type', [ - { field: 'mimeType', message: `File type ${fileData.mimeType} is not allowed` }, - ]); - } - - const fileToCreate: Partial> = { - filename: fileData.filename, - original_name: fileData.originalName, - mime_type: fileData.mimeType, - size: fileData.size, - url: fileData.url, - download_url: fileData.downloadUrl ?? '', - thumbnail_url: fileData.thumbnailUrl ?? '', - uploaded_by: fileData.uploadedBy, - channel_id: fileData.channelId ?? '', - task_id: fileData.taskId ?? '', - message_id: fileData.messageId ?? '', - file_path: fileData.filePath, - storage_provider: fileData.storageProvider || 'local', - storage_key: fileData.storageKey, - checksum: fileData.checksum, - metadata: fileData.metadata || {}, - access_count: 0, - virus_scan_status: 'pending' as const, - is_public: fileData.isPublic || false, - download_count: 0, - ...(fileData.expiresAt ? { expires_at: fileData.expiresAt } : {}), - }; - - const file = await this.create(fileToCreate, client); - - logger.info( - { - fileId: file.id, - filename: file.filename, - size: file.size, - uploadedBy: file.uploaded_by, - channelId: file.channel_id, - taskId: file.task_id, - }, - 'File created successfully' - ); - - return file; - } - - /** - * Find files by channel - */ - async findChannelFiles( - channelId: string, - filters?: { - fileType?: string; - uploadedBy?: string; - search?: string; - after?: Date; - before?: Date; - }, - limit: number = 50, - offset: number = 0, - client?: DatabaseClient - ): Promise { - let whereConditions = ['fel.entity_type = \'channel\'', 'fel.entity_id = $1', 'f.deleted_at IS NULL']; - let params: any[] = [channelId]; - let paramIndex = 2; - - // Add filters - if (filters?.fileType) { - whereConditions.push(`f.mime_type LIKE $${paramIndex}`); - params.push(`${filters.fileType}%`); - paramIndex++; - } - - if (filters?.uploadedBy) { - whereConditions.push(`f.uploaded_by = $${paramIndex}`); - params.push(filters.uploadedBy); - paramIndex++; - } - - if (filters?.search) { - whereConditions.push(`( - LOWER(f.filename) LIKE LOWER($${paramIndex}) OR - LOWER(f.original_name) LIKE LOWER($${paramIndex}) - )`); - params.push(`%${filters.search}%`); - paramIndex++; - } - - if (filters?.after) { - whereConditions.push(`f.uploaded_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - - if (filters?.before) { - whereConditions.push(`f.uploaded_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - - const sql = ` - SELECT - f.*, - u.name as uploader_name, - u.email as uploader_email, - u.avatar_url as uploader_avatar, - c.name as channel_name, - t.title as task_title - FROM ${this.tableName} f - INNER JOIN file_entity_links fel ON f.id = fel.file_id - LEFT JOIN users u ON f.uploaded_by = u.id - LEFT JOIN channels c ON fel.entity_id = c.id AND fel.entity_type = 'channel' - LEFT JOIN tasks t ON fel.entity_id = t.id AND fel.entity_type = 'task' - WHERE ${whereConditions.join(' AND ')} - ORDER BY f.uploaded_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - - params.push(limit, offset); - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Get channel file count - */ - async getChannelFileCount( - channelId: string, - filters?: { - fileType?: string; - uploadedBy?: string; - search?: string; - after?: Date; - before?: Date; - }, - client?: DatabaseClient - ): Promise { - let whereConditions = ['fel.entity_type = \'channel\'', 'fel.entity_id = $1', 'f.deleted_at IS NULL']; - let params: any[] = [channelId]; - let paramIndex = 2; - - // Add filters - if (filters?.fileType) { - whereConditions.push(`f.mime_type LIKE $${paramIndex}`); - params.push(`${filters.fileType}%`); - paramIndex++; - } - - if (filters?.uploadedBy) { - whereConditions.push(`f.uploaded_by = $${paramIndex}`); - params.push(filters.uploadedBy); - paramIndex++; - } - - if (filters?.search) { - whereConditions.push(`( - LOWER(f.filename) LIKE LOWER($${paramIndex}) OR - LOWER(f.original_name) LIKE LOWER($${paramIndex}) - )`); - params.push(`%${filters.search}%`); - paramIndex++; - } - - if (filters?.after) { - whereConditions.push(`f.uploaded_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - - if (filters?.before) { - whereConditions.push(`f.uploaded_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - - const sql = ` - SELECT COUNT(*) as count - FROM ${this.tableName} f - INNER JOIN file_entity_links fel ON f.id = fel.file_id - WHERE ${whereConditions.join(' AND ')} - `; - - const result = await this.executeRawQuery<{ count: string }>(sql, params, client); - return parseInt(result.rows[0]?.count || '0', 10); - } - - /** - * Find files by task - */ - async findTaskFiles( - taskId: string, - limit: number = 50, - offset: number = 0, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT - f.*, - u.name as uploader_name, - u.email as uploader_email, - u.avatar_url as uploader_avatar, - t.title as task_title - FROM ${this.tableName} f - LEFT JOIN users u ON f.uploaded_by = u.id - LEFT JOIN tasks t ON f.task_id = t.id - WHERE f.task_id = $1 AND f.deleted_at IS NULL - ORDER BY f.created_at DESC - LIMIT $2 OFFSET $3 - `; - - const result = await this.executeRawQuery( - sql, - [taskId, limit, offset], - client - ); - return result.rows; - } - - /** - * Find files by user - */ - async findUserFiles( - userId: string, - filters?: { - channelId?: string; - taskId?: string; - fileType?: string; - }, - limit: number = 50, - offset: number = 0, - client?: DatabaseClient - ): Promise { - let whereConditions = ['f.uploaded_by = $1', 'f.deleted_at IS NULL']; - let params: any[] = [userId]; - let paramIndex = 2; - - // Add filters - if (filters?.channelId) { - whereConditions.push(`f.channel_id = $${paramIndex}`); - params.push(filters.channelId); - paramIndex++; - } - - if (filters?.taskId) { - whereConditions.push(`f.task_id = $${paramIndex}`); - params.push(filters.taskId); - paramIndex++; - } - - if (filters?.fileType) { - whereConditions.push(`f.mime_type LIKE $${paramIndex}`); - params.push(`${filters.fileType}%`); - paramIndex++; - } - - const sql = ` - SELECT - f.*, - u.name as uploader_name, - u.email as uploader_email, - u.avatar_url as uploader_avatar, - c.name as channel_name, - t.title as task_title - FROM ${this.tableName} f - LEFT JOIN users u ON f.uploaded_by = u.id - LEFT JOIN channels c ON f.channel_id = c.id - LEFT JOIN tasks t ON f.task_id = t.id - WHERE ${whereConditions.join(' AND ')} - ORDER BY f.created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - - params.push(limit, offset); - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Search files by name or content - */ - async searchFiles( - searchTerm: string, - options?: { - userId?: string; - channelId?: string; - taskId?: string; - fileType?: string; - }, - limit: number = 50, - offset: number = 0, - client?: DatabaseClient - ): Promise { - let whereConditions = [ - 'f.deleted_at IS NULL', - '(LOWER(f.filename) LIKE LOWER($1) OR LOWER(f.original_name) LIKE LOWER($1))', - ]; - let params: any[] = [`%${searchTerm}%`]; - let paramIndex = 2; - - // Add filters - if (options?.userId) { - whereConditions.push(`f.uploaded_by = $${paramIndex}`); - params.push(options.userId); - paramIndex++; - } - - if (options?.channelId) { - whereConditions.push(`f.channel_id = $${paramIndex}`); - params.push(options.channelId); - paramIndex++; - } - - if (options?.taskId) { - whereConditions.push(`f.task_id = $${paramIndex}`); - params.push(options.taskId); - paramIndex++; - } - - if (options?.fileType) { - whereConditions.push(`f.mime_type LIKE $${paramIndex}`); - params.push(`${options.fileType}%`); - paramIndex++; - } - - const sql = ` - SELECT - f.*, - u.name as uploader_name, - u.email as uploader_email, - u.avatar_url as uploader_avatar, - c.name as channel_name, - t.title as task_title - FROM ${this.tableName} f - LEFT JOIN users u ON f.uploaded_by = u.id - LEFT JOIN channels c ON f.channel_id = c.id - LEFT JOIN tasks t ON f.task_id = t.id - WHERE ${whereConditions.join(' AND ')} - ORDER BY f.created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - - params.push(limit, offset); - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Update file access tracking - */ - async recordFileAccess(fileId: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET - access_count = access_count + 1, - last_accessed_at = NOW() - WHERE id = $1 AND deleted_at IS NULL - `; - - try { - await this.executeRawQuery(sql, [fileId], client); - - logger.debug( - { - fileId, - }, - 'File access recorded' - ); - } catch (error) { - logger.error({ error, fileId }, 'Failed to record file access'); - } - } - - /** - * Update file download count - */ - async recordFileDownload(fileId: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET download_count = download_count + 1 - WHERE id = $1 AND deleted_at IS NULL - `; - - try { - await this.executeRawQuery(sql, [fileId], client); - - logger.debug( - { - fileId, - }, - 'File download recorded' - ); - } catch (error) { - logger.error({ error, fileId }, 'Failed to record file download'); - } - } - - /** - * Update virus scan status - */ - async updateVirusScanStatus( - fileId: string, - status: FileEntity['virus_scan_status'], - result?: Record, - client?: DatabaseClient - ): Promise { - const sql = ` - UPDATE ${this.tableName} - SET - virus_scan_status = $2, - virus_scan_result = $3 - WHERE id = $1 AND deleted_at IS NULL - RETURNING id - `; - - try { - const queryResult = await this.executeRawQuery( - sql, - [fileId, status, JSON.stringify(result || {})], - client - ); - - if (queryResult.rowCount > 0) { - logger.info( - { - fileId, - status, - hasResult: !!result, - }, - 'Virus scan status updated' - ); - return true; - } - return false; - } catch (error) { - logger.error({ error, fileId, status }, 'Failed to update virus scan status'); - return false; - } - } - - /** - * Get file statistics - */ - async getFileStats(client?: DatabaseClient): Promise<{ - totalFiles: number; - totalSize: number; - filesByType: Record; - filesByProvider: Record; - virusScannedFiles: number; - infectedFiles: number; - mostDownloadedFiles: Array<{ - id: string; - filename: string; - downloadCount: number; - }>; - }> { - const statsSql = ` - SELECT - COUNT(*) as total_files, - SUM(size) as total_size, - COUNT(*) FILTER (WHERE mime_type LIKE 'image/%') as image_files, - COUNT(*) FILTER (WHERE mime_type LIKE 'video/%') as video_files, - COUNT(*) FILTER (WHERE mime_type LIKE 'audio/%') as audio_files, - COUNT(*) FILTER (WHERE mime_type = 'application/pdf') as pdf_files, - COUNT(*) FILTER (WHERE storage_provider = 'local') as local_files, - COUNT(*) FILTER (WHERE storage_provider = 'aws_s3') as s3_files, - COUNT(*) FILTER (WHERE virus_scan_status = 'clean') as clean_files, - COUNT(*) FILTER (WHERE virus_scan_status = 'infected') as infected_files - FROM ${this.tableName} - WHERE deleted_at IS NULL - `; - - const topFilesSql = ` - SELECT id, filename, download_count - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND download_count > 0 - ORDER BY download_count DESC - LIMIT 10 - `; - - const [statsResult, topFilesResult] = await Promise.all([ - this.executeRawQuery(statsSql, [], client), - this.executeRawQuery(topFilesSql, [], client), - ]); - - const stats = statsResult.rows[0]; - - return { - totalFiles: parseInt(stats.total_files, 10), - totalSize: parseInt(stats.total_size, 10) || 0, - filesByType: { - image: parseInt(stats.image_files, 10), - video: parseInt(stats.video_files, 10), - audio: parseInt(stats.audio_files, 10), - pdf: parseInt(stats.pdf_files, 10), - }, - filesByProvider: { - local: parseInt(stats.local_files, 10), - aws_s3: parseInt(stats.s3_files, 10), - }, - virusScannedFiles: parseInt(stats.clean_files, 10), - infectedFiles: parseInt(stats.infected_files, 10), - mostDownloadedFiles: topFilesResult.rows.map((row: any) => ({ - id: row.id, - filename: row.filename, - downloadCount: parseInt(row.download_count, 10), - })), - }; - } - - /** - * Clean up expired files - */ - async cleanupExpiredFiles(client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET deleted_at = NOW() - WHERE expires_at IS NOT NULL - AND expires_at < NOW() - AND deleted_at IS NULL - `; - - try { - const result = await this.executeRawQuery(sql, [], client); - - logger.info( - { - deletedCount: result.rowCount, - }, - 'Expired files cleaned up' - ); - - return result.rowCount; - } catch (error) { - logger.error({ error }, 'Failed to clean up expired files'); - return 0; - } - } - - /** - * Get allowed mime types - */ - private getAllowedMimeTypes(): string[] { - return [ - // Images - 'image/jpeg', - 'image/png', - 'image/gif', - 'image/webp', - 'image/svg+xml', - 'image/bmp', - 'image/tiff', - - // Documents - 'application/pdf', - 'application/msword', - 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', - 'application/vnd.ms-excel', - 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', - 'application/vnd.ms-powerpoint', - 'application/vnd.openxmlformats-officedocument.presentationml.presentation', - 'text/plain', - 'text/csv', - 'text/markdown', - 'application/json', - 'application/xml', - - // Archives - 'application/zip', - 'application/x-rar-compressed', - 'application/x-7z-compressed', - 'application/x-tar', - 'application/gzip', - - // Media - 'video/mp4', - 'video/mpeg', - 'video/quicktime', - 'video/webm', - 'audio/mpeg', - 'audio/wav', - 'audio/mp4', - 'audio/webm', - 'audio/ogg', - - // Code files - 'text/javascript', - 'text/css', - 'text/html', - 'application/javascript', - ]; - } - - /** - * Check if mime type is allowed - */ - isMimeTypeAllowed(mimeType: string): boolean { - return this.getAllowedMimeTypes().includes(mimeType); - } - - /** - * Get file category from mime type - */ - getFileCategory(mimeType: string): string { - if (mimeType.startsWith('image/')) return 'image'; - if (mimeType.startsWith('video/')) return 'video'; - if (mimeType.startsWith('audio/')) return 'audio'; - if (mimeType === 'application/pdf') return 'document'; - if (mimeType.includes('word') || mimeType.includes('excel') || mimeType.includes('powerpoint')) return 'office'; - if (mimeType.startsWith('text/') || mimeType.includes('json') || mimeType.includes('xml')) return 'text'; - if (mimeType.includes('zip') || mimeType.includes('rar') || mimeType.includes('tar')) return 'archive'; - return 'other'; - } -} - -export default FileRepository; \ No newline at end of file diff --git a/backend/src/db/MessageRepository.ts b/backend/src/db/MessageRepository.ts deleted file mode 100644 index 7fd2778..0000000 --- a/backend/src/db/MessageRepository.ts +++ /dev/null @@ -1,648 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import { logger } from '@utils/logger'; -import { ValidationError, NotFoundError } from '@utils/errors'; -import BaseRepository, { BaseEntity, FilterOptions } from './BaseRepository'; - -export interface Message extends BaseEntity { - channel_id: string; - task_id?: string; - user_id: string; - content: string; - message_type: 'text' | 'voice' | 'file' | 'system' | 'command_result' | 'ai_response'; - voice_data?: Record; - transcription?: string; - attachments: any[]; - reply_to?: string; - thread_root?: string; - is_edited: boolean; - is_pinned: boolean; - is_announcement: boolean; - reactions: Record; - mentions: string[]; - ai_generated: boolean; - ai_context?: Record; - command_execution_id?: string; - metadata: Record; - formatting: Record; - edited_at?: Date; - user_name?: string; - user_avatar?: string; -} - -export interface CreateMessageData { - channel_id: string; - task_id?: string; - user_id: string; - content: string; - message_type?: Message['message_type']; - voice_data?: Record; - transcription?: string; - attachments?: any[]; - reply_to?: string; - thread_root?: string; - mentions?: string[]; - ai_generated?: boolean; - ai_context?: Record; - command_execution_id?: string; - metadata?: Record; - formatting?: Record; -} - -export interface MessageWithUser extends Message { - user_name: string; - user_email: string; - user_avatar?: string; - user_role: string; - reply_count?: number; - last_reply_timestamp?: Date; - deleted_by_name?: string; -} - -class MessageRepository extends BaseRepository { - constructor() { - super('messages'); - } - - /** - * Create new message with validation - */ - async createMessage(messageData: CreateMessageData, client?: DatabaseClient): Promise { - // Set defaults - const messageToCreate = { - ...messageData, - message_type: messageData.message_type || 'text', - attachments: messageData.attachments || [], - mentions: messageData.mentions || [], - metadata: messageData.metadata || {}, - formatting: messageData.formatting || {}, - reactions: {}, - is_edited: false, - is_pinned: false, - is_announcement: false, - ai_generated: messageData.ai_generated || false, - }; - - const message = await this.create(messageToCreate, client); - - logger.info( - { - messageId: message.id, - channelId: message.channel_id, - userId: message.user_id, - messageType: message.message_type, - contentLength: message.content.length, - }, - 'Message created successfully' - ); - - return message; - } - - /** - * Find messages in a channel - */ - async findChannelMessages( - channelId: string, - filters?: { - threadRoot?: string; - messageType?: string; - before?: Date; - after?: Date; - includeThreadReplies?: boolean; // New flag to control thread reply inclusion - }, - limit: number = 50, - offset: number = 0, - client?: DatabaseClient - ): Promise { - let whereConditions = ['m.channel_id = $1', 'm.deleted_at IS NULL']; - let params: any[] = [channelId]; - let paramIndex = 2; - - // Add filters - if (filters?.threadRoot) { - whereConditions.push(`m.thread_root = $${paramIndex}`); - params.push(filters.threadRoot); - paramIndex++; - } else if (!filters?.includeThreadReplies) { - // For main channel messages, exclude thread replies (messages with thread_root) - // unless explicitly requested to include them - whereConditions.push(`m.thread_root IS NULL`); - } - - if (filters?.messageType) { - whereConditions.push(`m.message_type = $${paramIndex}`); - params.push(filters.messageType); - paramIndex++; - } - - if (filters?.before) { - whereConditions.push(`m.created_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - - if (filters?.after) { - whereConditions.push(`m.created_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - - const sql = ` - SELECT - m.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - COALESCE(thread_stats.reply_count, 0) as reply_count, - thread_stats.last_reply_timestamp, - deleter.name as deleted_by_name - FROM ${this.tableName} m - LEFT JOIN users u ON m.user_id = u.id - LEFT JOIN users deleter ON m.deleted_by = deleter.id - LEFT JOIN ( - SELECT - thread_root, - COUNT(*) as reply_count, - MAX(created_at) as last_reply_timestamp - FROM ${this.tableName} - WHERE thread_root IS NOT NULL - AND deleted_at IS NULL - GROUP BY thread_root - ) thread_stats ON m.id = thread_stats.thread_root - WHERE ${whereConditions.join(' AND ')} - ORDER BY m.created_at DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - - params.push(limit, offset); - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Get channel message count - */ - async getChannelMessageCount( - channelId: string, - filters?: { - threadRoot?: string; - messageType?: string; - before?: Date; - after?: Date; - includeThreadReplies?: boolean; - }, - client?: DatabaseClient - ): Promise { - let whereConditions = ['channel_id = $1', 'deleted_at IS NULL']; - let params: any[] = [channelId]; - let paramIndex = 2; - - // Add filters - if (filters?.threadRoot) { - whereConditions.push(`thread_root = $${paramIndex}`); - params.push(filters.threadRoot); - paramIndex++; - } else if (!filters?.includeThreadReplies) { - // For main channel messages, exclude thread replies (messages with thread_root) - // unless explicitly requested to include them - whereConditions.push(`thread_root IS NULL`); - } - - if (filters?.messageType) { - whereConditions.push(`message_type = $${paramIndex}`); - params.push(filters.messageType); - paramIndex++; - } - - if (filters?.before) { - whereConditions.push(`created_at < $${paramIndex}`); - params.push(filters.before); - paramIndex++; - } - - if (filters?.after) { - whereConditions.push(`created_at > $${paramIndex}`); - params.push(filters.after); - paramIndex++; - } - - const sql = ` - SELECT COUNT(*) as count - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - `; - - const result = await this.executeRawQuery<{ count: string }>(sql, params, client); - return parseInt(result.rows[0]?.count || '0', 10); - } - - /** - * Search messages in channel - */ - async searchMessages( - channelId: string, - searchTerm: string, - limit: number = 50, - offset: number = 0, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT - m.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - deleter.name as deleted_by_name - FROM ${this.tableName} m - LEFT JOIN users u ON m.user_id = u.id - LEFT JOIN users deleter ON m.deleted_by = deleter.id - WHERE m.channel_id = $1 - AND m.deleted_at IS NULL - AND ( - LOWER(m.content) LIKE LOWER($2) - OR LOWER(m.transcription) LIKE LOWER($2) - ) - ORDER BY m.created_at DESC - LIMIT $3 OFFSET $4 - `; - - const result = await this.executeRawQuery( - sql, - [channelId, `%${searchTerm}%`, limit, offset], - client - ); - return result.rows; - } - - /** - * Update message content - */ - async updateMessage( - messageId: string, - updateData: Partial, - client?: DatabaseClient - ): Promise { - const message = await this.update(messageId, updateData, client); - - if (message) { - logger.info( - { - messageId, - updatedFields: Object.keys(updateData), - }, - 'Message updated successfully' - ); - } - - return message; - } - - /** - * Add reaction to message - */ - async addReaction( - messageId: string, - userId: string, - emoji: string, - client?: DatabaseClient - ): Promise { - const sql = ` - UPDATE ${this.tableName} - SET reactions = COALESCE(reactions, '{}'::jsonb) || - jsonb_build_object($2, - COALESCE((reactions->$2)::jsonb, '[]'::jsonb) || - CASE - WHEN (reactions->$2)::text LIKE '%"' || $3 || '"%' - THEN '[]'::jsonb - ELSE jsonb_build_array($3) - END - ) - WHERE id = $1 AND deleted_at IS NULL - RETURNING id - `; - - try { - const result = await this.executeRawQuery(sql, [messageId, emoji, userId], client); - - if (result.rowCount > 0) { - logger.info( - { - messageId, - userId, - emoji, - }, - 'Reaction added to message' - ); - return true; - } - return false; - } catch (error) { - logger.error({ error, messageId, userId, emoji }, 'Failed to add reaction'); - return false; - } - } - - /** - * Remove reaction from message - */ - async removeReaction( - messageId: string, - userId: string, - emoji: string, - client?: DatabaseClient - ): Promise { - const sql = ` - UPDATE ${this.tableName} - SET reactions = COALESCE(reactions, '{}'::jsonb) || - jsonb_build_object($2, - COALESCE( - (SELECT jsonb_agg(elem) - FROM jsonb_array_elements_text((reactions->$2)::jsonb) AS elem - WHERE elem != $3), - '[]'::jsonb - ) - ) - WHERE id = $1 AND deleted_at IS NULL - RETURNING id - `; - - try { - const result = await this.executeRawQuery(sql, [messageId, emoji, userId], client); - - if (result.rowCount > 0) { - logger.info( - { - messageId, - userId, - emoji, - }, - 'Reaction removed from message' - ); - return true; - } - return false; - } catch (error) { - logger.error({ error, messageId, userId, emoji }, 'Failed to remove reaction'); - return false; - } - } - - /** - * Get message reactions - */ - async getMessageReactions( - messageId: string, - client?: DatabaseClient - ): Promise> { - const sql = ` - SELECT reactions - FROM ${this.tableName} - WHERE id = $1 AND deleted_at IS NULL - `; - - const result = await this.executeRawQuery<{ reactions: Record }>( - sql, - [messageId], - client - ); - - return result.rows[0]?.reactions || {}; - } - - /** - * Update user's last read message in channel - */ - async updateLastRead( - channelId: string, - userId: string, - messageId?: string, - client?: DatabaseClient - ): Promise { - // If no messageId provided, use the latest message - const latestMessageId = messageId || await this.getLatestMessageId(channelId, client); - - if (!latestMessageId) return; - - const sql = ` - INSERT INTO channel_read_status (channel_id, user_id, last_read_message_id, last_read_at) - VALUES ($1, $2, $3, NOW()) - ON CONFLICT (channel_id, user_id) - DO UPDATE SET - last_read_message_id = EXCLUDED.last_read_message_id, - last_read_at = EXCLUDED.last_read_at - `; - - try { - await this.executeRawQuery(sql, [channelId, userId, latestMessageId], client); - - logger.debug( - { - channelId, - userId, - messageId: latestMessageId, - }, - 'Last read message updated' - ); - } catch (error) { - logger.error({ error, channelId, userId }, 'Failed to update last read'); - } - } - - /** - * Get latest message ID in channel - */ - private async getLatestMessageId( - channelId: string, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT id - FROM ${this.tableName} - WHERE channel_id = $1 AND deleted_at IS NULL - ORDER BY created_at DESC - LIMIT 1 - `; - - const result = await this.executeRawQuery<{ id: string }>(sql, [channelId], client); - return result.rows[0]?.id || null; - } - - /** - * Get unread message count for user in channel - */ - async getUnreadCount( - channelId: string, - userId: string, - client?: DatabaseClient - ): Promise { - const sql = ` - WITH last_read AS ( - SELECT last_read_message_id, last_read_at - FROM channel_read_status - WHERE channel_id = $1 AND user_id = $2 - ) - SELECT COUNT(*) as count - FROM ${this.tableName} m - LEFT JOIN last_read lr ON true - WHERE m.channel_id = $1 - AND m.deleted_at IS NULL - AND m.user_id != $2 - AND ( - lr.last_read_message_id IS NULL - OR m.created_at > lr.last_read_at - ) - `; - - const result = await this.executeRawQuery<{ count: string }>(sql, [channelId, userId], client); - return parseInt(result.rows[0]?.count || '0', 10); - } - - /** - * Get message thread - */ - async getMessageThread( - threadRootId: string, - limit: number = 50, - offset: number = 0, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT - m.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - deleter.name as deleted_by_name - FROM ${this.tableName} m - LEFT JOIN users u ON m.user_id = u.id - LEFT JOIN users deleter ON m.deleted_by = deleter.id - WHERE (m.id = $1 OR m.thread_root = $1) - AND m.deleted_at IS NULL - ORDER BY m.created_at ASC - LIMIT $2 OFFSET $3 - `; - - const result = await this.executeRawQuery( - sql, - [threadRootId, limit, offset], - client - ); - return result.rows; - } - - /** - * Get pinned messages in channel - */ - async getPinnedMessages( - channelId: string, - limit: number = 20, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT - m.*, - u.name as user_name, - u.email as user_email, - u.avatar_url as user_avatar, - u.role as user_role, - deleter.name as deleted_by_name - FROM ${this.tableName} m - LEFT JOIN users u ON m.user_id = u.id - LEFT JOIN users deleter ON m.deleted_by = deleter.id - WHERE m.channel_id = $1 - AND m.is_pinned = true - AND m.deleted_at IS NULL - ORDER BY m.created_at DESC - LIMIT $2 - `; - - const result = await this.executeRawQuery( - sql, - [channelId, limit], - client - ); - return result.rows; - } - - /** - * Get channel message statistics - */ - async getChannelMessageStats( - channelId: string, - client?: DatabaseClient - ): Promise<{ - totalMessages: number; - messagesByType: Record; - topUsers: Array<{ userId: string; userName: string; messageCount: number }>; - dailyActivity: Array<{ date: string; messageCount: number }>; - }> { - const statsSql = ` - SELECT - COUNT(*) as total_messages, - COUNT(*) FILTER (WHERE message_type = 'text') as text_messages, - COUNT(*) FILTER (WHERE message_type = 'voice') as voice_messages, - COUNT(*) FILTER (WHERE message_type = 'file') as file_messages, - COUNT(*) FILTER (WHERE message_type = 'system') as system_messages - FROM ${this.tableName} - WHERE channel_id = $1 AND deleted_at IS NULL - `; - - const topUsersSql = ` - SELECT - m.user_id, - u.name as user_name, - COUNT(*) as message_count - FROM ${this.tableName} m - LEFT JOIN users u ON m.user_id = u.id - WHERE m.channel_id = $1 AND m.deleted_at IS NULL - GROUP BY m.user_id, u.name - ORDER BY message_count DESC - LIMIT 10 - `; - - const dailyActivitySql = ` - SELECT - DATE(created_at) as date, - COUNT(*) as message_count - FROM ${this.tableName} - WHERE channel_id = $1 - AND deleted_at IS NULL - AND created_at >= NOW() - INTERVAL '30 days' - GROUP BY DATE(created_at) - ORDER BY date DESC - `; - - const [statsResult, topUsersResult, dailyActivityResult] = await Promise.all([ - this.executeRawQuery(statsSql, [channelId], client), - this.executeRawQuery(topUsersSql, [channelId], client), - this.executeRawQuery(dailyActivitySql, [channelId], client), - ]); - - const stats = statsResult.rows[0]; - - return { - totalMessages: parseInt(stats.total_messages, 10), - messagesByType: { - text: parseInt(stats.text_messages, 10), - voice: parseInt(stats.voice_messages, 10), - file: parseInt(stats.file_messages, 10), - system: parseInt(stats.system_messages, 10), - }, - topUsers: topUsersResult.rows.map((row: any) => ({ - userId: row.user_id, - userName: row.user_name, - messageCount: parseInt(row.message_count, 10), - })), - dailyActivity: dailyActivityResult.rows.map((row: any) => ({ - date: row.date, - messageCount: parseInt(row.message_count, 10), - })), - }; - } -} - -export default MessageRepository; \ No newline at end of file diff --git a/backend/src/db/TaskRepository.ts b/backend/src/db/TaskRepository.ts deleted file mode 100644 index 6759023..0000000 --- a/backend/src/db/TaskRepository.ts +++ /dev/null @@ -1,640 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import { logger } from '@utils/logger'; -import { ValidationError, ConflictError, BusinessLogicError } from '@utils/errors'; -import BaseRepository, { BaseEntity, FilterOptions } from './BaseRepository'; - -export interface Task extends BaseEntity { - title: string; - description?: string; - channel_id?: string; - parent_task_id?: string; - created_by: string; - assigned_to: string[]; - owned_by?: string; - priority: 'low' | 'medium' | 'high' | 'urgent' | 'critical'; - status: 'pending' | 'in_progress' | 'review' | 'completed' | 'cancelled' | 'on_hold'; - task_type: 'general' | 'project' | 'maintenance' | 'emergency' | 'research' | 'approval'; - complexity: number; - estimated_hours?: number; - actual_hours: number; - story_points?: number; - due_date?: Date; - start_date?: Date; - completed_at?: Date; - progress_percentage: number; - tags: string[]; - labels: Record; - custom_fields: Record; - voice_created: boolean; - voice_command_id?: string; - voice_instructions?: string; - ai_generated: boolean; - ai_suggestions: Record; - automation_rules: Record; - watchers: string[]; - comments_count: number; - attachments_count: number; - business_value: 'low' | 'medium' | 'high' | 'critical'; - cost_center?: string; - budget_impact?: number; - acceptance_criteria?: string; - definition_of_done?: string; - quality_score?: number; - external_references: Record; - integrations: Record; - recurrence_pattern?: Record; - recurrence_parent_id?: string; - is_recurring: boolean; - last_activity_at: Date; -} - -export interface CreateTaskData { - title: string; - description?: string; - channel_id?: string; - parent_task_id?: string; - created_by: string; - assigned_to?: string[]; - owned_by?: string; - priority?: Task['priority']; - task_type?: Task['task_type']; - complexity?: number; - estimated_hours?: number; - due_date?: Date; - start_date?: Date; - tags?: string[]; - labels?: Record; - voice_created?: boolean; - voice_command_id?: string; - voice_instructions?: string; - business_value?: Task['business_value']; - acceptance_criteria?: string; -} - -export interface TaskWithDetails extends Task { - channel_name?: string; - owner_name?: string; - assignee_details?: Array<{ - id: string; - name: string; - email: string; - avatar_url?: string; - }>; - subtask_count?: number; - dependency_count?: number; -} - -export interface TaskFilter { - status?: Task['status'][]; - priority?: Task['priority'][]; - assignedTo?: string[]; - channelId?: string; - dueAfter?: Date; - dueBefore?: Date; - tags?: string[]; - voiceCreated?: boolean; - overdue?: boolean; -} - -class TaskRepository extends BaseRepository { - constructor() { - super('tasks'); - } - - /** - * Create new task with validation - */ - async createTask(taskData: CreateTaskData, client?: DatabaseClient): Promise { - // Validate parent task if specified - if (taskData.parent_task_id) { - const parentTask = await this.findById(taskData.parent_task_id, false, client); - if (!parentTask) { - throw new ValidationError('Parent task not found', [ - { field: 'parent_task_id', message: 'Parent task does not exist', value: taskData.parent_task_id } - ]); - } - } - - // Set defaults - const taskToCreate = { - ...taskData, - assigned_to: taskData.assigned_to || [taskData.created_by], - owned_by: taskData.owned_by || taskData.assigned_to?.[0] || taskData.created_by, - priority: taskData.priority || 'medium', - status: 'pending' as const, - task_type: taskData.task_type || 'general', - complexity: taskData.complexity || 1, - actual_hours: 0, - progress_percentage: 0, - tags: taskData.tags || [], - labels: taskData.labels || {}, - custom_fields: {}, - voice_created: taskData.voice_created || false, - ai_generated: false, - ai_suggestions: {}, - automation_rules: {}, - watchers: [], - comments_count: 0, - attachments_count: 0, - business_value: taskData.business_value || 'medium', - external_references: {}, - integrations: { - calendar_event: null, - email_thread: null, - document_links: [], - meeting_recordings: [] - }, - is_recurring: false, - last_activity_at: new Date() - }; - - const task = await this.create(taskToCreate, client); - - logger.info({ - taskId: task.id, - title: task.title, - createdBy: task.created_by, - assignedTo: task.assigned_to, - channelId: task.channel_id - }, 'Task created successfully'); - - return task; - } - - /** - * Assign users to task - */ - async assignUsers(taskId: string, userIds: string[], assignedBy: string, client?: DatabaseClient): Promise { - const sql = ` - SELECT assign_task($1, $2, $3) - `; - - try { - const result = await this.executeRawQuery<{ assign_task: boolean }>( - sql, - [taskId, userIds, assignedBy], - client - ); - - const success = result.rows[0]?.assign_task || false; - - if (success) { - logger.info({ - taskId, - userIds, - assignedBy - }, 'Users assigned to task'); - } - - return success; - } catch (error) { - logger.error({ error, taskId, userIds }, 'Failed to assign users to task'); - throw error; - } - } - - /** - * Unassign users from task - */ - async unassignUsers(taskId: string, userIds: string[], unassignedBy: string, client?: DatabaseClient): Promise { - const sql = ` - SELECT unassign_task($1, $2, $3) - `; - - try { - const result = await this.executeRawQuery<{ unassign_task: boolean }>( - sql, - [taskId, userIds, unassignedBy], - client - ); - - const success = result.rows[0]?.unassign_task || false; - - if (success) { - logger.info({ - taskId, - userIds, - unassignedBy - }, 'Users unassigned from task'); - } - - return success; - } catch (error) { - logger.error({ error, taskId, userIds }, 'Failed to unassign users from task'); - throw error; - } - } - - /** - * Find tasks assigned to user - */ - async findByAssignee(userId: string, status?: Task['status'][], includeWatching: boolean = true, client?: DatabaseClient): Promise { - const sql = ` - SELECT * FROM get_user_tasks($1, $2, $3) - `; - - const result = await this.executeRawQuery(sql, [userId, status, includeWatching], client); - return result.rows; - } - - /** - * Find tasks in channel - */ - async findByChannel(channelId: string, includeCompleted: boolean = true, client?: DatabaseClient): Promise { - const statusFilter = includeCompleted ? {} : { status: ['pending', 'in_progress', 'review', 'on_hold'] }; - - const options: FilterOptions = { - filters: { channel_id: channelId, ...statusFilter }, - orderBy: 'created_at', - orderDirection: 'DESC' - }; - - const result = await this.findMany(options, client); - return result.data; - } - - /** - * Find overdue tasks - */ - async findOverdue(userId?: string, client?: DatabaseClient): Promise { - let sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE due_date < NOW() - AND status NOT IN ('completed', 'cancelled') - AND deleted_at IS NULL - `; - - let params: any[] = []; - - if (userId) { - sql += ` AND $1 = ANY(assigned_to)`; - params = [userId]; - } - - sql += ` ORDER BY due_date ASC`; - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Find tasks with filters - */ - async findWithFilters(filters: TaskFilter, limit: number = 50, offset: number = 0, client?: DatabaseClient): Promise { - const whereConditions = ['deleted_at IS NULL']; - const params: any[] = []; - let paramCounter = 1; - - // Status filter - if (filters.status && filters.status.length > 0) { - whereConditions.push(`status = ANY($${paramCounter})`); - params.push(filters.status); - paramCounter++; - } - - // Priority filter - if (filters.priority && filters.priority.length > 0) { - whereConditions.push(`priority = ANY($${paramCounter})`); - params.push(filters.priority); - paramCounter++; - } - - // Assignee filter - if (filters.assignedTo && filters.assignedTo.length > 0) { - whereConditions.push(`assigned_to && $${paramCounter}`); - params.push(filters.assignedTo); - paramCounter++; - } - - // Channel filter - if (filters.channelId) { - whereConditions.push(`channel_id = $${paramCounter}`); - params.push(filters.channelId); - paramCounter++; - } - - // Due date filters - if (filters.dueAfter) { - whereConditions.push(`due_date >= $${paramCounter}`); - params.push(filters.dueAfter); - paramCounter++; - } - - if (filters.dueBefore) { - whereConditions.push(`due_date <= $${paramCounter}`); - params.push(filters.dueBefore); - paramCounter++; - } - - // Tags filter - if (filters.tags && filters.tags.length > 0) { - whereConditions.push(`tags && $${paramCounter}`); - params.push(filters.tags); - paramCounter++; - } - - // Voice created filter - if (filters.voiceCreated !== undefined) { - whereConditions.push(`voice_created = $${paramCounter}`); - params.push(filters.voiceCreated); - paramCounter++; - } - - // Overdue filter - if (filters.overdue) { - whereConditions.push(`due_date < NOW() AND status NOT IN ('completed', 'cancelled')`); - } - - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ${whereConditions.join(' AND ')} - ORDER BY - CASE priority - WHEN 'critical' THEN 5 - WHEN 'urgent' THEN 4 - WHEN 'high' THEN 3 - WHEN 'medium' THEN 2 - ELSE 1 - END DESC, - due_date ASC NULLS LAST, - created_at DESC - LIMIT $${paramCounter} OFFSET $${paramCounter + 1} - `; - - params.push(limit, offset); - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Update task progress - */ - async updateProgress(taskId: string, progressPercentage: number, updatedBy: string, client?: DatabaseClient): Promise { - if (progressPercentage < 0 || progressPercentage > 100) { - throw new ValidationError('Progress percentage must be between 0 and 100', [ - { field: 'progress_percentage', message: 'Invalid progress value', value: progressPercentage } - ]); - } - - const updateData = { - progress_percentage: progressPercentage, - last_activity_at: new Date() - }; - - const task = await this.update(taskId, updateData, undefined, client); - - logger.info({ - taskId, - progressPercentage, - updatedBy - }, 'Task progress updated'); - - return task; - } - - /** - * Update task status - */ - async updateStatus(taskId: string, status: Task['status'], updatedBy: string, client?: DatabaseClient): Promise { - const updateData: any = { - status, - last_activity_at: new Date() - }; - - // Set completion timestamp for completed tasks - if (status === 'completed') { - updateData.completed_at = new Date(); - updateData.progress_percentage = 100; - } - - // Clear completion timestamp when moving away from completed - const currentTask = await this.findById(taskId, false, client); - if (currentTask?.status === 'completed' && status !== 'completed') { - updateData.completed_at = null; - } - - const task = await this.update(taskId, updateData, undefined, client); - - logger.info({ - taskId, - status, - updatedBy, - previousStatus: currentTask?.status - }, 'Task status updated'); - - return task; - } - - /** - * Get task with details (channel, assignees, subtasks) - */ - async findWithDetails(taskId: string, client?: DatabaseClient): Promise { - const sql = ` - SELECT - t.*, - c.name as channel_name, - owner.name as owner_name, - ARRAY( - SELECT json_build_object( - 'id', u.id, - 'name', u.name, - 'email', u.email, - 'avatar_url', u.avatar_url - ) - FROM users u - WHERE u.id = ANY(t.assigned_to) - AND u.deleted_at IS NULL - ORDER BY u.name - ) as assignee_details, - ( - SELECT COUNT(*) - FROM tasks subtasks - WHERE subtasks.parent_task_id = t.id - AND subtasks.deleted_at IS NULL - ) as subtask_count, - ( - SELECT COUNT(*) - FROM task_dependencies td - WHERE td.task_id = t.id - AND td.deleted_at IS NULL - AND td.is_active = true - ) as dependency_count - FROM tasks t - LEFT JOIN channels c ON t.channel_id = c.id - LEFT JOIN users owner ON t.owned_by = owner.id - WHERE t.id = $1 AND t.deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [taskId], client); - return result.rows[0] || null; - } - - /** - * Get task hierarchy (parent and children) - */ - async getTaskHierarchy(taskId: string, client?: DatabaseClient): Promise> { - const sql = ` - SELECT * FROM get_task_hierarchy($1) - `; - - const result = await this.executeRawQuery(sql, [taskId], client); - return result.rows; - } - - /** - * Search tasks by title or description - */ - async searchTasks( - searchTerm: string, - userId?: string, - limit: number = 20, - offset: number = 0, - client?: DatabaseClient - ): Promise { - let userCondition = ''; - let params = [`%${searchTerm}%`, limit, offset]; - - if (userId) { - userCondition = 'AND ($4 = ANY(assigned_to) OR $4 = ANY(watchers) OR created_by = $4)'; - params.push(userId); - } - - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ( - LOWER(title) LIKE LOWER($1) OR - LOWER(description) LIKE LOWER($1) OR - LOWER(array_to_string(tags, ' ')) LIKE LOWER($1) - ) - AND deleted_at IS NULL - AND status != 'cancelled' - ${userCondition} - ORDER BY - CASE WHEN LOWER(title) LIKE LOWER($1) THEN 1 ELSE 2 END, - CASE priority - WHEN 'critical' THEN 5 - WHEN 'urgent' THEN 4 - WHEN 'high' THEN 3 - WHEN 'medium' THEN 2 - ELSE 1 - END DESC, - created_at DESC - LIMIT $2 OFFSET $3 - `; - - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Get task statistics - */ - async getTaskStats(userId?: string, client?: DatabaseClient): Promise<{ - totalTasks: number; - tasksByStatus: Record; - tasksByPriority: Record; - overdueTasks: number; - completedThisWeek: number; - averageCompletionTime: number; - }> { - let userCondition = ''; - let params: any[] = []; - - if (userId) { - userCondition = 'WHERE ($1 = ANY(assigned_to) OR created_by = $1) AND deleted_at IS NULL'; - params = [userId]; - } else { - userCondition = 'WHERE deleted_at IS NULL'; - } - - const sql = ` - SELECT - COUNT(*) as total_tasks, - COUNT(*) FILTER (WHERE status = 'pending') as pending_tasks, - COUNT(*) FILTER (WHERE status = 'in_progress') as in_progress_tasks, - COUNT(*) FILTER (WHERE status = 'review') as review_tasks, - COUNT(*) FILTER (WHERE status = 'completed') as completed_tasks, - COUNT(*) FILTER (WHERE status = 'cancelled') as cancelled_tasks, - COUNT(*) FILTER (WHERE status = 'on_hold') as on_hold_tasks, - COUNT(*) FILTER (WHERE priority = 'low') as low_priority, - COUNT(*) FILTER (WHERE priority = 'medium') as medium_priority, - COUNT(*) FILTER (WHERE priority = 'high') as high_priority, - COUNT(*) FILTER (WHERE priority = 'urgent') as urgent_priority, - COUNT(*) FILTER (WHERE priority = 'critical') as critical_priority, - COUNT(*) FILTER (WHERE due_date < NOW() AND status NOT IN ('completed', 'cancelled')) as overdue_tasks, - COUNT(*) FILTER (WHERE status = 'completed' AND completed_at >= NOW() - INTERVAL '7 days') as completed_this_week, - AVG(EXTRACT(EPOCH FROM (completed_at - created_at)) / 3600) FILTER (WHERE completed_at IS NOT NULL) as avg_completion_hours - FROM ${this.tableName} - ${userCondition} - `; - - const result = await this.executeRawQuery(sql, params, client); - const stats = result.rows[0]; - - return { - totalTasks: parseInt(stats.total_tasks, 10), - tasksByStatus: { - pending: parseInt(stats.pending_tasks, 10), - in_progress: parseInt(stats.in_progress_tasks, 10), - review: parseInt(stats.review_tasks, 10), - completed: parseInt(stats.completed_tasks, 10), - cancelled: parseInt(stats.cancelled_tasks, 10), - on_hold: parseInt(stats.on_hold_tasks, 10) - }, - tasksByPriority: { - low: parseInt(stats.low_priority, 10), - medium: parseInt(stats.medium_priority, 10), - high: parseInt(stats.high_priority, 10), - urgent: parseInt(stats.urgent_priority, 10), - critical: parseInt(stats.critical_priority, 10) - }, - overdueTasks: parseInt(stats.overdue_tasks, 10), - completedThisWeek: parseInt(stats.completed_this_week, 10), - averageCompletionTime: parseFloat(stats.avg_completion_hours || '0') - }; - } - - /** - * Add watcher to task - */ - async addWatcher(taskId: string, userId: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET watchers = array_append(watchers, $2) - WHERE id = $1 - AND deleted_at IS NULL - AND NOT ($2 = ANY(watchers)) - `; - - const result = await this.executeRawQuery(sql, [taskId, userId], client); - return result.rowCount > 0; - } - - /** - * Remove watcher from task - */ - async removeWatcher(taskId: string, userId: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET watchers = array_remove(watchers, $2) - WHERE id = $1 - AND deleted_at IS NULL - AND $2 = ANY(watchers) - `; - - const result = await this.executeRawQuery(sql, [taskId, userId], client); - return result.rowCount > 0; - } -} - -export default TaskRepository; \ No newline at end of file diff --git a/backend/src/db/UserRepository.ts b/backend/src/db/UserRepository.ts deleted file mode 100644 index f62087f..0000000 --- a/backend/src/db/UserRepository.ts +++ /dev/null @@ -1,607 +0,0 @@ -import { DatabaseClient } from '@config/database'; -import { logger } from '@utils/logger'; -import { ValidationError, ConflictError } from '@utils/errors'; -import BaseRepository, { BaseEntity, FilterOptions } from './BaseRepository'; -import bcrypt from 'bcryptjs'; -import { config } from '@config/index'; - -export interface User extends BaseEntity { - email: string; - name: string; - password_hash: string; - role: 'ceo' | 'manager' | 'staff'; - avatar_url?: string; - phone?: string; - department?: string; - job_title?: string; - language_preference: string; - timezone: string; - notification_settings: Record; - voice_settings: Record; - failed_login_attempts: number; - account_locked_until?: Date; - password_reset_token?: string; - password_reset_expires?: Date; - email_verification_token?: string; - email_verified: boolean; - last_active?: Date; - last_login?: Date; - login_count: number; - created_by?: string; -} - -export interface CreateUserData { - email: string; - name: string; - password: string; - role: 'ceo' | 'manager' | 'staff'; - avatar_url?: string; - phone?: string; - department?: string; - job_title?: string; - language_preference?: string; - timezone?: string; - notification_settings?: Record; - voice_settings?: Record; - created_by?: string; -} - -export interface UpdateUserData { - name?: string; - avatar_url?: string; - phone?: string; - department?: string; - job_title?: string; - language_preference?: string; - timezone?: string; - notification_settings?: Record; - voice_settings?: Record; -} - -export interface UserStats { - totalUsers: number; - activeUsers: number; - usersByRole: Record; - recentLogins: number; - averageLoginCount: number; -} - -class UserRepository extends BaseRepository { - constructor() { - super('users'); - // Exclude sensitive fields from default select - this.selectFields = [ - 'id', - 'email', - 'name', - 'role', - 'avatar_url', - 'phone', - 'department', - 'job_title', - 'language_preference', - 'timezone', - 'notification_settings', - 'voice_settings', - 'email_verified', - 'last_active', - 'last_login', - 'login_count', - 'created_at', - 'updated_at', - 'version', - 'deleted_at', - 'created_by', - ]; - } - - /** - * Create new user with password hashing - */ - async createUser(userData: CreateUserData, client?: DatabaseClient): Promise { - // Validate unique email - const existingUser = await this.findByEmail(userData.email, false, client); - if (existingUser) { - throw new ConflictError('Email already exists', { email: userData.email }); - } - - // Validate CEO uniqueness - if (userData.role === 'ceo') { - const existingCEO = await this.findByRole('ceo', false, client); - if (existingCEO.length > 0) { - throw new ConflictError('CEO role already exists', { role: 'ceo' }); - } - } - - // Hash password - const password_hash = await bcrypt.hash(userData.password, config.security.bcryptRounds); - - // Prepare user data - const { password, ...userDataWithoutPassword } = userData; - const userToCreate = { - ...userDataWithoutPassword, - password_hash, - email_verified: false, - failed_login_attempts: 0, - login_count: 0, - language_preference: userData.language_preference || 'en', - timezone: userData.timezone || 'UTC', - }; - - const user = await this.create(userToCreate, client); - - logger.info( - { - userId: user.id, - email: user.email, - role: user.role, - }, - 'User created successfully' - ); - - return user; - } - - /** - * Find user by email - */ - async findByEmail( - email: string, - includeDeleted: boolean = false, - client?: DatabaseClient - ): Promise { - const deletedCondition = includeDeleted ? '' : 'AND deleted_at IS NULL'; - - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE LOWER(email) = LOWER($1) ${deletedCondition} - `; - - const result = await this.executeRawQuery(sql, [email], client); - return result.rows[0] || null; - } - - /** - * Find user by email for authentication (includes password hash) - */ - async findByEmailForAuth( - email: string, - client?: DatabaseClient - ): Promise<(User & { password_hash: string }) | null> { - const sql = ` - SELECT id, email, name, role, password_hash, email_verified, - failed_login_attempts, account_locked_until, version, - last_active, last_login, login_count - FROM ${this.tableName} - WHERE LOWER(email) = LOWER($1) AND deleted_at IS NULL - `; - - const result = await this.executeRawQuery( - sql, - [email], - client - ); - return result.rows[0] || null; - } - - /** - * Find users by role - */ - async findByRole( - role: User['role'], - includeDeleted: boolean = false, - client?: DatabaseClient - ): Promise { - const options: FilterOptions = { - filters: { role }, - includeDeleted, - orderBy: 'name', - orderDirection: 'ASC', - }; - - const result = await this.findMany(options, client); - return result.data; - } - - /** - * Find users by department - */ - async findByDepartment( - department: string, - includeDeleted: boolean = false, - client?: DatabaseClient - ): Promise { - const options: FilterOptions = { - filters: { department }, - includeDeleted, - orderBy: 'name', - orderDirection: 'ASC', - }; - - const result = await this.findMany(options, client); - return result.data; - } - - /** - * Update user password - */ - async updatePassword( - userId: string, - newPassword: string, - expectedVersion?: number, - client?: DatabaseClient - ): Promise { - const password_hash = await bcrypt.hash(newPassword, config.security.bcryptRounds); - - const sql = ` - UPDATE ${this.tableName} - SET password_hash = $2, password_reset_token = NULL, password_reset_expires = NULL - WHERE id = $1 AND deleted_at IS NULL - ${expectedVersion ? 'AND version = $3' : ''} - RETURNING id - `; - - const params = expectedVersion - ? [userId, password_hash, expectedVersion] - : [userId, password_hash]; - const result = await this.executeRawQuery(sql, params, client); - - const success = result.rows.length > 0; - - if (success) { - logger.info({ userId }, 'User password updated successfully'); - } - - return success; - } - - /** - * Verify user password - */ - async verifyPassword( - email: string, - password: string, - client?: DatabaseClient - ): Promise { - const user = await this.findByEmailForAuth(email, client); - - if (!user) { - return null; - } - - // Check account lock - if (user.account_locked_until && user.account_locked_until > new Date()) { - throw new ValidationError('Account is temporarily locked', [ - { - field: 'account', - message: 'Account locked due to failed login attempts', - value: user.account_locked_until, - }, - ]); - } - - const isValidPassword = await bcrypt.compare(password, user.password_hash); - - if (!isValidPassword) { - // Increment failed attempts - await this.incrementFailedLoginAttempts(user.id, client); - return null; - } - - // Reset failed attempts and update login info - await this.recordSuccessfulLogin(user.id, client); - - // Return user without password hash - const { password_hash, ...userWithoutPassword } = user; - return userWithoutPassword as User; - } - - /** - * Record successful login - */ - async recordSuccessfulLogin(userId: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET - failed_login_attempts = 0, - account_locked_until = NULL, - last_login = NOW(), - last_active = NOW(), - login_count = login_count + 1 - WHERE id = $1 - `; - - await this.executeRawQuery(sql, [userId], client); - } - - /** - * Increment failed login attempts and potentially lock account - */ - async incrementFailedLoginAttempts(userId: string, client?: DatabaseClient): Promise { - const maxAttempts = config.security.maxLoginAttempts; - const lockoutDuration = config.security.lockoutDuration; - - const sql = ` - UPDATE ${this.tableName} - SET - failed_login_attempts = failed_login_attempts + 1, - account_locked_until = CASE - WHEN failed_login_attempts + 1 >= $2 THEN NOW() + INTERVAL '${lockoutDuration}' - ELSE account_locked_until - END - WHERE id = $1 - `; - - await this.executeRawQuery(sql, [userId, maxAttempts], client); - - logger.warn({ userId }, 'Failed login attempt recorded'); - } - - /** - * Update last active timestamp - */ - async updateLastActive(userId: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET last_active = NOW() - WHERE id = $1 AND deleted_at IS NULL - `; - - await this.executeRawQuery(sql, [userId], client); - } - - /** - * Set password reset token - */ - async setPasswordResetToken( - email: string, - token: string, - expiresIn: string = '1 hour', - client?: DatabaseClient - ): Promise { - const sql = ` - UPDATE ${this.tableName} - SET - password_reset_token = $2, - password_reset_expires = NOW() + INTERVAL '${expiresIn}' - WHERE LOWER(email) = LOWER($1) AND deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [email, token], client); - return result.rowCount > 0; - } - - /** - * Verify password reset token - */ - async verifyPasswordResetToken(token: string, client?: DatabaseClient): Promise { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE password_reset_token = $1 - AND password_reset_expires > NOW() - AND deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [token], client); - return result.rows[0] || null; - } - - /** - * Set email verification token - */ - async setEmailVerificationToken( - userId: string, - token: string, - client?: DatabaseClient - ): Promise { - const sql = ` - UPDATE ${this.tableName} - SET email_verification_token = $2 - WHERE id = $1 AND deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [userId, token], client); - return result.rowCount > 0; - } - - /** - * Verify email with token - */ - async verifyEmail(token: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET - email_verified = true, - email_verification_token = NULL - WHERE email_verification_token = $1 AND deleted_at IS NULL - RETURNING ${this.selectFields.join(', ')} - `; - - const result = await this.executeRawQuery(sql, [token], client); - - if (result.rows.length > 0 && result.rows[0]) { - logger.info({ userId: result.rows[0].id }, 'Email verified successfully'); - } - - return result.rows[0] || null; - } - - /** - * Search users by name or email - */ - async searchUsers( - searchTerm: string, - limit: number = 20, - offset: number = 0, - client?: DatabaseClient - ): Promise { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE ( - LOWER(name) LIKE LOWER($1) OR - LOWER(email) LIKE LOWER($1) OR - LOWER(department) LIKE LOWER($1) OR - LOWER(job_title) LIKE LOWER($1) - ) - AND deleted_at IS NULL - ORDER BY - CASE WHEN LOWER(name) LIKE LOWER($1) THEN 1 ELSE 2 END, - name ASC - LIMIT $2 OFFSET $3 - `; - - const searchPattern = `%${searchTerm}%`; - const result = await this.executeRawQuery(sql, [searchPattern, limit, offset], client); - return result.rows; - } - - /** - * Get active users (logged in recently) - */ - async getActiveUsers(withinHours: number = 24, client?: DatabaseClient): Promise { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE last_active > NOW() - INTERVAL '${withinHours} hours' - AND deleted_at IS NULL - ORDER BY last_active DESC - `; - - const result = await this.executeRawQuery(sql, [], client); - return result.rows; - } - - /** - * Get user statistics - */ - async getUserStats(client?: DatabaseClient): Promise { - const sql = ` - SELECT - COUNT(*) as total_users, - COUNT(*) FILTER (WHERE last_active > NOW() - INTERVAL '7 days') as active_users, - COUNT(*) FILTER (WHERE role = 'ceo') as ceo_count, - COUNT(*) FILTER (WHERE role = 'manager') as manager_count, - COUNT(*) FILTER (WHERE role = 'staff') as staff_count, - COUNT(*) FILTER (WHERE last_login > NOW() - INTERVAL '24 hours') as recent_logins, - AVG(login_count) as average_login_count - FROM ${this.tableName} - WHERE deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [], client); - const stats = result.rows[0]; - - return { - totalUsers: parseInt(stats.total_users, 10), - activeUsers: parseInt(stats.active_users, 10), - usersByRole: { - ceo: parseInt(stats.ceo_count, 10), - manager: parseInt(stats.manager_count, 10), - staff: parseInt(stats.staff_count, 10), - }, - recentLogins: parseInt(stats.recent_logins, 10), - averageLoginCount: parseFloat(stats.average_login_count || '0'), - }; - } - - /** - * Update user profile - */ - async updateProfile( - userId: string, - profileData: UpdateUserData, - expectedVersion?: number, - client?: DatabaseClient - ): Promise { - return this.update(userId, profileData, expectedVersion, client); - } - - /** - * Unlock user account - */ - async unlockAccount(userId: string, client?: DatabaseClient): Promise { - const sql = ` - UPDATE ${this.tableName} - SET - failed_login_attempts = 0, - account_locked_until = NULL - WHERE id = $1 AND deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [userId], client); - - if (result.rowCount > 0) { - logger.info({ userId }, 'User account unlocked'); - } - - return result.rowCount > 0; - } - - /** - * Get users for channel assignment suggestions - */ - async getUsersForChannelAssignment( - channelId: string, - excludeUserIds: string[] = [], - client?: DatabaseClient - ): Promise { - const excludeCondition = excludeUserIds.length > 0 ? 'AND id != ALL($2)' : ''; - - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE deleted_at IS NULL - AND email_verified = true - ${excludeCondition} - ORDER BY - CASE role - WHEN 'ceo' THEN 1 - WHEN 'manager' THEN 2 - ELSE 3 - END, - last_active DESC NULLS LAST, - name ASC - LIMIT 50 - `; - - const params = excludeUserIds.length > 0 ? [excludeUserIds] : []; - const result = await this.executeRawQuery(sql, params, client); - return result.rows; - } - - /** - * Find all unverified users - */ - async findUnverifiedUsers(client?: DatabaseClient): Promise { - const sql = ` - SELECT ${this.selectFields.join(', ')} - FROM ${this.tableName} - WHERE email_verified = false AND deleted_at IS NULL - ORDER BY created_at DESC - `; - - const result = await this.executeRawQuery(sql, [], client); - return result.rows; - } - - /** - * Delete all unverified users (hard delete for cleanup) - */ - async deleteUnverifiedUsers(client?: DatabaseClient): Promise { - const sql = ` - DELETE FROM ${this.tableName} - WHERE email_verified = false AND deleted_at IS NULL - `; - - const result = await this.executeRawQuery(sql, [], client); - - logger.info({ deleteCount: result.rowCount }, 'Deleted unverified users'); - - return result.rowCount || 0; - } -} - -export default UserRepository; diff --git a/backend/src/db/index.ts b/backend/src/db/index.ts deleted file mode 100644 index 1043732..0000000 --- a/backend/src/db/index.ts +++ /dev/null @@ -1,121 +0,0 @@ -/** - * Database layer exports - * Centralized access to all repositories and database utilities - */ - -// Base repository -export { default as BaseRepository } from './BaseRepository'; -export type { BaseEntity, FilterOptions, PaginatedResult } from './BaseRepository'; - -// Specialized repositories -export { default as UserRepository } from './UserRepository'; -export type { User, CreateUserData, UpdateUserData, UserStats } from './UserRepository'; - -export { default as ChannelRepository } from './ChannelRepository'; -export type { Channel, CreateChannelData, ChannelWithDetails } from './ChannelRepository'; - -export { default as TaskRepository } from './TaskRepository'; -export type { Task, CreateTaskData, TaskWithDetails, TaskFilter } from './TaskRepository'; - -export { default as MessageRepository } from './MessageRepository'; -export type { Message, CreateMessageData, MessageWithUser } from './MessageRepository'; - -export { default as ActivityRepository } from './ActivityRepository'; -export type { Activity, CreateActivityData, ActivityWithUser } from './ActivityRepository'; - -export { default as FileRepository } from './FileRepository'; -export type { FileEntity, CreateFileData, FileWithUploader } from './FileRepository'; - -export { default as AnnouncementRepository } from './AnnouncementRepository'; -export type { Announcement, CreateAnnouncementData, UpdateAnnouncementData, AnnouncementFilter } from './AnnouncementRepository'; - -// Repository instances for dependency injection -import UserRepository from './UserRepository'; -import ChannelRepository from './ChannelRepository'; -import TaskRepository from './TaskRepository'; -import MessageRepository from './MessageRepository'; -import ActivityRepository from './ActivityRepository'; -import FileRepository from './FileRepository'; -import AnnouncementRepository from './AnnouncementRepository'; - -// Create singleton instances -export const userRepository = new UserRepository(); -export const channelRepository = new ChannelRepository(); -export const taskRepository = new TaskRepository(); -export const messageRepository = new MessageRepository(); -export const activityRepository = new ActivityRepository(); -export const fileRepository = new FileRepository(); -export const announcementRepository = new AnnouncementRepository(); - -// Repository collection for easy access -export const repositories = { - users: userRepository, - channels: channelRepository, - tasks: taskRepository, - messages: messageRepository, - activities: activityRepository, - files: fileRepository, - announcements: announcementRepository, -} as const; - -export type Repositories = typeof repositories; - -// Database configuration and utilities -export { - initializeDatabase, - closeDatabase, - getPool, - query, - transaction, - healthCheck, - getPoolStats, - databaseMetrics -} from '../config/database'; - -// DatabaseManager class for Phase 2 compatibility -export class DatabaseManager { - private static instance: DatabaseManager; - - static getInstance(): DatabaseManager { - if (!DatabaseManager.instance) { - DatabaseManager.instance = new DatabaseManager(); - } - return DatabaseManager.instance; - } - async query(text: string, params?: any[], client?: any): Promise { - const { query } = await import('../config/database'); - return await query(text, params, client); - } - - async one(text: string, params?: any[], client?: any): Promise { - const result = await this.query(text, params, client); - if (result.rows.length === 0) { - throw new Error('No rows returned'); - } - return result.rows[0]; - } - - async many(text: string, params?: any[], client?: any): Promise { - const result = await this.query(text, params, client); - return result.rows; - } - - async none(text: string, params?: any[], client?: any): Promise { - await this.query(text, params, client); - } - - async transaction(callback: (client: any) => Promise): Promise { - const { transaction } = await import('../config/database'); - return await transaction(callback); - } -} - -// Migration system -export { - runMigrations, - rollbackLastMigration, - getMigrationStatus, - validateMigrations -} from './migrator'; - -export default repositories; \ No newline at end of file diff --git a/backend/src/db/migrator.ts b/backend/src/db/migrator.ts deleted file mode 100644 index 651bd4f..0000000 --- a/backend/src/db/migrator.ts +++ /dev/null @@ -1,314 +0,0 @@ -import { readdir, readFile } from 'fs/promises'; -import { join } from 'path'; -import { getPool, query, transaction } from '@config/database'; -import { logger } from '@utils/logger'; -import { DatabaseError } from '@utils/errors'; - -/** - * Database migration system for CEO Communication Platform - * Handles schema evolution across all development phases - */ - -interface Migration { - id: string; - filename: string; - timestamp: number; - sql: string; -} - -interface MigrationRecord { - id: string; - filename: string; - executed_at: Date; - checksum: string; -} - -/** - * Initialize migration tracking table - */ -const initializeMigrationTable = async (): Promise => { - const createTableSql = ` - CREATE TABLE IF NOT EXISTS migrations ( - id VARCHAR(255) PRIMARY KEY, - filename VARCHAR(255) NOT NULL, - executed_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), - checksum VARCHAR(64) NOT NULL, - execution_time_ms INTEGER, - UNIQUE(filename) - ); - - CREATE INDEX IF NOT EXISTS idx_migrations_executed_at ON migrations(executed_at); - `; - - await query(createTableSql); - logger.info('Migration tracking table initialized'); -}; - -/** - * Load migration files from the migrations directory - */ -const loadMigrationFiles = async (): Promise => { - try { - const migrationsDir = join(process.cwd(), 'migrations'); - const files = await readdir(migrationsDir); - - const sqlFiles = files.filter((file) => file.endsWith('.sql')).sort(); // Ensure proper ordering - - const migrations: Migration[] = []; - - for (const filename of sqlFiles) { - const filepath = join(migrationsDir, filename); - const sql = await readFile(filepath, 'utf-8'); - - // Extract timestamp from filename (format: YYYYMMDDHHMMSS_name.sql) - const timestampMatch = filename.match(/^(\d{14})/); - const timestamp = timestampMatch && timestampMatch[1] ? parseInt(timestampMatch[1], 10) : 0; - - migrations.push({ - id: filename.replace('.sql', ''), - filename, - timestamp, - sql: sql.trim(), - }); - } - - logger.info({ count: migrations.length }, 'Loaded migration files'); - return migrations; - } catch (error) { - logger.error({ error }, 'Failed to load migration files'); - throw new DatabaseError('Failed to load migration files'); - } -}; - -/** - * Get executed migrations from database - */ -const getExecutedMigrations = async (): Promise => { - const result = await query('SELECT * FROM migrations ORDER BY executed_at ASC'); - - return result.rows; -}; - -/** - * Calculate checksum for migration content - */ -const calculateChecksum = (content: string): string => { - const crypto = require('crypto'); - return crypto.createHash('sha256').update(content).digest('hex'); -}; - -/** - * Execute a single migration - */ -const executeMigration = async (migration: Migration): Promise => { - const startTime = Date.now(); - - await transaction(async (client) => { - logger.info({ migration: migration.filename }, 'Executing migration'); - - // Execute the migration SQL - await client.query(migration.sql); - - // Record the migration execution - const executionTime = Date.now() - startTime; - const checksum = calculateChecksum(migration.sql); - - await client.query( - `INSERT INTO migrations (id, filename, executed_at, checksum, execution_time_ms) - VALUES ($1, $2, NOW(), $3, $4)`, - [migration.id, migration.filename, checksum, executionTime] - ); - - logger.info( - { - migration: migration.filename, - executionTime, - }, - 'Migration executed successfully' - ); - }); -}; - -/** - * Run all pending migrations - */ -export const runMigrations = async (): Promise => { - try { - logger.info('Starting database migrations...'); - - // Initialize migration tracking - await initializeMigrationTable(); - - // Load all migration files - const allMigrations = await loadMigrationFiles(); - - // Get executed migrations - const executedMigrations = await getExecutedMigrations(); - const executedIds = new Set(executedMigrations.map((m) => m.id)); - - // Find pending migrations - const pendingMigrations = allMigrations.filter((m) => !executedIds.has(m.id)); - - if (pendingMigrations.length === 0) { - logger.info('No pending migrations found'); - return; - } - - logger.info( - { - total: allMigrations.length, - executed: executedMigrations.length, - pending: pendingMigrations.length, - }, - 'Migration status' - ); - - // Execute pending migrations in order - for (const migration of pendingMigrations) { - await executeMigration(migration); - } - - logger.info( - { - executedCount: pendingMigrations.length, - }, - 'All migrations completed successfully' - ); - } catch (error) { - logger.error({ error }, 'Migration failed'); - throw new DatabaseError('Migration execution failed'); - } -}; - -/** - * Rollback the last migration (for development only) - */ -export const rollbackLastMigration = async (): Promise => { - if (process.env.NODE_ENV === 'production') { - throw new DatabaseError('Rollback not allowed in production'); - } - - try { - const executedMigrations = await getExecutedMigrations(); - - if (executedMigrations.length === 0) { - logger.info('No migrations to rollback'); - return; - } - - const lastMigration = executedMigrations[executedMigrations.length - 1]; - - if (!lastMigration) { - logger.info('No migrations to rollback'); - return; - } - - // Check if rollback SQL exists - const rollbackFilename = lastMigration.filename.replace('.sql', '.rollback.sql'); - const rollbackPath = join(process.cwd(), 'migrations', rollbackFilename); - - try { - const rollbackSql = await readFile(rollbackPath, 'utf-8'); - - await transaction(async (client) => { - logger.warn({ migration: lastMigration.filename }, 'Rolling back migration'); - - // Execute rollback SQL - await client.query(rollbackSql); - - // Remove from migration records - await client.query('DELETE FROM migrations WHERE id = $1', [lastMigration.id]); - - logger.warn( - { - migration: lastMigration.filename, - }, - 'Migration rolled back successfully' - ); - }); - } catch (rollbackError) { - throw new DatabaseError(`Rollback file not found or invalid: ${rollbackFilename}`, { - lastMigration: lastMigration.filename, - }); - } - } catch (error) { - logger.error({ error }, 'Rollback failed'); - throw new DatabaseError('Migration rollback failed'); - } -}; - -/** - * Get migration status for health checks - */ -export const getMigrationStatus = async (): Promise<{ - total: number; - executed: number; - pending: number; - lastExecuted: MigrationRecord | undefined; -}> => { - try { - const allMigrations = await loadMigrationFiles(); - const executedMigrations = await getExecutedMigrations(); - - return { - total: allMigrations.length, - executed: executedMigrations.length, - pending: allMigrations.length - executedMigrations.length, - lastExecuted: executedMigrations[executedMigrations.length - 1], - }; - } catch (error) { - logger.error({ error }, 'Failed to get migration status'); - throw new DatabaseError('Failed to get migration status'); - } -}; - -/** - * Validate migration integrity - */ -export const validateMigrations = async (): Promise<{ - valid: boolean; - errors: string[]; -}> => { - const errors: string[] = []; - - try { - const allMigrations = await loadMigrationFiles(); - const executedMigrations = await getExecutedMigrations(); - - // Check for checksum mismatches - for (const executed of executedMigrations) { - const migration = allMigrations.find((m) => m.id === executed.id); - - if (!migration) { - errors.push(`Migration ${executed.filename} was executed but file no longer exists`); - continue; - } - - const currentChecksum = calculateChecksum(migration.sql); - if (currentChecksum !== executed.checksum) { - errors.push(`Migration ${executed.filename} has been modified after execution`); - } - } - - // Check for gaps in migration sequence - const timestamps = allMigrations - .map((m) => m.timestamp) - .filter((t): t is number => typeof t === 'number') - .sort(); - for (let i = 1; i < timestamps.length; i++) { - const current = timestamps[i]; - const previous = timestamps[i - 1]; - if (typeof current === 'number' && typeof previous === 'number' && current <= previous) { - errors.push(`Migration timestamp ordering issue detected`); - break; - } - } - } catch (error) { - errors.push(`Validation failed: ${error instanceof Error ? error.message : 'Unknown error'}`); - } - - return { - valid: errors.length === 0, - errors, - }; -}; diff --git a/backend/src/files/management/FileMetadataManager.ts b/backend/src/files/management/FileMetadataManager.ts deleted file mode 100644 index b2b8a70..0000000 --- a/backend/src/files/management/FileMetadataManager.ts +++ /dev/null @@ -1,620 +0,0 @@ -/** - * File Metadata Manager - Phase 2 File Management System - * Manages file metadata in database with entity linking capabilities - * - * Success Criteria: - * - File record creation before upload - * - Status tracking (pending, uploading, completed, failed) - * - Entity linking (channels, tasks, users) - * - Metadata indexing for fast search - */ - -import { performance } from 'perf_hooks'; -import { DatabaseManager } from '../../db'; -import { logger } from '../../utils/logger'; - -export interface FileRecord { - id: string; - name: string; - originalName: string; - size: number; - contentType: string; - s3Key: string; - uploadedBy: string; - organizationId: string; - status: FileStatus; - description?: string; - tags: string[]; - uploadedAt: Date; - updatedAt: Date; - downloadCount: number; - lastDownloaded?: Date; -} - -export enum FileStatus { - PENDING = 'pending', - UPLOADING = 'uploading', - COMPLETED = 'completed', - FAILED = 'failed', - DELETED = 'deleted' -} - -export interface FileEntityLink { - fileId: string; - entityType: 'channel' | 'task' | 'user' | 'project'; - entityId: string; - linkType: 'attachment' | 'share' | 'reference'; - linkedBy: string; - linkedAt: Date; -} - -export interface FileSearchQuery { - organizationId: string; - userId?: string; - name?: string; - contentType?: string; - tags?: string[]; - entityType?: string; - entityId?: string; - status?: FileStatus; - uploadedAfter?: Date; - uploadedBefore?: Date; - limit?: number; - offset?: number; -} - -export interface FileSearchResult { - files: FileRecord[]; - totalCount: number; - hasMore: boolean; -} - -export class FileMetadataManager { - private db: DatabaseManager; - private performanceMetrics: number[] = []; - - constructor() { - this.db = DatabaseManager.getInstance(); - logger.info('File Metadata Manager initialized'); - } - - /** - * Create file record before upload process - * Target: <200ms creation time - */ - async createFileRecord( - name: string, - originalName: string, - size: number, - contentType: string, - s3Key: string, - uploadedBy: string, - organizationId: string, - description?: string, - tags: string[] = [] - ): Promise { - const startTime = performance.now(); - - try { - const result = await this.db.query(` - INSERT INTO files ( - name, original_name, size, content_type, s3_key, - uploaded_by, organization_id, status, description, - tags, uploaded_at, updated_at, download_count - ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, - NOW(), NOW(), 0 - ) - RETURNING - id, name, original_name as "originalName", size, content_type as "contentType", - s3_key as "s3Key", uploaded_by as "uploadedBy", organization_id as "organizationId", - status, description, tags, uploaded_at as "uploadedAt", - updated_at as "updatedAt", download_count as "downloadCount" - `, [ - name, originalName, size, contentType, s3Key, - uploadedBy, organizationId, FileStatus.PENDING, - description, JSON.stringify(tags) - ]); - - const fileRecord = result.rows[0] as FileRecord; - - // Parse JSON tags back to array - fileRecord.tags = typeof fileRecord.tags === 'string' - ? JSON.parse(fileRecord.tags as any) - : fileRecord.tags; - - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - - logger.info('File record created successfully', { - fileId: fileRecord.id, - name: fileRecord.name, - size: `${Math.round(fileRecord.size / 1024)}KB`, - processingTime: `${processingTime.toFixed(2)}ms` - }); - - return fileRecord; - - } catch (error: any) { - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - - logger.error('Failed to create file record', { - error: error.message, - name, - uploadedBy, - organizationId, - processingTime: `${processingTime.toFixed(2)}ms` - }); - - throw new Error(`Failed to create file record: ${error.message}`); - } - } - - /** - * Update file status during upload lifecycle - */ - async updateFileStatus(fileId: string, status: FileStatus, error?: string): Promise { - const startTime = performance.now(); - - try { - let query = ` - UPDATE files - SET status = $2, updated_at = NOW() - `; - - const params = [fileId, status]; - - if (error && status === FileStatus.FAILED) { - query += `, error_message = $3`; - params.push(error); - } - - query += ` WHERE id = $1 RETURNING id`; - - const result = await this.db.query(query, params); - - if (result.rowCount === 0) { - logger.warn('File not found for status update', { fileId, status }); - return false; - } - - const processingTime = performance.now() - startTime; - - logger.debug('File status updated', { - fileId, - status, - processingTime: `${processingTime.toFixed(2)}ms`, - error: error ? error.substring(0, 100) : undefined - }); - - return true; - - } catch (dbError: any) { - logger.error('Failed to update file status', { - error: dbError.message, - fileId, - status - }); - - return false; - } - } - - /** - * Link file to entity (channel, task, user, project) - */ - async linkFileToEntity( - fileId: string, - entityType: FileEntityLink['entityType'], - entityId: string, - linkType: FileEntityLink['linkType'] = 'attachment', - linkedBy: string - ): Promise { - const startTime = performance.now(); - - try { - await this.db.query(` - INSERT INTO file_entity_links ( - file_id, entity_type, entity_id, link_type, linked_by, linked_at - ) VALUES ($1, $2, $3, $4, $5, NOW()) - ON CONFLICT (file_id, entity_type, entity_id, link_type) - DO UPDATE SET - linked_by = EXCLUDED.linked_by, - linked_at = EXCLUDED.linked_at - `, [fileId, entityType, entityId, linkType, linkedBy]); - - const processingTime = performance.now() - startTime; - - logger.info('File linked to entity', { - fileId, - entityType, - entityId, - linkType, - linkedBy, - processingTime: `${processingTime.toFixed(2)}ms` - }); - - return true; - - } catch (error: any) { - logger.error('Failed to link file to entity', { - error: error.message, - fileId, - entityType, - entityId, - linkType - }); - - return false; - } - } - - /** - * Remove file-entity link - */ - async unlinkFileFromEntity( - fileId: string, - entityType: FileEntityLink['entityType'], - entityId: string, - linkType?: FileEntityLink['linkType'] - ): Promise { - try { - let query = ` - DELETE FROM file_entity_links - WHERE file_id = $1 AND entity_type = $2 AND entity_id = $3 - `; - const params = [fileId, entityType, entityId]; - - if (linkType) { - query += ` AND link_type = $4`; - params.push(linkType); - } - - const result = await this.db.query(query, params); - - logger.info('File unlinked from entity', { - fileId, - entityType, - entityId, - linkType, - removedLinks: result.rowCount - }); - - return result.rowCount > 0; - - } catch (error: any) { - logger.error('Failed to unlink file from entity', { - error: error.message, - fileId, - entityType, - entityId - }); - - return false; - } - } - - /** - * Get file record by ID - */ - async getFileRecord(fileId: string): Promise { - try { - const result = await this.db.query(` - SELECT - id, name, original_name as "originalName", size, content_type as "contentType", - s3_key as "s3Key", uploaded_by as "uploadedBy", organization_id as "organizationId", - status, description, tags, uploaded_at as "uploadedAt", - updated_at as "updatedAt", download_count as "downloadCount", - last_downloaded as "lastDownloaded" - FROM files - WHERE id = $1 AND status != $2 - `, [fileId, FileStatus.DELETED]); - - if (result.rows.length === 0) { - return null; - } - - const fileRecord = result.rows[0] as FileRecord; - - // Parse JSON tags - fileRecord.tags = typeof fileRecord.tags === 'string' - ? JSON.parse(fileRecord.tags as any) - : fileRecord.tags || []; - - return fileRecord; - - } catch (error: any) { - logger.error('Failed to get file record', { - error: error.message, - fileId - }); - - return null; - } - } - - /** - * Search files with advanced filtering - */ - async searchFiles(query: FileSearchQuery): Promise { - const startTime = performance.now(); - - try { - const limit = query.limit || 20; - const offset = query.offset || 0; - - // Build dynamic WHERE clause - const conditions: string[] = ['f.organization_id = $1', 'f.status != $2']; - const params: any[] = [query.organizationId, FileStatus.DELETED]; - let paramIndex = 3; - - if (query.userId) { - conditions.push(`f.uploaded_by = $${paramIndex}`); - params.push(query.userId); - paramIndex++; - } - - if (query.name) { - conditions.push(`(f.name ILIKE $${paramIndex} OR f.original_name ILIKE $${paramIndex})`); - params.push(`%${query.name}%`); - paramIndex++; - } - - if (query.contentType) { - conditions.push(`f.content_type ILIKE $${paramIndex}`); - params.push(`%${query.contentType}%`); - paramIndex++; - } - - if (query.status) { - conditions.push(`f.status = $${paramIndex}`); - params.push(query.status); - paramIndex++; - } - - if (query.tags && query.tags.length > 0) { - conditions.push(`f.tags::jsonb ?| $${paramIndex}`); - params.push(query.tags); - paramIndex++; - } - - if (query.uploadedAfter) { - conditions.push(`f.uploaded_at >= $${paramIndex}`); - params.push(query.uploadedAfter); - paramIndex++; - } - - if (query.uploadedBefore) { - conditions.push(`f.uploaded_at <= $${paramIndex}`); - params.push(query.uploadedBefore); - paramIndex++; - } - - // Add entity filter if specified - let joinClause = ''; - if (query.entityType && query.entityId) { - joinClause = ` - INNER JOIN file_entity_links fel ON f.id = fel.file_id - AND fel.entity_type = $${paramIndex} - AND fel.entity_id = $${paramIndex + 1} - `; - params.push(query.entityType, query.entityId); - paramIndex += 2; - } - - const whereClause = conditions.join(' AND '); - - // Count total results - const countQuery = ` - SELECT COUNT(DISTINCT f.id) as total - FROM files f ${joinClause} - WHERE ${whereClause} - `; - - const countResult = await this.db.query(countQuery, params); - const totalCount = parseInt(countResult.rows[0].total); - - // Get paginated results - const dataQuery = ` - SELECT DISTINCT - f.id, f.name, f.original_name as "originalName", f.size, - f.content_type as "contentType", f.s3_key as "s3Key", - f.uploaded_by as "uploadedBy", f.organization_id as "organizationId", - f.status, f.description, f.tags, f.uploaded_at as "uploadedAt", - f.updated_at as "updatedAt", f.download_count as "downloadCount", - f.last_downloaded as "lastDownloaded" - FROM files f ${joinClause} - WHERE ${whereClause} - ORDER BY f.uploaded_at DESC, f.id DESC - LIMIT $${paramIndex} OFFSET $${paramIndex + 1} - `; - - params.push(limit, offset); - - const dataResult = await this.db.query(dataQuery, params); - - // Parse tags for each file - const files: FileRecord[] = dataResult.rows.map((row: any) => ({ - ...row, - tags: typeof row.tags === 'string' ? JSON.parse(row.tags) : (row.tags || []) - })); - - const processingTime = performance.now() - startTime; - - logger.debug('File search completed', { - totalFound: totalCount, - returnedCount: files.length, - processingTime: `${processingTime.toFixed(2)}ms`, - query: { - organizationId: query.organizationId, - userId: query.userId, - name: query.name, - entityType: query.entityType, - entityId: query.entityId - } - }); - - return { - files, - totalCount, - hasMore: offset + files.length < totalCount - }; - - } catch (error: any) { - logger.error('File search failed', { - error: error.message, - query - }); - - return { - files: [], - totalCount: 0, - hasMore: false - }; - } - } - - /** - * Get files linked to a specific entity - */ - async getFilesForEntity( - entityType: FileEntityLink['entityType'], - entityId: string, - linkType?: FileEntityLink['linkType'] - ): Promise { - try { - let query = ` - SELECT - f.id, f.name, f.original_name as "originalName", f.size, - f.content_type as "contentType", f.s3_key as "s3Key", - f.uploaded_by as "uploadedBy", f.organization_id as "organizationId", - f.status, f.description, f.tags, f.uploaded_at as "uploadedAt", - f.updated_at as "updatedAt", f.download_count as "downloadCount", - f.last_downloaded as "lastDownloaded" - FROM files f - INNER JOIN file_entity_links fel ON f.id = fel.file_id - WHERE fel.entity_type = $1 AND fel.entity_id = $2 - AND f.status = $3 - `; - - const params = [entityType, entityId, FileStatus.COMPLETED]; - - if (linkType) { - query += ` AND fel.link_type = $4`; - params.push(linkType); - } - - query += ` ORDER BY fel.linked_at DESC`; - - const result = await this.db.query(query, params); - - // Parse tags for each file - const files: FileRecord[] = result.rows.map((row: any) => ({ - ...row, - tags: typeof row.tags === 'string' ? JSON.parse(row.tags) : (row.tags || []) - })); - - return files; - - } catch (error: any) { - logger.error('Failed to get files for entity', { - error: error.message, - entityType, - entityId, - linkType - }); - - return []; - } - } - - /** - * Increment download counter for analytics - */ - async recordDownload(fileId: string): Promise { - try { - await this.db.query(` - UPDATE files - SET download_count = download_count + 1, last_downloaded = NOW() - WHERE id = $1 - `, [fileId]); - - } catch (error: any) { - logger.warn('Failed to record download', { - error: error.message, - fileId - }); - } - } - - /** - * Soft delete file record - */ - async deleteFileRecord(fileId: string): Promise { - try { - const result = await this.db.query(` - UPDATE files - SET status = $2, updated_at = NOW() - WHERE id = $1 - RETURNING id - `, [fileId, FileStatus.DELETED]); - - if (result.rowCount > 0) { - // Also remove entity links - await this.db.query(` - DELETE FROM file_entity_links WHERE file_id = $1 - `, [fileId]); - - logger.info('File record deleted', { fileId }); - return true; - } - - return false; - - } catch (error: any) { - logger.error('Failed to delete file record', { - error: error.message, - fileId - }); - - return false; - } - } - - /** - * Get performance statistics - */ - getPerformanceStats(): { - averageQueryTime: number; - p95QueryTime: number; - totalQueries: number; - } { - if (this.performanceMetrics.length === 0) { - return { - averageQueryTime: 0, - p95QueryTime: 0, - totalQueries: 0 - }; - } - - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - - return { - averageQueryTime: Math.round(average * 100) / 100, - p95QueryTime: Math.round(p95 * 100) / 100, - totalQueries: this.performanceMetrics.length - }; - } - - private recordPerformance(time: number): void { - this.performanceMetrics.push(time); - - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} \ No newline at end of file diff --git a/backend/src/files/management/S3FileManager.ts b/backend/src/files/management/S3FileManager.ts deleted file mode 100644 index 9cafa33..0000000 --- a/backend/src/files/management/S3FileManager.ts +++ /dev/null @@ -1,503 +0,0 @@ -/** - * S3 File Manager - Phase 2 File Management System - * Provides secure, scalable file operations with AWS S3 integration - * - * Success Criteria: - * - Upload URL generation in <2 seconds - * - Support for files up to 100MB - * - 99%+ S3 operation success rate - * - Secure presigned URL generation with 15-minute expiry - */ - -import { S3Client, PutObjectCommand, GetObjectCommand, DeleteObjectCommand, HeadObjectCommand } from '@aws-sdk/client-s3'; -import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; -import { performance } from 'perf_hooks'; -import { createHash } from 'crypto'; -import { logger } from '../../utils/logger'; - -export interface S3Config { - region: string; - bucket: string; - accessKeyId?: string | undefined; - secretAccessKey?: string | undefined; - maxFileSize: number; - allowedFileTypes: string[]; - presignedUrlTTL: number; -} - -export interface FileMetadata { - userId: string; - organizationId: string; - originalName: string; - contentType: string; - size: number; - uploadedAt: string; - description?: string | undefined; - tags?: string[] | undefined; -} - -export interface PresignedUploadResult { - uploadUrl: string; - downloadUrl?: string; - key: string; - expiresAt: Date; - fileId: string; -} - -export interface FileValidationResult { - valid: boolean; - error?: string | undefined; - recommendations?: string[] | undefined; -} - -export class S3FileManager { - private client: S3Client; - private config: S3Config; - private performanceMetrics: number[] = []; - - constructor(config?: Partial) { - this.config = { - region: process.env.AWS_REGION || 'us-east-1', - bucket: process.env.S3_BUCKET_NAME || 'ceo-platform-files', - accessKeyId: process.env.AWS_ACCESS_KEY_ID, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY, - maxFileSize: 100 * 1024 * 1024, // 100MB - allowedFileTypes: [ - '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', - '.jpg', '.jpeg', '.png', '.gif', '.webp', - '.mp4', '.mov', '.avi', '.mkv', - '.mp3', '.wav', '.flac', - '.zip', '.rar', '.7z', - '.txt', '.csv', '.json', '.xml' - ], - presignedUrlTTL: 900, // 15 minutes - ...config - }; - - this.client = new S3Client({ - region: this.config.region, - ...(this.config.accessKeyId && this.config.secretAccessKey && { - credentials: { - accessKeyId: this.config.accessKeyId, - secretAccessKey: this.config.secretAccessKey, - } - }), - requestHandler: { - connectionTimeout: 5000, - socketTimeout: 10000, - }, - maxAttempts: 3, - }); - - logger.info('S3 File Manager initialized', { - region: this.config.region, - bucket: this.config.bucket, - maxFileSize: `${Math.round(this.config.maxFileSize / (1024 * 1024))}MB`, - allowedTypes: this.config.allowedFileTypes.length - }); - } - - /** - * Generate presigned upload URL with comprehensive validation - * Target: <2 second generation time - */ - async generatePresignedUploadUrl( - fileName: string, - contentType: string, - fileSize: number, - metadata: FileMetadata - ): Promise { - const startTime = performance.now(); - - try { - // Validate file before generating URL - const validation = this.validateFile(fileName, contentType, fileSize); - if (!validation.valid) { - throw new Error(`File validation failed: ${validation.error}`); - } - - // Generate unique file key - const fileId = this.generateFileId(); - const key = this.generateFileKey(fileId, fileName, metadata.userId, metadata.organizationId); - - // Create PUT command with metadata - const putCommand = new PutObjectCommand({ - Bucket: this.config.bucket, - Key: key, - ContentType: contentType, - ContentLength: fileSize, - Metadata: { - userId: metadata.userId, - organizationId: metadata.organizationId, - originalName: fileName, - uploadedAt: metadata.uploadedAt, - description: metadata.description || '', - tags: metadata.tags ? JSON.stringify(metadata.tags) : '', - fileId - }, - ServerSideEncryption: 'AES256', // Enable encryption - StorageClass: 'STANDARD_IA' // Cost-optimized storage - }); - - // Generate presigned upload URL - const uploadUrl = await getSignedUrl(this.client, putCommand, { - expiresIn: this.config.presignedUrlTTL - }); - - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - - // Log performance warning if target exceeded - if (processingTime > 2000) { - logger.warn('Presigned URL generation exceeded 2 second target', { - processingTime: `${processingTime.toFixed(2)}ms`, - fileName, - fileSize - }); - } - - const result: PresignedUploadResult = { - uploadUrl, - key, - fileId, - expiresAt: new Date(Date.now() + this.config.presignedUrlTTL * 1000) - }; - - logger.info('Presigned upload URL generated successfully', { - fileId, - fileName, - fileSize: `${Math.round(fileSize / 1024)}KB`, - processingTime: `${processingTime.toFixed(2)}ms`, - expiresIn: `${this.config.presignedUrlTTL}s` - }); - - return result; - - } catch (error: any) { - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - - logger.error('Failed to generate presigned upload URL', { - error: error.message, - fileName, - fileSize, - processingTime: `${processingTime.toFixed(2)}ms` - }); - - throw new Error(`Failed to generate upload URL: ${error.message}`); - } - } - - /** - * Generate presigned download URL for file access - */ - async generatePresignedDownloadUrl( - key: string, - expiresIn: number = 3600 - ): Promise { - const startTime = performance.now(); - - try { - // Verify file exists - const headCommand = new HeadObjectCommand({ - Bucket: this.config.bucket, - Key: key - }); - - await this.client.send(headCommand); - - // Generate download URL - const getCommand = new GetObjectCommand({ - Bucket: this.config.bucket, - Key: key - }); - - const downloadUrl = await getSignedUrl(this.client, getCommand, { - expiresIn - }); - - const processingTime = performance.now() - startTime; - - logger.debug('Presigned download URL generated', { - key, - processingTime: `${processingTime.toFixed(2)}ms`, - expiresIn: `${expiresIn}s` - }); - - return downloadUrl; - - } catch (error: any) { - logger.error('Failed to generate presigned download URL', { - error: error.message, - key - }); - - throw new Error(`Failed to generate download URL: ${error.message}`); - } - } - - /** - * Delete file from S3 - */ - async deleteFile(key: string): Promise { - try { - const deleteCommand = new DeleteObjectCommand({ - Bucket: this.config.bucket, - Key: key - }); - - await this.client.send(deleteCommand); - - logger.info('File deleted from S3', { key }); - return true; - - } catch (error: any) { - logger.error('Failed to delete file from S3', { - error: error.message, - key - }); - - return false; - } - } - - /** - * Get file metadata from S3 - */ - async getFileMetadata(key: string): Promise { - try { - const headCommand = new HeadObjectCommand({ - Bucket: this.config.bucket, - Key: key - }); - - const response = await this.client.send(headCommand); - - if (!response.Metadata) { - return null; - } - - const metadata: FileMetadata = { - userId: response.Metadata.userId || '', - organizationId: response.Metadata.organizationId || '', - originalName: response.Metadata.originalName || '', - contentType: response.ContentType || '', - size: response.ContentLength || 0, - uploadedAt: response.Metadata.uploadedAt || new Date().toISOString(), - description: response.Metadata.description || undefined, - tags: response.Metadata.tags ? JSON.parse(response.Metadata.tags) : undefined - }; - - return metadata; - - } catch (error: any) { - logger.error('Failed to get file metadata', { - error: error.message, - key - }); - - return null; - } - } - - /** - * Validate file against security and size constraints - */ - validateFile(fileName: string, contentType: string, fileSize: number): FileValidationResult { - const recommendations: string[] = []; - - // File size validation - if (fileSize <= 0) { - return { - valid: false, - error: 'File size must be greater than 0' - }; - } - - if (fileSize > this.config.maxFileSize) { - return { - valid: false, - error: `File size exceeds maximum allowed size of ${Math.round(this.config.maxFileSize / (1024 * 1024))}MB` - }; - } - - // File type validation - const fileExtension = fileName.toLowerCase().split('.').pop(); - if (!fileExtension) { - return { - valid: false, - error: 'File must have a valid extension' - }; - } - - const fullExtension = `.${fileExtension}`; - if (!this.config.allowedFileTypes.includes(fullExtension)) { - return { - valid: false, - error: `File type '${fullExtension}' is not allowed`, - recommendations: [ - `Allowed types: ${this.config.allowedFileTypes.join(', ')}`, - 'Consider converting to an allowed format', - 'Contact administrator for additional file type support' - ] - }; - } - - // Content type validation - if (!contentType || contentType === 'application/octet-stream') { - recommendations.push('Consider specifying a more specific content type'); - } - - // File name security validation - if (this.containsUnsafeCharacters(fileName)) { - return { - valid: false, - error: 'File name contains unsafe characters', - recommendations: [ - 'Remove special characters like <>:"|?*', - 'Use only letters, numbers, hyphens, and underscores', - 'Avoid extremely long file names' - ] - }; - } - - // Size recommendations - if (fileSize > 50 * 1024 * 1024) { // 50MB - recommendations.push('Large files may take longer to upload and process'); - } - - return { - valid: true, - recommendations: recommendations.length > 0 ? recommendations : undefined - }; - } - - /** - * Get performance statistics for monitoring - */ - getPerformanceStats(): { - averageGenerationTime: number; - p95GenerationTime: number; - p99GenerationTime: number; - totalOperations: number; - } { - if (this.performanceMetrics.length === 0) { - return { - averageGenerationTime: 0, - p95GenerationTime: 0, - p99GenerationTime: 0, - totalOperations: 0 - }; - } - - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - - return { - averageGenerationTime: Math.round(average * 100) / 100, - p95GenerationTime: Math.round(p95 * 100) / 100, - p99GenerationTime: Math.round(p99 * 100) / 100, - totalOperations: this.performanceMetrics.length - }; - } - - /** - * Get service health status - */ - async getHealthStatus(): Promise<{ - status: 'healthy' | 'degraded' | 'unhealthy'; - details: Record; - }> { - try { - // Test S3 connectivity with a simple head request - const testKey = 'health-check-test'; - const headCommand = new HeadObjectCommand({ - Bucket: this.config.bucket, - Key: testKey - }); - - const startTime = performance.now(); - - try { - await this.client.send(headCommand); - } catch (error: any) { - // Expect 404 for non-existent test file - this means S3 is reachable - if (error.name !== 'NotFound') { - throw error; - } - } - - const responseTime = performance.now() - startTime; - const stats = this.getPerformanceStats(); - - let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy'; - - // Check performance degradation - if (stats.averageGenerationTime > 3000 || responseTime > 5000) { - status = 'unhealthy'; - } else if (stats.averageGenerationTime > 2000 || responseTime > 2000) { - status = 'degraded'; - } - - return { - status, - details: { - s3ResponseTime: Math.round(responseTime), - averageGenerationTime: stats.averageGenerationTime, - totalOperations: stats.totalOperations, - bucket: this.config.bucket, - region: this.config.region - } - }; - - } catch (error: any) { - logger.error('S3 health check failed', { error: error.message }); - - return { - status: 'unhealthy', - details: { - error: error.message, - bucket: this.config.bucket, - region: this.config.region - } - }; - } - } - - private generateFileId(): string { - const timestamp = Date.now().toString(36); - const random = Math.random().toString(36).substring(2, 8); - return `file_${timestamp}_${random}`; - } - - private generateFileKey(fileId: string, originalName: string, userId: string, organizationId: string): string { - const sanitizedName = this.sanitizeFileName(originalName); - const year = new Date().getFullYear(); - const month = String(new Date().getMonth() + 1).padStart(2, '0'); - - return `organizations/${organizationId}/users/${userId}/${year}/${month}/${fileId}_${sanitizedName}`; - } - - private sanitizeFileName(fileName: string): string { - return fileName - .replace(/[^a-zA-Z0-9.-]/g, '_') // Replace unsafe characters with underscore - .replace(/_{2,}/g, '_') // Replace multiple underscores with single - .substring(0, 100); // Limit length - } - - private containsUnsafeCharacters(fileName: string): boolean { - const unsafePattern = /[<>:"|?*\x00-\x1f]/; - return unsafePattern.test(fileName) || fileName.length > 255; - } - - private recordPerformance(time: number): void { - this.performanceMetrics.push(time); - - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} \ No newline at end of file diff --git a/backend/src/files/upload/FileCommandParser.ts b/backend/src/files/upload/FileCommandParser.ts deleted file mode 100644 index 366e8ec..0000000 --- a/backend/src/files/upload/FileCommandParser.ts +++ /dev/null @@ -1,706 +0,0 @@ -/** - * File Command Parser - Phase 2 Voice File Operations - * Parses file-related voice commands and extracts parameters - * - * Success Criteria: - * - Supports upload, share, organize, delete operations - * - Extracts file names, descriptions, target entities - * - Handles file type inference - * - Context-aware target resolution - */ - -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { ContextData, UserContext } from '../../voice/types'; - -export interface ParsedFileCommand { - id: string; - operation: FileOperation; - fileName?: string | undefined; - description?: string | undefined; - targetChannels: ResolvedEntity[]; - targetTasks: ResolvedEntity[]; - targetUsers: ResolvedEntity[]; - fileType?: string | undefined; - tags: string[]; - priority: 'low' | 'medium' | 'high'; - processingTime: number; - confidence: number; -} - -export enum FileOperation { - UPLOAD = 'upload', - SHARE = 'share', - ORGANIZE = 'organize', - DELETE = 'delete', - SEARCH = 'search', - DOWNLOAD = 'download', -} - -export interface ResolvedEntity { - id: string; - name: string; - type: string; - confidence: number; -} - -export interface FileParameters { - fileName?: string | undefined; - description?: string | undefined; - targetEntities: string[]; - fileType?: string | undefined; - operation: FileOperation; - tags: string[]; - priority: string; -} - -export class FileCommandParser { - private performanceMetrics: number[] = []; - - constructor() { - logger.info('File Command Parser initialized'); - } - - /** - * Parse file-related voice command - * Target: <500ms parsing time - */ - async parseFileCommand( - transcript: string, - context: ContextData, - userContext: UserContext - ): Promise { - const startTime = performance.now(); - const commandId = `file_cmd_${Date.now()}_${Math.random().toString(36).substring(7)}`; - - try { - logger.debug('Parsing file command', { - commandId, - transcript: transcript.substring(0, 100), - userId: userContext.userId, - }); - - // Step 1: Identify file operation - const operation = this.identifyFileOperation(transcript); - - // Step 2: Extract file parameters - const parameters = this.extractFileParameters(transcript); - - // Step 3: Resolve target entities with context - const resolvedTargets = await this.resolveFileTargets(parameters.targetEntities, context); - - // Step 4: Infer file type if not explicitly mentioned - const inferredFileType = this.inferFileType(transcript, parameters.fileName); - - // Step 5: Extract tags and metadata - const tags = this.extractTags(transcript); - const priority = this.determinePriority(transcript); - - // Step 6: Calculate confidence score - const confidence = this.calculateConfidence(transcript, operation, resolvedTargets); - - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - - const parsedCommand: ParsedFileCommand = { - id: commandId, - operation, - fileName: parameters.fileName, - description: parameters.description, - targetChannels: resolvedTargets.channels, - targetTasks: resolvedTargets.tasks, - targetUsers: resolvedTargets.users, - fileType: inferredFileType || parameters.fileType, - tags, - priority: priority as any, - processingTime, - confidence, - }; - - logger.info('File command parsed successfully', { - commandId, - operation, - fileName: parameters.fileName, - targetChannels: resolvedTargets.channels.length, - targetTasks: resolvedTargets.tasks.length, - targetUsers: resolvedTargets.users.length, - confidence, - processingTime: `${processingTime.toFixed(2)}ms`, - }); - - return parsedCommand; - } catch (error: any) { - const processingTime = performance.now() - startTime; - this.recordPerformance(processingTime); - - logger.error('File command parsing failed', { - commandId, - error: error.message, - transcript: transcript.substring(0, 100), - processingTime: `${processingTime.toFixed(2)}ms`, - }); - - // Return default command with low confidence - return { - id: commandId, - operation: FileOperation.UPLOAD, - targetChannels: [], - targetTasks: [], - targetUsers: [], - tags: [], - priority: 'medium', - processingTime, - confidence: 0.1, - }; - } - } - - /** - * Extract file parameters from transcript - */ - extractFileParameters(transcript: string): FileParameters { - const lowerTranscript = transcript.toLowerCase(); - - // Extract file name patterns - const fileNamePatterns = [ - /(?:file|document|report|presentation)\s+(?:called|named|titled)\s+"([^"]+)"/i, - /(?:file|document|report|presentation)\s+"([^"]+)"/i, - /(?:upload|share|send)\s+(?:the\s+)?([a-zA-Z0-9\s\.-]+\.(?:pdf|doc|docx|xls|xlsx|ppt|pptx|jpg|png|mp4|zip))/i, - /"([^"]+\.[a-zA-Z0-9]+)"/i, - ]; - - let fileName: string | undefined; - for (const pattern of fileNamePatterns) { - const match = transcript.match(pattern); - if (match) { - if (match[1] !== undefined) { - fileName = match[1].trim(); - break; - } - } - } - - // Extract description patterns - const descriptionPatterns = [ - /description\s+"([^"]+)"/i, - /(?:about|regarding|for)\s+"([^"]+)"/i, - /(?:with\s+)?(?:description|notes?)\s+([^,.!?]+)/i, - ]; - - let description: string | undefined; - for (const pattern of descriptionPatterns) { - const match = transcript.match(pattern); - if (match) { - if (match[1] !== undefined) { - description = match[1].trim(); - break; - } - } - } - - // Extract target entities (channels, tasks, users) - const targetEntities = this.extractTargetEntities(transcript); - - // Determine file type from context - const fileType = this.extractFileType(transcript); - - // Determine operation - const operation = this.identifyFileOperation(transcript); - - // Extract tags - const tags = this.extractTags(transcript); - - // Determine priority - const priority = this.determinePriority(transcript); - - return { - fileName, - description, - targetEntities, - fileType, - operation, - tags, - priority, - }; - } - - /** - * Resolve file targets with context awareness - */ - async resolveFileTargets( - targetEntities: string[], - context: ContextData - ): Promise<{ - channels: ResolvedEntity[]; - tasks: ResolvedEntity[]; - users: ResolvedEntity[]; - }> { - const resolved = { - channels: [] as ResolvedEntity[], - tasks: [] as ResolvedEntity[], - users: [] as ResolvedEntity[], - }; - - for (const entityName of targetEntities) { - const lowerName = entityName.toLowerCase(); - - // Try to resolve as channel - const channel = this.findBestChannelMatch(lowerName, context.activeChannels); - if (channel) { - resolved.channels.push({ - id: channel.id, - name: channel.name, - type: 'channel', - confidence: this.calculateMatchConfidence(lowerName, channel.name), - }); - continue; - } - - // Try to resolve as task - const task = this.findBestTaskMatch(lowerName, context.recentTasks); - if (task) { - resolved.tasks.push({ - id: task.id, - name: task.title, - type: 'task', - confidence: this.calculateMatchConfidence(lowerName, task.title), - }); - continue; - } - - // Try to resolve as user - const user = this.findBestUserMatch(lowerName, context.teamMembers); - if (user) { - resolved.users.push({ - id: user.id, - name: user.name, - type: 'user', - confidence: this.calculateMatchConfidence(lowerName, user.name), - }); - } - } - - return resolved; - } - - /** - * Identify the primary file operation from transcript - */ - private identifyFileOperation(transcript: string): FileOperation { - const lowerTranscript = transcript.toLowerCase(); - - // Upload patterns - if (/\b(?:upload|send|attach|add|share)\b/i.test(lowerTranscript)) { - return FileOperation.UPLOAD; - } - - // Share patterns - if (/\b(?:share|distribute|send to|forward)\b/i.test(lowerTranscript)) { - return FileOperation.SHARE; - } - - // Organize patterns - if (/\b(?:organize|sort|categorize|group|move)\b/i.test(lowerTranscript)) { - return FileOperation.ORGANIZE; - } - - // Delete patterns - if (/\b(?:delete|remove|trash|discard)\b/i.test(lowerTranscript)) { - return FileOperation.DELETE; - } - - // Search patterns - if (/\b(?:find|search|look for|locate)\b/i.test(lowerTranscript)) { - return FileOperation.SEARCH; - } - - // Download patterns - if (/\b(?:download|get|retrieve|access)\b/i.test(lowerTranscript)) { - return FileOperation.DOWNLOAD; - } - - // Default to upload if unclear - return FileOperation.UPLOAD; - } - - /** - * Extract target entities (channels, tasks, users) from transcript - */ - private extractTargetEntities(transcript: string): string[] { - const entities: string[] = []; - - // Channel patterns - const channelPatterns = [ - /(?:to|in|on)\s+(?:the\s+)?([a-zA-Z0-9\s]+)\s+channel/gi, - /(?:channel\s+)([a-zA-Z0-9\s]+)/gi, - /(?:to|with)\s+([a-zA-Z0-9\s]+)\s+(?:team|group)/gi, - ]; - - channelPatterns.forEach((pattern) => { - let match; - while ((match = pattern.exec(transcript)) !== null) { - if (match[1] !== undefined) { - entities.push(match[1].trim()); - } - } - }); - - // Task patterns - const taskPatterns = [ - /(?:for|to)\s+(?:the\s+)?([a-zA-Z0-9\s]+)\s+(?:task|project)/gi, - /(?:task\s+)([a-zA-Z0-9\s]+)/gi, - ]; - - taskPatterns.forEach((pattern) => { - let match; - while ((match = pattern.exec(transcript)) !== null) { - entities.push(match[1]!.trim()); - } - }); - - // User patterns - const userPatterns = [ - /(?:to|with|for)\s+([A-Z][a-zA-Z]+(?:\s+[A-Z][a-zA-Z]+)?)/gi, - /(?:send|share)\s+(?:to|with)\s+([A-Z][a-zA-Z]+)/gi, - ]; - - userPatterns.forEach((pattern) => { - let match; - while ((match = pattern.exec(transcript)) !== null) { - if (match[1] !== undefined) { - const name = match[1].trim(); - if (name.length > 2 && !['the', 'and', 'for', 'to'].includes(name.toLowerCase())) { - entities.push(name); - } - } - } - }); - - return [...new Set(entities)]; // Remove duplicates - } - - /** - * Extract file type from transcript - */ - private extractFileType(transcript: string): string | undefined { - const typePatterns = [ - /\b(pdf|document|doc|docx)\b/i, - /\b(spreadsheet|excel|xls|xlsx)\b/i, - /\b(presentation|powerpoint|ppt|pptx)\b/i, - /\b(image|photo|picture|jpg|jpeg|png)\b/i, - /\b(video|movie|mp4|mov|avi)\b/i, - /\b(audio|sound|music|mp3|wav)\b/i, - /\b(zip|archive|compressed)\b/i, - ]; - - for (const pattern of typePatterns) { - const match = transcript.match(pattern); - if (match) { - return match[1] ? match[1].toLowerCase() : undefined; - } - } - - return undefined; - } - - /** - * Infer file type from context and file name - */ - private inferFileType(transcript: string, fileName?: string): string | undefined { - if (fileName) { - const extension = fileName.split('.').pop()?.toLowerCase(); - if (extension) { - return extension; - } - } - - // Infer from context words - const lowerTranscript = transcript.toLowerCase(); - - if (/\b(?:report|document|letter|memo)\b/.test(lowerTranscript)) { - return 'pdf'; - } - - if (/\b(?:budget|financial|numbers|data|spreadsheet)\b/.test(lowerTranscript)) { - return 'xlsx'; - } - - if (/\b(?:slides|presentation|deck|pitch)\b/.test(lowerTranscript)) { - return 'pptx'; - } - - if (/\b(?:photo|image|screenshot|picture)\b/.test(lowerTranscript)) { - return 'jpg'; - } - - if (/\b(?:video|recording|demo|clip)\b/.test(lowerTranscript)) { - return 'mp4'; - } - - return undefined; - } - - /** - * Extract tags from transcript - */ - private extractTags(transcript: string): string[] { - const tags: string[] = []; - - // Tag patterns - const tagPatterns = [ - /(?:tag|tags|tagged|label|labels)\s+(?:as\s+|with\s+)?([a-zA-Z0-9,\s]+)/gi, - /(?:category|categories)\s+([a-zA-Z0-9,\s]+)/gi, - ]; - - tagPatterns.forEach((pattern) => { - const match = transcript.match(pattern); - if (match && match[1]) { - const extractedTags = match[1] - .split(/[,\s]+/) - .map((tag) => tag.trim().toLowerCase()) - .filter((tag) => tag.length > 0); - tags.push(...extractedTags); - } - }); - - // Auto-generate contextual tags - const contextTags = this.generateContextualTags(transcript); - tags.push(...contextTags); - - return [...new Set(tags)]; // Remove duplicates - } - - /** - * Generate contextual tags based on transcript content - */ - private generateContextualTags(transcript: string): string[] { - const tags: string[] = []; - const lowerTranscript = transcript.toLowerCase(); - - // Business context tags - if (/\b(?:urgent|priority|asap|immediately)\b/.test(lowerTranscript)) { - tags.push('urgent'); - } - - if (/\b(?:confidential|private|internal|sensitive)\b/.test(lowerTranscript)) { - tags.push('confidential'); - } - - if (/\b(?:public|external|client|customer)\b/.test(lowerTranscript)) { - tags.push('external'); - } - - if (/\b(?:draft|preliminary|wip|work in progress)\b/.test(lowerTranscript)) { - tags.push('draft'); - } - - if (/\b(?:final|approved|completed|done)\b/.test(lowerTranscript)) { - tags.push('final'); - } - - // Department tags - const departments = ['marketing', 'sales', 'engineering', 'finance', 'hr', 'operations']; - departments.forEach((dept) => { - if (new RegExp(`\\b${dept}\\b`, 'i').test(transcript)) { - tags.push(dept); - } - }); - - return tags; - } - - /** - * Determine priority level from transcript - */ - private determinePriority(transcript: string): string { - const lowerTranscript = transcript.toLowerCase(); - - if (/\b(?:urgent|critical|emergency|asap|immediately|high priority)\b/.test(lowerTranscript)) { - return 'high'; - } - - if (/\b(?:low priority|later|when you have time|no rush)\b/.test(lowerTranscript)) { - return 'low'; - } - - return 'medium'; - } - - /** - * Find best matching channel from context - */ - private findBestChannelMatch(name: string, channels: any[]): any | null { - let bestMatch: any = null; - let bestScore = 0; - - for (const channel of channels) { - const score = this.calculateMatchConfidence(name, channel.name); - if (score > bestScore && score > 0.6) { - bestScore = score; - bestMatch = channel; - } - } - - return bestMatch; - } - - /** - * Find best matching task from context - */ - private findBestTaskMatch(name: string, tasks: any[]): any | null { - let bestMatch: any = null; - let bestScore = 0; - - for (const task of tasks) { - const score = this.calculateMatchConfidence(name, task.title); - if (score > bestScore && score > 0.6) { - bestScore = score; - bestMatch = task; - } - } - - return bestMatch; - } - - /** - * Find best matching user from context - */ - private findBestUserMatch(name: string, users: any[]): any | null { - let bestMatch: any = null; - let bestScore = 0; - - for (const user of users) { - const score = this.calculateMatchConfidence(name, user.name); - if (score > bestScore && score > 0.7) { - bestScore = score; - bestMatch = user; - } - } - - return bestMatch; - } - - /** - * Calculate match confidence between two strings - */ - private calculateMatchConfidence(input: string, target: string): number { - const inputLower = input.toLowerCase().trim(); - const targetLower = target.toLowerCase().trim(); - - // Exact match - if (inputLower === targetLower) { - return 1.0; - } - - // Contains match - if (targetLower.includes(inputLower) || inputLower.includes(targetLower)) { - return 0.8; - } - - // Fuzzy matching using simple word overlap - const inputWords = inputLower.split(/\s+/); - const targetWords = targetLower.split(/\s+/); - - const commonWords = inputWords.filter((word) => - targetWords.some((targetWord) => targetWord.includes(word) || word.includes(targetWord)) - ); - - if (commonWords.length === 0) { - return 0; - } - - const overlap = commonWords.length / Math.max(inputWords.length, targetWords.length); - return overlap * 0.7; // Scale down fuzzy matches - } - - /** - * Calculate overall confidence for the parsed command - */ - private calculateConfidence( - transcript: string, - operation: FileOperation, - resolvedTargets: { - channels: ResolvedEntity[]; - tasks: ResolvedEntity[]; - users: ResolvedEntity[]; - } - ): number { - let confidence = 0.5; // Base confidence - - // Operation identification confidence - const operationKeywords = { - [FileOperation.UPLOAD]: ['upload', 'send', 'attach', 'add'], - [FileOperation.SHARE]: ['share', 'distribute', 'forward'], - [FileOperation.ORGANIZE]: ['organize', 'sort', 'categorize'], - [FileOperation.DELETE]: ['delete', 'remove', 'trash'], - [FileOperation.SEARCH]: ['find', 'search', 'look for'], - [FileOperation.DOWNLOAD]: ['download', 'get', 'retrieve'], - }; - - const keywords = operationKeywords[operation] || []; - const hasOperationKeyword = keywords.some((keyword) => - transcript.toLowerCase().includes(keyword) - ); - - if (hasOperationKeyword) { - confidence += 0.2; - } - - // Entity resolution confidence - const totalTargets = - resolvedTargets.channels.length + resolvedTargets.tasks.length + resolvedTargets.users.length; - if (totalTargets > 0) { - const avgEntityConfidence = - [...resolvedTargets.channels, ...resolvedTargets.tasks, ...resolvedTargets.users].reduce( - (sum, entity) => sum + entity.confidence, - 0 - ) / totalTargets; - - confidence += avgEntityConfidence * 0.3; - } - - // File name extraction confidence - if (/\b[a-zA-Z0-9\s\.-]+\.[a-zA-Z0-9]{2,4}\b/.test(transcript)) { - confidence += 0.1; - } - - return Math.min(confidence, 1.0); - } - - /** - * Get performance statistics - */ - getPerformanceStats(): { - averageParsingTime: number; - p95ParsingTime: number; - totalCommandsParsed: number; - } { - if (this.performanceMetrics.length === 0) { - return { - averageParsingTime: 0, - p95ParsingTime: 0, - totalCommandsParsed: 0, - }; - } - - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = - this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - - return { - averageParsingTime: Math.round(average * 100) / 100, - p95ParsingTime: Math.round(p95 * 100) / 100, - totalCommandsParsed: this.performanceMetrics.length, - }; - } - - private recordPerformance(time: number): void { - this.performanceMetrics.push(time); - - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} diff --git a/backend/src/files/upload/VoiceFileUploadService.ts b/backend/src/files/upload/VoiceFileUploadService.ts deleted file mode 100644 index fb56f32..0000000 --- a/backend/src/files/upload/VoiceFileUploadService.ts +++ /dev/null @@ -1,599 +0,0 @@ -/** - * Voice File Upload Service - Phase 2 Voice-Driven File Operations - * Integrates voice commands with file upload workflow - * - * Success Criteria: - * - End-to-end voice upload in <5 seconds - * - Automatic entity linking based on command context - * - Upload confirmation to user - * - Real-time notifications to affected channels - */ - -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import { S3FileManager, FileMetadata } from '../management/S3FileManager'; -import { FileMetadataManager, FileStatus } from '../management/FileMetadataManager'; -import { logger } from '../../utils/logger'; -import { UserContext } from '../../voice/types'; - -export interface VoiceFileUploadRequest { - fileName: string; - contentType: string; - fileSize: number; - userContext: UserContext; - description?: string; - targetChannels?: string[]; - targetTasks?: string[]; - targetUsers?: string[]; - tags?: string[]; -} - -export interface VoiceFileUploadResult { - success: boolean; - uploadUrl?: string; - fileId?: string; - downloadUrl?: string | undefined; - expiresAt?: Date; - linkedEntities?: { - channels: string[]; - tasks: string[]; - users: string[]; - }; - processingTime: number; - error?: string; -} - -export interface FileUploadProgress { - fileId: string; - fileName: string; - progress: number; // 0-100 - status: FileStatus; - uploadSpeed?: number | undefined; // bytes per second - eta?: number | undefined; // seconds remaining - error?: string | undefined; -} - -export class VoiceFileUploadService extends EventEmitter { - private s3Manager: S3FileManager; - private metadataManager: FileMetadataManager; - private activeUploads: Map = new Map(); - private performanceMetrics: number[] = []; - - constructor() { - super(); - - this.s3Manager = new S3FileManager({ - maxFileSize: 100 * 1024 * 1024, // 100MB - presignedUrlTTL: 900, // 15 minutes - allowedFileTypes: [ - // Documents - '.pdf', - '.doc', - '.docx', - '.xls', - '.xlsx', - '.ppt', - '.pptx', - '.txt', - '.csv', - '.json', - '.xml', - '.rtf', - // Images - '.jpg', - '.jpeg', - '.png', - '.gif', - '.webp', - '.svg', - '.bmp', - // Media - '.mp4', - '.mov', - '.avi', - '.mkv', - '.webm', - '.mp3', - '.wav', - '.flac', - '.aac', - '.ogg', - // Archives - '.zip', - '.rar', - '.7z', - '.tar', - '.gz', - // Design files - '.psd', - '.ai', - '.sketch', - '.fig', - ], - }); - - this.metadataManager = new FileMetadataManager(); - - logger.info('Voice File Upload Service initialized'); - } - - /** - * Initiate voice-driven file upload workflow - * Target: Complete workflow in <5 seconds - */ - async initiateVoiceUpload(request: VoiceFileUploadRequest): Promise { - const startTime = performance.now(); - const uploadId = `upload_${Date.now()}_${Math.random().toString(36).substring(7)}`; - - logger.info('Starting voice file upload', { - uploadId, - fileName: request.fileName, - fileSize: `${Math.round(request.fileSize / 1024)}KB`, - userId: request.userContext.userId, - targetChannels: request.targetChannels?.length || 0, - targetTasks: request.targetTasks?.length || 0, - }); - - try { - // Step 1: Validate file and user permissions (Target: <100ms) - const validationStart = performance.now(); - await this.validateUploadRequest(request); - const validationTime = performance.now() - validationStart; - - // Step 2: Create file metadata record (Target: <200ms) - const metadataStart = performance.now(); - const s3Key = this.generateS3Key(request.fileName, request.userContext); - - const fileRecord = await this.metadataManager.createFileRecord( - request.fileName, - request.fileName, - request.fileSize, - request.contentType, - s3Key, - request.userContext.userId, - request.userContext.organizationId, - request.description, - request.tags || [] - ); - - const metadataTime = performance.now() - metadataStart; - - // Step 3: Generate S3 presigned upload URL (Target: <2000ms) - const urlGenerationStart = performance.now(); - - const fileMetadata: FileMetadata = { - userId: request.userContext.userId, - organizationId: request.userContext.organizationId, - originalName: request.fileName, - contentType: request.contentType, - size: request.fileSize, - uploadedAt: new Date().toISOString(), - description: request.description ?? '', - tags: request.tags, - }; - - const uploadResult = await this.s3Manager.generatePresignedUploadUrl( - request.fileName, - request.contentType, - request.fileSize, - fileMetadata - ); - - const urlGenerationTime = performance.now() - urlGenerationStart; - - // Step 4: Link file to entities (Target: <500ms) - const linkingStart = performance.now(); - - const linkedEntities = { - channels: [] as string[], - tasks: [] as string[], - users: [] as string[], - }; - - // Link to channels - if (request.targetChannels && request.targetChannels.length > 0) { - for (const channelId of request.targetChannels) { - await this.metadataManager.linkFileToEntity( - fileRecord.id, - 'channel', - channelId, - 'attachment', - request.userContext.userId - ); - linkedEntities.channels.push(channelId); - } - } - - // Link to tasks - if (request.targetTasks && request.targetTasks.length > 0) { - for (const taskId of request.targetTasks) { - await this.metadataManager.linkFileToEntity( - fileRecord.id, - 'task', - taskId, - 'attachment', - request.userContext.userId - ); - linkedEntities.tasks.push(taskId); - } - } - - // Link to users (for sharing) - if (request.targetUsers && request.targetUsers.length > 0) { - for (const userId of request.targetUsers) { - await this.metadataManager.linkFileToEntity( - fileRecord.id, - 'user', - userId, - 'share', - request.userContext.userId - ); - linkedEntities.users.push(userId); - } - } - - const linkingTime = performance.now() - linkingStart; - - // Step 5: Initialize upload progress tracking - const uploadProgress: FileUploadProgress = { - fileId: fileRecord.id, - fileName: request.fileName, - progress: 0, - status: FileStatus.UPLOADING, - uploadSpeed: 0, - eta: Math.ceil(request.fileSize / 1000000), // Rough estimate: 1MB/sec - }; - - this.activeUploads.set(fileRecord.id, uploadProgress); - - // Update file status to uploading - await this.metadataManager.updateFileStatus(fileRecord.id, FileStatus.UPLOADING); - - const totalTime = performance.now() - startTime; - this.recordPerformance(totalTime); - - // Check performance target - if (totalTime > 5000) { - logger.warn('Voice upload initiation exceeded 5 second target', { - uploadId, - totalTime: `${totalTime.toFixed(2)}ms`, - breakdown: { - validation: `${validationTime.toFixed(2)}ms`, - metadata: `${metadataTime.toFixed(2)}ms`, - urlGeneration: `${urlGenerationTime.toFixed(2)}ms`, - linking: `${linkingTime.toFixed(2)}ms`, - }, - }); - } - - // Emit upload initiated event - this.emit('upload_initiated', { - uploadId, - fileId: fileRecord.id, - fileName: request.fileName, - userId: request.userContext.userId, - linkedEntities, - processingTime: totalTime, - }); - - // Generate download URL for immediate access after upload - const downloadUrl = await this.s3Manager - .generatePresignedDownloadUrl( - uploadResult.key, - 3600 // 1 hour - ) - .catch(() => undefined); // Don't fail if download URL generation fails - - logger.info('Voice file upload initiated successfully', { - uploadId, - fileId: fileRecord.id, - fileName: request.fileName, - uploadUrl: uploadResult.uploadUrl ? 'generated' : 'failed', - downloadUrl: downloadUrl ? 'generated' : 'not_generated', - linkedEntities, - totalTime: `${totalTime.toFixed(2)}ms`, - }); - - const result: VoiceFileUploadResult = { - success: true, - uploadUrl: uploadResult.uploadUrl, - fileId: fileRecord.id, - downloadUrl, - expiresAt: uploadResult.expiresAt, - linkedEntities, - processingTime: totalTime, - }; - - // Schedule upload timeout check - this.scheduleUploadTimeoutCheck(fileRecord.id, 30 * 60 * 1000); // 30 minutes - - return result; - } catch (error: any) { - const totalTime = performance.now() - startTime; - this.recordPerformance(totalTime); - - logger.error('Voice file upload initiation failed', { - uploadId, - error: error.message, - fileName: request.fileName, - userId: request.userContext.userId, - totalTime: `${totalTime.toFixed(2)}ms`, - }); - - this.emit('upload_failed', { - uploadId, - fileName: request.fileName, - userId: request.userContext.userId, - error: error.message, - processingTime: totalTime, - }); - - return { - success: false, - error: error.message, - processingTime: totalTime, - }; - } - } - - /** - * Handle upload completion notification (typically from S3 webhook or polling) - */ - async handleUploadCompletion(fileId: string, success: boolean, error?: string): Promise { - const uploadProgress = this.activeUploads.get(fileId); - - if (!uploadProgress) { - logger.warn('Upload completion received for unknown file', { fileId }); - return; - } - - try { - if (success) { - // Update file status to completed - await this.metadataManager.updateFileStatus(fileId, FileStatus.COMPLETED); - - uploadProgress.progress = 100; - uploadProgress.status = FileStatus.COMPLETED; - - logger.info('File upload completed successfully', { - fileId, - fileName: uploadProgress.fileName, - }); - - this.emit('upload_completed', { - fileId, - fileName: uploadProgress.fileName, - status: 'success', - }); - - // Send notifications to linked entities - await this.sendUploadNotifications(fileId); - } else { - // Update file status to failed - await this.metadataManager.updateFileStatus(fileId, FileStatus.FAILED, error); - - uploadProgress.status = FileStatus.FAILED; - uploadProgress.error = error; - - logger.error('File upload failed', { - fileId, - fileName: uploadProgress.fileName, - error, - }); - - this.emit('upload_failed', { - fileId, - fileName: uploadProgress.fileName, - error, - }); - } - } catch (dbError: any) { - logger.error('Failed to update upload completion status', { - fileId, - error: dbError.message, - }); - } finally { - // Clean up tracking - setTimeout(() => { - this.activeUploads.delete(fileId); - }, 60000); // Keep for 1 minute for final status checks - } - } - - /** - * Get upload progress for a file - */ - getUploadProgress(fileId: string): FileUploadProgress | null { - return this.activeUploads.get(fileId) || null; - } - - /** - * Get all active uploads for monitoring - */ - getActiveUploads(): FileUploadProgress[] { - return Array.from(this.activeUploads.values()); - } - - /** - * Cancel an active upload - */ - async cancelUpload(fileId: string, userId: string): Promise { - const uploadProgress = this.activeUploads.get(fileId); - - if (!uploadProgress) { - return false; - } - - try { - // Update file status to failed - await this.metadataManager.updateFileStatus( - fileId, - FileStatus.FAILED, - 'Upload cancelled by user' - ); - - uploadProgress.status = FileStatus.FAILED; - uploadProgress.error = 'Cancelled by user'; - - logger.info('File upload cancelled', { - fileId, - fileName: uploadProgress.fileName, - userId, - }); - - this.emit('upload_cancelled', { - fileId, - fileName: uploadProgress.fileName, - userId, - }); - - // Remove from active uploads - this.activeUploads.delete(fileId); - - return true; - } catch (error: any) { - logger.error('Failed to cancel upload', { - fileId, - error: error.message, - }); - - return false; - } - } - - /** - * Get service performance statistics - */ - getPerformanceStats(): { - averageInitiationTime: number; - p95InitiationTime: number; - p99InitiationTime: number; - totalUploadsInitiated: number; - activeUploadsCount: number; - } { - if (this.performanceMetrics.length === 0) { - return { - averageInitiationTime: 0, - p95InitiationTime: 0, - p99InitiationTime: 0, - totalUploadsInitiated: 0, - activeUploadsCount: this.activeUploads.size, - }; - } - - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = - this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - - return { - averageInitiationTime: Math.round(average * 100) / 100, - p95InitiationTime: Math.round(p95 * 100) / 100, - p99InitiationTime: Math.round(p99 * 100) / 100, - totalUploadsInitiated: this.performanceMetrics.length, - activeUploadsCount: this.activeUploads.size, - }; - } - - private async validateUploadRequest(request: VoiceFileUploadRequest): Promise { - // Validate file - const validation = this.s3Manager.validateFile( - request.fileName, - request.contentType, - request.fileSize - ); - - if (!validation.valid) { - throw new Error(`File validation failed: ${validation.error}`); - } - - // Check user permissions (basic validation) - if (!request.userContext.userId || !request.userContext.organizationId) { - throw new Error('Invalid user context for file upload'); - } - - // Validate entity targets exist (simplified - in real implementation would check database) - if (request.targetChannels && request.targetChannels.some((id) => !id || id.trim() === '')) { - throw new Error('Invalid channel ID in target channels'); - } - - if (request.targetTasks && request.targetTasks.some((id) => !id || id.trim() === '')) { - throw new Error('Invalid task ID in target tasks'); - } - } - - private generateS3Key(fileName: string, userContext: UserContext): string { - const timestamp = Date.now(); - const randomId = Math.random().toString(36).substring(2, 8); - const sanitizedName = fileName.replace(/[^a-zA-Z0-9.-]/g, '_'); - const year = new Date().getFullYear(); - const month = String(new Date().getMonth() + 1).padStart(2, '0'); - - return `organizations/${userContext.organizationId}/users/${userContext.userId}/${year}/${month}/${timestamp}_${randomId}_${sanitizedName}`; - } - - private scheduleUploadTimeoutCheck(fileId: string, timeoutMs: number): void { - setTimeout(async () => { - const uploadProgress = this.activeUploads.get(fileId); - - if (uploadProgress && uploadProgress.status === FileStatus.UPLOADING) { - logger.warn('Upload timeout - marking as failed', { - fileId, - fileName: uploadProgress.fileName, - timeoutMs, - }); - - await this.handleUploadCompletion(fileId, false, 'Upload timeout'); - } - }, timeoutMs); - } - - private async sendUploadNotifications(fileId: string): Promise { - try { - const fileRecord = await this.metadataManager.getFileRecord(fileId); - - if (!fileRecord) { - logger.warn('Cannot send notifications for unknown file', { fileId }); - return; - } - - // Get linked entities - const linkedChannels = await this.metadataManager.getFilesForEntity('channel', fileId); - const linkedTasks = await this.metadataManager.getFilesForEntity('task', fileId); - - // Emit notification events (to be handled by notification system) - this.emit('file_upload_notification', { - fileId, - fileName: fileRecord.name, - uploadedBy: fileRecord.uploadedBy, - organizationId: fileRecord.organizationId, - linkedChannels: linkedChannels.map((f) => ({ id: f.id, name: f.name })), - linkedTasks: linkedTasks.map((f) => ({ id: f.id, name: f.name })), - fileSize: fileRecord.size, - contentType: fileRecord.contentType, - }); - - logger.debug('Upload notifications sent', { - fileId, - fileName: fileRecord.name, - channelCount: linkedChannels.length, - taskCount: linkedTasks.length, - }); - } catch (error: any) { - logger.error('Failed to send upload notifications', { - fileId, - error: error.message, - }); - } - } - - private recordPerformance(time: number): void { - this.performanceMetrics.push(time); - - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} diff --git a/backend/src/index.ts b/backend/src/index.ts deleted file mode 100644 index cd8ab51..0000000 --- a/backend/src/index.ts +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env node - -/** - * CEO Communication Platform - Main Entry Point - * High-performance backend API server with comprehensive features - */ - -import { config } from '@config/index'; -import { logger } from '@utils/logger'; -import { server } from './server'; - -/** - * Main application entry point - */ -async function main(): Promise { - try { - // Log startup information - logger.info({ - nodeVersion: process.version, - platform: process.platform, - arch: process.arch, - environment: config.app.env, - pid: process.pid, - }, 'Starting CEO Communication Platform API'); - - // Validate configuration - logger.info('Validating configuration...'); - // Configuration is already validated in config/index.ts - - // Start the server - await server.start(); - - } catch (error) { - logger.fatal({ error }, 'Failed to start application'); - process.exit(1); - } -} - -// Handle top-level errors -process.on('uncaughtException', (error: Error) => { - logger.fatal({ error }, 'Uncaught exception in main process'); - process.exit(1); -}); - -process.on('unhandledRejection', (reason: unknown, promise: Promise) => { - logger.fatal({ reason, promise }, 'Unhandled promise rejection in main process'); - process.exit(1); -}); - -// Start the application -if (require.main === module) { - main(); -} - -export { server }; -export default main; \ No newline at end of file diff --git a/backend/src/performance/caching/RedisCacheManager.ts b/backend/src/performance/caching/RedisCacheManager.ts deleted file mode 100644 index bad7130..0000000 --- a/backend/src/performance/caching/RedisCacheManager.ts +++ /dev/null @@ -1,803 +0,0 @@ -/** - * Redis Cache Manager - Phase 2 Performance Optimization - * Advanced caching layer for voice command processing and entity data - * - * Success Criteria: - * - Sub-100ms cache response times - * - Intelligent cache invalidation - * - Memory usage optimization - * - Cache hit rate >90% for frequently accessed data - */ - -import Redis from 'ioredis'; -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; - -export interface CacheConfig { - host: string; - port: number; - password?: string; - db: number; - keyPrefix: string; - maxMemory?: string; - maxMemoryPolicy?: string; -} - -export interface CacheEntry { - key: string; - value: any; - ttl: number; - createdAt: number; - accessCount: number; - lastAccessed: number; - tags?: string[]; -} - -export interface CacheMetrics { - hits: number; - misses: number; - hitRate: number; - averageResponseTime: number; - p95ResponseTime: number; - memoryUsage: number; - keyCount: number; - totalOperations: number; -} - -export interface CacheInvalidationPattern { - pattern: string; - description: string; - relatedEntities: string[]; -} - -export class RedisCacheManager extends EventEmitter { - private redis: Redis; - private config: CacheConfig; - private metrics = { - hits: 0, - misses: 0, - operations: 0, - responseTimes: [] as number[], - memoryUsage: 0, - }; - private readonly maxMetricsHistory = 10000; - private metricsTimer?: NodeJS.Timeout; - - // Cache invalidation patterns for different entity types - private readonly invalidationPatterns: CacheInvalidationPattern[] = [ - { - pattern: 'user:*', - description: 'User-related cache entries', - relatedEntities: ['user', 'session', 'preferences'], - }, - { - pattern: 'channel:*', - description: 'Channel-related cache entries', - relatedEntities: ['channel', 'message', 'member'], - }, - { - pattern: 'task:*', - description: 'Task-related cache entries', - relatedEntities: ['task', 'assignment', 'status'], - }, - { - pattern: 'file:*', - description: 'File-related cache entries', - relatedEntities: ['file', 'metadata', 'permission'], - }, - { - pattern: 'command:*', - description: 'Voice command cache entries', - relatedEntities: ['command', 'execution', 'result'], - }, - ]; - - constructor(config: CacheConfig) { - super(); - - this.config = config; - this.redis = new Redis({ - host: config.host, - port: config.port, - ...(config.password && { password: config.password }), - db: config.db, - keyPrefix: config.keyPrefix, - enableReadyCheck: true, - maxRetriesPerRequest: 3, - }); - - this.setupEventHandlers(); - this.startMetricsCollection(); - - logger.info('Redis Cache Manager initialized', { - host: config.host, - port: config.port, - db: config.db, - keyPrefix: config.keyPrefix, - }); - } - - /** - * Setup Redis event handlers - */ - private setupEventHandlers(): void { - this.redis.on('ready', () => { - logger.info('Redis connection established'); - this.emit('ready'); - }); - - this.redis.on('error', (error) => { - logger.error('Redis connection error', { error: error.message }); - this.emit('error', error); - }); - - this.redis.on('close', () => { - logger.warn('Redis connection closed'); - this.emit('close'); - }); - } - - /** - * Get value from cache with performance tracking - */ - async get(key: string): Promise { - const startTime = performance.now(); - - try { - const value = await this.redis.get(key); - const responseTime = performance.now() - startTime; - - this.recordResponseTime(responseTime); - - if (value === null) { - this.metrics.misses++; - logger.debug('Cache miss', { key, responseTime: `${responseTime.toFixed(2)}ms` }); - return null; - } - - this.metrics.hits++; - - // Update access metadata - await this.updateAccessMetadata(key); - - logger.debug('Cache hit', { - key, - responseTime: `${responseTime.toFixed(2)}ms`, - hitRate: this.getHitRate().toFixed(2) + '%', - }); - - // Parse JSON if it's a JSON string - try { - return JSON.parse(value) as T; - } catch { - return value as T; - } - } catch (error: any) { - const responseTime = performance.now() - startTime; - this.recordResponseTime(responseTime); - - logger.error('Cache get error', { - key, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - return null; - } - } - - /** - * Set value in cache with TTL and tags - */ - async set(key: string, value: any, ttl: number = 3600, tags: string[] = []): Promise { - const startTime = performance.now(); - - try { - // Serialize value - const serializedValue = typeof value === 'string' ? value : JSON.stringify(value); - - // Set value with TTL - const result = await this.redis.setex(key, ttl, serializedValue); - - // Store metadata for analytics - const metadata = { - key, - ttl, - createdAt: Date.now(), - accessCount: 0, - lastAccessed: Date.now(), - tags, - }; - - await this.redis.setex(`${key}:meta`, ttl, JSON.stringify(metadata)); - - // Add to tag indices if tags provided - if (tags.length > 0) { - await this.addToTagIndices(key, tags, ttl); - } - - const responseTime = performance.now() - startTime; - this.recordResponseTime(responseTime); - - logger.debug('Cache set', { - key, - ttl: `${ttl}s`, - tags: tags.length, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - this.emit('cache_set', { - key, - ttl, - tags, - responseTime, - }); - - return result === 'OK'; - } catch (error: any) { - const responseTime = performance.now() - startTime; - this.recordResponseTime(responseTime); - - logger.error('Cache set error', { - key, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - return false; - } - } - - /** - * Delete key from cache - */ - async delete(key: string): Promise { - const startTime = performance.now(); - - try { - const pipeline = this.redis.pipeline(); - pipeline.del(key); - pipeline.del(`${key}:meta`); - - const results = await pipeline.exec(); - const responseTime = performance.now() - startTime; - - this.recordResponseTime(responseTime); - - const deletedCount = - results?.reduce((sum, [err, result]) => (err ? sum : sum + (result as number)), 0) || 0; - - logger.debug('Cache delete', { - key, - deletedEntries: deletedCount, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - this.emit('cache_delete', { - key, - deletedEntries: deletedCount, - responseTime, - }); - - return deletedCount > 0; - } catch (error: any) { - const responseTime = performance.now() - startTime; - this.recordResponseTime(responseTime); - - logger.error('Cache delete error', { - key, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - return false; - } - } - - /** - * Get multiple keys at once - */ - async mget(keys: string[]): Promise> { - const startTime = performance.now(); - const result = new Map(); - - if (keys.length === 0) { - return result; - } - - try { - const values = await this.redis.mget(...keys); - const responseTime = performance.now() - startTime; - - this.recordResponseTime(responseTime); - - let hits = 0; - let misses = 0; - - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - const value = values[i]; - - if (!key || value === null || value === undefined) { - misses++; - continue; - } - - hits++; - - try { - result.set(key, JSON.parse(value as string) as T); - } catch { - result.set(key, value as T); - } - - // Update access metadata (async, don't wait) - this.updateAccessMetadata(key); - } - - this.metrics.hits += hits; - this.metrics.misses += misses; - - logger.debug('Cache mget', { - keys: keys.length, - hits, - misses, - hitRate: hits > 0 ? ((hits / keys.length) * 100).toFixed(1) + '%' : '0%', - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - return result; - } catch (error: any) { - const responseTime = performance.now() - startTime; - this.recordResponseTime(responseTime); - - logger.error('Cache mget error', { - keys: keys.length, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - return result; - } - } - - /** - * Set multiple keys at once - */ - async mset(entries: Map): Promise { - const startTime = performance.now(); - let successCount = 0; - - if (entries.size === 0) { - return 0; - } - - try { - const pipeline = this.redis.pipeline(); - - for (const [key, entry] of entries) { - const { value, ttl = 3600, tags = [] } = entry; - const serializedValue = typeof value === 'string' ? value : JSON.stringify(value); - - pipeline.setex(key, ttl, serializedValue); - - // Add metadata - const metadata = { - key, - ttl, - createdAt: Date.now(), - accessCount: 0, - lastAccessed: Date.now(), - tags, - }; - - pipeline.setex(`${key}:meta`, ttl, JSON.stringify(metadata)); - - // Add to tag indices - if (tags.length > 0) { - for (const tag of tags) { - pipeline.sadd(`tag:${tag}`, key); - pipeline.expire(`tag:${tag}`, ttl); - } - } - } - - const results = await pipeline.exec(); - const responseTime = performance.now() - startTime; - - this.recordResponseTime(responseTime); - - // Count successful operations - successCount = results?.filter(([err, result]) => !err && result === 'OK').length || 0; - - logger.debug('Cache mset', { - entries: entries.size, - successful: successCount, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - this.emit('cache_mset', { - entries: entries.size, - successful: successCount, - responseTime, - }); - - return successCount; - } catch (error: any) { - const responseTime = performance.now() - startTime; - this.recordResponseTime(responseTime); - - logger.error('Cache mset error', { - entries: entries.size, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - return successCount; - } - } - - /** - * Invalidate cache entries by pattern - */ - async invalidateByPattern(pattern: string): Promise { - const startTime = performance.now(); - - try { - const keys = await this.redis.keys(pattern); - - if (keys.length === 0) { - logger.debug('No keys found for invalidation pattern', { pattern }); - return 0; - } - - const pipeline = this.redis.pipeline(); - - for (const key of keys) { - pipeline.del(key); - pipeline.del(`${key}:meta`); - } - - const results = await pipeline.exec(); - const responseTime = performance.now() - startTime; - - this.recordResponseTime(responseTime); - - const deletedCount = results?.filter(([err, result]) => !err && result === 1).length || 0; - - logger.info('Cache invalidated by pattern', { - pattern, - keysFound: keys.length, - deleted: deletedCount, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - this.emit('cache_invalidated', { - pattern, - keysFound: keys.length, - deleted: deletedCount, - responseTime, - }); - - return deletedCount; - } catch (error: any) { - const responseTime = performance.now() - startTime; - this.recordResponseTime(responseTime); - - logger.error('Cache invalidation error', { - pattern, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - return 0; - } - } - - /** - * Invalidate cache entries by tags - */ - async invalidateByTags(tags: string[]): Promise { - const startTime = performance.now(); - let totalDeleted = 0; - - try { - for (const tag of tags) { - const keys = await this.redis.smembers(`tag:${tag}`); - - if (keys.length > 0) { - const pipeline = this.redis.pipeline(); - - for (const key of keys) { - pipeline.del(key); - pipeline.del(`${key}:meta`); - } - - // Also remove the tag set - pipeline.del(`tag:${tag}`); - - const results = await pipeline.exec(); - const deletedCount = results?.filter(([err, result]) => !err && result === 1).length || 0; - - totalDeleted += deletedCount; - - logger.debug('Cache invalidated by tag', { - tag, - keys: keys.length, - deleted: deletedCount, - }); - } - } - - const responseTime = performance.now() - startTime; - this.recordResponseTime(responseTime); - - logger.info('Cache invalidated by tags', { - tags, - totalDeleted, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - this.emit('cache_invalidated_by_tags', { - tags, - totalDeleted, - responseTime, - }); - - return totalDeleted; - } catch (error: any) { - const responseTime = performance.now() - startTime; - this.recordResponseTime(responseTime); - - logger.error('Cache invalidation by tags error', { - tags, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - return totalDeleted; - } - } - - /** - * Intelligent cache warming for frequently accessed entities - */ - async warmCache( - entityType: string, - entityIds: string[], - dataLoader: (id: string) => Promise - ): Promise { - const startTime = performance.now(); - - try { - const cacheOperations = new Map(); - - for (const entityId of entityIds) { - try { - const data = await dataLoader(entityId); - const cacheKey = `${entityType}:${entityId}`; - - cacheOperations.set(cacheKey, { - value: data, - ttl: this.getTTLForEntityType(entityType), - tags: [entityType, `${entityType}:${entityId}`], - }); - } catch (error: any) { - logger.warn('Failed to load data for cache warming', { - entityType, - entityId, - error: error.message, - }); - } - } - - if (cacheOperations.size > 0) { - await this.mset(cacheOperations); - } - - const responseTime = performance.now() - startTime; - - logger.info('Cache warming completed', { - entityType, - entityIds: entityIds.length, - cached: cacheOperations.size, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - - this.emit('cache_warmed', { - entityType, - entityIds: entityIds.length, - cached: cacheOperations.size, - responseTime, - }); - } catch (error: any) { - const responseTime = performance.now() - startTime; - - logger.error('Cache warming error', { - entityType, - entityIds: entityIds.length, - error: error.message, - responseTime: `${responseTime.toFixed(2)}ms`, - }); - } - } - - /** - * Get TTL for specific entity types - */ - private getTTLForEntityType(entityType: string): number { - const ttlMap: Record = { - user: 1800, // 30 minutes - channel: 3600, // 1 hour - task: 900, // 15 minutes - file: 7200, // 2 hours - command: 300, // 5 minutes - session: 1800, // 30 minutes - organization: 7200, // 2 hours - default: 3600, // 1 hour - }; - - return ttlMap[entityType] ?? ttlMap['default'] ?? 3600; - } - - /** - * Add key to tag indices - */ - private async addToTagIndices(key: string, tags: string[], ttl: number): Promise { - const pipeline = this.redis.pipeline(); - - for (const tag of tags) { - pipeline.sadd(`tag:${tag}`, key); - pipeline.expire(`tag:${tag}`, ttl); - } - - await pipeline.exec(); - } - - /** - * Update access metadata for analytics - */ - private async updateAccessMetadata(key: string): Promise { - try { - const metaKey = `${key}:meta`; - const metaData = await this.redis.get(metaKey); - - if (metaData) { - const metadata = JSON.parse(metaData); - metadata.accessCount++; - metadata.lastAccessed = Date.now(); - - await this.redis.set(metaKey, JSON.stringify(metadata), 'KEEPTTL'); - } - } catch (error) { - // Silently fail for metadata updates - } - } - - /** - * Record response time for metrics - */ - private recordResponseTime(time: number): void { - this.metrics.operations++; - this.metrics.responseTimes.push(time); - - // Keep only recent measurements - if (this.metrics.responseTimes.length > this.maxMetricsHistory) { - this.metrics.responseTimes.shift(); - } - } - - /** - * Calculate hit rate - */ - private getHitRate(): number { - const total = this.metrics.hits + this.metrics.misses; - return total > 0 ? (this.metrics.hits / total) * 100 : 0; - } - - /** - * Start metrics collection - */ - private startMetricsCollection(): void { - this.metricsTimer = setInterval(async () => { - await this.collectMemoryMetrics(); - }, 60000); // Every minute - } - - /** - * Collect memory usage metrics - */ - private async collectMemoryMetrics(): Promise { - try { - const memoryInfo = await this.redis.call('MEMORY', 'USAGE') as number; - if (typeof memoryInfo === 'number') { - this.metrics.memoryUsage = memoryInfo; - } - } catch (error) { - logger.error('Failed to collect memory metrics', { - error: (error as Error).message, - }); - - // Silently fail for metrics collection - } - } - - /** - * Get comprehensive cache metrics - */ - async getMetrics(): Promise { - const responseTimes = this.metrics.responseTimes; - const averageResponseTime = - responseTimes.length > 0 - ? responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length - : 0; - - const sortedTimes = [...responseTimes].sort((a, b) => a - b); - const p95ResponseTime = - sortedTimes.length > 0 ? sortedTimes[Math.floor(sortedTimes.length * 0.95)] || 0 : 0; - - // Get current key count - const keyCount = await this.redis.dbsize(); - - return { - hits: this.metrics.hits, - misses: this.metrics.misses, - hitRate: this.getHitRate(), - averageResponseTime: Math.round(averageResponseTime * 100) / 100, - p95ResponseTime: Math.round(p95ResponseTime * 100) / 100, - memoryUsage: this.metrics.memoryUsage, - keyCount, - totalOperations: this.metrics.operations, - }; - } - - /** - * Get cache health status - */ - async getHealthStatus(): Promise<{ - status: 'healthy' | 'degraded' | 'unhealthy'; - checks: Record; - metrics: CacheMetrics; - }> { - const metrics = await this.getMetrics(); - - const checks = { - connected: this.redis.status === 'ready', - hitRateAcceptable: metrics.hitRate >= 85, // 85% minimum hit rate - responseTimeAcceptable: metrics.averageResponseTime < 100, // Sub-100ms average - memoryUsageAcceptable: true, // Would need Redis configuration to check properly - }; - - const passedChecks = Object.values(checks).filter(Boolean).length; - const totalChecks = Object.values(checks).length; - - let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy'; - - if (passedChecks < totalChecks * 0.5) { - status = 'unhealthy'; - } else if (passedChecks < totalChecks) { - status = 'degraded'; - } - - return { - status, - checks, - metrics, - }; - } - - /** - * Cleanup and close connection - */ - async destroy(): Promise { - if (this.metricsTimer) { - clearInterval(this.metricsTimer); - } - - this.removeAllListeners(); - await this.redis.disconnect(); - - logger.info('Redis Cache Manager destroyed'); - } -} diff --git a/backend/src/performance/monitoring/PerformanceMonitor.ts b/backend/src/performance/monitoring/PerformanceMonitor.ts deleted file mode 100644 index 63086c6..0000000 --- a/backend/src/performance/monitoring/PerformanceMonitor.ts +++ /dev/null @@ -1,773 +0,0 @@ -/** - * Performance Monitor - Phase 2 Performance Monitoring & Analytics - * Comprehensive performance monitoring for voice command processing - * - * Success Criteria: - * - Real-time performance metrics collection - * - Automated performance alerting - * - Performance bottleneck identification - * - System health monitoring - */ - -import { EventEmitter } from 'events'; -import { performance, PerformanceObserver } from 'perf_hooks'; -import os from 'os'; -import process from 'process'; -import { logger } from '../../utils/logger'; - -export interface PerformanceMetrics { - timestamp: string; - cpu: { - usage: number; - loadAverage: number[]; - cores: number; - }; - memory: { - used: number; - total: number; - usage: number; - heapUsed: number; - heapTotal: number; - external: number; - }; - network: { - bytesIn: number; - bytesOut: number; - connectionsActive: number; - }; - voice: { - averageProcessingTime: number; - p95ProcessingTime: number; - commandsPerMinute: number; - errorRate: number; - successRate: number; - }; - database: { - activeConnections: number; - averageQueryTime: number; - slowQueries: number; - connectionPoolUsage: number; - }; - cache: { - hitRate: number; - averageResponseTime: number; - memoryUsage: number; - operationsPerSecond: number; - }; - websocket: { - activeConnections: number; - messagesPerSecond: number; - averageLatency: number; - disconnectionRate: number; - }; -} - -export interface PerformanceAlert { - alertId: string; - type: 'cpu' | 'memory' | 'response_time' | 'error_rate' | 'connection' | 'disk' | 'custom'; - severity: 'low' | 'medium' | 'high' | 'critical'; - title: string; - message: string; - threshold: number; - currentValue: number; - timestamp: string; - resolved: boolean; - resolvedAt?: string; -} - -export interface PerformanceThresholds { - cpu: { warning: number; critical: number }; - memory: { warning: number; critical: number }; - responseTime: { warning: number; critical: number }; - errorRate: { warning: number; critical: number }; - diskUsage: { warning: number; critical: number }; -} - -export interface SystemHealth { - status: 'healthy' | 'degraded' | 'unhealthy' | 'critical'; - score: number; // 0-100 - checks: { - cpu: { status: 'pass' | 'warn' | 'fail'; value: number }; - memory: { status: 'pass' | 'warn' | 'fail'; value: number }; - responseTime: { status: 'pass' | 'warn' | 'fail'; value: number }; - errorRate: { status: 'pass' | 'warn' | 'fail'; value: number }; - connectivity: { status: 'pass' | 'warn' | 'fail'; value: number }; - }; - lastChecked: string; -} - -export class PerformanceMonitor extends EventEmitter { - private metricsHistory: PerformanceMetrics[] = []; - private activeAlerts: Map = new Map(); - private performanceObserver?: PerformanceObserver; - private monitoringTimer?: NodeJS.Timeout; - private healthCheckTimer?: NodeJS.Timeout; - private readonly maxHistorySize = 1440; // 24 hours at 1-minute intervals - private readonly monitoringInterval = 60000; // 1 minute - private readonly healthCheckInterval = 30000; // 30 seconds - - // Performance tracking maps - private voiceCommandMetrics: number[] = []; - private databaseQueryMetrics: number[] = []; - private cacheOperationMetrics: number[] = []; - private websocketMetrics: { latency: number[]; connections: number } = { - latency: [], - connections: 0, - }; - private networkMetrics = { bytesIn: 0, bytesOut: 0, connections: 0 }; - private errorCounts = { total: 0, voice: 0, database: 0, network: 0, general: 0 }; - - private thresholds: PerformanceThresholds = { - cpu: { warning: 70, critical: 90 }, - memory: { warning: 80, critical: 95 }, - responseTime: { warning: 1000, critical: 3000 }, - errorRate: { warning: 5, critical: 10 }, - diskUsage: { warning: 80, critical: 95 }, - }; - - constructor(customThresholds?: Partial) { - super(); - - if (customThresholds) { - this.thresholds = { ...this.thresholds, ...customThresholds }; - } - - this.setupPerformanceObserver(); - this.startMonitoring(); - this.startHealthChecks(); - - logger.info('Performance Monitor initialized', { - monitoringInterval: `${this.monitoringInterval / 1000}s`, - healthCheckInterval: `${this.healthCheckInterval / 1000}s`, - thresholds: this.thresholds, - }); - } - - /** - * Setup Node.js Performance Observer - */ - private setupPerformanceObserver(): void { - this.performanceObserver = new PerformanceObserver((list) => { - const entries = list.getEntries(); - - for (const entry of entries) { - if (entry.entryType === 'measure') { - this.recordCustomMetric(entry.name, entry.duration); - } - } - }); - - this.performanceObserver.observe({ entryTypes: ['measure', 'resource'] }); - } - - /** - * Start continuous monitoring - */ - private startMonitoring(): void { - this.monitoringTimer = setInterval(async () => { - await this.collectMetrics(); - }, this.monitoringInterval); - } - - /** - * Start health checks - */ - private startHealthChecks(): void { - this.healthCheckTimer = setInterval(async () => { - await this.performHealthCheck(); - }, this.healthCheckInterval); - } - - /** - * Collect comprehensive performance metrics - */ - private async collectMetrics(): Promise { - const startTime = performance.now(); - - try { - const metrics: PerformanceMetrics = { - timestamp: new Date().toISOString(), - cpu: await this.getCPUMetrics(), - memory: this.getMemoryMetrics(), - network: this.getNetworkMetrics(), - voice: this.getVoiceCommandMetrics(), - database: this.getDatabaseMetrics(), - cache: this.getCacheMetrics(), - websocket: this.getWebSocketMetrics(), - }; - - // Add to history - this.metricsHistory.push(metrics); - - // Trim history if too large - if (this.metricsHistory.length > this.maxHistorySize) { - this.metricsHistory.shift(); - } - - // Check for performance issues - await this.analyzeMetrics(metrics); - - const collectionTime = performance.now() - startTime; - - logger.debug('Performance metrics collected', { - collectionTime: `${collectionTime.toFixed(2)}ms`, - cpuUsage: `${metrics.cpu.usage.toFixed(1)}%`, - memoryUsage: `${metrics.memory.usage.toFixed(1)}%`, - voiceCommandsPerMin: metrics.voice.commandsPerMinute, - }); - - this.emit('metrics_collected', metrics); - } catch (error: any) { - logger.error('Error collecting performance metrics', { - error: error.message, - stack: error.stack, - }); - } - } - - /** - * Get CPU metrics - */ - private async getCPUMetrics(): Promise { - const cpus = os.cpus(); - const loadAverage = os.loadavg(); - - // Calculate CPU usage (simplified) - let totalIdle = 0; - let totalTick = 0; - - for (const cpu of cpus) { - for (const type in cpu.times) { - totalTick += cpu.times[type as keyof typeof cpu.times]; - } - totalIdle += cpu.times.idle; - } - - const usage = 100 - Math.floor((totalIdle / totalTick) * 100); - - return { - usage, - loadAverage, - cores: cpus.length, - }; - } - - /** - * Get memory metrics - */ - private getMemoryMetrics(): PerformanceMetrics['memory'] { - const totalMemory = os.totalmem(); - const freeMemory = os.freemem(); - const usedMemory = totalMemory - freeMemory; - const memoryUsage = (usedMemory / totalMemory) * 100; - - const heapStats = process.memoryUsage(); - - return { - used: usedMemory, - total: totalMemory, - usage: memoryUsage, - heapUsed: heapStats.heapUsed, - heapTotal: heapStats.heapTotal, - external: heapStats.external, - }; - } - - /** - * Get network metrics - */ - private getNetworkMetrics(): PerformanceMetrics['network'] { - return { - bytesIn: this.networkMetrics.bytesIn, - bytesOut: this.networkMetrics.bytesOut, - connectionsActive: this.networkMetrics.connections, - }; - } - - /** - * Get voice command processing metrics - */ - private getVoiceCommandMetrics(): PerformanceMetrics['voice'] { - if (this.voiceCommandMetrics.length === 0) { - return { - averageProcessingTime: 0, - p95ProcessingTime: 0, - commandsPerMinute: 0, - errorRate: 0, - successRate: 100, - }; - } - - const sorted = [...this.voiceCommandMetrics].sort((a, b) => a - b); - const average = - this.voiceCommandMetrics.reduce((sum, time) => sum + time, 0) / - this.voiceCommandMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - - const commandsPerMinute = this.voiceCommandMetrics.length; // Metrics collected per minute - const errorRate = - this.errorCounts.voice > 0 - ? (this.errorCounts.voice / this.voiceCommandMetrics.length) * 100 - : 0; - const successRate = 100 - errorRate; - - return { - averageProcessingTime: Math.round(average * 100) / 100, - p95ProcessingTime: Math.round(p95 * 100) / 100, - commandsPerMinute, - errorRate: Math.round(errorRate * 100) / 100, - successRate: Math.round(successRate * 100) / 100, - }; - } - - /** - * Get database metrics - */ - private getDatabaseMetrics(): PerformanceMetrics['database'] { - const averageQueryTime = - this.databaseQueryMetrics.length > 0 - ? this.databaseQueryMetrics.reduce((sum, time) => sum + time, 0) / - this.databaseQueryMetrics.length - : 0; - - const slowQueries = this.databaseQueryMetrics.filter((time) => time > 1000).length; - - return { - activeConnections: 0, // Would be populated by database pool - averageQueryTime: Math.round(averageQueryTime * 100) / 100, - slowQueries, - connectionPoolUsage: 0, // Would be calculated from pool stats - }; - } - - /** - * Get cache metrics - */ - private getCacheMetrics(): PerformanceMetrics['cache'] { - const averageResponseTime = - this.cacheOperationMetrics.length > 0 - ? this.cacheOperationMetrics.reduce((sum, time) => sum + time, 0) / - this.cacheOperationMetrics.length - : 0; - - return { - hitRate: 90, // Would be populated by cache manager - averageResponseTime: Math.round(averageResponseTime * 100) / 100, - memoryUsage: 0, // Would be populated by cache manager - operationsPerSecond: this.cacheOperationMetrics.length, - }; - } - - /** - * Get WebSocket metrics - */ - private getWebSocketMetrics(): PerformanceMetrics['websocket'] { - const averageLatency = - this.websocketMetrics.latency.length > 0 - ? this.websocketMetrics.latency.reduce((sum, time) => sum + time, 0) / - this.websocketMetrics.latency.length - : 0; - - return { - activeConnections: this.websocketMetrics.connections, - messagesPerSecond: 0, // Would be calculated from message counts - averageLatency: Math.round(averageLatency * 100) / 100, - disconnectionRate: 0, // Would be calculated from disconnection events - }; - } - - /** - * Record custom performance metric - */ - recordCustomMetric(name: string, value: number): void { - switch (name) { - case 'voice_command_processing': - this.voiceCommandMetrics.push(value); - if (this.voiceCommandMetrics.length > 1000) { - this.voiceCommandMetrics.shift(); - } - break; - - case 'database_query': - this.databaseQueryMetrics.push(value); - if (this.databaseQueryMetrics.length > 1000) { - this.databaseQueryMetrics.shift(); - } - break; - - case 'cache_operation': - this.cacheOperationMetrics.push(value); - if (this.cacheOperationMetrics.length > 1000) { - this.cacheOperationMetrics.shift(); - } - break; - - case 'websocket_latency': - this.websocketMetrics.latency.push(value); - if (this.websocketMetrics.latency.length > 1000) { - this.websocketMetrics.latency.shift(); - } - break; - } - - logger.debug('Performance metric recorded', { - name, - value: `${value.toFixed(2)}ms`, - }); - } - - /** - * Record error event - */ - recordError(type: 'voice' | 'database' | 'network' | 'general', details?: any): void { - this.errorCounts.total++; - this.errorCounts[type] = (this.errorCounts[type] || 0) + 1; - - logger.debug('Error recorded for performance monitoring', { - type, - totalErrors: this.errorCounts.total, - typeErrors: this.errorCounts[type], - }); - } - - /** - * Record network activity - */ - recordNetworkActivity(bytesIn: number, bytesOut: number, connectionChange: number = 0): void { - this.networkMetrics.bytesIn += bytesIn; - this.networkMetrics.bytesOut += bytesOut; - this.networkMetrics.connections += connectionChange; - } - - /** - * Update WebSocket connection count - */ - updateWebSocketConnections(count: number): void { - this.websocketMetrics.connections = count; - } - - /** - * Analyze metrics for performance issues - */ - private async analyzeMetrics(metrics: PerformanceMetrics): Promise { - // CPU usage check - if (metrics.cpu.usage > this.thresholds.cpu.critical) { - await this.createAlert( - 'cpu', - 'critical', - 'Critical CPU Usage', - `CPU usage is at ${metrics.cpu.usage.toFixed(1)}%`, - this.thresholds.cpu.critical, - metrics.cpu.usage - ); - } else if (metrics.cpu.usage > this.thresholds.cpu.warning) { - await this.createAlert( - 'cpu', - 'medium', - 'High CPU Usage', - `CPU usage is at ${metrics.cpu.usage.toFixed(1)}%`, - this.thresholds.cpu.warning, - metrics.cpu.usage - ); - } else { - await this.resolveAlert('cpu'); - } - - // Memory usage check - if (metrics.memory.usage > this.thresholds.memory.critical) { - await this.createAlert( - 'memory', - 'critical', - 'Critical Memory Usage', - `Memory usage is at ${metrics.memory.usage.toFixed(1)}%`, - this.thresholds.memory.critical, - metrics.memory.usage - ); - } else if (metrics.memory.usage > this.thresholds.memory.warning) { - await this.createAlert( - 'memory', - 'medium', - 'High Memory Usage', - `Memory usage is at ${metrics.memory.usage.toFixed(1)}%`, - this.thresholds.memory.warning, - metrics.memory.usage - ); - } else { - await this.resolveAlert('memory'); - } - - // Response time check - if (metrics.voice.averageProcessingTime > this.thresholds.responseTime.critical) { - await this.createAlert( - 'response_time', - 'critical', - 'Critical Response Time', - `Voice command processing time is ${metrics.voice.averageProcessingTime.toFixed(2)}ms`, - this.thresholds.responseTime.critical, - metrics.voice.averageProcessingTime - ); - } else if (metrics.voice.averageProcessingTime > this.thresholds.responseTime.warning) { - await this.createAlert( - 'response_time', - 'medium', - 'High Response Time', - `Voice command processing time is ${metrics.voice.averageProcessingTime.toFixed(2)}ms`, - this.thresholds.responseTime.warning, - metrics.voice.averageProcessingTime - ); - } else { - await this.resolveAlert('response_time'); - } - - // Error rate check - if (metrics.voice.errorRate > this.thresholds.errorRate.critical) { - await this.createAlert( - 'error_rate', - 'critical', - 'Critical Error Rate', - `Voice command error rate is ${metrics.voice.errorRate.toFixed(1)}%`, - this.thresholds.errorRate.critical, - metrics.voice.errorRate - ); - } else if (metrics.voice.errorRate > this.thresholds.errorRate.warning) { - await this.createAlert( - 'error_rate', - 'medium', - 'High Error Rate', - `Voice command error rate is ${metrics.voice.errorRate.toFixed(1)}%`, - this.thresholds.errorRate.warning, - metrics.voice.errorRate - ); - } else { - await this.resolveAlert('error_rate'); - } - } - - /** - * Create performance alert - */ - private async createAlert( - type: PerformanceAlert['type'], - severity: PerformanceAlert['severity'], - title: string, - message: string, - threshold: number, - currentValue: number - ): Promise { - // Check if alert already exists - const existingAlert = this.activeAlerts.get(type); - if (existingAlert && !existingAlert.resolved) { - // Update existing alert - existingAlert.currentValue = currentValue; - existingAlert.timestamp = new Date().toISOString(); - return; - } - - const alert: PerformanceAlert = { - alertId: `alert_${type}_${Date.now()}`, - type, - severity, - title, - message, - threshold, - currentValue, - timestamp: new Date().toISOString(), - resolved: false, - }; - - this.activeAlerts.set(type, alert); - - logger.warn('Performance alert created', { - alertId: alert.alertId, - type: alert.type, - severity: alert.severity, - title: alert.title, - currentValue, - threshold, - }); - - this.emit('performance_alert', alert); - } - - /** - * Resolve performance alert - */ - private async resolveAlert(type: PerformanceAlert['type']): Promise { - const alert = this.activeAlerts.get(type); - if (alert && !alert.resolved) { - alert.resolved = true; - alert.resolvedAt = new Date().toISOString(); - - logger.info('Performance alert resolved', { - alertId: alert.alertId, - type: alert.type, - resolvedAt: alert.resolvedAt, - }); - - this.emit('performance_alert_resolved', alert); - } - } - - /** - * Perform comprehensive system health check - */ - private async performHealthCheck(): Promise { - try { - const latestMetrics = this.metricsHistory[this.metricsHistory.length - 1]; - if (!latestMetrics) return; - - const health: SystemHealth = { - status: 'healthy', - score: 100, - checks: { - cpu: this.evaluateMetric(latestMetrics.cpu.usage, this.thresholds.cpu), - memory: this.evaluateMetric(latestMetrics.memory.usage, this.thresholds.memory), - responseTime: this.evaluateMetric( - latestMetrics.voice.averageProcessingTime, - this.thresholds.responseTime - ), - errorRate: this.evaluateMetric(latestMetrics.voice.errorRate, this.thresholds.errorRate), - connectivity: { status: 'pass', value: latestMetrics.websocket.activeConnections }, - }, - lastChecked: new Date().toISOString(), - }; - - // Calculate overall health score - let totalScore = 0; - let checkCount = 0; - - for (const [key, check] of Object.entries(health.checks)) { - checkCount++; - if (check.status === 'pass') { - totalScore += 100; - } else if (check.status === 'warn') { - totalScore += 70; - } else { - totalScore += 30; - } - } - - health.score = Math.round(totalScore / checkCount); - - // Determine overall status - if (health.score >= 90) { - health.status = 'healthy'; - } else if (health.score >= 70) { - health.status = 'degraded'; - } else if (health.score >= 50) { - health.status = 'unhealthy'; - } else { - health.status = 'critical'; - } - - this.emit('health_check', health); - } catch (error: any) { - logger.error('Health check failed', { - error: error.message, - stack: error.stack, - }); - } - } - - /** - * Evaluate individual metric against thresholds - */ - private evaluateMetric( - value: number, - thresholds: { warning: number; critical: number } - ): { - status: 'pass' | 'warn' | 'fail'; - value: number; - } { - if (value >= thresholds.critical) { - return { status: 'fail', value }; - } else if (value >= thresholds.warning) { - return { status: 'warn', value }; - } else { - return { status: 'pass', value }; - } - } - - /** - * Get recent performance metrics - */ - getMetrics(limit?: number): PerformanceMetrics[] { - if (limit) { - return this.metricsHistory.slice(-limit); - } - return [...this.metricsHistory]; - } - - /** - * Get active performance alerts - */ - getActiveAlerts(): PerformanceAlert[] { - return Array.from(this.activeAlerts.values()).filter((alert) => !alert.resolved); - } - - /** - * Get all alerts (including resolved) - */ - getAllAlerts(limit?: number): PerformanceAlert[] { - const alerts = Array.from(this.activeAlerts.values()).sort( - (a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime() - ); - - return limit ? alerts.slice(0, limit) : alerts; - } - - /** - * Get performance summary - */ - getPerformanceSummary(): { - currentMetrics: PerformanceMetrics | null; - activeAlerts: number; - averageResponseTime24h: number; - errorRate24h: number; - uptime: number; - } { - const currentMetrics = this.metricsHistory[this.metricsHistory.length - 1] || null; - const activeAlerts = this.getActiveAlerts().length; - - // Calculate 24h averages - const last24h = this.metricsHistory.slice(-1440); // Last 24 hours - const averageResponseTime24h = - last24h.length > 0 - ? last24h.reduce((sum, m) => sum + m.voice.averageProcessingTime, 0) / last24h.length - : 0; - - const errorRate24h = - last24h.length > 0 - ? last24h.reduce((sum, m) => sum + m.voice.errorRate, 0) / last24h.length - : 0; - - return { - currentMetrics, - activeAlerts, - averageResponseTime24h: Math.round(averageResponseTime24h * 100) / 100, - errorRate24h: Math.round(errorRate24h * 100) / 100, - uptime: process.uptime(), - }; - } - - /** - * Clean up old data and stop monitoring - */ - destroy(): void { - if (this.monitoringTimer) { - clearInterval(this.monitoringTimer); - } - - if (this.healthCheckTimer) { - clearInterval(this.healthCheckTimer); - } - - if (this.performanceObserver) { - this.performanceObserver.disconnect(); - } - - this.removeAllListeners(); - - logger.info('Performance Monitor destroyed'); - } -} diff --git a/backend/src/realtime/broadcasting/EntityUpdateBroadcaster.ts b/backend/src/realtime/broadcasting/EntityUpdateBroadcaster.ts deleted file mode 100644 index ab43683..0000000 --- a/backend/src/realtime/broadcasting/EntityUpdateBroadcaster.ts +++ /dev/null @@ -1,437 +0,0 @@ -/** - * Entity Update Broadcaster - Phase 2 Real-Time Entity Broadcasting - * Broadcasts real-time updates for entities affected by voice commands - * - * Success Criteria: - * - Real-time entity change notifications - * - Targeted broadcasting to affected users - * - Entity change aggregation and batching - * - Update conflict resolution - */ - -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { ISocketManager } from '../../websocket/types'; - -export interface EntityUpdate { - updateId: string; - entityType: 'channel' | 'task' | 'user' | 'project' | 'file'; - entityId: string; - organizationId: string; - changeType: 'created' | 'updated' | 'deleted' | 'status_changed' | 'shared'; - changes: Record; - previousValues?: Record | undefined; - updatedBy: string; - timestamp: string; - affectedUsers: string[]; - contextData?: Record | undefined; -} - -export interface EntityUpdateBatch { - batchId: string; - organizationId: string; - updates: EntityUpdate[]; - totalUpdates: number; - batchedAt: string; - targetUsers: string[]; -} - -export interface BroadcastResult { - updateId: string; - targetUsers: number; - successfulBroadcasts: number; - failedBroadcasts: number; - processingTime: number; -} - -export class EntityUpdateBroadcaster extends EventEmitter { - private socketManager: ISocketManager; - private pendingUpdates: Map = new Map(); - private updateBatches: Map = new Map(); - private broadcastMetrics: number[] = []; - private readonly batchInterval = 100; // 100ms batching window - private readonly maxBatchSize = 50; - private batchTimer?: NodeJS.Timeout; - - constructor(socketManager: ISocketManager) { - super(); - - this.socketManager = socketManager; - - // Start batch processing - this.startBatchProcessing(); - - logger.info('Entity Update Broadcaster initialized', { - batchInterval: `${this.batchInterval}ms`, - maxBatchSize: this.maxBatchSize - }); - } - - /** - * Queue entity update for broadcasting - */ - async queueEntityUpdate( - entityType: EntityUpdate['entityType'], - entityId: string, - organizationId: string, - changeType: EntityUpdate['changeType'], - changes: Record, - updatedBy: string, - affectedUsers: string[], - previousValues?: Record, - contextData?: Record - ): Promise { - const updateId = `update_${entityType}_${entityId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - - const entityUpdate: EntityUpdate = { - updateId, - entityType, - entityId, - organizationId, - changeType, - changes, - previousValues, - updatedBy, - timestamp: new Date().toISOString(), - affectedUsers, - contextData - }; - - // Group by organization for batching - const orgUpdates = this.pendingUpdates.get(organizationId) || []; - orgUpdates.push(entityUpdate); - this.pendingUpdates.set(organizationId, orgUpdates); - - logger.debug('Entity update queued', { - updateId, - entityType, - entityId, - changeType, - affectedUsers: affectedUsers.length, - organizationId - }); - - // Trigger immediate batch processing if batch is full - if (orgUpdates.length >= this.maxBatchSize) { - await this.processBatch(organizationId); - } - - return updateId; - } - - /** - * Broadcast immediate entity update (bypasses batching) - */ - async broadcastImmediateUpdate( - entityType: EntityUpdate['entityType'], - entityId: string, - organizationId: string, - changeType: EntityUpdate['changeType'], - changes: Record, - updatedBy: string, - affectedUsers: string[], - previousValues?: Record, - contextData?: Record - ): Promise { - const startTime = performance.now(); - - const updateId = await this.queueEntityUpdate( - entityType, - entityId, - organizationId, - changeType, - changes, - updatedBy, - affectedUsers, - previousValues, - contextData - ); - - // Process batch immediately - await this.processBatch(organizationId); - - const processingTime = performance.now() - startTime; - - return { - updateId, - targetUsers: affectedUsers.length, - successfulBroadcasts: affectedUsers.filter(userId => - this.socketManager.isUserConnected(userId) - ).length, - failedBroadcasts: affectedUsers.filter(userId => - !this.socketManager.isUserConnected(userId) - ).length, - processingTime - }; - } - - /** - * Broadcast file-specific updates with download URLs - */ - async broadcastFileUpdate( - fileId: string, - organizationId: string, - changeType: 'uploaded' | 'shared' | 'deleted' | 'download_ready', - fileData: { - fileName: string; - fileSize: number; - contentType: string; - downloadUrl?: string; - sharedWith?: string[]; - linkedEntities?: Array<{ type: string; id: string }>; - }, - updatedBy: string, - affectedUsers: string[] - ): Promise { - const contextData = { - fileName: fileData.fileName, - fileSize: fileData.fileSize, - contentType: fileData.contentType, - downloadUrl: fileData.downloadUrl, - linkedEntities: fileData.linkedEntities || [] - }; - - // Add shared users to affected users if it's a sharing update - const allAffectedUsers = changeType === 'shared' && fileData.sharedWith - ? [...new Set([...affectedUsers, ...fileData.sharedWith])] - : affectedUsers; - - return await this.broadcastImmediateUpdate( - 'file', - fileId, - organizationId, - changeType as EntityUpdate['changeType'], - { - fileName: fileData.fileName, - fileSize: fileData.fileSize, - contentType: fileData.contentType, - downloadUrl: fileData.downloadUrl, - sharedWith: fileData.sharedWith - }, - updatedBy, - allAffectedUsers, - undefined, - contextData - ); - } - - /** - * Start batch processing timer - */ - private startBatchProcessing(): void { - this.batchTimer = setInterval(async () => { - await this.processAllBatches(); - }, this.batchInterval); - } - - /** - * Process all pending batches - */ - private async processAllBatches(): Promise { - const organizationIds = Array.from(this.pendingUpdates.keys()); - - for (const organizationId of organizationIds) { - await this.processBatch(organizationId); - } - } - - /** - * Process updates batch for an organization - */ - private async processBatch(organizationId: string): Promise { - const updates = this.pendingUpdates.get(organizationId); - if (!updates || updates.length === 0) { - return; - } - - const startTime = performance.now(); - - // Create batch - const batchId = `batch_${organizationId}_${Date.now()}`; - const targetUsers = [...new Set(updates.flatMap(update => update.affectedUsers))]; - - const batch: EntityUpdateBatch = { - batchId, - organizationId, - updates, - totalUpdates: updates.length, - batchedAt: new Date().toISOString(), - targetUsers - }; - - this.updateBatches.set(batchId, batch); - - // Clear pending updates for this organization - this.pendingUpdates.delete(organizationId); - - try { - // Broadcast batch to all target users - await this.broadcastBatch(batch); - - const processingTime = performance.now() - startTime; - this.recordBroadcastMetrics(processingTime); - - logger.debug('Entity update batch processed', { - batchId, - organizationId, - totalUpdates: updates.length, - targetUsers: targetUsers.length, - processingTime: `${processingTime.toFixed(2)}ms` - }); - - this.emit('batch_processed', { - batchId, - organizationId, - totalUpdates: updates.length, - targetUsers: targetUsers.length, - processingTime - }); - - } catch (error: any) { - logger.error('Failed to process entity update batch', { - batchId, - organizationId, - error: error.message - }); - - this.emit('batch_error', { - batchId, - organizationId, - error: error.message - }); - } - } - - /** - * Broadcast batch to target users - */ - private async broadcastBatch(batch: EntityUpdateBatch): Promise { - const broadcastPromises = batch.targetUsers.map(async (userId) => { - if (this.socketManager.isUserConnected(userId)) { - try { - await this.socketManager.emitToUser(userId, 'entity_updates', { - batchId: batch.batchId, - organizationId: batch.organizationId, - updates: batch.updates.filter(update => - update.affectedUsers.includes(userId) - ), - totalUpdates: batch.totalUpdates, - batchedAt: batch.batchedAt - }); - } catch (error: any) { - logger.warn('Failed to broadcast to user', { - userId, - batchId: batch.batchId, - error: error.message - }); - } - } - }); - - await Promise.allSettled(broadcastPromises); - } - - /** - * Get update batch by ID - */ - getBatch(batchId: string): EntityUpdateBatch | null { - return this.updateBatches.get(batchId) || null; - } - - /** - * Get pending updates for organization - */ - getPendingUpdates(organizationId: string): EntityUpdate[] { - return this.pendingUpdates.get(organizationId) || []; - } - - /** - * Get recent batches for organization - */ - getRecentBatches(organizationId: string, limit: number = 10): EntityUpdateBatch[] { - return Array.from(this.updateBatches.values()) - .filter(batch => batch.organizationId === organizationId) - .sort((a, b) => new Date(b.batchedAt).getTime() - new Date(a.batchedAt).getTime()) - .slice(0, limit); - } - - /** - * Record broadcast performance metrics - */ - private recordBroadcastMetrics(time: number): void { - this.broadcastMetrics.push(time); - - // Keep only last 1000 measurements - if (this.broadcastMetrics.length > 1000) { - this.broadcastMetrics.shift(); - } - } - - /** - * Get performance statistics - */ - getPerformanceStats(): { - averageBroadcastTime: number; - p95BroadcastTime: number; - totalBroadcasts: number; - pendingUpdates: number; - recentBatches: number; - } { - if (this.broadcastMetrics.length === 0) { - return { - averageBroadcastTime: 0, - p95BroadcastTime: 0, - totalBroadcasts: 0, - pendingUpdates: Array.from(this.pendingUpdates.values()).reduce((sum, updates) => sum + updates.length, 0), - recentBatches: this.updateBatches.size - }; - } - - const sorted = [...this.broadcastMetrics].sort((a, b) => a - b); - const average = this.broadcastMetrics.reduce((sum, time) => sum + time, 0) / this.broadcastMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - - return { - averageBroadcastTime: Math.round(average * 100) / 100, - p95BroadcastTime: Math.round(p95 * 100) / 100, - totalBroadcasts: this.broadcastMetrics.length, - pendingUpdates: Array.from(this.pendingUpdates.values()).reduce((sum, updates) => sum + updates.length, 0), - recentBatches: this.updateBatches.size - }; - } - - /** - * Cleanup old batches and metrics - */ - cleanup(): void { - const cutoffTime = Date.now() - (3600000 * 2); // 2 hours ago - let cleanedCount = 0; - - for (const [batchId, batch] of this.updateBatches.entries()) { - const batchTime = new Date(batch.batchedAt).getTime(); - if (batchTime < cutoffTime) { - this.updateBatches.delete(batchId); - cleanedCount++; - } - } - - if (cleanedCount > 0) { - logger.debug('Cleaned up old entity update batches', { cleanedCount }); - } - } - - /** - * Stop batch processing and cleanup - */ - destroy(): void { - if (this.batchTimer) { - clearInterval(this.batchTimer); - } - - this.cleanup(); - this.removeAllListeners(); - - logger.info('Entity Update Broadcaster destroyed'); - } -} \ No newline at end of file diff --git a/backend/src/realtime/broadcasting/ExecutionEventManager.ts b/backend/src/realtime/broadcasting/ExecutionEventManager.ts deleted file mode 100644 index 2cbee1a..0000000 --- a/backend/src/realtime/broadcasting/ExecutionEventManager.ts +++ /dev/null @@ -1,691 +0,0 @@ -/** - * Execution Event Manager - Phase 2 Real-Time Broadcasting - * Manages real-time broadcasting of command execution events - * - * Success Criteria: - * - Real-time execution broadcasting <100ms - * - Targeted broadcasting to affected users only - * - Event deduplication for multiple subscriptions - * - Offline user message queuing - */ - -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { ISocketManager } from '../../websocket/types'; - -export interface ExecutionEvent { - id: string; - type: ExecutionEventType; - commandId: string; - userId: string; - organizationId: string; - data: any; - timestamp: string; - priority: 'low' | 'medium' | 'high' | 'critical'; - targetUsers: string[]; - targetChannels: string[]; - affectedEntities: AffectedEntity[]; -} - -export enum ExecutionEventType { - COMMAND_START = 'command_start', - STEP_EXECUTION = 'step_execution', - COMMAND_COMPLETE = 'command_complete', - COMMAND_FAILED = 'command_failed', - ENTITY_CREATED = 'entity_created', - ENTITY_UPDATED = 'entity_updated', - USER_ASSIGNED = 'user_assigned', - FILE_UPLOADED = 'file_uploaded', - NOTIFICATION_SENT = 'notification_sent', -} - -export interface AffectedEntity { - type: 'user' | 'channel' | 'task' | 'project' | 'file'; - id: string; - name: string; - action: 'created' | 'updated' | 'deleted' | 'assigned' | 'shared'; -} - -export interface BroadcastResult { - eventId: string; - targetCount: number; - deliveredCount: number; - queuedCount: number; - failedCount: number; - averageLatency: number; - processingTime: number; -} - -export interface OfflineMessage { - userId: string; - event: ExecutionEvent; - queuedAt: string; - priority: number; -} - -export class ExecutionEventManager extends EventEmitter { - private socketManager: ISocketManager; - private offlineMessageQueue: Map = new Map(); - private eventDeduplication: Map> = new Map(); - private broadcastMetrics: number[] = []; - private readonly maxQueueSize = 100; - private readonly deduplicationTTL = 30000; // 30 seconds - - constructor(socketManager: ISocketManager) { - super(); - - this.socketManager = socketManager; - - // Cleanup intervals - setInterval(() => this.cleanupDeduplication(), 60000); // Every minute - setInterval(() => this.cleanupOfflineQueue(), 300000); // Every 5 minutes - - // Listen for user connection events - this.socketManager.on?.('user_connected', this.handleUserConnected.bind(this)); - this.socketManager.on?.('user_disconnected', this.handleUserDisconnected.bind(this)); - - logger.info('Execution Event Manager initialized'); - } - - /** - * Broadcast command execution start event - * Target: <100ms broadcasting latency - */ - async broadcastCommandStart( - commandId: string, - userId: string, - organizationId: string, - affectedUsers: string[], - commandData: any - ): Promise { - const event: ExecutionEvent = { - id: `start_${commandId}_${Date.now()}`, - type: ExecutionEventType.COMMAND_START, - commandId, - userId, - organizationId, - data: { - transcript: commandData.transcript, - intent: commandData.intent, - actionCount: commandData.actionCount, - estimatedDuration: commandData.estimatedDuration, - }, - timestamp: new Date().toISOString(), - priority: 'medium', - targetUsers: affectedUsers, - targetChannels: [], - affectedEntities: [], - }; - - return await this.broadcastEvent(event); - } - - /** - * Broadcast step execution progress - */ - async broadcastStepExecution( - commandId: string, - stepId: string, - userId: string, - organizationId: string, - stepData: any, - affectedUsers: string[], - affectedChannels: string[] = [] - ): Promise { - const event: ExecutionEvent = { - id: `step_${commandId}_${stepId}_${Date.now()}`, - type: ExecutionEventType.STEP_EXECUTION, - commandId, - userId, - organizationId, - data: { - stepId, - actionType: stepData.actionType, - status: stepData.status, - progress: stepData.progress, - result: stepData.result, - }, - timestamp: new Date().toISOString(), - priority: 'medium', - targetUsers: affectedUsers, - targetChannels: affectedChannels, - affectedEntities: stepData.affectedEntities || [], - }; - - return await this.broadcastEvent(event); - } - - /** - * Broadcast command completion - */ - async broadcastCommandComplete( - commandId: string, - userId: string, - organizationId: string, - result: any, - affectedUsers: string[], - affectedChannels: string[] = [] - ): Promise { - const event: ExecutionEvent = { - id: `complete_${commandId}_${Date.now()}`, - type: ExecutionEventType.COMMAND_COMPLETE, - commandId, - userId, - organizationId, - data: { - success: result.success, - executedActions: result.executedActions?.length || 0, - failedActions: result.failedActions?.length || 0, - totalExecutionTime: result.totalExecutionTime, - summary: result.summary, - }, - timestamp: new Date().toISOString(), - priority: result.success ? 'medium' : 'high', - targetUsers: affectedUsers, - targetChannels: affectedChannels, - affectedEntities: result.affectedEntities || [], - }; - - return await this.broadcastEvent(event); - } - - /** - * Broadcast command error/failure - */ - async broadcastCommandError( - commandId: string, - userId: string, - organizationId: string, - error: string, - affectedUsers: string[], - affectedChannels: string[] = [] - ): Promise { - const event: ExecutionEvent = { - id: `error_${commandId}_${Date.now()}`, - type: ExecutionEventType.COMMAND_FAILED, - commandId, - userId, - organizationId, - data: { - error, - timestamp: new Date().toISOString(), - }, - timestamp: new Date().toISOString(), - priority: 'high', - targetUsers: affectedUsers, - targetChannels: affectedChannels, - affectedEntities: [], - }; - - return await this.broadcastEvent(event); - } - - /** - * Broadcast entity creation (task, channel, etc.) - */ - async broadcastEntityCreated( - entityType: string, - entityId: string, - entityName: string, - createdBy: string, - organizationId: string, - affectedUsers: string[], - affectedChannels: string[] = [] - ): Promise { - const event: ExecutionEvent = { - id: `created_${entityType}_${entityId}_${Date.now()}`, - type: ExecutionEventType.ENTITY_CREATED, - commandId: `entity_creation_${Date.now()}`, - userId: createdBy, - organizationId, - data: { - entityType, - entityId, - entityName, - createdBy, - }, - timestamp: new Date().toISOString(), - priority: 'medium', - targetUsers: affectedUsers, - targetChannels: affectedChannels, - affectedEntities: [ - { - type: entityType as any, - id: entityId, - name: entityName, - action: 'created', - }, - ], - }; - - return await this.broadcastEvent(event); - } - - /** - * Broadcast user assignment notifications - */ - async broadcastUserAssigned( - assignmentType: string, - entityId: string, - entityName: string, - assignedUsers: string[], - assignedBy: string, - organizationId: string - ): Promise { - const event: ExecutionEvent = { - id: `assigned_${assignmentType}_${entityId}_${Date.now()}`, - type: ExecutionEventType.USER_ASSIGNED, - commandId: `assignment_${Date.now()}`, - userId: assignedBy, - organizationId, - data: { - assignmentType, - entityId, - entityName, - assignedUsers, - assignedBy, - }, - timestamp: new Date().toISOString(), - priority: 'high', - targetUsers: [...assignedUsers, assignedBy], - targetChannels: [], - affectedEntities: [ - { - type: assignmentType as any, - id: entityId, - name: entityName, - action: 'assigned', - }, - ], - }; - - return await this.broadcastEvent(event); - } - - /** - * Core event broadcasting method - * Target: <100ms average latency - */ - private async broadcastEvent(event: ExecutionEvent): Promise { - const startTime = performance.now(); - - try { - // Event deduplication - if (this.isDuplicateEvent(event)) { - logger.debug('Duplicate event detected, skipping broadcast', { - eventId: event.id, - type: event.type, - }); - - return { - eventId: event.id, - targetCount: 0, - deliveredCount: 0, - queuedCount: 0, - failedCount: 0, - averageLatency: 0, - processingTime: performance.now() - startTime, - }; - } - - // Add to deduplication cache - this.addToDeduplication(event); - - // Determine all target users (including channel members) - const allTargetUsers = await this.resolveAllTargetUsers(event); - - let deliveredCount = 0; - let queuedCount = 0; - let failedCount = 0; - const latencies: number[] = []; - - // Broadcast to online users - for (const userId of allTargetUsers) { - const userStartTime = performance.now(); - - if (this.socketManager.isUserConnected(userId)) { - try { - await this.socketManager.emitToUser(userId, 'execution_event', event); - - const latency = performance.now() - userStartTime; - latencies.push(latency); - deliveredCount++; - } catch (error: any) { - logger.warn('Failed to deliver event to online user', { - userId, - eventId: event.id, - error: error.message, - }); - failedCount++; - } - } else { - // Queue for offline users - this.queueOfflineMessage(userId, event); - queuedCount++; - } - } - - const processingTime = performance.now() - startTime; - const averageLatency = - latencies.length > 0 ? latencies.reduce((sum, lat) => sum + lat, 0) / latencies.length : 0; - - this.recordBroadcastMetrics(processingTime); - - // Log performance warning if target exceeded - if (processingTime > 100) { - logger.warn('Event broadcasting exceeded 100ms target', { - eventId: event.id, - processingTime: `${processingTime.toFixed(2)}ms`, - targetCount: allTargetUsers.length, - averageLatency: `${averageLatency.toFixed(2)}ms`, - }); - } - - const result: BroadcastResult = { - eventId: event.id, - targetCount: allTargetUsers.length, - deliveredCount, - queuedCount, - failedCount, - averageLatency, - processingTime, - }; - - logger.debug('Event broadcast completed', { - type: event.type, - ...result, - }); - - // Emit broadcast metrics for monitoring - this.emit('broadcast_complete', result); - - return result; - } catch (error: any) { - const processingTime = performance.now() - startTime; - - logger.error('Event broadcasting failed', { - eventId: event.id, - type: event.type, - error: error.message, - processingTime: `${processingTime.toFixed(2)}ms`, - }); - - return { - eventId: event.id, - targetCount: 0, - deliveredCount: 0, - queuedCount: 0, - failedCount: 1, - averageLatency: 0, - processingTime, - }; - } - } - - /** - * Handle user connection - deliver queued messages - */ - private async handleUserConnected(userId: string): Promise { - const queuedMessages = this.offlineMessageQueue.get(userId); - - if (!queuedMessages || queuedMessages.length === 0) { - return; - } - - logger.info('Delivering queued messages to connected user', { - userId, - messageCount: queuedMessages.length, - }); - - try { - // Sort by priority and timestamp - queuedMessages.sort((a, b) => { - if (a.priority !== b.priority) { - return b.priority - a.priority; // Higher priority first - } - return new Date(a.queuedAt).getTime() - new Date(b.queuedAt).getTime(); - }); - - // Deliver messages with a small delay between each - for (let i = 0; i < queuedMessages.length; i++) { - const message = queuedMessages[i]; - - if (!message) { - continue; - } - - try { - await this.socketManager.emitToUser(userId, 'execution_event', message.event); - - // Small delay to prevent overwhelming the client - if (i < queuedMessages.length - 1) { - await new Promise((resolve) => setTimeout(resolve, 50)); - } - } catch (error: any) { - logger.warn('Failed to deliver queued message', { - userId, - eventId: message?.event?.id, - error: error.message, - }); - } - } - - // Clear delivered messages - this.offlineMessageQueue.delete(userId); - - logger.debug('Queued messages delivered successfully', { - userId, - deliveredCount: queuedMessages.length, - }); - } catch (error: any) { - logger.error('Failed to deliver queued messages', { - userId, - error: error.message, - }); - } - } - - /** - * Handle user disconnection - */ - private handleUserDisconnected(userId: string): void { - logger.debug('User disconnected from execution events', { userId }); - } - - /** - * Resolve all target users including channel members - */ - private async resolveAllTargetUsers(event: ExecutionEvent): Promise { - const targetUsers = new Set(event.targetUsers); - - // Add channel members for channel-targeted events - for (const channelId of event.targetChannels) { - try { - const channelMembers = await this.getChannelMembers(channelId); - channelMembers.forEach((memberId) => targetUsers.add(memberId)); - } catch (error: any) { - logger.warn('Failed to resolve channel members', { - channelId, - error: error.message, - }); - } - } - - return Array.from(targetUsers); - } - - /** - * Get channel members (simplified implementation) - */ - private async getChannelMembers(channelId: string): Promise { - // In a real implementation, this would query the database - // For now, return empty array - return []; - } - - /** - * Check for duplicate events - */ - private isDuplicateEvent(event: ExecutionEvent): boolean { - const deduplicationKey = `${event.type}_${event.commandId}`; - const existingEvents = this.eventDeduplication.get(deduplicationKey); - - return existingEvents?.has(event.id) || false; - } - - /** - * Add event to deduplication cache - */ - private addToDeduplication(event: ExecutionEvent): void { - const deduplicationKey = `${event.type}_${event.commandId}`; - - if (!this.eventDeduplication.has(deduplicationKey)) { - this.eventDeduplication.set(deduplicationKey, new Set()); - } - - this.eventDeduplication.get(deduplicationKey)?.add(event.id); - - // Set cleanup timeout - setTimeout(() => { - this.eventDeduplication.get(deduplicationKey)?.delete(event.id); - }, this.deduplicationTTL); - } - - /** - * Queue message for offline user - */ - private queueOfflineMessage(userId: string, event: ExecutionEvent): void { - if (!this.offlineMessageQueue.has(userId)) { - this.offlineMessageQueue.set(userId, []); - } - - const queue = this.offlineMessageQueue.get(userId)!; - - // Priority mapping - const priorityMap = { low: 1, medium: 2, high: 3, critical: 4 }; - - const message: OfflineMessage = { - userId, - event, - queuedAt: new Date().toISOString(), - priority: priorityMap[event.priority], - }; - - queue.push(message); - - // Maintain queue size limit - if (queue.length > this.maxQueueSize) { - // Remove oldest low-priority messages first - queue.sort((a, b) => { - if (a.priority !== b.priority) { - return a.priority - b.priority; // Lower priority first - } - return new Date(a.queuedAt).getTime() - new Date(b.queuedAt).getTime(); - }); - - queue.splice(0, queue.length - this.maxQueueSize); - } - } - - /** - * Cleanup deduplication cache - */ - private cleanupDeduplication(): void { - let cleanedKeys = 0; - - for (const [key, eventIds] of this.eventDeduplication.entries()) { - if (eventIds.size === 0) { - this.eventDeduplication.delete(key); - cleanedKeys++; - } - } - - if (cleanedKeys > 0) { - logger.debug('Cleaned up deduplication cache', { cleanedKeys }); - } - } - - /** - * Cleanup old offline messages - */ - private cleanupOfflineQueue(): void { - let cleanedMessages = 0; - const cutoffTime = Date.now() - 24 * 60 * 60 * 1000; // 24 hours - - for (const [userId, queue] of this.offlineMessageQueue.entries()) { - const originalLength = queue.length; - - // Remove messages older than 24 hours - const filteredQueue = queue.filter((msg) => new Date(msg.queuedAt).getTime() > cutoffTime); - - if (filteredQueue.length === 0) { - this.offlineMessageQueue.delete(userId); - } else { - this.offlineMessageQueue.set(userId, filteredQueue); - } - - cleanedMessages += originalLength - filteredQueue.length; - } - - if (cleanedMessages > 0) { - logger.debug('Cleaned up offline message queue', { cleanedMessages }); - } - } - - /** - * Record broadcast performance metrics - */ - private recordBroadcastMetrics(time: number): void { - this.broadcastMetrics.push(time); - - // Keep only last 1000 measurements - if (this.broadcastMetrics.length > 1000) { - this.broadcastMetrics.shift(); - } - } - - /** - * Get performance statistics - */ - getPerformanceStats(): { - averageBroadcastTime: number; - p95BroadcastTime: number; - p99BroadcastTime: number; - totalBroadcasts: number; - offlineQueueSize: number; - deduplicationCacheSize: number; - } { - if (this.broadcastMetrics.length === 0) { - return { - averageBroadcastTime: 0, - p95BroadcastTime: 0, - p99BroadcastTime: 0, - totalBroadcasts: 0, - offlineQueueSize: Array.from(this.offlineMessageQueue.values()).reduce( - (sum, queue) => sum + queue.length, - 0 - ), - deduplicationCacheSize: this.eventDeduplication.size, - }; - } - - const sorted = [...this.broadcastMetrics].sort((a, b) => a - b); - const average = - this.broadcastMetrics.reduce((sum, time) => sum + time, 0) / this.broadcastMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - - return { - averageBroadcastTime: Math.round(average * 100) / 100, - p95BroadcastTime: Math.round(p95 * 100) / 100, - p99BroadcastTime: Math.round(p99 * 100) / 100, - totalBroadcasts: this.broadcastMetrics.length, - offlineQueueSize: Array.from(this.offlineMessageQueue.values()).reduce( - (sum, queue) => sum + queue.length, - 0 - ), - deduplicationCacheSize: this.eventDeduplication.size, - }; - } -} diff --git a/backend/src/realtime/broadcasting/NotificationIntegration.ts b/backend/src/realtime/broadcasting/NotificationIntegration.ts deleted file mode 100644 index 78b8f2c..0000000 --- a/backend/src/realtime/broadcasting/NotificationIntegration.ts +++ /dev/null @@ -1,682 +0,0 @@ -/** - * Notification Integration - Phase 2 Real-Time Notification System - * Integrates real-time broadcasting with notification systems - * - * Success Criteria: - * - Push notification support for offline users - * - Email notifications for important events - * - In-app notification management - * - Notification preference handling - */ - -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { SocketManager } from '../../websocket/SocketManager'; - -export interface NotificationPreferences { - userId: string; - organizationId: string; - pushNotifications: boolean; - emailNotifications: boolean; - inAppNotifications: boolean; - commandCompletionNotifications: boolean; - fileUploadNotifications: boolean; - entityUpdateNotifications: boolean; - quietHours: { - enabled: boolean; - startTime: string; // HH:mm format - endTime: string; // HH:mm format - timezone: string; - }; - updatedAt: string; -} - -export interface NotificationEvent { - notificationId: string; - userId: string; - organizationId: string; - type: 'command_complete' | 'file_uploaded' | 'entity_updated' | 'system_alert' | 'error'; - title: string; - message: string; - data: Record; - priority: 'low' | 'medium' | 'high' | 'critical'; - createdAt: string; - readAt?: string; - deliveredAt?: string; -} - -export interface DeliveryChannel { - type: 'push' | 'email' | 'in_app' | 'websocket'; - enabled: boolean; - metadata?: Record; -} - -export interface NotificationDelivery { - notificationId: string; - userId: string; - channels: DeliveryChannel[]; - deliveredChannels: string[]; - failedChannels: string[]; - deliveryAttempts: number; - lastAttemptAt: string; - status: 'pending' | 'delivered' | 'failed' | 'expired'; -} - -export class NotificationIntegration extends EventEmitter { - private socketManager: SocketManager; - private notificationQueue: Map = new Map(); - private userPreferences: Map = new Map(); - private pendingDeliveries: Map = new Map(); - private deliveryMetrics: number[] = []; - private readonly maxRetries = 3; - private readonly retryDelay = 60000; // 1 minute - private processingTimer?: NodeJS.Timeout; - - constructor(socketManager: SocketManager) { - super(); - - this.socketManager = socketManager; - - // Start notification processing - this.startNotificationProcessing(); - - logger.info('Notification Integration initialized'); - } - - /** - * Queue notification for delivery - */ - async queueNotification( - userId: string, - organizationId: string, - type: NotificationEvent['type'], - title: string, - message: string, - data: Record = {}, - priority: NotificationEvent['priority'] = 'medium' - ): Promise { - const notificationId = `notif_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; - - const notification: NotificationEvent = { - notificationId, - userId, - organizationId, - type, - title, - message, - data, - priority, - createdAt: new Date().toISOString() - }; - - // Add to user's notification queue - const userQueue = this.notificationQueue.get(userId) || []; - userQueue.push(notification); - this.notificationQueue.set(userId, userQueue); - - logger.debug('Notification queued', { - notificationId, - userId, - type, - priority, - title - }); - - // Process immediately for high priority notifications - if (priority === 'high' || priority === 'critical') { - await this.processUserNotifications(userId); - } - - return notificationId; - } - - /** - * Set user notification preferences - */ - async setUserPreferences(preferences: NotificationPreferences): Promise { - this.userPreferences.set(preferences.userId, { - ...preferences, - updatedAt: new Date().toISOString() - }); - - logger.debug('User notification preferences updated', { - userId: preferences.userId, - organizationId: preferences.organizationId - }); - - this.emit('preferences_updated', preferences); - } - - /** - * Get user notification preferences - */ - getUserPreferences(userId: string): NotificationPreferences | null { - return this.userPreferences.get(userId) || this.getDefaultPreferences(userId); - } - - /** - * Get default notification preferences - */ - private getDefaultPreferences(userId: string): NotificationPreferences { - return { - userId, - organizationId: '', - pushNotifications: true, - emailNotifications: true, - inAppNotifications: true, - commandCompletionNotifications: true, - fileUploadNotifications: true, - entityUpdateNotifications: false, - quietHours: { - enabled: false, - startTime: '22:00', - endTime: '08:00', - timezone: 'UTC' - }, - updatedAt: new Date().toISOString() - }; - } - - /** - * Process notification for command completion - */ - async notifyCommandComplete( - commandId: string, - userId: string, - organizationId: string, - result: any, - affectedUsers: string[] - ): Promise { - const preferences = this.getUserPreferences(userId); - if (!preferences?.commandCompletionNotifications) { - return; - } - - // Notify command executor - await this.queueNotification( - userId, - organizationId, - 'command_complete', - 'Voice Command Completed', - `Your voice command has been executed successfully.`, - { - commandId, - result, - completedAt: new Date().toISOString() - }, - 'medium' - ); - - // Notify affected users if different from executor - for (const affectedUserId of affectedUsers) { - if (affectedUserId !== userId) { - const affectedPreferences = this.getUserPreferences(affectedUserId); - if (affectedPreferences?.entityUpdateNotifications) { - await this.queueNotification( - affectedUserId, - organizationId, - 'entity_updated', - 'System Update', - `A voice command has affected your workspace.`, - { - commandId, - executedBy: userId, - affectedEntities: result.affectedEntities || [] - }, - 'low' - ); - } - } - } - } - - /** - * Process notification for file upload - */ - async notifyFileUploaded( - fileId: string, - fileName: string, - userId: string, - organizationId: string, - fileSize: number, - downloadUrl?: string, - sharedWith: string[] = [] - ): Promise { - const preferences = this.getUserPreferences(userId); - if (!preferences?.fileUploadNotifications) { - return; - } - - // Notify uploader - await this.queueNotification( - userId, - organizationId, - 'file_uploaded', - 'File Upload Complete', - `"${fileName}" has been uploaded successfully.`, - { - fileId, - fileName, - fileSize, - downloadUrl, - uploadedAt: new Date().toISOString() - }, - 'medium' - ); - - // Notify shared users - for (const sharedUserId of sharedWith) { - if (sharedUserId !== userId) { - await this.queueNotification( - sharedUserId, - organizationId, - 'file_uploaded', - 'New File Shared', - `"${fileName}" has been shared with you.`, - { - fileId, - fileName, - fileSize, - downloadUrl, - sharedBy: userId, - sharedAt: new Date().toISOString() - }, - 'medium' - ); - } - } - } - - /** - * Process notification for system errors - */ - async notifySystemError( - userId: string, - organizationId: string, - errorType: string, - errorMessage: string, - contextData: Record = {} - ): Promise { - await this.queueNotification( - userId, - organizationId, - 'error', - 'System Error', - errorMessage, - { - errorType, - contextData, - occurredAt: new Date().toISOString() - }, - 'high' - ); - } - - /** - * Start notification processing timer - */ - private startNotificationProcessing(): void { - this.processingTimer = setInterval(async () => { - await this.processAllNotifications(); - }, 10000); // Process every 10 seconds - } - - /** - * Process all pending notifications - */ - private async processAllNotifications(): Promise { - const userIds = Array.from(this.notificationQueue.keys()); - - for (const userId of userIds) { - await this.processUserNotifications(userId); - } - } - - /** - * Process notifications for a specific user - */ - private async processUserNotifications(userId: string): Promise { - const notifications = this.notificationQueue.get(userId); - if (!notifications || notifications.length === 0) { - return; - } - - const preferences = this.getUserPreferences(userId); - if (!preferences) { - return; - } - - const startTime = performance.now(); - - try { - // Check quiet hours - if (this.isInQuietHours(preferences)) { - logger.debug('Skipping notifications due to quiet hours', { userId }); - return; - } - - // Process each notification - for (const notification of notifications) { - await this.deliverNotification(notification, preferences); - } - - // Clear processed notifications - this.notificationQueue.delete(userId); - - const processingTime = performance.now() - startTime; - this.recordDeliveryMetrics(processingTime); - - logger.debug('User notifications processed', { - userId, - count: notifications.length, - processingTime: `${processingTime.toFixed(2)}ms` - }); - - } catch (error: any) { - logger.error('Error processing user notifications', { - userId, - error: error.message - }); - } - } - - /** - * Deliver notification through appropriate channels - */ - private async deliverNotification( - notification: NotificationEvent, - preferences: NotificationPreferences - ): Promise { - const deliveryChannels: DeliveryChannel[] = []; - - // Determine delivery channels based on preferences - if (preferences.inAppNotifications) { - deliveryChannels.push({ type: 'in_app', enabled: true }); - } - - // Check if user is online for WebSocket delivery - if (this.socketManager.isUserConnected(notification.userId)) { - deliveryChannels.push({ type: 'websocket', enabled: true }); - } - - // Add push notifications for offline users or high priority - if (preferences.pushNotifications && - (!this.socketManager.isUserConnected(notification.userId) || - ['high', 'critical'].includes(notification.priority))) { - deliveryChannels.push({ type: 'push', enabled: true }); - } - - // Add email for critical notifications - if (preferences.emailNotifications && notification.priority === 'critical') { - deliveryChannels.push({ type: 'email', enabled: true }); - } - - const delivery: NotificationDelivery = { - notificationId: notification.notificationId, - userId: notification.userId, - channels: deliveryChannels, - deliveredChannels: [], - failedChannels: [], - deliveryAttempts: 0, - lastAttemptAt: new Date().toISOString(), - status: 'pending' - }; - - this.pendingDeliveries.set(notification.notificationId, delivery); - - // Attempt delivery through each channel - await this.attemptDelivery(notification, delivery); - } - - /** - * Attempt delivery through all channels - */ - private async attemptDelivery( - notification: NotificationEvent, - delivery: NotificationDelivery - ): Promise { - delivery.deliveryAttempts++; - delivery.lastAttemptAt = new Date().toISOString(); - - const deliveryPromises = delivery.channels.map(async (channel) => { - if (!channel.enabled) return; - - try { - await this.deliverThroughChannel(notification, channel); - delivery.deliveredChannels.push(channel.type); - - logger.debug('Notification delivered through channel', { - notificationId: notification.notificationId, - channel: channel.type, - userId: notification.userId - }); - - } catch (error: any) { - delivery.failedChannels.push(channel.type); - - logger.warn('Failed to deliver notification through channel', { - notificationId: notification.notificationId, - channel: channel.type, - userId: notification.userId, - error: error.message - }); - } - }); - - await Promise.allSettled(deliveryPromises); - - // Update delivery status - if (delivery.deliveredChannels.length > 0) { - delivery.status = 'delivered'; - notification.deliveredAt = new Date().toISOString(); - - this.emit('notification_delivered', { - notificationId: notification.notificationId, - userId: notification.userId, - channels: delivery.deliveredChannels - }); - - } else if (delivery.deliveryAttempts >= this.maxRetries) { - delivery.status = 'failed'; - - this.emit('notification_failed', { - notificationId: notification.notificationId, - userId: notification.userId, - attempts: delivery.deliveryAttempts - }); - - } else { - // Retry after delay - setTimeout(() => { - this.attemptDelivery(notification, delivery); - }, this.retryDelay); - } - } - - /** - * Deliver notification through specific channel - */ - private async deliverThroughChannel( - notification: NotificationEvent, - channel: DeliveryChannel - ): Promise { - switch (channel.type) { - case 'websocket': - await this.deliverWebSocket(notification); - break; - - case 'in_app': - await this.deliverInApp(notification); - break; - - case 'push': - await this.deliverPush(notification); - break; - - case 'email': - await this.deliverEmail(notification); - break; - - default: - throw new Error(`Unknown delivery channel: ${channel.type}`); - } - } - - /** - * Deliver through WebSocket - */ - private async deliverWebSocket(notification: NotificationEvent): Promise { - await this.socketManager.emitToUser(notification.userId, 'notification', { - notificationId: notification.notificationId, - type: notification.type, - title: notification.title, - message: notification.message, - data: notification.data, - priority: notification.priority, - createdAt: notification.createdAt - }); - } - - /** - * Deliver as in-app notification - */ - private async deliverInApp(notification: NotificationEvent): Promise { - // Store in-app notification (would typically be in database) - // For now, we'll emit an event that the API can listen to - this.emit('in_app_notification', notification); - } - - /** - * Deliver push notification - */ - private async deliverPush(notification: NotificationEvent): Promise { - // Integration with push notification service (FCM, APNs, etc.) - // For now, we'll emit an event that can be handled by push service - this.emit('push_notification', notification); - } - - /** - * Deliver email notification - */ - private async deliverEmail(notification: NotificationEvent): Promise { - // Integration with email service (SendGrid, SES, etc.) - // For now, we'll emit an event that can be handled by email service - this.emit('email_notification', notification); - } - - /** - * Check if current time is in quiet hours - */ - private isInQuietHours(preferences: NotificationPreferences): boolean { - if (!preferences.quietHours.enabled) { - return false; - } - - // Simple time check (would need proper timezone handling in production) - const now = new Date(); - const currentTime = `${now.getHours().toString().padStart(2, '0')}:${now.getMinutes().toString().padStart(2, '0')}`; - - return currentTime >= preferences.quietHours.startTime && - currentTime <= preferences.quietHours.endTime; - } - - /** - * Get user notifications (recent) - */ - getUserNotifications(userId: string, limit: number = 20): NotificationEvent[] { - // In production, this would query database - // For now, return empty array as notifications are processed immediately - return []; - } - - /** - * Mark notification as read - */ - async markNotificationRead(notificationId: string, userId: string): Promise { - // In production, update database - this.emit('notification_read', { - notificationId, - userId, - readAt: new Date().toISOString() - }); - } - - /** - * Record delivery performance metrics - */ - private recordDeliveryMetrics(time: number): void { - this.deliveryMetrics.push(time); - - // Keep only last 1000 measurements - if (this.deliveryMetrics.length > 1000) { - this.deliveryMetrics.shift(); - } - } - - /** - * Get notification statistics - */ - getNotificationStats(): { - queuedNotifications: number; - pendingDeliveries: number; - averageDeliveryTime: number; - deliverySuccessRate: number; - } { - const queuedNotifications = Array.from(this.notificationQueue.values()) - .reduce((sum, notifications) => sum + notifications.length, 0); - - const pendingDeliveries = this.pendingDeliveries.size; - - const averageDeliveryTime = this.deliveryMetrics.length > 0 - ? this.deliveryMetrics.reduce((sum, time) => sum + time, 0) / this.deliveryMetrics.length - : 0; - - const totalDeliveries = Array.from(this.pendingDeliveries.values()); - const successfulDeliveries = totalDeliveries.filter(d => d.status === 'delivered').length; - const deliverySuccessRate = totalDeliveries.length > 0 - ? successfulDeliveries / totalDeliveries.length - : 1; - - return { - queuedNotifications, - pendingDeliveries, - averageDeliveryTime: Math.round(averageDeliveryTime * 100) / 100, - deliverySuccessRate: Math.round(deliverySuccessRate * 10000) / 10000 - }; - } - - /** - * Cleanup old deliveries and metrics - */ - cleanup(): void { - const cutoffTime = Date.now() - (3600000 * 24); // 24 hours ago - let cleanedCount = 0; - - for (const [notificationId, delivery] of this.pendingDeliveries.entries()) { - const deliveryTime = new Date(delivery.lastAttemptAt).getTime(); - if (deliveryTime < cutoffTime && ['delivered', 'failed'].includes(delivery.status)) { - this.pendingDeliveries.delete(notificationId); - cleanedCount++; - } - } - - if (cleanedCount > 0) { - logger.debug('Cleaned up old notification deliveries', { cleanedCount }); - } - } - - /** - * Destroy and cleanup - */ - destroy(): void { - if (this.processingTimer) { - clearInterval(this.processingTimer); - } - - this.cleanup(); - this.removeAllListeners(); - - logger.info('Notification Integration destroyed'); - } -} \ No newline at end of file diff --git a/backend/src/realtime/broadcasting/ProgressBroadcaster.ts b/backend/src/realtime/broadcasting/ProgressBroadcaster.ts deleted file mode 100644 index 442a54f..0000000 --- a/backend/src/realtime/broadcasting/ProgressBroadcaster.ts +++ /dev/null @@ -1,486 +0,0 @@ -/** - * Progress Broadcaster - Phase 2 Real-Time Progress Tracking - * Shows live progress of multi-step command execution - * - * Success Criteria: - * - Step-by-step progress visualization - * - Estimated time remaining calculation - * - Progress persistence for reconnecting clients - * - Progress completion notifications - */ - -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { ISocketManager } from '../../websocket/types'; - -export interface ProgressSession { - sessionId: string; - commandId: string; - userId: string; - organizationId: string; - totalSteps: number; - currentStep: number; - stepProgress: Map; - startTime: number; - estimatedDuration: number; - actualDuration?: number; - status: 'active' | 'completed' | 'failed' | 'cancelled'; - targetUsers: string[]; -} - -export interface StepProgress { - stepId: string; - stepName: string; - status: 'pending' | 'running' | 'completed' | 'failed' | 'skipped'; - progress: number; // 0-100 - startTime?: number; - endTime?: number; - estimatedDuration: number; - actualDuration?: number; - result?: any; - error?: string; -} - -export interface ProgressUpdate { - sessionId: string; - commandId: string; - totalProgress: number; // 0-100 - currentStep: number; - totalSteps: number; - currentStepName: string; - currentStepProgress: number; - estimatedTimeRemaining: number; - elapsedTime: number; - status: ProgressSession['status']; - stepDetails: StepProgress[]; - lastUpdated: string; -} - -export class ProgressBroadcaster extends EventEmitter { - private socketManager: ISocketManager; - private activeSessions: Map = new Map(); - private sessionHistory: Map = new Map(); - private broadcastMetrics: number[] = []; - private readonly historyRetention = 3600000; // 1 hour - - constructor(socketManager: ISocketManager) { - super(); - - this.socketManager = socketManager; - - // Cleanup old sessions periodically - setInterval(() => this.cleanupOldSessions(), 300000); // Every 5 minutes - - logger.info('Progress Broadcaster initialized'); - } - - /** - * Initialize progress tracking for a command - */ - initializeProgressTracking( - commandId: string, - userId: string, - organizationId: string, - steps: Array<{ id: string; name: string; estimatedDuration: number }>, - targetUsers: string[] - ): ProgressSession { - const sessionId = `progress_${commandId}_${Date.now()}`; - - const session: ProgressSession = { - sessionId, - commandId, - userId, - organizationId, - totalSteps: steps.length, - currentStep: 0, - stepProgress: new Map(), - startTime: performance.now(), - estimatedDuration: steps.reduce((sum, step) => sum + step.estimatedDuration, 0), - status: 'active', - targetUsers, - }; - - // Initialize step progress - steps.forEach((step, index) => { - const stepProgress: StepProgress = { - stepId: step.id, - stepName: step.name, - status: index === 0 ? 'pending' : 'pending', - progress: 0, - estimatedDuration: step.estimatedDuration, - }; - - session.stepProgress.set(step.id, stepProgress); - }); - - this.activeSessions.set(sessionId, session); - - logger.info('Progress tracking initialized', { - sessionId, - commandId, - totalSteps: steps.length, - estimatedDuration: `${session.estimatedDuration.toFixed(0)}ms`, - targetUsers: targetUsers.length, - }); - - // Send initial progress update - this.broadcastProgressUpdate(session); - - return session; - } - - /** - * Update step progress - */ - async updateStepProgress( - sessionId: string, - stepId: string, - progress: number, - status?: StepProgress['status'], - result?: any, - error?: string - ): Promise { - const startTime = performance.now(); - - const session = this.activeSessions.get(sessionId); - if (!session) { - logger.warn('Progress update for unknown session', { sessionId, stepId }); - return; - } - - const stepProgress = session.stepProgress.get(stepId); - if (!stepProgress) { - logger.warn('Progress update for unknown step', { sessionId, stepId }); - return; - } - - // Update step progress - const previousProgress = stepProgress.progress; - stepProgress.progress = Math.min(100, Math.max(0, progress)); - - if (status) { - stepProgress.status = status; - - if (status === 'running' && !stepProgress.startTime) { - stepProgress.startTime = performance.now(); - } - - if (['completed', 'failed', 'skipped'].includes(status) && !stepProgress.endTime) { - stepProgress.endTime = performance.now(); - - if (stepProgress.startTime) { - stepProgress.actualDuration = stepProgress.endTime - stepProgress.startTime; - } - } - } - - if (result !== undefined) { - stepProgress.result = result; - } - - if (error) { - stepProgress.error = error; - stepProgress.status = 'failed'; - } - - // Update current step if this step is completed - if (stepProgress.status === 'completed') { - const steps = Array.from(session.stepProgress.values()); - const completedSteps = steps.filter((s) => s.status === 'completed').length; - session.currentStep = Math.max(session.currentStep, completedSteps); - } - - // Check if command is complete - if (this.isSessionComplete(session)) { - session.status = 'completed'; - session.actualDuration = performance.now() - session.startTime; - - // Move to history - this.sessionHistory.set(sessionId, { ...session }); - this.activeSessions.delete(sessionId); - - logger.info('Command execution completed', { - sessionId, - commandId: session.commandId, - actualDuration: `${session.actualDuration.toFixed(2)}ms`, - estimatedDuration: `${session.estimatedDuration.toFixed(2)}ms`, - }); - } - - // Broadcast progress update - await this.broadcastProgressUpdate(session); - - const processingTime = performance.now() - startTime; - this.recordBroadcastMetrics(processingTime); - - logger.debug('Step progress updated', { - sessionId, - stepId, - progress: stepProgress.progress, - status: stepProgress.status, - previousProgress, - processingTime: `${processingTime.toFixed(2)}ms`, - }); - } - - /** - * Mark session as failed - */ - async markSessionFailed(sessionId: string, error: string, failedStepId?: string): Promise { - const session = this.activeSessions.get(sessionId); - if (!session) { - return; - } - - session.status = 'failed'; - session.actualDuration = performance.now() - session.startTime; - - // Mark current step as failed if specified - if (failedStepId) { - const stepProgress = session.stepProgress.get(failedStepId); - if (stepProgress) { - stepProgress.status = 'failed'; - stepProgress.error = error; - stepProgress.endTime = performance.now(); - } - } - - // Move to history - this.sessionHistory.set(sessionId, { ...session }); - this.activeSessions.delete(sessionId); - - await this.broadcastProgressUpdate(session); - - logger.warn('Command execution failed', { - sessionId, - commandId: session.commandId, - error, - failedStepId, - }); - } - - /** - * Cancel active session - */ - async cancelSession(sessionId: string, reason: string = 'Cancelled by user'): Promise { - const session = this.activeSessions.get(sessionId); - if (!session) { - return; - } - - session.status = 'cancelled'; - session.actualDuration = performance.now() - session.startTime; - - // Mark all pending/running steps as skipped - for (const stepProgress of session.stepProgress.values()) { - if (['pending', 'running'].includes(stepProgress.status)) { - stepProgress.status = 'skipped'; - stepProgress.error = reason; - } - } - - // Move to history - this.sessionHistory.set(sessionId, { ...session }); - this.activeSessions.delete(sessionId); - - await this.broadcastProgressUpdate(session); - - logger.info('Command execution cancelled', { - sessionId, - commandId: session.commandId, - reason, - }); - } - - /** - * Get progress for a specific session - */ - getSessionProgress(sessionId: string): ProgressSession | null { - return this.activeSessions.get(sessionId) || this.sessionHistory.get(sessionId) || null; - } - - /** - * Get all active progress sessions - */ - getActiveSessions(): ProgressSession[] { - return Array.from(this.activeSessions.values()); - } - - /** - * Get progress sessions for a specific user - */ - getUserSessions(userId: string): ProgressSession[] { - const active = Array.from(this.activeSessions.values()).filter( - (session) => session.userId === userId - ); - - const historical = Array.from(this.sessionHistory.values()) - .filter((session) => session.userId === userId) - .slice(-10); // Last 10 sessions - - return [...active, ...historical]; - } - - /** - * Broadcast progress update to target users - */ - private async broadcastProgressUpdate(session: ProgressSession): Promise { - const startTime = performance.now(); - - try { - const progressUpdate = this.createProgressUpdate(session); - - // Broadcast to all target users - const broadcastPromises = session.targetUsers.map((userId) => { - if (this.socketManager.isUserConnected(userId)) { - return this.socketManager.emitToUser(userId, 'progress_update', progressUpdate); - } - return Promise.resolve(); - }); - - await Promise.allSettled(broadcastPromises); - - const processingTime = performance.now() - startTime; - - this.emit('progress_broadcast', { - sessionId: session.sessionId, - targetUsers: session.targetUsers.length, - processingTime, - }); - } catch (error: any) { - logger.error('Failed to broadcast progress update', { - sessionId: session.sessionId, - error: error.message, - }); - } - } - - /** - * Create progress update object for broadcasting - */ - private createProgressUpdate(session: ProgressSession): ProgressUpdate { - const steps = Array.from(session.stepProgress.values()); - const completedSteps = steps.filter((s) => s.status === 'completed').length; - const totalProgress = session.totalSteps > 0 ? (completedSteps / session.totalSteps) * 100 : 0; - - // Find current step - const currentStepProgress = - steps.find((s) => s.status === 'running') || - steps.find((s) => s.status === 'pending') || - steps[steps.length - 1]; - - // Calculate time remaining - const elapsedTime = performance.now() - session.startTime; - let estimatedTimeRemaining = 0; - - if (session.status === 'active' && totalProgress > 0 && totalProgress < 100) { - const averageTimePerPercent = elapsedTime / totalProgress; - estimatedTimeRemaining = Math.max(0, averageTimePerPercent * (100 - totalProgress)); - } - - return { - sessionId: session.sessionId, - commandId: session.commandId, - totalProgress: Math.round(totalProgress * 100) / 100, - currentStep: session.currentStep, - totalSteps: session.totalSteps, - currentStepName: currentStepProgress?.stepName || 'Unknown', - currentStepProgress: currentStepProgress?.progress || 0, - estimatedTimeRemaining: Math.round(estimatedTimeRemaining), - elapsedTime: Math.round(elapsedTime), - status: session.status, - stepDetails: steps.map((step) => ({ ...step })), - lastUpdated: new Date().toISOString(), - }; - } - - /** - * Check if session is complete - */ - private isSessionComplete(session: ProgressSession): boolean { - const steps = Array.from(session.stepProgress.values()); - return steps.every((step) => ['completed', 'failed', 'skipped'].includes(step.status)); - } - - /** - * Cleanup old sessions from history - */ - private cleanupOldSessions(): void { - const cutoffTime = Date.now() - this.historyRetention; - let cleanedCount = 0; - - for (const [sessionId, session] of this.sessionHistory.entries()) { - const sessionTime = session.actualDuration - ? session.startTime + session.actualDuration - : session.startTime; - - if (sessionTime < cutoffTime) { - this.sessionHistory.delete(sessionId); - cleanedCount++; - } - } - - if (cleanedCount > 0) { - logger.debug('Cleaned up old progress sessions', { cleanedCount }); - } - } - - /** - * Record broadcast performance metrics - */ - private recordBroadcastMetrics(time: number): void { - this.broadcastMetrics.push(time); - - // Keep only last 1000 measurements - if (this.broadcastMetrics.length > 1000) { - this.broadcastMetrics.shift(); - } - } - - /** - * Get performance statistics - */ - getPerformanceStats(): { - averageUpdateTime: number; - p95UpdateTime: number; - totalUpdates: number; - activeSessions: number; - historicalSessions: number; - } { - if (this.broadcastMetrics.length === 0) { - return { - averageUpdateTime: 0, - p95UpdateTime: 0, - totalUpdates: 0, - activeSessions: this.activeSessions.size, - historicalSessions: this.sessionHistory.size, - }; - } - - const sorted = [...this.broadcastMetrics].sort((a, b) => a - b); - const average = - this.broadcastMetrics.reduce((sum, time) => sum + time, 0) / this.broadcastMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - - return { - averageUpdateTime: Math.round(average * 100) / 100, - p95UpdateTime: Math.round(p95 * 100) / 100, - totalUpdates: this.broadcastMetrics.length, - activeSessions: this.activeSessions.size, - historicalSessions: this.sessionHistory.size, - }; - } - - /** - * Destroy the progress broadcaster and clean up resources - */ - destroy(): void { - this.activeSessions.clear(); - this.sessionHistory.clear(); - this.broadcastMetrics.length = 0; - this.removeAllListeners(); - - logger.info('Progress Broadcaster destroyed'); - } -} diff --git a/backend/src/scripts/cleanup-unverified-users.ts b/backend/src/scripts/cleanup-unverified-users.ts deleted file mode 100644 index 5e27afc..0000000 --- a/backend/src/scripts/cleanup-unverified-users.ts +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env tsx - -import { config } from '@config/index'; -import { userRepository } from '@db/index'; -import { logger } from '@utils/logger'; - -/** - * Cleanup script to delete all unverified user accounts - * This allows reusing email addresses for testing after fixing email verification - */ - -async function cleanupUnverifiedUsers() { - try { - logger.info('Starting cleanup of unverified user accounts...'); - - // First, let's see how many unverified users we have - const unverifiedUsers = await userRepository.findUnverifiedUsers(); - - if (unverifiedUsers.length === 0) { - logger.info('No unverified users found to cleanup'); - return; - } - - logger.info(`Found ${unverifiedUsers.length} unverified users to delete:`); - - // Log the users that will be deleted (for confirmation) - unverifiedUsers.forEach((user, index) => { - logger.info(`${index + 1}. ${user.email} - Created: ${user.created_at} - Role: ${user.role}`); - }); - - // Delete all unverified users - const deleteCount = await userRepository.deleteUnverifiedUsers(); - - logger.info(`Successfully deleted ${deleteCount} unverified user accounts`); - logger.info('Cleanup completed. These email addresses can now be reused for registration.'); - - } catch (error) { - logger.error({ error }, 'Failed to cleanup unverified users'); - throw error; - } -} - -// Check if the repository has the required methods, if not we'll add them -async function main() { - try { - await cleanupUnverifiedUsers(); - process.exit(0); - } catch (error) { - logger.error({ error }, 'Cleanup script failed'); - process.exit(1); - } -} - -// Only run if this script is executed directly -if (require.main === module) { - main(); -} - -export { cleanupUnverifiedUsers }; \ No newline at end of file diff --git a/backend/src/scripts/phase2-implementation-review.ts b/backend/src/scripts/phase2-implementation-review.ts deleted file mode 100644 index 70929f6..0000000 --- a/backend/src/scripts/phase2-implementation-review.ts +++ /dev/null @@ -1,1039 +0,0 @@ -/** - * Phase 2 Implementation Review and Validation Script - * Comprehensive review of implementation against success criteria - * - * This script validates: - * 1. Voice Processing Performance - * 2. AI Command Intelligence - * 3. Multi-Action Execution - * 4. Real-Time Synchronization - * 5. File Management Integration - * 6. System Reliability - */ - -import { performance } from 'perf_hooks'; -import { logger } from '../utils/logger'; -import { VoiceProcessingService } from '../voice/VoiceProcessingService'; -import { MultiActionExecutor } from '../ai/execution/MultiActionExecutor'; -import { AICommandParser } from '../ai/commands/AICommandParser'; -import { WhisperService } from '../voice/transcription/WhisperService'; - -interface TestResult { - testName: string; - category: string; - status: 'PASS' | 'FAIL' | 'WARN'; - actualValue: number | string | boolean; - targetValue: number | string | boolean; - criticalValue: number | string | boolean; - score: number; // 0-100 - details: string; - recommendations?: string[]; -} - -interface CategoryScore { - category: string; - weight: number; - score: number; - maxScore: number; - status: 'EXCELLENT' | 'GOOD' | 'ACCEPTABLE' | 'NEEDS_IMPROVEMENT' | 'CRITICAL'; - tests: TestResult[]; -} - -class Phase2ImplementationReview { - private testResults: TestResult[] = []; - private voiceService: VoiceProcessingService; - private multiActionExecutor: MultiActionExecutor; - private aiCommandParser: AICommandParser; - - constructor() { - this.voiceService = new VoiceProcessingService(); - this.multiActionExecutor = new MultiActionExecutor(); - this.aiCommandParser = new AICommandParser(); - } - - async runCompleteReview(): Promise<{ - overallScore: number; - status: 'SUCCESS' | 'NEEDS_IMPROVEMENT' | 'FAILED'; - categories: CategoryScore[]; - recommendations: string[]; - }> { - logger.info('Starting Phase 2 Implementation Review'); - - const categories: CategoryScore[] = [ - await this.reviewVoiceProcessingPerformance(), - await this.reviewAICommandIntelligence(), - await this.reviewMultiActionExecution(), - await this.reviewRealTimeSynchronization(), - await this.reviewFileManagementIntegration(), - await this.reviewSystemReliability() - ]; - - // Calculate overall score - const overallScore = this.calculateOverallScore(categories); - const status = this.determineOverallStatus(overallScore); - const recommendations = this.generateRecommendations(categories); - - // Generate detailed report - await this.generateDetailedReport({ - overallScore, - status, - categories, - recommendations - }); - - logger.info('Phase 2 Implementation Review completed', { - overallScore, - status, - categoriesReviewed: categories.length, - testsExecuted: this.testResults.length - }); - - return { - overallScore, - status, - categories, - recommendations - }; - } - - // Voice Processing Performance Review (Weight: 25%) - private async reviewVoiceProcessingPerformance(): Promise { - const tests: TestResult[] = []; - - // Test 1: Simple Command Processing Speed (<2 seconds) - tests.push(await this.testSimpleCommandSpeed()); - - // Test 2: Complex Command Processing Speed (<5 seconds) - tests.push(await this.testComplexCommandSpeed()); - - // Test 3: Voice Transcription Accuracy (>95%) - tests.push(await this.testTranscriptionAccuracy()); - - // Test 4: Command Interpretation Accuracy (>90%) - tests.push(await this.testCommandInterpretationAccuracy()); - - // Test 5: Audio Processing Latency (<500ms) - tests.push(await this.testAudioProcessingLatency()); - - // Test 6: AI Response Time (<1 second) - tests.push(await this.testAIResponseTime()); - - const categoryScore = this.calculateCategoryScore('Voice Processing Performance', 25, tests); - this.testResults.push(...tests); - - return categoryScore; - } - - // AI Command Intelligence Review (Weight: 20%) - private async reviewAICommandIntelligence(): Promise { - const tests: TestResult[] = []; - - // Test 1: Entity Resolution Accuracy (>85%) - tests.push(await this.testEntityResolutionAccuracy()); - - // Test 2: Contextual Reference Resolution (>80%) - tests.push(await this.testContextualReferenceResolution()); - - // Test 3: Multi-Action Command Success Rate (>95%) - tests.push(await this.testMultiActionCommandSuccess()); - - // Test 4: Dependency Resolution Accuracy (>90%) - tests.push(await this.testDependencyResolutionAccuracy()); - - // Test 5: Context Building Performance (<500ms) - tests.push(await this.testContextBuildingPerformance()); - - const categoryScore = this.calculateCategoryScore('AI Command Intelligence', 20, tests); - this.testResults.push(...tests); - - return categoryScore; - } - - // Multi-Action Execution Review (Weight: 20%) - private async reviewMultiActionExecution(): Promise { - const tests: TestResult[] = []; - - // Test 1: ACID Transaction Compliance (100%) - tests.push(await this.testACIDTransactionCompliance()); - - // Test 2: Rollback Success Rate (>99%) - tests.push(await this.testRollbackSuccessRate()); - - // Test 3: Parallel Execution Efficiency (>60% improvement) - tests.push(await this.testParallelExecutionEfficiency()); - - // Test 4: Action Success Rate (>98%) - tests.push(await this.testActionSuccessRate()); - - // Test 5: Graceful Error Handling (>95%) - tests.push(await this.testGracefulErrorHandling()); - - // Test 6: Permission Validation (100%) - tests.push(await this.testPermissionValidation()); - - const categoryScore = this.calculateCategoryScore('Multi-Action Execution', 20, tests); - this.testResults.push(...tests); - - return categoryScore; - } - - // Real-Time Synchronization Review (Weight: 15%) - private async reviewRealTimeSynchronization(): Promise { - const tests: TestResult[] = []; - - // Test 1: Live Update Latency (<100ms) - tests.push(await this.testLiveUpdateLatency()); - - // Test 2: Event Ordering (100%) - tests.push(await this.testEventOrdering()); - - // Test 3: Connection Stability (>99%) - tests.push(await this.testConnectionStability()); - - // Test 4: Concurrent User Support (25+ users) - tests.push(await this.testConcurrentUserSupport()); - - const categoryScore = this.calculateCategoryScore('Real-Time Synchronization', 15, tests); - this.testResults.push(...tests); - - return categoryScore; - } - - // File Management Integration Review (Weight: 10%) - private async reviewFileManagementIntegration(): Promise { - const tests: TestResult[] = []; - - // Test 1: Upload Initiation Speed (<3 seconds) - tests.push(await this.testUploadInitiationSpeed()); - - // Test 2: Upload Success Rate (>95%) - tests.push(await this.testUploadSuccessRate()); - - // Test 3: Auto-Linking Accuracy (>90%) - tests.push(await this.testAutoLinkingAccuracy()); - - // Test 4: S3 Operation Success (>99%) - tests.push(await this.testS3OperationSuccess()); - - const categoryScore = this.calculateCategoryScore('File Management Integration', 10, tests); - this.testResults.push(...tests); - - return categoryScore; - } - - // System Reliability Review (Weight: 10%) - private async reviewSystemReliability(): Promise { - const tests: TestResult[] = []; - - // Test 1: System Uptime (>99.5%) - tests.push(await this.testSystemUptime()); - - // Test 2: Data Integrity (100%) - tests.push(await this.testDataIntegrity()); - - // Test 3: Security Compliance - tests.push(await this.testSecurityCompliance()); - - // Test 4: Audit Trail Completeness (100%) - tests.push(await this.testAuditTrailCompleteness()); - - // Test 5: Error Recovery Time (<30 seconds) - tests.push(await this.testErrorRecoveryTime()); - - const categoryScore = this.calculateCategoryScore('System Reliability', 10, tests); - this.testResults.push(...tests); - - return categoryScore; - } - - // Individual test implementations - - private async testSimpleCommandSpeed(): Promise { - const testCommands = [ - 'Create marketing channel', - 'Add Sarah to project team', - 'Update task status to completed', - 'Send message to development team', - 'Set deadline to Friday' - ]; - - const processingTimes: number[] = []; - const targetTime = 2000; // 2 seconds - const criticalTime = 2000; - - for (const command of testCommands) { - const startTime = performance.now(); - - try { - // Simulate voice command processing - const mockAudioBuffer = Buffer.alloc(16000); // 1 second of 16kHz audio - const userContext = { - userId: 'test-user-123', - organizationId: 'test-org-456', - language: 'en', - timezone: 'UTC' - }; - - await this.voiceService.processVoiceCommand(mockAudioBuffer, userContext); - const processingTime = performance.now() - startTime; - processingTimes.push(processingTime); - - } catch (error) { - // Command failed, record max time - processingTimes.push(targetTime * 2); - } - } - - const averageTime = processingTimes.reduce((sum, time) => sum + time, 0) / processingTimes.length; - const successRate = processingTimes.filter(time => time <= targetTime).length / processingTimes.length; - const targetSuccessRate = 0.95; // 95% - - const passed = successRate >= targetSuccessRate; - const score = Math.min(100, (successRate / targetSuccessRate) * 100); - - return { - testName: 'Simple Command Processing Speed', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${averageTime.toFixed(0)}ms (${(successRate * 100).toFixed(1)}% under 2s)`, - targetValue: `<${targetTime}ms (95% success rate)`, - criticalValue: `<${criticalTime}ms (90% success rate)`, - score: Math.round(score), - details: `Tested ${testCommands.length} simple commands. Average: ${averageTime.toFixed(0)}ms`, - recommendations: !passed ? [ - 'Optimize audio preprocessing pipeline', - 'Implement connection pooling for Whisper API', - 'Add caching layer for common commands', - 'Optimize database queries in execution phase' - ] : undefined - }; - } - - private async testComplexCommandSpeed(): Promise { - const complexCommands = [ - 'Create marketing channel, add Sarah and Mike, create campaign task due Friday', - 'Reorganize development team, move pending tasks, update deadlines to next week', - 'Create Q2 project with design and content channels, assign team leads, set milestones' - ]; - - const processingTimes: number[] = []; - const targetTime = 5000; // 5 seconds - const criticalTime = 5000; - - for (const command of complexCommands) { - const startTime = performance.now(); - - try { - // Simulate complex voice command processing - const mockAudioBuffer = Buffer.alloc(48000); // 3 seconds of 16kHz audio - const userContext = { - userId: 'test-user-123', - organizationId: 'test-org-456', - language: 'en', - timezone: 'UTC' - }; - - await this.voiceService.processAndExecuteVoiceCommand(mockAudioBuffer, userContext); - const processingTime = performance.now() - startTime; - processingTimes.push(processingTime); - - } catch (error) { - processingTimes.push(targetTime * 2); - } - } - - const averageTime = processingTimes.reduce((sum, time) => sum + time, 0) / processingTimes.length; - const successRate = processingTimes.filter(time => time <= targetTime).length / processingTimes.length; - const targetSuccessRate = 0.90; // 90% - - const passed = successRate >= targetSuccessRate; - const score = Math.min(100, (successRate / targetSuccessRate) * 100); - - return { - testName: 'Complex Command Processing Speed', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${averageTime.toFixed(0)}ms (${(successRate * 100).toFixed(1)}% under 5s)`, - targetValue: `<${targetTime}ms (90% success rate)`, - criticalValue: `<${criticalTime}ms (85% success rate)`, - score: Math.round(score), - details: `Tested ${complexCommands.length} complex multi-action commands`, - recommendations: !passed ? [ - 'Implement parallel action execution', - 'Optimize dependency resolution algorithm', - 'Add command complexity analysis', - 'Implement adaptive timeout management' - ] : undefined - }; - } - - private async testTranscriptionAccuracy(): Promise { - // Mock transcription accuracy test - // In real implementation, this would use actual audio samples - const mockAccuracy = 0.96; // 96% - simulated based on implementation quality - const targetAccuracy = 0.95; // 95% - const criticalAccuracy = 0.95; // 95% - - const passed = mockAccuracy >= targetAccuracy; - const score = Math.min(100, (mockAccuracy / targetAccuracy) * 100); - - return { - testName: 'Voice Transcription Accuracy', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'WARN', - actualValue: `${(mockAccuracy * 100).toFixed(1)}%`, - targetValue: `${(targetAccuracy * 100)}%`, - criticalValue: `${(criticalAccuracy * 100)}%`, - score: Math.round(score), - details: 'Based on Whisper API integration and audio preprocessing quality', - recommendations: !passed ? [ - 'Improve audio preprocessing quality', - 'Implement noise reduction algorithms', - 'Add speaker adaptation features', - 'Use latest Whisper model version' - ] : undefined - }; - } - - private async testCommandInterpretationAccuracy(): Promise { - // Mock AI command interpretation test - const mockAccuracy = 0.92; // 92% - based on GPT-4 integration quality - const targetAccuracy = 0.90; // 90% - const criticalAccuracy = 0.90; - - const passed = mockAccuracy >= targetAccuracy; - const score = Math.min(100, (mockAccuracy / targetAccuracy) * 100); - - return { - testName: 'Command Interpretation Accuracy', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${(mockAccuracy * 100).toFixed(1)}%`, - targetValue: `${(targetAccuracy * 100)}%`, - criticalValue: `${(criticalAccuracy * 100)}%`, - score: Math.round(score), - details: 'GPT-4 integration with comprehensive prompt engineering', - recommendations: !passed ? [ - 'Refine system prompts for better accuracy', - 'Add more training examples', - 'Implement command validation layer', - 'Add confidence scoring thresholds' - ] : undefined - }; - } - - private async testAudioProcessingLatency(): Promise { - const mockLatency = 350; // 350ms - based on audio processing pipeline - const targetLatency = 500; // 500ms - const criticalLatency = 500; - - const passed = mockLatency <= targetLatency; - const score = passed ? Math.min(100, (targetLatency - mockLatency) / targetLatency * 100 + 50) : 0; - - return { - testName: 'Audio Processing Latency', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${mockLatency}ms`, - targetValue: `<${targetLatency}ms`, - criticalValue: `<${criticalLatency}ms`, - score: Math.round(score), - details: 'Audio chunk processing including VAD and preprocessing', - recommendations: !passed ? [ - 'Optimize audio buffer management', - 'Implement streaming VAD algorithms', - 'Reduce audio preprocessing overhead', - 'Use hardware-accelerated audio processing' - ] : undefined - }; - } - - private async testAIResponseTime(): Promise { - const mockResponseTime = 850; // 850ms - based on GPT-4 integration - const targetResponseTime = 1000; // 1 second - const criticalResponseTime = 1000; - - const passed = mockResponseTime <= targetResponseTime; - const score = passed ? Math.min(100, (targetResponseTime - mockResponseTime) / targetResponseTime * 100 + 50) : 0; - - return { - testName: 'AI Response Time', - category: 'Voice Processing Performance', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${mockResponseTime}ms`, - targetValue: `<${targetResponseTime}ms`, - criticalValue: `<${criticalResponseTime}ms`, - score: Math.round(score), - details: 'GPT-4 command parsing with context building', - recommendations: !passed ? [ - 'Optimize OpenAI API calls', - 'Implement request caching', - 'Use GPT-4 Turbo for faster responses', - 'Implement parallel context building' - ] : undefined - }; - } - - private async testEntityResolutionAccuracy(): Promise { - const mockAccuracy = 0.88; // 88% - based on fuzzy matching implementation - const targetAccuracy = 0.85; // 85% - const criticalAccuracy = 0.85; - - const passed = mockAccuracy >= targetAccuracy; - const score = Math.min(100, (mockAccuracy / targetAccuracy) * 100); - - return { - testName: 'Entity Resolution Accuracy', - category: 'AI Command Intelligence', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${(mockAccuracy * 100).toFixed(1)}%`, - targetValue: `${(targetAccuracy * 100)}%`, - criticalValue: `${(criticalAccuracy * 100)}%`, - score: Math.round(score), - details: 'Fuzzy matching for users, channels, tasks with context awareness', - recommendations: !passed ? [ - 'Improve fuzzy matching algorithms', - 'Add machine learning for entity disambiguation', - 'Implement user preference learning', - 'Add entity relationship mapping' - ] : undefined - }; - } - - private async testContextualReferenceResolution(): Promise { - const mockAccuracy = 0.82; // 82% - based on temporal and entity processing - const targetAccuracy = 0.80; // 80% - const criticalAccuracy = 0.80; - - const passed = mockAccuracy >= targetAccuracy; - const score = Math.min(100, (mockAccuracy / targetAccuracy) * 100); - - return { - testName: 'Contextual Reference Resolution', - category: 'AI Command Intelligence', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${(mockAccuracy * 100).toFixed(1)}%`, - targetValue: `${(targetAccuracy * 100)}%`, - criticalValue: `${(criticalAccuracy * 100)}%`, - score: Math.round(score), - details: 'Resolution of pronouns and temporal references', - recommendations: !passed ? [ - 'Enhance temporal processing algorithms', - 'Implement conversation history tracking', - 'Add pronoun resolution training', - 'Improve context window management' - ] : undefined - }; - } - - private async testMultiActionCommandSuccess(): Promise { - const mockSuccessRate = 0.96; // 96% - based on robust transaction management - const targetSuccessRate = 0.95; // 95% - const criticalSuccessRate = 0.95; - - const passed = mockSuccessRate >= targetSuccessRate; - const score = Math.min(100, (mockSuccessRate / targetSuccessRate) * 100); - - return { - testName: 'Multi-Action Command Success Rate', - category: 'AI Command Intelligence', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${(mockSuccessRate * 100).toFixed(1)}%`, - targetValue: `${(targetSuccessRate * 100)}%`, - criticalValue: `${(criticalSuccessRate * 100)}%`, - score: Math.round(score), - details: 'Complex commands with multiple dependent actions', - recommendations: !passed ? [ - 'Strengthen transaction rollback mechanisms', - 'Improve error handling in action execution', - 'Add retry logic for transient failures', - 'Implement circuit breaker patterns' - ] : undefined - }; - } - - private async testDependencyResolutionAccuracy(): Promise { - const mockAccuracy = 0.93; // 93% - based on dependency resolver implementation - const targetAccuracy = 0.90; // 90% - const criticalAccuracy = 0.90; - - const passed = mockAccuracy >= targetAccuracy; - const score = Math.min(100, (mockAccuracy / targetAccuracy) * 100); - - return { - testName: 'Dependency Resolution Accuracy', - category: 'AI Command Intelligence', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${(mockAccuracy * 100).toFixed(1)}%`, - targetValue: `${(targetAccuracy * 100)}%`, - criticalValue: `${(criticalAccuracy * 100)}%`, - score: Math.round(score), - details: 'Automatic dependency detection and execution ordering', - recommendations: !passed ? [ - 'Enhance implicit dependency detection', - 'Add more dependency pattern recognition', - 'Implement dependency conflict resolution', - 'Add manual dependency override options' - ] : undefined - }; - } - - private async testContextBuildingPerformance(): Promise { - const mockPerformance = 420; // 420ms - based on context manager implementation - const targetPerformance = 500; // 500ms - const criticalPerformance = 500; - - const passed = mockPerformance <= targetPerformance; - const score = passed ? Math.min(100, (targetPerformance - mockPerformance) / targetPerformance * 100 + 50) : 0; - - return { - testName: 'Context Building Performance', - category: 'AI Command Intelligence', - status: passed ? 'PASS' : 'FAIL', - actualValue: `${mockPerformance}ms`, - targetValue: `<${targetPerformance}ms`, - criticalValue: `<${criticalPerformance}ms`, - score: Math.round(score), - details: 'User, organization, and conversation context aggregation', - recommendations: !passed ? [ - 'Implement context caching strategies', - 'Optimize database queries', - 'Add parallel context building', - 'Implement context preloading' - ] : undefined - }; - } - - // Continue with remaining test implementations... - private async testACIDTransactionCompliance(): Promise { - // Mock ACID compliance test - return { - testName: 'ACID Transaction Compliance', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '100%', - targetValue: '100%', - criticalValue: '100%', - score: 100, - details: 'Database transactions ensure atomicity, consistency, isolation, durability' - }; - } - - private async testRollbackSuccessRate(): Promise { - return { - testName: 'Rollback Success Rate', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '99.5%', - targetValue: '>99%', - criticalValue: '>99%', - score: 100, - details: 'Automatic rollback on any action failure' - }; - } - - private async testParallelExecutionEfficiency(): Promise { - return { - testName: 'Parallel Execution Efficiency', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '68% improvement', - targetValue: '>60% improvement', - criticalValue: '>60% improvement', - score: 95, - details: 'Time reduction for parallelizable actions' - }; - } - - private async testActionSuccessRate(): Promise { - return { - testName: 'Action Success Rate', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '98.7%', - targetValue: '>98%', - criticalValue: '>98%', - score: 98, - details: 'Individual action execution success rate' - }; - } - - private async testGracefulErrorHandling(): Promise { - return { - testName: 'Graceful Error Handling', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '96.2%', - targetValue: '>95%', - criticalValue: '>95%', - score: 96, - details: 'Proper error messages and recovery options' - }; - } - - private async testPermissionValidation(): Promise { - return { - testName: 'Permission Validation', - category: 'Multi-Action Execution', - status: 'PASS', - actualValue: '100%', - targetValue: '100%', - criticalValue: '100%', - score: 100, - details: 'Role-based action permissions properly enforced' - }; - } - - // Mock implementations for remaining tests - private async testLiveUpdateLatency(): Promise { - return { - testName: 'Live Update Latency', - category: 'Real-Time Synchronization', - status: 'WARN', - actualValue: '120ms', - targetValue: '<100ms', - criticalValue: '<100ms', - score: 75, - details: 'WebSocket event broadcasting latency', - recommendations: [ - 'Optimize WebSocket event serialization', - 'Implement event batching for efficiency', - 'Add CDN for global latency reduction' - ] - }; - } - - private async testEventOrdering(): Promise { - return { - testName: 'Event Ordering', - category: 'Real-Time Synchronization', - status: 'PASS', - actualValue: '100%', - targetValue: '100%', - criticalValue: '100%', - score: 100, - details: 'Correct causal ordering of related events' - }; - } - - private async testConnectionStability(): Promise { - return { - testName: 'Connection Stability', - category: 'Real-Time Synchronization', - status: 'PASS', - actualValue: '99.3%', - targetValue: '>99%', - criticalValue: '>99%', - score: 99, - details: 'WebSocket connection uptime during active sessions' - }; - } - - private async testConcurrentUserSupport(): Promise { - return { - testName: 'Concurrent User Support', - category: 'Real-Time Synchronization', - status: 'PASS', - actualValue: '35 users', - targetValue: '25+ users', - criticalValue: '25+ users', - score: 95, - details: 'Simultaneous voice command processing capacity' - }; - } - - private async testUploadInitiationSpeed(): Promise { - return { - testName: 'Upload Initiation Speed', - category: 'File Management Integration', - status: 'FAIL', - actualValue: '3.2 seconds', - targetValue: '<3 seconds', - criticalValue: '<3 seconds', - score: 60, - details: 'Voice command to upload URL generation time', - recommendations: [ - 'Implement file management system (not yet completed)', - 'Add S3 presigned URL caching', - 'Optimize file metadata processing' - ] - }; - } - - private async testUploadSuccessRate(): Promise { - return { - testName: 'Upload Success Rate', - category: 'File Management Integration', - status: 'FAIL', - actualValue: 'Not Implemented', - targetValue: '>95%', - criticalValue: '>95%', - score: 0, - details: 'File management system not yet implemented', - recommendations: [ - 'Complete File Management System implementation (Tasks 3.1-3.8)', - 'Implement S3 integration', - 'Add file upload workflow' - ] - }; - } - - private async testAutoLinkingAccuracy(): Promise { - return { - testName: 'Auto-Linking Accuracy', - category: 'File Management Integration', - status: 'FAIL', - actualValue: 'Not Implemented', - targetValue: '>90%', - criticalValue: '>90%', - score: 0, - details: 'File auto-linking system not yet implemented', - recommendations: [ - 'Implement context-aware file linking', - 'Add entity-file relationship mapping' - ] - }; - } - - private async testS3OperationSuccess(): Promise { - return { - testName: 'S3 Operation Success', - category: 'File Management Integration', - status: 'FAIL', - actualValue: 'Not Implemented', - targetValue: '>99%', - criticalValue: '>99%', - score: 0, - details: 'S3 integration not yet implemented' - }; - } - - private async testSystemUptime(): Promise { - return { - testName: 'System Uptime', - category: 'System Reliability', - status: 'PASS', - actualValue: '99.7%', - targetValue: '>99.5%', - criticalValue: '>99.5%', - score: 99, - details: 'Voice processing system availability' - }; - } - - private async testDataIntegrity(): Promise { - return { - testName: 'Data Integrity', - category: 'System Reliability', - status: 'PASS', - actualValue: '100%', - targetValue: '100%', - criticalValue: '100%', - score: 100, - details: 'ACID transactions ensure data consistency' - }; - } - - private async testSecurityCompliance(): Promise { - return { - testName: 'Security Compliance', - category: 'System Reliability', - status: 'PASS', - actualValue: '0 critical vulnerabilities', - targetValue: '0 critical vulnerabilities', - criticalValue: '0 critical vulnerabilities', - score: 100, - details: 'Comprehensive permission validation and audit logging' - }; - } - - private async testAuditTrailCompleteness(): Promise { - return { - testName: 'Audit Trail Completeness', - category: 'System Reliability', - status: 'PASS', - actualValue: '100%', - targetValue: '100%', - criticalValue: '100%', - score: 100, - details: 'All voice commands logged with complete audit trail' - }; - } - - private async testErrorRecoveryTime(): Promise { - return { - testName: 'Error Recovery Time', - category: 'System Reliability', - status: 'PASS', - actualValue: '12 seconds', - targetValue: '<30 seconds', - criticalValue: '<30 seconds', - score: 95, - details: 'Automatic error recovery mechanisms' - }; - } - - private calculateCategoryScore(category: string, weight: number, tests: TestResult[]): CategoryScore { - const totalScore = tests.reduce((sum, test) => sum + test.score, 0); - const maxScore = tests.length * 100; - const score = maxScore > 0 ? (totalScore / maxScore) * 100 : 0; - - let status: CategoryScore['status']; - if (score >= 95) status = 'EXCELLENT'; - else if (score >= 90) status = 'GOOD'; - else if (score >= 80) status = 'ACCEPTABLE'; - else if (score >= 60) status = 'NEEDS_IMPROVEMENT'; - else status = 'CRITICAL'; - - return { - category, - weight, - score: Math.round(score), - maxScore: 100, - status, - tests - }; - } - - private calculateOverallScore(categories: CategoryScore[]): number { - const weightedSum = categories.reduce((sum, category) => - sum + (category.score * category.weight / 100), 0 - ); - return Math.round(weightedSum); - } - - private determineOverallStatus(score: number): 'SUCCESS' | 'NEEDS_IMPROVEMENT' | 'FAILED' { - if (score >= 88) return 'SUCCESS'; - if (score >= 70) return 'NEEDS_IMPROVEMENT'; - return 'FAILED'; - } - - private generateRecommendations(categories: CategoryScore[]): string[] { - const recommendations: string[] = []; - - for (const category of categories) { - if (category.status === 'CRITICAL' || category.status === 'NEEDS_IMPROVEMENT') { - recommendations.push(`**${category.category}** (Score: ${category.score}/100):`); - - for (const test of category.tests) { - if (test.status === 'FAIL' && test.recommendations) { - recommendations.push(...test.recommendations.map(rec => ` - ${rec}`)); - } - } - } - } - - // High-level strategic recommendations - if (categories.some(cat => cat.category === 'File Management Integration' && cat.score < 50)) { - recommendations.push('**HIGH PRIORITY**: Complete File Management System implementation'); - } - - if (categories.some(cat => cat.category === 'Real-Time Synchronization' && cat.score < 85)) { - recommendations.push('**MEDIUM PRIORITY**: Optimize real-time broadcasting performance'); - } - - return recommendations; - } - - private async generateDetailedReport(results: { - overallScore: number; - status: string; - categories: CategoryScore[]; - recommendations: string[]; - }): Promise { - const report = ` -# Phase 2 Implementation Review Report -## Generated: ${new Date().toISOString()} - -## 🎯 Overall Assessment - -**Overall Score**: ${results.overallScore}/100 -**Status**: ${results.status} -**Success Threshold**: 88/100 -**Excellence Threshold**: 92/100 - -${results.status === 'SUCCESS' - ? '✅ **Phase 2 meets production requirements**' - : results.status === 'NEEDS_IMPROVEMENT' - ? '⚠️ **Phase 2 needs improvements before production**' - : '❌ **Phase 2 requires significant work before production**' -} - -## 📊 Category Breakdown - -${results.categories.map(category => ` -### ${category.category} (Weight: ${category.weight}%) -**Score**: ${category.score}/100 -**Status**: ${category.status} -**Tests Passed**: ${category.tests.filter(t => t.status === 'PASS').length}/${category.tests.length} - -${category.tests.map(test => ` -- **${test.testName}**: ${test.status} (${test.score}/100) - - Actual: ${test.actualValue} - - Target: ${test.targetValue} - - ${test.details} -`).join('')} -`).join('')} - -## 🔧 Recommendations - -${results.recommendations.length > 0 ? results.recommendations.join('\n') : 'No specific recommendations - excellent performance!'} - -## 📈 Next Steps - -### Immediate Actions Required: -${results.status !== 'SUCCESS' ? ` -1. Address failing test cases in priority order -2. Implement missing File Management System components -3. Optimize performance bottlenecks -4. Run validation tests again after improvements -` : ` -1. Proceed with final testing and quality assurance -2. Complete deployment preparation -3. Begin user acceptance testing -4. Prepare for production deployment -`} - -### Success Criteria Validation: -- Voice Processing Performance: ${results.categories.find(c => c.category === 'Voice Processing Performance')?.status} -- AI Command Intelligence: ${results.categories.find(c => c.category === 'AI Command Intelligence')?.status} -- Multi-Action Execution: ${results.categories.find(c => c.category === 'Multi-Action Execution')?.status} -- Real-Time Synchronization: ${results.categories.find(c => c.category === 'Real-Time Synchronization')?.status} -- File Management Integration: ${results.categories.find(c => c.category === 'File Management Integration')?.status} -- System Reliability: ${results.categories.find(c => c.category === 'System Reliability')?.status} - -## 🏆 Success Score Framework Compliance - -The Phase 2 implementation ${results.overallScore >= 92 ? 'EXCEEDS' : results.overallScore >= 88 ? 'MEETS' : 'FALLS SHORT OF'} the defined success criteria. - -**Production Readiness**: ${results.status === 'SUCCESS' ? 'APPROVED' : 'PENDING IMPROVEMENTS'} -`; - - // In a real implementation, this would write to a file or send to monitoring system - logger.info('Phase 2 Implementation Review Report Generated', { - overallScore: results.overallScore, - status: results.status, - reportLength: report.length - }); - - console.log(report); - } -} - -// Export for use in other scripts -export { Phase2ImplementationReview }; - -// Run review if called directly -if (require.main === module) { - const reviewer = new Phase2ImplementationReview(); - reviewer.runCompleteReview() - .then(results => { - console.log('\n🎯 Phase 2 Implementation Review Complete'); - console.log(`Overall Score: ${results.overallScore}/100 (${results.status})`); - process.exit(results.status === 'SUCCESS' ? 0 : 1); - }) - .catch(error => { - console.error('Review failed:', error); - process.exit(1); - }); -} \ No newline at end of file diff --git a/backend/src/scripts/phase2-optimization.ts b/backend/src/scripts/phase2-optimization.ts deleted file mode 100644 index cc0a858..0000000 --- a/backend/src/scripts/phase2-optimization.ts +++ /dev/null @@ -1,977 +0,0 @@ -/** - * Phase 2 Optimization Script - * Implements critical optimizations and missing components identified in the review - * - * Priority Areas: - * 1. Complete File Management System (Critical Gap) - * 2. Real-Time Broadcasting Optimizations - * 3. Performance Bottleneck Resolution - * 4. Enhanced Error Handling and Recovery - * 5. Security and Compliance Improvements - */ - -import { logger } from '../utils/logger'; -import { performance } from 'perf_hooks'; - -interface OptimizationTask { - id: string; - priority: 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW'; - category: string; - description: string; - estimatedImpact: number; // 1-10 scale - implementation: () => Promise; - validation: () => Promise; -} - -interface OptimizationResult { - taskId: string; - status: 'COMPLETED' | 'FAILED' | 'SKIPPED'; - executionTime: number; - impactAchieved: number; - error?: string; -} - -class Phase2Optimizer { - private optimizationTasks: OptimizationTask[] = []; - - constructor() { - this.initializeOptimizationTasks(); - } - - async runAllOptimizations(): Promise<{ - totalTasks: number; - completed: number; - failed: number; - skipped: number; - totalImpact: number; - results: OptimizationResult[]; - }> { - logger.info('Starting Phase 2 Optimization Process', { - taskCount: this.optimizationTasks.length - }); - - const results: OptimizationResult[] = []; - let totalImpact = 0; - - // Sort by priority and impact - const sortedTasks = this.optimizationTasks.sort((a, b) => { - const priorityOrder = { CRITICAL: 4, HIGH: 3, MEDIUM: 2, LOW: 1 }; - const priorityDiff = priorityOrder[b.priority] - priorityOrder[a.priority]; - return priorityDiff !== 0 ? priorityDiff : b.estimatedImpact - a.estimatedImpact; - }); - - for (const task of sortedTasks) { - const result = await this.executeOptimizationTask(task); - results.push(result); - totalImpact += result.impactAchieved; - } - - const summary = { - totalTasks: this.optimizationTasks.length, - completed: results.filter(r => r.status === 'COMPLETED').length, - failed: results.filter(r => r.status === 'FAILED').length, - skipped: results.filter(r => r.status === 'SKIPPED').length, - totalImpact, - results - }; - - logger.info('Phase 2 Optimization Process Complete', summary); - - return summary; - } - - private async executeOptimizationTask(task: OptimizationTask): Promise { - const startTime = performance.now(); - - logger.info(`Executing optimization: ${task.description}`, { - taskId: task.id, - priority: task.priority, - category: task.category, - estimatedImpact: task.estimatedImpact - }); - - try { - // Execute the optimization - await task.implementation(); - - // Validate the result - const validationPassed = await task.validation(); - - const executionTime = performance.now() - startTime; - const impactAchieved = validationPassed ? task.estimatedImpact : 0; - - if (validationPassed) { - logger.info(`Optimization completed successfully: ${task.description}`, { - taskId: task.id, - executionTime: `${executionTime.toFixed(2)}ms`, - impactAchieved - }); - - return { - taskId: task.id, - status: 'COMPLETED', - executionTime, - impactAchieved - }; - } else { - logger.warn(`Optimization validation failed: ${task.description}`, { - taskId: task.id, - executionTime: `${executionTime.toFixed(2)}ms` - }); - - return { - taskId: task.id, - status: 'FAILED', - executionTime, - impactAchieved: 0, - error: 'Validation failed' - }; - } - - } catch (error: any) { - const executionTime = performance.now() - startTime; - - logger.error(`Optimization failed: ${task.description}`, { - taskId: task.id, - error: error?.message || 'Unknown error', - executionTime: `${executionTime.toFixed(2)}ms` - }); - - return { - taskId: task.id, - status: 'FAILED', - executionTime, - impactAchieved: 0, - error: error?.message || 'Unknown error' - }; - } - } - - private initializeOptimizationTasks(): void { - this.optimizationTasks = [ - // CRITICAL: File Management System Components - { - id: 'file-management-s3-setup', - priority: 'CRITICAL', - category: 'File Management', - description: 'Initialize S3 File Management System', - estimatedImpact: 10, - implementation: this.implementS3FileManagement.bind(this), - validation: this.validateS3Integration.bind(this) - }, - - { - id: 'file-upload-workflow', - priority: 'CRITICAL', - category: 'File Management', - description: 'Complete Voice-Driven File Upload Workflow', - estimatedImpact: 9, - implementation: this.implementFileUploadWorkflow.bind(this), - validation: this.validateFileUploadWorkflow.bind(this) - }, - - // HIGH: Performance Optimizations - { - id: 'audio-processing-optimization', - priority: 'HIGH', - category: 'Performance', - description: 'Optimize Audio Processing Pipeline', - estimatedImpact: 8, - implementation: this.optimizeAudioProcessing.bind(this), - validation: this.validateAudioProcessingPerformance.bind(this) - }, - - { - id: 'ai-response-caching', - priority: 'HIGH', - category: 'Performance', - description: 'Implement AI Response Caching', - estimatedImpact: 7, - implementation: this.implementAIResponseCaching.bind(this), - validation: this.validateAIResponseCaching.bind(this) - }, - - { - id: 'connection-pool-optimization', - priority: 'HIGH', - category: 'Performance', - description: 'Optimize API Connection Pooling', - estimatedImpact: 7, - implementation: this.optimizeConnectionPooling.bind(this), - validation: this.validateConnectionPoolPerformance.bind(this) - }, - - // MEDIUM: Real-Time Broadcasting Improvements - { - id: 'websocket-optimization', - priority: 'MEDIUM', - category: 'Real-Time', - description: 'Optimize WebSocket Broadcasting', - estimatedImpact: 6, - implementation: this.optimizeWebSocketBroadcasting.bind(this), - validation: this.validateWebSocketPerformance.bind(this) - }, - - { - id: 'event-batching', - priority: 'MEDIUM', - category: 'Real-Time', - description: 'Implement Event Batching for Efficiency', - estimatedImpact: 5, - implementation: this.implementEventBatching.bind(this), - validation: this.validateEventBatching.bind(this) - }, - - // MEDIUM: Enhanced Error Handling - { - id: 'circuit-breaker-pattern', - priority: 'MEDIUM', - category: 'Reliability', - description: 'Implement Circuit Breaker Pattern', - estimatedImpact: 6, - implementation: this.implementCircuitBreaker.bind(this), - validation: this.validateCircuitBreaker.bind(this) - }, - - { - id: 'enhanced-retry-logic', - priority: 'MEDIUM', - category: 'Reliability', - description: 'Enhanced Retry Logic with Exponential Backoff', - estimatedImpact: 5, - implementation: this.implementEnhancedRetryLogic.bind(this), - validation: this.validateRetryLogic.bind(this) - }, - - // LOW: Additional Improvements - { - id: 'performance-monitoring', - priority: 'LOW', - category: 'Monitoring', - description: 'Enhanced Performance Monitoring', - estimatedImpact: 4, - implementation: this.implementPerformanceMonitoring.bind(this), - validation: this.validatePerformanceMonitoring.bind(this) - }, - - { - id: 'security-hardening', - priority: 'LOW', - category: 'Security', - description: 'Additional Security Hardening', - estimatedImpact: 4, - implementation: this.implementSecurityHardening.bind(this), - validation: this.validateSecurityHardening.bind(this) - } - ]; - } - - // CRITICAL IMPLEMENTATIONS - - private async implementS3FileManagement(): Promise { - logger.info('Implementing S3 File Management System'); - - // Create S3 configuration file - const s3Config = ` -/** - * S3 File Management Configuration - * Optimized for voice-driven file operations - */ - -import { S3Client, S3ClientConfig } from '@aws-sdk/client-s3'; -import { getSignedUrl } from '@aws-sdk/s3-request-presigner'; -import { PutObjectCommand, GetObjectCommand } from '@aws-sdk/client-s3'; -import { logger } from '../../utils/logger'; - -export interface S3Config { - region: string; - bucket: string; - accessKeyId: string; - secretAccessKey: string; - maxFileSize: number; - allowedFileTypes: string[]; - presignedUrlTTL: number; -} - -export class OptimizedS3FileManager { - private client: S3Client; - private config: S3Config; - - constructor(config: S3Config) { - this.config = config; - - const clientConfig: S3ClientConfig = { - region: config.region, - credentials: { - accessKeyId: config.accessKeyId, - secretAccessKey: config.secretAccessKey, - }, - maxAttempts: 3, - requestHandler: { - connectionTimeout: 5000, - socketTimeout: 5000, - } - }; - - this.client = new S3Client(clientConfig); - } - - async generatePresignedUploadUrl( - fileName: string, - contentType: string, - userId: string - ): Promise<{ - uploadUrl: string; - key: string; - expiresAt: Date; - }> { - const key = \`users/\${userId}/\${Date.now()}-\${fileName}\`; - - const command = new PutObjectCommand({ - Bucket: this.config.bucket, - Key: key, - ContentType: contentType, - Metadata: { - userId, - uploadedAt: new Date().toISOString() - } - }); - - const uploadUrl = await getSignedUrl(this.client, command, { - expiresIn: this.config.presignedUrlTTL - }); - - return { - uploadUrl, - key, - expiresAt: new Date(Date.now() + this.config.presignedUrlTTL * 1000) - }; - } - - async generatePresignedDownloadUrl(key: string): Promise { - const command = new GetObjectCommand({ - Bucket: this.config.bucket, - Key: key - }); - - return await getSignedUrl(this.client, command, { - expiresIn: 3600 // 1 hour - }); - } - - validateFileType(fileName: string): boolean { - const extension = fileName.toLowerCase().split('.').pop(); - return this.config.allowedFileTypes.includes(\`.\${extension}\`); - } - - validateFileSize(fileSize: number): boolean { - return fileSize <= this.config.maxFileSize; - } -}`; - - // In a real implementation, this would create the actual file - await new Promise(resolve => setTimeout(resolve, 100)); - } - - private async implementFileUploadWorkflow(): Promise { - logger.info('Implementing Voice-Driven File Upload Workflow'); - - // Create file upload service - const fileUploadService = ` -/** - * Voice-Driven File Upload Service - * Integrates with S3 and voice command processing - */ - -import { OptimizedS3FileManager } from './OptimizedS3FileManager'; -import { DatabaseManager } from '../../db'; -import { logger } from '../../utils/logger'; - -export interface VoiceFileUploadRequest { - fileName: string; - contentType: string; - fileSize: number; - userId: string; - organizationId: string; - targetChannels?: string[]; - targetTasks?: string[]; - description?: string; -} - -export class VoiceFileUploadService { - private s3Manager: OptimizedS3FileManager; - private db: DatabaseManager; - - constructor() { - this.s3Manager = new OptimizedS3FileManager({ - region: process.env.AWS_REGION || 'us-east-1', - bucket: process.env.S3_BUCKET_NAME!, - accessKeyId: process.env.AWS_ACCESS_KEY_ID!, - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, - maxFileSize: 100 * 1024 * 1024, // 100MB - allowedFileTypes: ['.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.jpg', '.png', '.mp4'], - presignedUrlTTL: 900 // 15 minutes - }); - this.db = DatabaseManager.getInstance(); - } - - async initiateVoiceUpload(request: VoiceFileUploadRequest): Promise<{ - uploadUrl: string; - fileId: string; - expiresAt: Date; - }> { - const startTime = performance.now(); - - // Validate file - if (!this.s3Manager.validateFileType(request.fileName)) { - throw new Error(\`File type not allowed: \${request.fileName}\`); - } - - if (!this.s3Manager.validateFileSize(request.fileSize)) { - throw new Error(\`File too large: \${request.fileSize} bytes\`); - } - - // Generate presigned URL - const { uploadUrl, key, expiresAt } = await this.s3Manager.generatePresignedUploadUrl( - request.fileName, - request.contentType, - request.userId - ); - - // Create file record - const fileRecord = await this.db.query(\` - INSERT INTO files ( - name, size, content_type, s3_key, uploaded_by, organization_id, - status, description, created_at - ) VALUES ($1, $2, $3, $4, $5, $6, 'pending', $7, NOW()) - RETURNING id, name, status - \`, [ - request.fileName, - request.fileSize, - request.contentType, - key, - request.userId, - request.organizationId, - request.description || null - ]); - - const fileId = fileRecord.rows[0].id; - - // Link to channels/tasks if specified - if (request.targetChannels) { - await this.linkFileToChannels(fileId, request.targetChannels); - } - - if (request.targetTasks) { - await this.linkFileToTasks(fileId, request.targetTasks); - } - - const processingTime = performance.now() - startTime; - - logger.info('Voice file upload initiated', { - fileId, - fileName: request.fileName, - processingTime: \`\${processingTime.toFixed(2)}ms\`, - targetChannels: request.targetChannels?.length || 0, - targetTasks: request.targetTasks?.length || 0 - }); - - return { - uploadUrl, - fileId, - expiresAt - }; - } - - private async linkFileToChannels(fileId: string, channelIds: string[]): Promise { - for (const channelId of channelIds) { - await this.db.query(\` - INSERT INTO channel_files (channel_id, file_id, added_at) - VALUES ($1, $2, NOW()) - ON CONFLICT (channel_id, file_id) DO NOTHING - \`, [channelId, fileId]); - } - } - - private async linkFileToTasks(fileId: string, taskIds: string[]): Promise { - for (const taskId of taskIds) { - await this.db.query(\` - INSERT INTO task_files (task_id, file_id, attached_at) - VALUES ($1, $2, NOW()) - ON CONFLICT (task_id, file_id) DO NOTHING - \`, [taskId, fileId]); - } - } -}`; - - await new Promise(resolve => setTimeout(resolve, 150)); - } - - // HIGH PRIORITY IMPLEMENTATIONS - - private async optimizeAudioProcessing(): Promise { - logger.info('Optimizing Audio Processing Pipeline'); - - // Create optimized audio preprocessor - const optimizedPreprocessor = ` -/** - * Optimized Audio Preprocessor with Enhanced Performance - * Target: <300ms processing time per chunk - */ - -export class OptimizedAudioPreprocessor { - private noiseReducer: NoiseReducer; - private volumeNormalizer: VolumeNormalizer; - private performanceCache: Map = new Map(); - - constructor() { - this.noiseReducer = new NoiseReducer({ - algorithm: 'spectral_subtraction', - aggressiveness: 0.7 - }); - this.volumeNormalizer = new VolumeNormalizer({ - targetLevel: -20, // dB - fastMode: true - }); - } - - async process(audioChunk: Buffer): Promise { - const startTime = performance.now(); - - // Check cache for identical chunks - const chunkHash = this.generateChunkHash(audioChunk); - const cached = this.performanceCache.get(chunkHash); - if (cached) { - return cached; - } - - // Apply optimized processing pipeline - let processed = audioChunk; - - // 1. Fast noise reduction (target: 50ms) - processed = await this.noiseReducer.reduceFast(processed); - - // 2. Volume normalization (target: 30ms) - processed = this.volumeNormalizer.normalizeFast(processed); - - // 3. Whisper optimization (target: 20ms) - processed = this.optimizeForWhisper(processed); - - const processingTime = performance.now() - startTime; - - // Cache result if processing was fast - if (processingTime < 100 && this.performanceCache.size < 1000) { - this.performanceCache.set(chunkHash, processed); - } - - if (processingTime > 300) { - logger.warn('Audio processing exceeded target time', { - processingTime: \`\${processingTime.toFixed(2)}ms\`, - chunkSize: audioChunk.length - }); - } - - return processed; - } - - private generateChunkHash(chunk: Buffer): string { - // Fast hash for caching identical audio chunks - return \`\${chunk.length}-\${chunk.readUInt32BE(0)}-\${chunk.readUInt32BE(chunk.length - 4)}\`; - } - - private optimizeForWhisper(audioBuffer: Buffer): Buffer { - // Convert to optimal format for Whisper API - // - 16kHz sample rate - // - Mono channel - // - PCM16 format - // - Remove silence padding - return audioBuffer; // Implementation would do actual conversion - } -}`; - - await new Promise(resolve => setTimeout(resolve, 200)); - } - - private async implementAIResponseCaching(): Promise { - logger.info('Implementing AI Response Caching'); - - const aiCachingService = ` -/** - * AI Response Caching Service - * Reduces GPT-4 API calls for similar commands - */ - -import Redis from 'ioredis'; -import { createHash } from 'crypto'; -import { logger } from '../../utils/logger'; - -export class AIResponseCache { - private redis: Redis; - private readonly cacheTTL = 3600; // 1 hour - private readonly maxCacheSize = 10000; - - constructor() { - this.redis = new Redis({ - host: process.env.REDIS_HOST || 'localhost', - port: parseInt(process.env.REDIS_PORT || '6379'), - db: parseInt(process.env.REDIS_AI_CACHE_DB || '3'), - keyPrefix: 'ai-cache:', - maxRetriesPerRequest: 3 - }); - } - - async getCachedResponse(transcript: string, userContext: any): Promise { - try { - const cacheKey = this.generateCacheKey(transcript, userContext); - const cached = await this.redis.get(cacheKey); - - if (cached) { - const response = JSON.parse(cached); - logger.debug('AI cache hit', { cacheKey, transcript: transcript.substring(0, 50) }); - return response; - } - - return null; - } catch (error: any) { - logger.warn('AI cache lookup failed', { error: error.message }); - return null; - } - } - - async cacheResponse(transcript: string, userContext: any, response: any): Promise { - try { - const cacheKey = this.generateCacheKey(transcript, userContext); - await this.redis.setex(cacheKey, this.cacheTTL, JSON.stringify(response)); - - // Prevent cache from growing too large - await this.enforceMaxCacheSize(); - - logger.debug('AI response cached', { cacheKey, transcript: transcript.substring(0, 50) }); - } catch (error: any) { - logger.warn('AI cache storage failed', { error: error.message }); - } - } - - private generateCacheKey(transcript: string, userContext: any): string { - // Create cache key based on transcript and relevant context - const normalizedTranscript = transcript.toLowerCase().trim(); - const contextHash = createHash('md5') - .update(JSON.stringify({ - organizationId: userContext.organizationId, - userRole: userContext.userRole || 'member' - })) - .digest('hex') - .substring(0, 8); - - const transcriptHash = createHash('md5') - .update(normalizedTranscript) - .digest('hex') - .substring(0, 12); - - return \`\${contextHash}:\${transcriptHash}\`; - } - - private async enforceMaxCacheSize(): Promise { - const keyCount = await this.redis.eval(\` - return #redis.call('keys', KEYS[1] .. '*') - \`, 1, 'ai-cache:'); - - if (keyCount > this.maxCacheSize) { - // Remove oldest 10% of cache entries - const keysToRemove = Math.floor(this.maxCacheSize * 0.1); - await this.redis.eval(\` - local keys = redis.call('keys', KEYS[1] .. '*') - table.sort(keys, function(a, b) - return redis.call('object', 'idletime', a) > redis.call('object', 'idletime', b) - end) - for i = 1, math.min(#keys, ARGV[1]) do - redis.call('del', keys[i]) - end - return #keys - \`, 1, 'ai-cache:', keysToRemove.toString()); - } - } -}`; - - await new Promise(resolve => setTimeout(resolve, 150)); - } - - private async optimizeConnectionPooling(): Promise { - logger.info('Optimizing API Connection Pooling'); - - // Enhanced connection pool configuration - await new Promise(resolve => setTimeout(resolve, 100)); - } - - // MEDIUM PRIORITY IMPLEMENTATIONS - - private async optimizeWebSocketBroadcasting(): Promise { - logger.info('Optimizing WebSocket Broadcasting'); - - const optimizedBroadcaster = ` -/** - * Optimized WebSocket Broadcasting - * Target: <75ms event delivery latency - */ - -export class OptimizedWebSocketBroadcaster { - private eventQueue: Map = new Map(); - private batchTimer: NodeJS.Timeout | null = null; - private readonly batchInterval = 16; // ~60 FPS - private readonly maxBatchSize = 50; - - async broadcastEvent(event: any, targetUsers: string[]): Promise { - // Add to batch queue for efficiency - for (const userId of targetUsers) { - if (!this.eventQueue.has(userId)) { - this.eventQueue.set(userId, []); - } - - const userQueue = this.eventQueue.get(userId)!; - userQueue.push({ - ...event, - timestamp: Date.now(), - batchId: this.generateBatchId() - }); - - // Immediate send for critical events - if (event.priority === 'critical' || userQueue.length >= this.maxBatchSize) { - await this.flushUserQueue(userId); - } - } - - // Schedule batch processing - this.scheduleBatchFlush(); - } - - private scheduleBatchFlush(): void { - if (this.batchTimer) return; - - this.batchTimer = setTimeout(() => { - this.flushAllQueues(); - this.batchTimer = null; - }, this.batchInterval); - } - - private async flushAllQueues(): Promise { - const flushPromises: Promise[] = []; - - for (const userId of this.eventQueue.keys()) { - flushPromises.push(this.flushUserQueue(userId)); - } - - await Promise.all(flushPromises); - } - - private async flushUserQueue(userId: string): Promise { - const queue = this.eventQueue.get(userId); - if (!queue || queue.length === 0) return; - - const events = queue.splice(0); - - // Send batched events with compression - const batchedEvent = { - type: 'BATCH_UPDATE', - events, - count: events.length, - timestamp: Date.now() - }; - - await this.sendToUser(userId, batchedEvent); - } - - private async sendToUser(userId: string, event: any): Promise { - // Implementation would send to actual WebSocket connection - // This is a placeholder for the optimization logic - logger.debug('Broadcasting optimized event', { - userId, - eventType: event.type, - eventCount: event.count || 1 - }); - } - - private generateBatchId(): string { - return Date.now().toString(36) + Math.random().toString(36).substring(2); - } -}`; - - await new Promise(resolve => setTimeout(resolve, 150)); - } - - private async implementEventBatching(): Promise { - logger.info('Implementing Event Batching for Efficiency'); - await new Promise(resolve => setTimeout(resolve, 100)); - } - - private async implementCircuitBreaker(): Promise { - logger.info('Implementing Circuit Breaker Pattern'); - - const circuitBreakerPattern = ` -/** - * Circuit Breaker Pattern Implementation - * Prevents cascading failures in external API calls - */ - -export enum CircuitState { - CLOSED = 'CLOSED', - OPEN = 'OPEN', - HALF_OPEN = 'HALF_OPEN' -} - -export class CircuitBreaker { - private state: CircuitState = CircuitState.CLOSED; - private failureCount = 0; - private lastFailureTime = 0; - private successCount = 0; - - constructor( - private failureThreshold: number = 5, - private timeoutDuration: number = 60000, // 1 minute - private monitoringPeriod: number = 30000 // 30 seconds - ) {} - - async execute(operation: () => Promise): Promise { - if (this.state === CircuitState.OPEN) { - if (Date.now() - this.lastFailureTime >= this.timeoutDuration) { - this.state = CircuitState.HALF_OPEN; - this.successCount = 0; - } else { - throw new Error('Circuit breaker is OPEN - operation not allowed'); - } - } - - try { - const result = await operation(); - this.onSuccess(); - return result; - } catch (error) { - this.onFailure(); - throw error; - } - } - - private onSuccess(): void { - this.failureCount = 0; - - if (this.state === CircuitState.HALF_OPEN) { - this.successCount++; - if (this.successCount >= 3) { // Require 3 successes to close - this.state = CircuitState.CLOSED; - } - } - } - - private onFailure(): void { - this.failureCount++; - this.lastFailureTime = Date.now(); - - if (this.failureCount >= this.failureThreshold) { - this.state = CircuitState.OPEN; - } - } - - getState(): CircuitState { - return this.state; - } - - getFailureCount(): number { - return this.failureCount; - } -}`; - - await new Promise(resolve => setTimeout(resolve, 120)); - } - - private async implementEnhancedRetryLogic(): Promise { - logger.info('Implementing Enhanced Retry Logic'); - await new Promise(resolve => setTimeout(resolve, 100)); - } - - // LOW PRIORITY IMPLEMENTATIONS - - private async implementPerformanceMonitoring(): Promise { - logger.info('Implementing Enhanced Performance Monitoring'); - await new Promise(resolve => setTimeout(resolve, 80)); - } - - private async implementSecurityHardening(): Promise { - logger.info('Implementing Additional Security Hardening'); - await new Promise(resolve => setTimeout(resolve, 80)); - } - - // VALIDATION METHODS - - private async validateS3Integration(): Promise { - // Mock validation - in real implementation would test S3 connection - return true; - } - - private async validateFileUploadWorkflow(): Promise { - // Mock validation - would test complete upload workflow - return true; - } - - private async validateAudioProcessingPerformance(): Promise { - // Mock validation - would benchmark audio processing speed - return true; - } - - private async validateAIResponseCaching(): Promise { - // Mock validation - would test cache hit rates - return true; - } - - private async validateConnectionPoolPerformance(): Promise { - return true; - } - - private async validateWebSocketPerformance(): Promise { - return true; - } - - private async validateEventBatching(): Promise { - return true; - } - - private async validateCircuitBreaker(): Promise { - return true; - } - - private async validateRetryLogic(): Promise { - return true; - } - - private async validatePerformanceMonitoring(): Promise { - return true; - } - - private async validateSecurityHardening(): Promise { - return true; - } -} - -// Export for use in other scripts -export { Phase2Optimizer }; - -// Run optimization if called directly -if (require.main === module) { - const optimizer = new Phase2Optimizer(); - optimizer.runAllOptimizations() - .then(results => { - console.log('\n🚀 Phase 2 Optimization Complete'); - console.log(`Tasks: ${results.completed}/${results.totalTasks} completed`); - console.log(`Total Impact: ${results.totalImpact}/100`); - - if (results.failed > 0) { - console.log(`⚠️ ${results.failed} tasks failed - review logs for details`); - process.exit(1); - } else { - console.log('✅ All optimizations completed successfully'); - process.exit(0); - } - }) - .catch(error => { - console.error('Optimization failed:', error); - process.exit(1); - }); -} \ No newline at end of file diff --git a/backend/src/scripts/seed-channels.ts b/backend/src/scripts/seed-channels.ts deleted file mode 100644 index 008ef8e..0000000 --- a/backend/src/scripts/seed-channels.ts +++ /dev/null @@ -1,373 +0,0 @@ -import { query, initializeDatabase } from '../config/database'; -import { logger } from '../utils/logger'; -import ChannelRepository from '../db/ChannelRepository'; - -const channelRepository = new ChannelRepository(); - -interface SeedChannel { - name: string; - description: string; - channel_type: 'project' | 'department' | 'initiative' | 'temporary' | 'emergency' | 'announcement'; - privacy_level: 'public' | 'private' | 'restricted'; - created_by: string; - members: string[]; - moderators: string[]; - settings: Record; - project_info: Record; -} - -/** - * Seed channels for development and testing - */ -export async function seedChannels() { - try { - logger.info('Starting channel seeding...'); - - // Initialize database connection - await initializeDatabase(); - - // Test database connection - const testResult = await query('SELECT NOW()'); - logger.info('Database connection test:', testResult.rows[0]); - - // Check if any channels exist - const existingChannels = await query('SELECT COUNT(*) as count FROM channels WHERE deleted_at IS NULL'); - const channelCount = parseInt(existingChannels.rows[0].count, 10); - - if (channelCount > 0) { - logger.info(`Found ${channelCount} existing channels, skipping seed`); - return; - } - - // Get users to assign as channel owners and members - const usersResult = await query('SELECT id, email, role FROM users WHERE deleted_at IS NULL ORDER BY created_at ASC LIMIT 10'); - const users = usersResult.rows; - - if (users.length === 0) { - logger.warn('No users found, cannot seed channels without users'); - return; - } - - // Find admin/CEO user for some channels - const adminUser = users.find(u => u.role === 'ceo') || users[0]; - const managerUsers = users.filter(u => u.role === 'manager') || users.slice(0, 2); - const staffUsers = users.filter(u => u.role === 'staff') || users; - - const seedChannels: SeedChannel[] = [ - { - name: 'General Discussion', - description: 'Main channel for general company discussions, announcements, and team updates.', - channel_type: 'announcement', - privacy_level: 'public', - created_by: adminUser.id, - members: users.map(u => u.id), - moderators: [adminUser.id, ...managerUsers.slice(0, 2).map(u => u.id)], - settings: { - allow_voice_commands: true, - voice_command_roles: ['ceo', 'manager'], - allow_file_uploads: true, - allow_external_sharing: false, - message_retention_days: 365, - require_approval_for_join: false, - notification_level: 'all', - read_receipts_enabled: true, - typing_indicators_enabled: true, - thread_replies_enabled: true, - message_reactions_enabled: true, - voice_transcription_enabled: true, - }, - project_info: { - priority: 'high', - tags: ['general', 'announcements', 'company-wide'], - stakeholders: users.map(u => u.id), - }, - }, - { - name: 'Project Alpha Development', - description: 'Development discussions, progress updates, and technical coordination for Project Alpha.', - channel_type: 'project', - privacy_level: 'private', - created_by: managerUsers[0]?.id || adminUser.id, - members: [adminUser.id, ...managerUsers.slice(0, 2).map(u => u.id), ...staffUsers.slice(0, 4).map(u => u.id)], - moderators: [managerUsers[0]?.id || adminUser.id, adminUser.id], - settings: { - allow_voice_commands: true, - voice_command_roles: ['ceo', 'manager'], - allow_file_uploads: true, - allow_external_sharing: false, - message_retention_days: 90, - require_approval_for_join: true, - notification_level: 'all', - read_receipts_enabled: true, - typing_indicators_enabled: true, - thread_replies_enabled: true, - message_reactions_enabled: true, - voice_transcription_enabled: true, - }, - project_info: { - start_date: new Date().toISOString(), - end_date: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(), // 90 days from now - priority: 'high', - tags: ['development', 'project-alpha', 'backend', 'frontend'], - milestones: [ - { - name: 'MVP Complete', - date: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - { - name: 'Beta Testing', - date: new Date(Date.now() + 60 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - { - name: 'Production Release', - date: new Date(Date.now() + 90 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - ], - deliverables: [ - 'API Documentation', - 'Frontend Application', - 'Backend Services', - 'Testing Suite', - 'Deployment Pipeline', - ], - stakeholders: [adminUser.id, ...managerUsers.slice(0, 2).map(u => u.id)], - }, - }, - { - name: 'Marketing Team', - description: 'Marketing strategies, campaigns, content planning, and brand discussions.', - channel_type: 'department', - privacy_level: 'restricted', - created_by: managerUsers[1]?.id || adminUser.id, - members: [adminUser.id, managerUsers[1]?.id || adminUser.id, ...staffUsers.slice(2, 5).map(u => u.id)], - moderators: [managerUsers[1]?.id || adminUser.id], - settings: { - allow_voice_commands: true, - voice_command_roles: ['ceo', 'manager'], - allow_file_uploads: true, - allow_external_sharing: true, - message_retention_days: 180, - require_approval_for_join: true, - notification_level: 'mentions', - read_receipts_enabled: false, - typing_indicators_enabled: true, - thread_replies_enabled: true, - message_reactions_enabled: true, - voice_transcription_enabled: false, - }, - project_info: { - priority: 'medium', - tags: ['marketing', 'campaigns', 'content', 'social-media'], - stakeholders: [adminUser.id, managerUsers[1]?.id || adminUser.id], - }, - }, - { - name: 'Design System', - description: 'UI/UX design discussions, component library updates, and design system maintenance.', - channel_type: 'initiative', - privacy_level: 'public', - created_by: staffUsers[0]?.id || adminUser.id, - members: [adminUser.id, ...staffUsers.slice(0, 3).map(u => u.id)], - moderators: [staffUsers[0]?.id || adminUser.id], - settings: { - allow_voice_commands: false, - voice_command_roles: [], - allow_file_uploads: true, - allow_external_sharing: false, - message_retention_days: 60, - require_approval_for_join: false, - notification_level: 'mentions', - read_receipts_enabled: false, - typing_indicators_enabled: true, - thread_replies_enabled: true, - message_reactions_enabled: true, - voice_transcription_enabled: false, - }, - project_info: { - priority: 'medium', - tags: ['design', 'ui', 'ux', 'components', 'style-guide'], - deliverables: [ - 'Component Library', - 'Design Tokens', - 'Usage Guidelines', - 'Figma Templates', - ], - stakeholders: [staffUsers[0]?.id || adminUser.id], - }, - }, - { - name: 'Random & Fun', - description: 'Casual conversations, memes, team bonding, and non-work related discussions.', - channel_type: 'temporary', - privacy_level: 'public', - created_by: staffUsers[1]?.id || adminUser.id, - members: users.map(u => u.id), - moderators: [staffUsers[1]?.id || adminUser.id], - settings: { - allow_voice_commands: false, - voice_command_roles: [], - allow_file_uploads: true, - allow_external_sharing: false, - message_retention_days: 30, - require_approval_for_join: false, - notification_level: 'none', - read_receipts_enabled: false, - typing_indicators_enabled: true, - thread_replies_enabled: false, - message_reactions_enabled: true, - voice_transcription_enabled: false, - }, - project_info: { - priority: 'low', - tags: ['social', 'fun', 'casual', 'team-bonding'], - stakeholders: [staffUsers[1]?.id || adminUser.id], - }, - }, - { - name: 'Q4 Planning Initiative', - description: 'Strategic planning, goal setting, and resource allocation for Q4 objectives.', - channel_type: 'initiative', - privacy_level: 'restricted', - created_by: adminUser.id, - members: [adminUser.id, ...managerUsers.map(u => u.id)], - moderators: [adminUser.id], - settings: { - allow_voice_commands: true, - voice_command_roles: ['ceo', 'manager'], - allow_file_uploads: true, - allow_external_sharing: false, - message_retention_days: 120, - require_approval_for_join: true, - notification_level: 'all', - read_receipts_enabled: true, - typing_indicators_enabled: true, - thread_replies_enabled: true, - message_reactions_enabled: false, - voice_transcription_enabled: true, - }, - project_info: { - start_date: new Date().toISOString(), - end_date: new Date(Date.now() + 120 * 24 * 60 * 60 * 1000).toISOString(), // 120 days from now - priority: 'high', - tags: ['planning', 'strategy', 'quarterly', 'goals', 'leadership'], - milestones: [ - { - name: 'Goal Setting Complete', - date: new Date(Date.now() + 14 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - { - name: 'Resource Allocation', - date: new Date(Date.now() + 28 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - { - name: 'Implementation Begin', - date: new Date(Date.now() + 45 * 24 * 60 * 60 * 1000).toISOString(), - status: 'pending', - }, - ], - deliverables: [ - 'Q4 Strategic Plan', - 'Budget Allocation', - 'Team Assignments', - 'Success Metrics', - 'Timeline & Milestones', - ], - stakeholders: [adminUser.id, ...managerUsers.map(u => u.id)], - }, - }, - ]; - - // Create categories if they don't exist (skip if categories table doesn't exist) - try { - const categoriesResult = await query('SELECT COUNT(*) as count FROM categories WHERE deleted_at IS NULL'); - const categoryCount = parseInt(categoriesResult.rows[0].count, 10); - - if (categoryCount === 0) { - logger.info('Creating default categories...'); - - const defaultCategories = [ - { name: 'General', description: 'General discussions and announcements', color: '#6B7280' }, - { name: 'Projects', description: 'Project-specific channels', color: '#10B981' }, - { name: 'Departments', description: 'Department and team channels', color: '#3B82F6' }, - { name: 'Initiatives', description: 'Special initiatives and programs', color: '#8B5CF6' }, - { name: 'Social', description: 'Social and casual discussions', color: '#F59E0B' }, - ]; - - for (const category of defaultCategories) { - await query( - 'INSERT INTO categories (name, description, metadata) VALUES ($1, $2, $3)', - [category.name, category.description, JSON.stringify({ color: category.color })] - ); - } - - logger.info(`Created ${defaultCategories.length} categories`); - } - } catch (error) { - logger.warn('Categories table may not exist, skipping category creation'); - } - - // Create channels - let createdCount = 0; - for (const channelData of seedChannels) { - try { - const channel = await channelRepository.createChannel({ - name: channelData.name, - description: channelData.description, - channel_type: channelData.channel_type, - privacy_level: channelData.privacy_level, - created_by: channelData.created_by, - members: channelData.members, - moderators: channelData.moderators, - settings: channelData.settings, - project_info: channelData.project_info, - }); - - // Add some initial activity stats - await query( - `UPDATE channels SET activity_stats = $1 WHERE id = $2`, - [ - JSON.stringify({ - total_messages: Math.floor(Math.random() * 50) + 1, - total_files: Math.floor(Math.random() * 10), - total_tasks: Math.floor(Math.random() * 5), - last_activity: new Date(Date.now() - Math.random() * 7 * 24 * 60 * 60 * 1000).toISOString(), - most_active_user: channelData.members[Math.floor(Math.random() * channelData.members.length)], - average_response_time: Math.floor(Math.random() * 120) + 30, // 30-150 minutes - peak_activity_hours: ['09:00', '10:00', '14:00', '15:00'], - }), - channel.id, - ] - ); - - logger.info(`Created channel: ${channel.name} (${channel.id})`); - createdCount++; - } catch (error) { - logger.error(`Failed to create channel ${channelData.name}:`, error); - } - } - - logger.info(`Channel seeding completed: ${createdCount}/${seedChannels.length} channels created`); - } catch (error) { - logger.error('Channel seeding failed:', error); - console.error('Full error details:', error); - throw error; - } -} - -// Run if called directly -if (require.main === module) { - seedChannels() - .then(() => { - logger.info('Channel seeding script completed successfully'); - process.exit(0); - }) - .catch((error) => { - logger.error('Channel seeding script failed:', error); - process.exit(1); - }); -} \ No newline at end of file diff --git a/backend/src/scripts/seed.ts b/backend/src/scripts/seed.ts deleted file mode 100644 index b042fca..0000000 --- a/backend/src/scripts/seed.ts +++ /dev/null @@ -1,846 +0,0 @@ -import { Pool } from 'pg'; -import { config } from '../config'; -import { logger } from '../utils/logger'; -import bcrypt from 'bcryptjs'; -import { faker } from '@faker-js/faker'; - -const pool = new Pool({ - connectionString: config.database.url, - min: config.database.pool.min, - max: config.database.pool.max, - ssl: config.app.isProduction || config.database.url.includes('rds.amazonaws.com') - ? { - rejectUnauthorized: false, - } - : false, -}); - -interface SeedUser { - id: string; - email: string; - name: string; - password_hash: string; - role: 'ceo' | 'manager' | 'staff'; - department: string; - job_title: string; - avatar_url?: string; - phone?: string; -} - -interface SeedChannel { - id: string; - name: string; - description: string; - channel_type: 'project' | 'department' | 'initiative' | 'temporary' | 'emergency' | 'announcement'; - privacy_level: 'public' | 'private' | 'restricted'; - created_by: string; - owned_by: string; - members: string[]; - moderators: string[]; -} - -interface SeedTask { - id: string; - title: string; - description: string; - channel_id: string; - created_by: string; - assigned_to: string[]; - owned_by: string; - priority: 'low' | 'medium' | 'high' | 'urgent' | 'critical'; - status: 'pending' | 'in_progress' | 'review' | 'completed' | 'cancelled' | 'on_hold'; - task_type: 'general' | 'project' | 'maintenance' | 'emergency' | 'research' | 'approval'; - complexity: number; - estimated_hours: number; - actual_hours: number; - progress_percentage: number; - due_date?: Date; - tags: string[]; -} - -export class DatabaseSeeder { - private users: SeedUser[] = []; - private channels: SeedChannel[] = []; - private tasks: SeedTask[] = []; - - async seed(): Promise { - try { - logger.info('Starting database seeding...'); - - // Clear existing data (in reverse dependency order) - await this.clearData(); - - // Create seed data - await this.createUsers(); - await this.createChannels(); - await this.createTasks(); - await this.createMessages(); - await this.createActivities(); - - logger.info('Database seeding completed successfully'); - } catch (error) { - logger.error('Error seeding database:', error); - console.error('Full error details:', error); - throw error; - } - } - - private async clearData(): Promise { - const client = await pool.connect(); - try { - await client.query('BEGIN'); - - // Clear in dependency order - await client.query('DELETE FROM activities WHERE created_at > NOW() - INTERVAL \'1 day\''); - await client.query('DELETE FROM messages WHERE created_at > NOW() - INTERVAL \'1 day\''); - await client.query('DELETE FROM task_assignment_history WHERE performed_at > NOW() - INTERVAL \'1 day\''); - await client.query('DELETE FROM tasks WHERE created_at > NOW() - INTERVAL \'1 day\''); - await client.query('DELETE FROM channel_member_history WHERE performed_at > NOW() - INTERVAL \'1 day\''); - await client.query('DELETE FROM channels WHERE created_at > NOW() - INTERVAL \'1 day\''); - // Delete all CEO users and seed data users - await client.query('DELETE FROM users WHERE role = \'ceo\' OR (created_at > NOW() - INTERVAL \'1 day\' AND (email LIKE \'%@seeddata.com\' OR email LIKE \'%@company.com\'))'); - - await client.query('COMMIT'); - logger.info('Cleared existing seed data'); - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } - - private async createUsers(): Promise { - const client = await pool.connect(); - - const predefinedUsers = [ - { - email: 'alex.ceo@company.com', - name: 'Alexander Mitchell', - role: 'ceo' as const, - department: 'Executive', - job_title: 'Chief Executive Officer', - avatar_url: 'https://images.unsplash.com/photo-1560250097-0b93528c311a?w=400&h=400&fit=crop&crop=face', - }, - { - email: 'sarah.manager@seeddata.com', - name: 'Sarah Chen', - role: 'manager' as const, - department: 'Engineering', - job_title: 'Engineering Manager', - avatar_url: 'https://images.unsplash.com/photo-1494790108755-2616b36c7e9d?w=400&h=400&fit=crop&crop=face', - }, - { - email: 'mike.manager@seeddata.com', - name: 'Michael Rodriguez', - role: 'manager' as const, - department: 'Product', - job_title: 'Product Manager', - avatar_url: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=400&h=400&fit=crop&crop=face', - }, - { - email: 'lisa.manager@seeddata.com', - name: 'Lisa Thompson', - role: 'manager' as const, - department: 'Marketing', - job_title: 'Marketing Director', - avatar_url: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=400&h=400&fit=crop&crop=face', - }, - ]; - - const passwordHash = await bcrypt.hash('TempPass123!', 12); - - try { - await client.query('BEGIN'); - - // Create predefined users - for (const userData of predefinedUsers) { - const userId = faker.string.uuid(); - - const result = await client.query(` - INSERT INTO users (id, email, name, password_hash, role, department, job_title, avatar_url, phone, timezone, language_preference, email_verified, last_active, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, true, NOW(), NOW(), NOW()) - ON CONFLICT (email) DO UPDATE SET - name = EXCLUDED.name, - password_hash = EXCLUDED.password_hash, - role = EXCLUDED.role, - department = EXCLUDED.department, - job_title = EXCLUDED.job_title, - avatar_url = EXCLUDED.avatar_url, - phone = EXCLUDED.phone, - timezone = EXCLUDED.timezone, - language_preference = EXCLUDED.language_preference, - email_verified = true, - last_active = NOW(), - updated_at = NOW() - RETURNING id, email, name, role, department, job_title, avatar_url, phone, timezone, language_preference - `, [userId, userData.email, userData.name, passwordHash, userData.role, userData.department, userData.job_title, userData.avatar_url, faker.phone.number(), 'America/New_York', 'en']); - - const createdUser = result.rows[0]; - const user: SeedUser = { - id: createdUser.id, - email: createdUser.email, - name: createdUser.name, - password_hash: passwordHash, - role: createdUser.role, - department: createdUser.department, - job_title: createdUser.job_title, - avatar_url: createdUser.avatar_url, - phone: createdUser.phone, - }; - - this.users.push(user); - } - - // Create additional staff members - const departments = ['Engineering', 'Product', 'Marketing', 'Sales', 'Support', 'Finance', 'HR']; - const jobTitles = { - 'Engineering': ['Senior Developer', 'DevOps Engineer', 'QA Engineer', 'Frontend Developer', 'Backend Developer'], - 'Product': ['Product Analyst', 'UX Designer', 'Product Owner', 'Business Analyst'], - 'Marketing': ['Content Manager', 'Social Media Manager', 'SEO Specialist', 'Brand Manager'], - 'Sales': ['Account Executive', 'Sales Representative', 'Business Development', 'Customer Success'], - 'Support': ['Technical Support', 'Customer Support', 'Documentation Specialist'], - 'Finance': ['Financial Analyst', 'Accountant', 'Controller'], - 'HR': ['HR Specialist', 'Recruiter', 'People Operations'] - }; - - for (let i = 0; i < 15; i++) { - const department = faker.helpers.arrayElement(departments); - const jobTitle = faker.helpers.arrayElement(jobTitles[department as keyof typeof jobTitles]); - - const userId = faker.string.uuid(); - const email = `${faker.internet.userName().toLowerCase()}@seeddata.com`; - const name = faker.person.fullName(); - const avatarUrl = faker.image.avatar(); - const phone = faker.phone.number(); - - const timezones = ['America/New_York', 'America/Los_Angeles', 'America/Chicago', 'Europe/London', 'Asia/Tokyo']; - const timezone = faker.helpers.arrayElement(timezones); - - const result = await client.query(` - INSERT INTO users (id, email, name, password_hash, role, department, job_title, avatar_url, phone, timezone, language_preference, email_verified, last_active, created_at, updated_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, true, NOW() - INTERVAL '${faker.number.int({min: 1, max: 7})} days', NOW() - INTERVAL '${faker.number.int({min: 1, max: 30})} days', NOW() - INTERVAL '${faker.number.int({min: 1, max: 15})} days') - ON CONFLICT (email) DO UPDATE SET - name = EXCLUDED.name, - password_hash = EXCLUDED.password_hash, - role = EXCLUDED.role, - department = EXCLUDED.department, - job_title = EXCLUDED.job_title, - avatar_url = EXCLUDED.avatar_url, - phone = EXCLUDED.phone, - timezone = EXCLUDED.timezone, - language_preference = EXCLUDED.language_preference, - email_verified = true, - last_active = NOW() - INTERVAL '1 day', - updated_at = NOW() - RETURNING id, email, name, role, department, job_title, avatar_url, phone, timezone, language_preference - `, [userId, email, name, passwordHash, 'staff', department, jobTitle, avatarUrl, phone, timezone, 'en']); - - const createdUser = result.rows[0]; - const user: SeedUser = { - id: createdUser.id, - email: createdUser.email, - name: createdUser.name, - password_hash: passwordHash, - role: createdUser.role, - department: createdUser.department, - job_title: createdUser.job_title, - avatar_url: createdUser.avatar_url, - phone: createdUser.phone, - }; - - this.users.push(user); - } - - await client.query('COMMIT'); - logger.info(`Created ${this.users.length} seed users`); - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } - - private async createChannels(): Promise { - const client = await pool.connect(); - - const ceo = this.users.find(u => u.role === 'ceo')!; - const managers = this.users.filter(u => u.role === 'manager'); - const staff = this.users.filter(u => u.role === 'staff'); - - const channelTemplates = [ - { - name: 'Product Development', - description: 'Main channel for product development discussions, roadmap planning, and feature releases', - channel_type: 'project' as const, - privacy_level: 'public' as const, - created_by: ceo.id, - owned_by: managers.find(m => m.department === 'Product')?.id || ceo.id, - members: [ceo.id, managers.find(m => m.department === 'Product')?.id, ...managers.map(m => m.id), ...staff.filter(s => ['Engineering', 'Product'].includes(s.department)).map(s => s.id)].filter(Boolean) as string[], - moderators: [ceo.id, managers.find(m => m.department === 'Product')?.id].filter(Boolean) as string[], - }, - { - name: 'Engineering Team', - description: 'Technical discussions, architecture decisions, and development coordination', - channel_type: 'department' as const, - privacy_level: 'private' as const, - created_by: managers.find(m => m.department === 'Engineering')?.id || ceo.id, - owned_by: managers.find(m => m.department === 'Engineering')?.id || ceo.id, - members: [ceo.id, ...staff.filter(s => s.department === 'Engineering').map(s => s.id), managers.find(m => m.department === 'Engineering')?.id].filter(Boolean) as string[], - moderators: [managers.find(m => m.department === 'Engineering')?.id].filter(Boolean) as string[], - }, - { - name: 'Marketing Campaigns', - description: 'Campaign planning, content creation, and marketing strategy discussions', - channel_type: 'department' as const, - privacy_level: 'public' as const, - created_by: managers.find(m => m.department === 'Marketing')?.id || ceo.id, - owned_by: managers.find(m => m.department === 'Marketing')?.id || ceo.id, - members: [ceo.id, ...staff.filter(s => s.department === 'Marketing').map(s => s.id), managers.find(m => m.department === 'Marketing')?.id].filter(Boolean) as string[], - moderators: [managers.find(m => m.department === 'Marketing')?.id].filter(Boolean) as string[], - }, - { - name: 'Executive Announcements', - description: 'Important company-wide announcements and strategic updates from leadership', - channel_type: 'announcement' as const, - privacy_level: 'public' as const, - created_by: ceo.id, - owned_by: ceo.id, - members: this.users.map(u => u.id), - moderators: [ceo.id, ...managers.map(m => m.id)], - }, - { - name: 'Q4 2024 Launch', - description: 'Coordination for the major Q4 product launch - cross-functional collaboration', - channel_type: 'initiative' as const, - privacy_level: 'restricted' as const, - created_by: ceo.id, - owned_by: managers.find(m => m.department === 'Product')?.id || ceo.id, - members: [ceo.id, managers.find(m => m.department === 'Product')?.id, ...managers.map(m => m.id), ...staff.filter(s => ['Engineering', 'Product', 'Marketing'].includes(s.department)).slice(0, 8).map(s => s.id)].filter(Boolean) as string[], - moderators: [ceo.id, managers.find(m => m.department === 'Product')?.id].filter(Boolean) as string[], - }, - { - name: 'Mobile App Beta', - description: 'Beta testing coordination and feedback collection for the new mobile application', - channel_type: 'project' as const, - privacy_level: 'private' as const, - created_by: managers.find(m => m.department === 'Product')?.id || ceo.id, - owned_by: managers.find(m => m.department === 'Product')?.id || ceo.id, - members: [ceo.id, managers.find(m => m.department === 'Product')?.id, ...staff.filter(s => ['Engineering', 'Product', 'Support'].includes(s.department)).slice(0, 6).map(s => s.id)].filter(Boolean) as string[], - moderators: [managers.find(m => m.department === 'Product')?.id].filter(Boolean) as string[], - }, - ]; - - try { - await client.query('BEGIN'); - - for (const channelData of channelTemplates) { - const channelId = faker.string.uuid(); - const channel: SeedChannel = { - id: channelId, - ...channelData, - }; - - await client.query(` - INSERT INTO channels (id, name, description, channel_type, privacy_level, created_by, owned_by, members, moderators, member_count, created_at, last_activity_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, NOW() - INTERVAL '${faker.number.int({min: 5, max: 25})} days', NOW() - INTERVAL '${faker.number.int({min: 1, max: 8})} hours') - `, [ - channel.id, - channel.name, - channel.description, - channel.channel_type, - channel.privacy_level, - channel.created_by, - channel.owned_by, - channel.members, - channel.moderators, - channel.members.length, - ]); - - this.channels.push(channel); - } - - await client.query('COMMIT'); - logger.info(`Created ${this.channels.length} seed channels`); - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } - - private async createTasks(): Promise { - const client = await pool.connect(); - - const taskTemplates = [ - { - title: 'Implement Two-Factor Authentication', - description: 'Add 2FA support for all user accounts to enhance security. Include SMS, email, and authenticator app options.', - priority: 'high' as const, - status: 'in_progress' as const, - task_type: 'project' as const, - complexity: 7, - estimated_hours: 40, - actual_hours: 28, - progress_percentage: 70, - tags: ['security', 'authentication', 'mobile'], - }, - { - title: 'Fix Payment Processing Bug', - description: 'Critical bug in payment processing causing failed transactions for amounts over $1000. Customer complaints increasing.', - priority: 'critical' as const, - status: 'pending' as const, - task_type: 'emergency' as const, - complexity: 8, - estimated_hours: 16, - actual_hours: 0, - progress_percentage: 0, - tags: ['bug', 'payments', 'critical'], - }, - { - title: 'Mobile App Performance Optimization', - description: 'Optimize mobile app loading times and reduce memory usage. Target 50% improvement in startup time.', - priority: 'medium' as const, - status: 'review' as const, - task_type: 'maintenance' as const, - complexity: 6, - estimated_hours: 32, - actual_hours: 35, - progress_percentage: 95, - tags: ['mobile', 'performance', 'optimization'], - }, - { - title: 'Q4 Marketing Campaign Planning', - description: 'Develop comprehensive marketing strategy for Q4 launch including social media, content marketing, and paid advertising.', - priority: 'high' as const, - status: 'in_progress' as const, - task_type: 'project' as const, - complexity: 5, - estimated_hours: 60, - actual_hours: 22, - progress_percentage: 40, - tags: ['marketing', 'campaign', 'q4-launch'], - }, - { - title: 'Database Migration to PostgreSQL 15', - description: 'Upgrade database to PostgreSQL 15 for better performance and new features. Plan for zero-downtime migration.', - priority: 'medium' as const, - status: 'pending' as const, - task_type: 'maintenance' as const, - complexity: 9, - estimated_hours: 48, - actual_hours: 0, - progress_percentage: 0, - tags: ['database', 'migration', 'infrastructure'], - }, - { - title: 'Customer Support Dashboard', - description: 'Build new dashboard for customer support team with ticket analytics, response time tracking, and customer satisfaction metrics.', - priority: 'medium' as const, - status: 'in_progress' as const, - task_type: 'project' as const, - complexity: 6, - estimated_hours: 45, - actual_hours: 18, - progress_percentage: 35, - tags: ['dashboard', 'support', 'analytics'], - }, - { - title: 'API Rate Limiting Implementation', - description: 'Implement rate limiting across all API endpoints to prevent abuse and ensure fair usage.', - priority: 'high' as const, - status: 'completed' as const, - task_type: 'project' as const, - complexity: 4, - estimated_hours: 24, - actual_hours: 26, - progress_percentage: 100, - tags: ['api', 'security', 'rate-limiting'], - }, - { - title: 'User Onboarding Flow Redesign', - description: 'Redesign user onboarding to improve conversion rates and reduce drop-off. A/B testing required.', - priority: 'high' as const, - status: 'review' as const, - task_type: 'project' as const, - complexity: 7, - estimated_hours: 55, - actual_hours: 52, - progress_percentage: 90, - tags: ['ux', 'onboarding', 'conversion'], - }, - { - title: 'Automated Testing Suite Expansion', - description: 'Expand automated testing coverage to 90% for all critical user flows and API endpoints.', - priority: 'medium' as const, - status: 'in_progress' as const, - task_type: 'maintenance' as const, - complexity: 8, - estimated_hours: 38, - actual_hours: 15, - progress_percentage: 25, - tags: ['testing', 'automation', 'quality'], - }, - { - title: 'GDPR Compliance Audit', - description: 'Complete GDPR compliance audit and implement necessary changes for European data protection requirements.', - priority: 'urgent' as const, - status: 'on_hold' as const, - task_type: 'approval' as const, - complexity: 5, - estimated_hours: 30, - actual_hours: 8, - progress_percentage: 15, - tags: ['gdpr', 'compliance', 'legal'], - }, - ]; - - try { - await client.query('BEGIN'); - - for (let i = 0; i < taskTemplates.length; i++) { - const template = taskTemplates[i]!; - const channel = faker.helpers.arrayElement(this.channels); - const creator = faker.helpers.arrayElement(channel.members); - const assignees = faker.helpers.arrayElements(channel.members, { min: 1, max: 3 }); - const owner = assignees[0]!; - - const taskId = faker.string.uuid(); - const task: SeedTask = { - id: taskId, - title: template.title, - description: template.description, - channel_id: channel.id, - created_by: creator, - assigned_to: assignees, - owned_by: owner, - priority: template.priority, - status: template.status, - task_type: template.task_type, - complexity: template.complexity, - estimated_hours: template.estimated_hours, - actual_hours: template.actual_hours, - progress_percentage: template.progress_percentage, - tags: template.tags, - due_date: faker.date.future({ days: faker.number.int({ min: 7, max: 60 }) }), - }; - - await client.query(` - INSERT INTO tasks ( - id, title, description, channel_id, created_by, assigned_to, owned_by, - priority, status, task_type, complexity, estimated_hours, actual_hours, - progress_percentage, due_date, tags, created_at, last_activity_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, - NOW() - INTERVAL '${faker.number.int({min: 1, max: 15})} days', - NOW() - INTERVAL '${faker.number.int({min: 1, max: 24})} hours') - `, [ - task.id, task.title, task.description, task.channel_id, task.created_by, - task.assigned_to, task.owned_by, task.priority, task.status, task.task_type, - task.complexity, task.estimated_hours, task.actual_hours, task.progress_percentage, - task.due_date, task.tags - ]); - - this.tasks.push(task); - } - - // Create additional random tasks - for (let i = 0; i < 15; i++) { - const channel = faker.helpers.arrayElement(this.channels); - const creator = faker.helpers.arrayElement(channel.members); - const assignees = faker.helpers.arrayElements(channel.members, { min: 1, max: 2 }); - const owner = assignees[0]!; - - const taskId = faker.string.uuid(); - const priorities = ['low', 'medium', 'high', 'urgent'] as const; - const statuses = ['pending', 'in_progress', 'review', 'completed', 'on_hold'] as const; - const types = ['general', 'project', 'maintenance', 'research'] as const; - const tags = ['frontend', 'backend', 'mobile', 'api', 'ui/ux', 'security', 'performance', 'testing', 'documentation']; - - const task: SeedTask = { - id: taskId, - title: faker.company.catchPhrase(), - description: faker.lorem.sentences(3), - channel_id: channel.id, - created_by: creator, - assigned_to: assignees, - owned_by: owner, - priority: faker.helpers.arrayElement(priorities), - status: faker.helpers.arrayElement(statuses), - task_type: faker.helpers.arrayElement(types), - complexity: faker.number.int({ min: 1, max: 10 }), - estimated_hours: faker.number.int({ min: 4, max: 80 }), - actual_hours: faker.number.int({ min: 0, max: 60 }), - progress_percentage: faker.number.int({ min: 0, max: 100 }), - tags: faker.helpers.arrayElements(tags, { min: 1, max: 3 }), - due_date: faker.helpers.maybe(() => faker.date.future({ days: faker.number.int({ min: 3, max: 90 }) }), { probability: 0.7 }), - }; - - await client.query(` - INSERT INTO tasks ( - id, title, description, channel_id, created_by, assigned_to, owned_by, - priority, status, task_type, complexity, estimated_hours, actual_hours, - progress_percentage, due_date, tags, created_at, last_activity_at - ) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, - NOW() - INTERVAL '${faker.number.int({min: 1, max: 20})} days', - NOW() - INTERVAL '${faker.number.int({min: 1, max: 48})} hours') - `, [ - task.id, task.title, task.description, task.channel_id, task.created_by, - task.assigned_to, task.owned_by, task.priority, task.status, task.task_type, - task.complexity, task.estimated_hours, task.actual_hours, task.progress_percentage, - task.due_date, task.tags - ]); - - this.tasks.push(task); - } - - await client.query('COMMIT'); - logger.info(`Created ${this.tasks.length} seed tasks`); - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } - - private async createMessages(): Promise { - const client = await pool.connect(); - - const messageTypes = ['text', 'file', 'voice', 'system'] as const; - const systemMessages = [ - 'Channel created', - 'User joined the channel', - 'Task created and assigned', - 'Task status updated', - 'File uploaded', - 'Meeting scheduled', - ]; - - try { - await client.query('BEGIN'); - - for (const channel of this.channels) { - const messageCount = faker.number.int({ min: 10, max: 40 }); - - for (let i = 0; i < messageCount; i++) { - const messageType = faker.helpers.arrayElement(messageTypes); - const sender = messageType === 'system' - ? this.users.find(u => u.role === 'ceo')?.id || faker.helpers.arrayElement(channel.members) - : faker.helpers.arrayElement(channel.members); - const messageId = faker.string.uuid(); - - let content = ''; - let metadata = {}; - - switch (messageType) { - case 'text': - content = faker.lorem.sentences(faker.number.int({ min: 1, max: 4 })); - break; - case 'file': - content = 'File shared: ' + faker.system.fileName(); - metadata = { - fileName: faker.system.fileName(), - fileSize: faker.number.int({ min: 1000, max: 5000000 }), - fileType: faker.helpers.arrayElement(['image/png', 'application/pdf', 'text/csv']), - }; - break; - case 'voice': - content = 'Voice message (transcription): ' + faker.lorem.sentences(2); - metadata = { - duration: faker.number.int({ min: 5, max: 120 }), - transcription: faker.lorem.sentences(2), - }; - break; - case 'system': - content = faker.helpers.arrayElement(systemMessages); - break; - } - - await client.query(` - INSERT INTO messages (id, channel_id, user_id, content, message_type, metadata, created_at) - VALUES ($1, $2, $3, $4, $5, $6, NOW() - INTERVAL '${faker.number.int({min: 1, max: 30})} days' + INTERVAL '${faker.number.int({min: 0, max: 23})} hours' + INTERVAL '${faker.number.int({min: 0, max: 59})} minutes') - `, [messageId, channel.id, sender, content, messageType, JSON.stringify(metadata)]); - } - } - - await client.query('COMMIT'); - logger.info('Created seed messages for all channels'); - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } - - private async createActivities(): Promise { - const client = await pool.connect(); - - const activityTypes = ['task_created', 'task_updated', 'task_completed', 'channel_created', 'user_joined', 'file_uploaded', 'message_sent', 'announcement'] as const; - - try { - await client.query('BEGIN'); - - // Create activities for tasks - for (const task of this.tasks) { - // Task creation activity - const createActivityId = faker.string.uuid(); - await client.query(` - INSERT INTO activities (id, user_id, activity_type, title, description, metadata, referenced_entity_id, channel_id, created_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW() - INTERVAL '${faker.number.int({min: 1, max: 15})} days') - `, [ - createActivityId, - task.created_by, - 'task_created', - `Created task: ${task.title}`, - `A new ${task.priority} priority task was created in ${this.channels.find(c => c.id === task.channel_id)?.name}`, - JSON.stringify({ - task_id: task.id, - priority: task.priority, - assigned_to: task.assigned_to, - }), - task.id, - task.channel_id, - ]); - - // Random task updates - if (task.status !== 'pending' && faker.datatype.boolean(0.7)) { - const updateActivityId = faker.string.uuid(); - await client.query(` - INSERT INTO activities (id, user_id, activity_type, title, description, metadata, referenced_entity_id, channel_id, created_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW() - INTERVAL '${faker.number.int({min: 1, max: 10})} days') - `, [ - updateActivityId, - task.owned_by, - 'task_updated', - `Updated task: ${task.title}`, - `Task status changed to ${task.status}. Progress: ${task.progress_percentage}%`, - JSON.stringify({ - task_id: task.id, - status: task.status, - progress: task.progress_percentage, - }), - task.id, - task.channel_id, - ]); - } - - // Task completion activities - if (task.status === 'completed') { - const completeActivityId = faker.string.uuid(); - await client.query(` - INSERT INTO activities (id, user_id, activity_type, title, description, metadata, referenced_entity_id, channel_id, created_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW() - INTERVAL '${faker.number.int({min: 1, max: 7})} days') - `, [ - completeActivityId, - task.owned_by, - 'task_completed', - `Completed task: ${task.title}`, - `Task was successfully completed ahead of schedule`, - JSON.stringify({ - task_id: task.id, - completion_time: task.actual_hours, - estimated_time: task.estimated_hours, - }), - task.id, - task.channel_id, - ]); - } - } - - // Create channel activities - for (const channel of this.channels) { - const createActivityId = faker.string.uuid(); - await client.query(` - INSERT INTO activities (id, user_id, activity_type, title, description, metadata, referenced_entity_id, channel_id, created_at) - VALUES ($1, $2, $3, $4, $5, $6, $7, $8, NOW() - INTERVAL '${faker.number.int({min: 5, max: 25})} days') - `, [ - createActivityId, - channel.created_by, - 'channel_created', - `Created channel: ${channel.name}`, - `New ${channel.channel_type} channel created for ${channel.description}`, - JSON.stringify({ - channel_id: channel.id, - channel_type: channel.channel_type, - privacy_level: channel.privacy_level, - }), - channel.id, - channel.id, - ]); - } - - // Create general announcements - const announcements = [ - { - title: 'Q4 All-Hands Meeting Scheduled', - description: 'Join us for the quarterly all-hands meeting next Friday at 2 PM. We\'ll be discussing Q3 results, Q4 goals, and exciting new product launches.', - metadata: { type: 'meeting', importance: 'high', attendees: 'all' }, - }, - { - title: 'New Security Policy Implementation', - description: 'Starting next week, all team members must enable two-factor authentication on their accounts. IT support is available for setup assistance.', - metadata: { type: 'policy', importance: 'high', deadline: '2024-01-15' }, - }, - { - title: 'Office Holiday Party', - description: 'You\'re invited to our annual holiday celebration! Join us for food, drinks, and team building activities on December 15th.', - metadata: { type: 'event', importance: 'medium', location: 'Main Office' }, - }, - { - title: 'System Maintenance Window', - description: 'Scheduled maintenance on our main servers will occur this Sunday from 2 AM to 6 AM. Some services may be temporarily unavailable.', - metadata: { type: 'maintenance', importance: 'medium', duration: '4 hours' }, - }, - ]; - - for (const announcement of announcements) { - const announcementId = faker.string.uuid(); - const announcer = this.users.find(u => u.role === 'ceo')?.id || faker.helpers.arrayElement(this.users.filter(u => u.role === 'manager')).id; - - await client.query(` - INSERT INTO activities (id, user_id, activity_type, title, description, metadata, created_at) - VALUES ($1, $2, $3, $4, $5, $6, NOW() - INTERVAL '${faker.number.int({min: 1, max: 14})} days') - `, [ - announcementId, - announcer, - 'ai_response', - announcement.title, - announcement.description, - JSON.stringify(announcement.metadata), - ]); - } - - await client.query('COMMIT'); - logger.info('Created comprehensive seed activities'); - } catch (error) { - await client.query('ROLLBACK'); - throw error; - } finally { - client.release(); - } - } -} - -// Self-executing function when run directly -async function runSeeder() { - const seeder = new DatabaseSeeder(); - try { - await seeder.seed(); - process.exit(0); - } catch (error) { - logger.error('Seeding failed:', error); - console.error('Full seeding error:', error); - process.exit(1); - } -} - -if (require.main === module) { - runSeeder(); -} - -export default DatabaseSeeder; \ No newline at end of file diff --git a/backend/src/scripts/verify-email-token.ts b/backend/src/scripts/verify-email-token.ts deleted file mode 100644 index e19d0c6..0000000 --- a/backend/src/scripts/verify-email-token.ts +++ /dev/null @@ -1,61 +0,0 @@ -#!/usr/bin/env tsx - -import { userRepository } from '@db/index'; -import { logger } from '@utils/logger'; - -/** - * Development script to manually verify email tokens - * Useful when emails are logged to console instead of sent - */ - -async function verifyEmailToken(token: string) { - try { - logger.info(`Attempting to verify email with token: ${token}`); - - const user = await userRepository.verifyEmail(token); - - if (user) { - logger.info({ - userId: user.id, - email: user.email, - name: user.name, - role: user.role - }, 'Email verified successfully! User can now log in.'); - - console.log('\n✅ EMAIL VERIFICATION SUCCESSFUL!'); - console.log(`User: ${user.name} (${user.email})`); - console.log(`Role: ${user.role}`); - console.log('The user can now log in to the application.\n'); - } else { - logger.warn('Invalid or expired verification token'); - console.log('\n❌ VERIFICATION FAILED!'); - console.log('Token is either invalid or expired.\n'); - } - - } catch (error) { - logger.error({ error, token }, 'Failed to verify email token'); - console.log('\n❌ VERIFICATION ERROR!'); - console.log(`Error: ${error instanceof Error ? error.message : 'Unknown error'}\n`); - } -} - -// Get token from command line argument -const token = process.argv[2]; - -if (!token) { - console.log('\n📧 Email Token Verification Tool'); - console.log('Usage: npx tsx src/scripts/verify-email-token.ts '); - console.log('\nExample:'); - console.log('npx tsx src/scripts/verify-email-token.ts eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...\n'); - process.exit(1); -} - -// Initialize database connection and verify token -import('@config/index').then(() => { - verifyEmailToken(token).then(() => { - process.exit(0); - }).catch((error) => { - console.error('Script failed:', error); - process.exit(1); - }); -}); \ No newline at end of file diff --git a/backend/src/security/SecurityManager.ts b/backend/src/security/SecurityManager.ts deleted file mode 100644 index 5e6b6c7..0000000 --- a/backend/src/security/SecurityManager.ts +++ /dev/null @@ -1,799 +0,0 @@ -/** - * Security Manager - Phase 2 Security & Authentication - * Comprehensive security layer for voice command processing - * - * Success Criteria: - * - Voice command authentication and authorization - * - Rate limiting and abuse prevention - * - Audit logging for security events - * - Input sanitization and validation - */ - -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import crypto from 'crypto'; -import jwt from 'jsonwebtoken'; -import bcrypt from 'bcryptjs'; -import { logger } from '../utils/logger'; - -export interface SecurityConfig { - jwtSecret: string; - jwtExpiresIn: string; - bcryptSaltRounds: number; - rateLimiting: { - windowMs: number; - maxRequests: number; - skipSuccessfulRequests: boolean; - }; - encryption: { - algorithm: string; - keyLength: number; - }; - auditLogging: { - enabled: boolean; - logLevel: 'all' | 'security' | 'critical'; - }; -} - -export interface AuthToken { - userId: string; - organizationId: string; - permissions: string[]; - sessionId: string; - issuedAt: number; - expiresAt: number; - deviceId?: string; - ipAddress?: string; -} - -export interface SecurityEvent { - eventId: string; - type: 'authentication' | 'authorization' | 'rate_limit' | 'validation' | 'suspicious_activity'; - severity: 'low' | 'medium' | 'high' | 'critical'; - userId?: string; - organizationId?: string; - action: string; - resource?: string; - ipAddress?: string; - userAgent?: string; - timestamp: string; - details: Record; -} - -export interface VoiceCommandSecurity { - commandId: string; - userId: string; - organizationId: string; - commandType: string; - permissions: string[]; - sensitiveDataMask: boolean; - approved: boolean; - reason?: string; -} - -export interface RateLimitResult { - allowed: boolean; - remaining: number; - resetTime: number; - retryAfter?: number; -} - -export class SecurityManager extends EventEmitter { - private config: SecurityConfig; - private securityEvents: SecurityEvent[] = []; - private rateLimits: Map = new Map(); - private suspiciousActivities: Map = new Map(); - private encryptionKey: Buffer; - private readonly maxAuditLogSize = 10000; - private cleanupTimer?: NodeJS.Timeout; - - constructor(config: SecurityConfig) { - super(); - - this.config = config; - this.encryptionKey = crypto.randomBytes(32); - - this.startCleanupTimer(); - - logger.info('Security Manager initialized', { - rateLimitWindow: `${config.rateLimiting.windowMs / 1000}s`, - maxRequests: config.rateLimiting.maxRequests, - auditLogging: config.auditLogging.enabled, - }); - } - - /** - * Authenticate user with JWT token - */ - async authenticateToken(token: string): Promise { - const startTime = performance.now(); - - try { - const decoded = jwt.verify(token, this.config.jwtSecret) as any; - - const authToken: AuthToken = { - userId: decoded.userId, - organizationId: decoded.organizationId, - permissions: decoded.permissions || [], - sessionId: decoded.sessionId, - issuedAt: decoded.iat * 1000, - expiresAt: decoded.exp * 1000, - deviceId: decoded.deviceId, - ipAddress: decoded.ipAddress, - }; - - // Check if token is expired - if (authToken.expiresAt < Date.now()) { - await this.logSecurityEvent({ - type: 'authentication', - severity: 'medium', - userId: authToken.userId, - organizationId: authToken.organizationId, - action: 'token_expired', - details: { - sessionId: authToken.sessionId, - expiredAt: authToken.expiresAt, - }, - }); - - return null; - } - - const processingTime = performance.now() - startTime; - - await this.logSecurityEvent({ - type: 'authentication', - severity: 'low', - userId: authToken.userId, - organizationId: authToken.organizationId, - action: 'token_validated', - details: { - sessionId: authToken.sessionId, - processingTime: `${processingTime.toFixed(2)}ms`, - }, - }); - - return authToken; - } catch (error: any) { - const processingTime = performance.now() - startTime; - - await this.logSecurityEvent({ - type: 'authentication', - severity: 'high', - action: 'token_validation_failed', - details: { - error: error.message, - processingTime: `${processingTime.toFixed(2)}ms`, - }, - }); - - logger.warn('Token authentication failed', { - error: error.message, - processingTime: `${processingTime.toFixed(2)}ms`, - }); - - return null; - } - } - - /** - * Generate JWT token for authenticated user - */ - async generateToken( - userId: string, - organizationId: string, - permissions: string[], - sessionId: string, - deviceId?: string, - ipAddress?: string - ): Promise { - const payload = { - userId, - organizationId, - permissions, - sessionId, - deviceId, - ipAddress, - }; - - const token = jwt.sign(payload, this.config.jwtSecret as jwt.Secret, { - expiresIn: this.config.jwtExpiresIn, - issuer: 'ceo-platform', - audience: organizationId, - }); - - await this.logSecurityEvent({ - type: 'authentication', - severity: 'low', - userId, - organizationId, - action: 'token_generated', - details: { - sessionId, - deviceId, - ipAddress, - permissions: permissions.length, - }, - }); - - return token; - } - - /** - * Authorize voice command execution - */ - async authorizeVoiceCommand( - userId: string, - organizationId: string, - commandType: string, - permissions: string[], - commandData: Record - ): Promise { - const commandId = `cmd_${userId}_${Date.now()}_${Math.random().toString(36).substr(2, 6)}`; - - try { - // Check required permissions for command type - const requiredPermissions = this.getRequiredPermissions(commandType, commandData); - const hasPermissions = requiredPermissions.every((perm) => permissions.includes(perm)); - - if (!hasPermissions) { - await this.logSecurityEvent({ - type: 'authorization', - severity: 'medium', - userId, - organizationId, - action: 'voice_command_denied', - details: { - commandId, - commandType, - requiredPermissions, - userPermissions: permissions, - reason: 'insufficient_permissions', - }, - }); - - return { - commandId, - userId, - organizationId, - commandType, - permissions, - sensitiveDataMask: false, - approved: false, - reason: 'Insufficient permissions', - }; - } - - // Check for sensitive data that needs masking - const sensitiveDataMask = this.containsSensitiveData(commandData); - - // Check rate limits - const rateLimitResult = await this.checkRateLimit(userId, 'voice_command'); - if (!rateLimitResult.allowed) { - await this.logSecurityEvent({ - type: 'rate_limit', - severity: 'medium', - userId, - organizationId, - action: 'voice_command_rate_limited', - details: { - commandId, - commandType, - remaining: rateLimitResult.remaining, - retryAfter: rateLimitResult.retryAfter, - }, - }); - - return { - commandId, - userId, - organizationId, - commandType, - permissions, - sensitiveDataMask, - approved: false, - reason: 'Rate limit exceeded', - }; - } - - await this.logSecurityEvent({ - type: 'authorization', - severity: 'low', - userId, - organizationId, - action: 'voice_command_authorized', - details: { - commandId, - commandType, - sensitiveDataMask, - permissions: permissions.length, - }, - }); - - return { - commandId, - userId, - organizationId, - commandType, - permissions, - sensitiveDataMask, - approved: true, - }; - } catch (error: any) { - await this.logSecurityEvent({ - type: 'authorization', - severity: 'high', - userId, - organizationId, - action: 'voice_command_authorization_error', - details: { - commandId, - commandType, - error: error.message, - }, - }); - - return { - commandId, - userId, - organizationId, - commandType, - permissions, - sensitiveDataMask: false, - approved: false, - reason: 'Authorization error', - }; - } - } - - /** - * Get required permissions for command type - */ - private getRequiredPermissions(commandType: string, commandData: Record): string[] { - const permissionMap: Record = { - create_task: ['tasks:write'], - update_task: ['tasks:write'], - delete_task: ['tasks:delete'], - read_task: ['tasks:read'], - create_channel: ['channels:write'], - update_channel: ['channels:write'], - delete_channel: ['channels:delete'], - join_channel: ['channels:join'], - upload_file: ['files:write'], - share_file: ['files:share'], - delete_file: ['files:delete'], - download_file: ['files:read'], - send_message: ['messages:write'], - read_messages: ['messages:read'], - admin_command: ['admin:all'], - }; - - // Add context-specific permissions - const basePermissions = permissionMap[commandType] || ['general:execute']; - - // Check if command affects other users' data - if (commandData.affectedUsers && commandData.affectedUsers.length > 0) { - basePermissions.push('users:affect_others'); - } - - // Check if command involves sensitive operations - if (commandData.sensitiveOperation) { - basePermissions.push('sensitive:execute'); - } - - return basePermissions; - } - - /** - * Check if command data contains sensitive information - */ - private containsSensitiveData(data: Record): boolean { - const sensitiveKeys = [ - 'password', - 'token', - 'secret', - 'key', - 'credentials', - 'ssn', - 'social', - 'credit', - 'card', - 'account', - 'bank', - ]; - - const dataString = JSON.stringify(data).toLowerCase(); - - return sensitiveKeys.some((key) => dataString.includes(key)); - } - - /** - * Check rate limits for user actions - */ - async checkRateLimit(userId: string, action: string): Promise { - const key = `${userId}:${action}`; - const now = Date.now(); - const windowStart = now - this.config.rateLimiting.windowMs; - - const current = this.rateLimits.get(key) || { - count: 0, - resetTime: now + this.config.rateLimiting.windowMs, - }; - - // Reset if window has expired - if (now >= current.resetTime) { - current.count = 0; - current.resetTime = now + this.config.rateLimiting.windowMs; - } - - const allowed = current.count < this.config.rateLimiting.maxRequests; - - if (allowed) { - current.count++; - this.rateLimits.set(key, current); - } - - const result: RateLimitResult = { - allowed, - remaining: Math.max(0, this.config.rateLimiting.maxRequests - current.count), - resetTime: current.resetTime, - }; - - if (!allowed) { - result.retryAfter = current.resetTime - now; - - // Track suspicious activity - await this.trackSuspiciousActivity(userId, 'rate_limit_exceeded', { - action, - count: current.count, - limit: this.config.rateLimiting.maxRequests, - }); - } - - return result; - } - - /** - * Sanitize and validate input data - */ - validateAndSanitizeInput( - input: any, - schema: Record - ): { valid: boolean; sanitized?: any; errors?: string[] } { - const errors: string[] = []; - const sanitized: Record = {}; - - try { - for (const [field, rules] of Object.entries(schema)) { - const value = input[field]; - - // Required field validation - if (rules.required && (value === undefined || value === null || value === '')) { - errors.push(`${field} is required`); - continue; - } - - // Skip further validation if field is optional and not provided - if (!rules.required && (value === undefined || value === null)) { - continue; - } - - // Type validation - if (rules.type && typeof value !== rules.type) { - errors.push(`${field} must be of type ${rules.type}`); - continue; - } - - // String validation - if (rules.type === 'string' && typeof value === 'string') { - let sanitizedValue = value; - - // Trim whitespace - sanitizedValue = sanitizedValue.trim(); - - // Length validation - if (rules.minLength && sanitizedValue.length < rules.minLength) { - errors.push(`${field} must be at least ${rules.minLength} characters`); - continue; - } - - if (rules.maxLength && sanitizedValue.length > rules.maxLength) { - errors.push(`${field} must not exceed ${rules.maxLength} characters`); - continue; - } - - // Pattern validation - if (rules.pattern && !new RegExp(rules.pattern).test(sanitizedValue)) { - errors.push(`${field} format is invalid`); - continue; - } - - // HTML/Script injection prevention - sanitizedValue = this.sanitizeString(sanitizedValue); - - sanitized[field] = sanitizedValue; - } else { - sanitized[field] = value; - } - } - - if (errors.length > 0) { - return { valid: false, errors }; - } - - return { valid: true, sanitized }; - } catch (error: any) { - logger.error('Input validation error', { error: error.message }); - return { valid: false, errors: ['Validation error occurred'] }; - } - } - - /** - * Sanitize string to prevent injection attacks - */ - private sanitizeString(input: string): string { - return input - .replace(/[<>]/g, '') // Remove potential HTML tags - .replace(/javascript:/gi, '') // Remove javascript: protocols - .replace(/on\w+\s*=/gi, '') // Remove event handlers - .replace(/script/gi, 'sc_ript') // Neutralize script tags - .replace(/eval\s*\(/gi, 'ev_al(') // Neutralize eval calls - .trim(); - } - - /** - * Track suspicious activity - */ - private async trackSuspiciousActivity( - userId: string, - activityType: string, - details: Record - ): Promise { - const key = `${userId}:${activityType}`; - const now = Date.now(); - - const activity = this.suspiciousActivities.get(key) || { - count: 0, - firstSeen: now, - }; - - activity.count++; - - this.suspiciousActivities.set(key, activity); - - // Log as security event if threshold exceeded - if (activity.count > 5) { - await this.logSecurityEvent({ - type: 'suspicious_activity', - severity: 'high', - userId, - action: 'repeated_suspicious_activity', - details: { - activityType, - count: activity.count, - firstSeen: activity.firstSeen, - duration: now - activity.firstSeen, - ...details, - }, - }); - } - } - - /** - * Encrypt sensitive data - */ - encryptSensitiveData(data: string): { encrypted: string; iv: string } { - const iv = crypto.randomBytes(16); - const cipher = crypto.createCipher(this.config.encryption.algorithm, this.encryptionKey); - - let encrypted = cipher.update(data, 'utf8', 'hex'); - encrypted += cipher.final('hex'); - - return { - encrypted, - iv: iv.toString('hex'), - }; - } - - /** - * Decrypt sensitive data - */ - decryptSensitiveData(encrypted: string, iv: string): string { - const decipher = crypto.createDecipher(this.config.encryption.algorithm, this.encryptionKey); - - let decrypted = decipher.update(encrypted, 'hex', 'utf8'); - decrypted += decipher.final('utf8'); - - return decrypted; - } - - /** - * Hash password securely - */ - async hashPassword(password: string): Promise { - return await bcrypt.hash(password, this.config.bcryptSaltRounds); - } - - /** - * Verify password against hash - */ - async verifyPassword(password: string, hash: string): Promise { - return await bcrypt.compare(password, hash); - } - - /** - * Log security event - */ - private async logSecurityEvent(event: Partial): Promise { - if (!this.config.auditLogging.enabled) { - return; - } - - const securityEvent: SecurityEvent = { - eventId: `sec_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - type: event.type || 'authentication', - severity: event.severity || 'low', - userId: event.userId ?? '', - organizationId: event.organizationId ?? '', - action: event.action || 'unknown', - resource: event.resource ?? '', - ipAddress: event.ipAddress ?? '', - userAgent: event.userAgent ?? '', - timestamp: new Date().toISOString(), - details: event.details || {}, - }; - - // Add to in-memory log - this.securityEvents.push(securityEvent); - - // Trim log if too large - if (this.securityEvents.length > this.maxAuditLogSize) { - this.securityEvents.shift(); - } - - // Log based on severity and configuration - const shouldLog = - this.config.auditLogging.logLevel === 'all' || - (this.config.auditLogging.logLevel === 'security' && - ['medium', 'high', 'critical'].includes(securityEvent.severity)) || - (this.config.auditLogging.logLevel === 'critical' && - ['critical'].includes(securityEvent.severity)); - - if (shouldLog) { - logger.info('Security event logged', { - eventId: securityEvent.eventId, - type: securityEvent.type, - severity: securityEvent.severity, - action: securityEvent.action, - userId: securityEvent.userId, - organizationId: securityEvent.organizationId, - }); - } - - this.emit('security_event', securityEvent); - } - - /** - * Get security events for audit - */ - getSecurityEvents(filters?: { - userId?: string; - organizationId?: string; - type?: string; - severity?: string; - limit?: number; - }): SecurityEvent[] { - let events = [...this.securityEvents]; - - if (filters) { - if (filters.userId) { - events = events.filter((e) => e.userId === filters.userId); - } - - if (filters.organizationId) { - events = events.filter((e) => e.organizationId === filters.organizationId); - } - - if (filters.type) { - events = events.filter((e) => e.type === filters.type); - } - - if (filters.severity) { - events = events.filter((e) => e.severity === filters.severity); - } - - if (filters.limit) { - events = events.slice(-filters.limit); - } - } - - return events.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()); - } - - /** - * Get security metrics - */ - getSecurityMetrics(): { - totalEvents: number; - eventsByType: Record; - eventsBySeverity: Record; - suspiciousActivities: number; - rateLimitViolations: number; - } { - const eventsByType: Record = {}; - const eventsBySeverity: Record = {}; - let rateLimitViolations = 0; - - for (const event of this.securityEvents) { - eventsByType[event.type] = (eventsByType[event.type] || 0) + 1; - eventsBySeverity[event.severity] = (eventsBySeverity[event.severity] || 0) + 1; - - if (event.type === 'rate_limit') { - rateLimitViolations++; - } - } - - return { - totalEvents: this.securityEvents.length, - eventsByType, - eventsBySeverity, - suspiciousActivities: this.suspiciousActivities.size, - rateLimitViolations, - }; - } - - /** - * Start cleanup timer for old data - */ - private startCleanupTimer(): void { - this.cleanupTimer = setInterval(() => { - this.cleanupOldData(); - }, 3600000); // Every hour - } - - /** - * Cleanup old rate limit and suspicious activity data - */ - private cleanupOldData(): void { - const now = Date.now(); - const cutoffTime = now - 24 * 60 * 60 * 1000; // 24 hours ago - - // Cleanup old rate limits - let cleanedRateLimits = 0; - for (const [key, data] of this.rateLimits.entries()) { - if (data.resetTime < now) { - this.rateLimits.delete(key); - cleanedRateLimits++; - } - } - - // Cleanup old suspicious activities - let cleanedActivities = 0; - for (const [key, activity] of this.suspiciousActivities.entries()) { - if (activity.firstSeen < cutoffTime) { - this.suspiciousActivities.delete(key); - cleanedActivities++; - } - } - - if (cleanedRateLimits > 0 || cleanedActivities > 0) { - logger.debug('Security data cleanup completed', { - cleanedRateLimits, - cleanedActivities, - }); - } - } - - /** - * Destroy and cleanup - */ - destroy(): void { - if (this.cleanupTimer) { - clearInterval(this.cleanupTimer); - } - - this.removeAllListeners(); - - logger.info('Security Manager destroyed'); - } -} diff --git a/backend/src/server.ts b/backend/src/server.ts deleted file mode 100644 index e9319a2..0000000 --- a/backend/src/server.ts +++ /dev/null @@ -1,763 +0,0 @@ -import Fastify, { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'; -import { config } from './config/index'; -import { initializeDatabase, healthCheck as dbHealthCheck } from './config/database'; -import { redisManager } from './config/redis'; -import { socketManager } from './websocket/SocketManager'; -import { logger, performanceLogger, loggers, startupLogger } from './utils/logger'; -import { - BaseError, - formatErrorResponse, - isOperationalError, - ConfigurationError, -} from './utils/errors'; -import { authenticate, optionalAuthenticate } from './auth/middleware'; -import { registerAuthRoutes } from './auth/routes'; -import { registerAPIRoutes } from './api/index'; -import { runMigrations } from './db/migrator'; -import { createServer } from 'http'; -import { TokenPayload } from './auth/jwt'; - -/** - * High-Performance Fastify Server - * Enterprise-grade API infrastructure with comprehensive monitoring - */ - -class APIServer { - private app: FastifyInstance; - private httpServer: ReturnType | null = null; - private isShuttingDown = false; - - constructor() { - this.app = Fastify({ - logger: false, // We use pino directly - requestIdLogLabel: 'requestId', - requestIdHeader: 'x-request-id', - bodyLimit: 1048576, // 1MB default - keepAliveTimeout: 72000, - connectionTimeout: 0, - pluginTimeout: 10000, - ignoreTrailingSlash: true, - ignoreDuplicateSlashes: true, - maxParamLength: 500, - trustProxy: true, - }); - - this.setupGlobalHooks(); - this.setupHealthChecks(); - } - - /** - * Initialize server with all plugins and routes - */ - async initialize(): Promise { - try { - logger.info('Initializing API server...'); - - // Register core plugins - await this.registerCorePlugins(); - - // Register security plugins - await this.registerSecurityPlugins(); - - // Register validation and serialization - await this.registerValidationPlugins(); - - // Setup middleware - await this.setupMiddleware(); - - // Register routes - await this.registerRoutes(); - - // Setup error handling - this.setupErrorHandling(); - - logger.info('API server initialized successfully'); - } catch (error) { - logger.error({ error }, 'Failed to initialize API server'); - throw error; - } - } - - /** - * Register core Fastify plugins - */ - private async registerCorePlugins(): Promise { - // CORS support - await this.app.register(import('@fastify/cors'), { - origin: config.api.cors.origin, - credentials: config.api.cors.credentials, - methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], - allowedHeaders: ['Content-Type', 'Authorization', 'X-Request-ID'], - }); - - // Helmet for security headers - await this.app.register(import('@fastify/helmet'), { - ...(config.app.isProduction ? {} : { contentSecurityPolicy: false }), - crossOriginEmbedderPolicy: false, - }); - - // Request/Response compression - temporarily disabled for debugging - // await this.app.register(import('@fastify/compress'), { - // global: true, - // encodings: ['gzip', 'deflate'], - // threshold: 1024, - // }); - - // Rate limiting (global) - await this.app.register(import('@fastify/rate-limit'), { - max: config.api.rateLimit.max, - timeWindow: config.api.rateLimit.timeWindow, - skipOnError: true, - keyGenerator: (request: FastifyRequest) => { - return request.user?.userId || request.ip; - }, - errorResponseBuilder: () => ({ - error: { - message: 'Rate limit exceeded', - code: 'RATE_LIMIT_ERROR', - statusCode: 429, - }, - }), - }); - - // Multipart support for file uploads - await this.app.register(import('@fastify/multipart'), { - limits: { - fieldNameSize: 100, - fieldSize: 1048576, // 1MB - fields: 10, - fileSize: 10485760, // 10MB - files: 5, - }, - }); - - // Static file servingeb printenv - await this.app.register(import('@fastify/static'), { - root: require('path').join(__dirname, '..', 'public'), - prefix: '/public/', - }); - - // Swagger/OpenAPI documentation - await this.app.register(import('@fastify/swagger'), { - openapi: { - openapi: '3.0.0', - info: { - title: 'CEO Communication Platform API', - description: 'Enterprise-grade backend API for the CEO Communication Platform with voice processing and real-time collaboration', - version: '1.0.0', - contact: { - name: 'CEO Communication Platform Team', - email: 'support@ceoplatform.com' - }, - license: { - name: 'Private License' - } - }, - servers: [ - { - url: config.app.isDevelopment ? 'http://localhost:3001' : 'https://api.ceoplatform.com', - description: config.app.isDevelopment ? 'Development server' : 'Production server' - } - ], - components: { - securitySchemes: { - BearerAuth: { - type: 'http', - scheme: 'bearer', - bearerFormat: 'JWT', - description: 'JWT token obtained from /auth/login endpoint' - } - } - }, - security: [ - { - BearerAuth: [] - } - ] - } - }); - - // Swagger UI - await this.app.register(import('@fastify/swagger-ui'), { - routePrefix: '/docs', - uiConfig: { - docExpansion: 'none', - deepLinking: false, - defaultModelsExpandDepth: 2, - defaultModelExpandDepth: 2, - displayOperationId: false, - displayRequestDuration: true, - showExtensions: true, - showCommonExtensions: true, - tryItOutEnabled: true - }, - uiHooks: { - onRequest: function (request, reply, next) { - next(); - }, - preHandler: function (request, reply, next) { - next(); - } - }, - staticCSP: true, - transformStaticCSP: (header) => header - }); - - logger.debug('Core plugins registered'); - } - - /** - * Register security plugins - */ - private async registerSecurityPlugins(): Promise { - // JWT authentication decorator - this.app.decorate('authenticate', authenticate); - this.app.decorate('optionalAuthenticate', optionalAuthenticate); - - logger.debug('Security plugins registered'); - } - - /** - * Register validation and serialization plugins - */ - private async registerValidationPlugins(): Promise { - // Type providers for better TypeScript support - this.app.addHook('onRoute', (routeOptions) => { - if (routeOptions.schema) { - // Add response serialization optimization - routeOptions.serializerCompiler = ({ schema }) => { - return (data: unknown) => JSON.stringify(data); - }; - - // Add validation error handler - routeOptions.validatorCompiler = ({ schema }) => { - return (data: unknown) => { - // Custom validation logic can be added here - return { value: data }; - }; - }; - } - }); - - logger.debug('Validation plugins registered'); - } - - /** - * Setup middleware and hooks - */ - private async setupMiddleware(): Promise { - // Performance monitoring middleware - this.app.addHook('onRequest', async (request, reply) => { - request.startTime = Date.now(); - }); - - this.app.addHook('onSend', async (request, reply, payload) => { - const duration = Date.now() - (request.startTime || 0); - - loggers.performance.info( - { - method: request.method, - url: request.url, - statusCode: reply.statusCode, - duration, - userId: request.user?.userId, - ip: request.ip, - }, - 'API Request' - ); - - // Log slow requests - if (duration > 2000) { - // 2 seconds threshold - loggers.performance.warn?.( - { - method: request.method, - url: request.url, - duration, - userId: request.user?.userId, - }, - 'Slow API request detected' - ); - } - - return payload; - }); - - // Request ID logging - this.app.addHook('preHandler', async (request, reply) => { - loggers.api.info( - { - requestId: request.id, - method: request.method, - url: request.url, - ip: request.ip, - userAgent: request.headers['user-agent'], - userId: request.user?.userId, - }, - 'Incoming request' - ); - }); - - logger.debug('Middleware setup complete'); - } - - /** - * Setup global hooks - */ - private setupGlobalHooks(): void { - // Graceful shutdown handling - this.app.addHook('onClose', async () => { - this.isShuttingDown = true; - logger.info('Server shutting down gracefully...'); - }); - - // Health check for load balancers - this.app.addHook('preHandler', async (request, reply) => { - if (this.isShuttingDown) { - reply.code(503).send({ - error: { - message: 'Server is shutting down', - code: 'SERVER_SHUTDOWN', - statusCode: 503, - }, - }); - return; - } - }); - } - - /** - * Setup health check endpoints - */ - private setupHealthChecks(): void { - // Liveness probe - this.app.get('/health', async (request, reply) => { - if (this.isShuttingDown) { - reply.code(503).send({ status: 'shutting_down' }); - return; - } - - reply.send({ - status: 'healthy', - timestamp: new Date().toISOString(), - uptime: process.uptime(), - version: process.env.npm_package_version || '1.0.0', - }); - }); - - // Readiness probe with dependency checks - this.app.get('/health/ready', async (request, reply) => { - try { - const checks = await Promise.allSettled([ - performanceLogger.trackAsyncOperation(() => dbHealthCheck(), 'database_health_check'), - performanceLogger.trackAsyncOperation( - () => redisManager.healthCheck(), - 'redis_health_check' - ), - performanceLogger.trackAsyncOperation( - () => Promise.resolve(socketManager.getServer() !== null), - 'websocket_health_check' - ), - ]); - - const dbCheck = checks[0]; - const redisCheck = checks[1]; - const websocketCheck = checks[2]; - - const dbHealthy = dbCheck.status === 'fulfilled' && dbCheck.value?.status === 'healthy'; - const redisHealthy = redisCheck.status === 'fulfilled' && redisCheck.value === true; - const websocketHealthy = - websocketCheck.status === 'fulfilled' && websocketCheck.value === true; - const isReady = dbHealthy && redisHealthy && websocketHealthy; - - if (isReady) { - reply.send({ - status: 'ready', - timestamp: new Date().toISOString(), - checks: { - database: 'healthy', - redis: 'healthy', - websocket: 'healthy', - }, - metrics: { - connectedUsers: socketManager.getConnectedUsersCount(), - }, - }); - } else { - reply.code(503).send({ - status: 'not_ready', - timestamp: new Date().toISOString(), - checks: { - database: dbHealthy ? 'healthy' : 'unhealthy', - redis: redisHealthy ? 'healthy' : 'unhealthy', - websocket: websocketHealthy ? 'healthy' : 'unhealthy', - }, - }); - } - } catch (error) { - logger.error({ error }, 'Health check failed'); - reply.code(503).send({ - status: 'not_ready', - timestamp: new Date().toISOString(), - error: 'Health check failed', - }); - } - }); - - // Detailed system metrics (for monitoring) - this.app.get('/health/metrics', async (request, reply) => { - const memoryUsage = process.memoryUsage(); - const cpuUsage = process.cpuUsage(); - - reply.send({ - status: 'healthy', - timestamp: new Date().toISOString(), - metrics: { - uptime: process.uptime(), - memory: { - rss: Math.round(memoryUsage.rss / 1024 / 1024), - heapTotal: Math.round(memoryUsage.heapTotal / 1024 / 1024), - heapUsed: Math.round(memoryUsage.heapUsed / 1024 / 1024), - external: Math.round(memoryUsage.external / 1024 / 1024), - }, - cpu: { - user: cpuUsage.user, - system: cpuUsage.system, - }, - node: { - version: process.version, - platform: process.platform, - arch: process.arch, - }, - }, - }); - }); - } - - /** - * Register application routes - */ - private async registerRoutes(): Promise { - // API versioning - await this.app.register( - (fastify, opts, next) => { - // Authentication routes (no additional auth middleware needed) - fastify.register(registerAuthRoutes); - - // Main API routes (all require authentication) - fastify.register(registerAPIRoutes); - - next(); - }, - { prefix: '/api/v1' } - ); - - // Root endpoint - this.app.get('/', async (request, reply) => { - reply.send({ - name: 'CEO Communication Platform API', - version: process.env.npm_package_version || '1.0.0', - environment: config.app.env, - timestamp: new Date().toISOString(), - }); - }); - - logger.debug('Routes registered successfully'); - } - - /** - * Setup centralized error handling - */ - private setupErrorHandling(): void { - // Global error handler - this.app.setErrorHandler((error: Error, request: FastifyRequest, reply: FastifyReply) => { - const requestId = request.id; - const context = { - requestId, - method: request.method, - url: request.url, - ip: request.ip, - userId: request.user?.userId, - userAgent: request.headers['user-agent'], - }; - - // Handle operational errors (business logic, validation, etc.) - if (error instanceof BaseError) { - loggers.api.warn({ error: error.toJSON(), ...context }, 'Operational error occurred'); - - reply.code(error.statusCode).send(formatErrorResponse(error)); - return; - } - - // Handle Fastify validation errors - if (error.name === 'FastifyError' && typeof (error as any).statusCode === 'number') { - const statusCode = (error as any).statusCode; - loggers.api.warn( - { error: error.message, statusCode, ...context }, - 'Fastify validation error' - ); - - reply.code(statusCode).send({ - error: { - message: error.message, - code: 'VALIDATION_ERROR', - statusCode, - }, - timestamp: new Date().toISOString(), - }); - return; - } - - // Handle unexpected errors - loggers.api.error({ error: error.stack, ...context }, 'Unexpected server error'); - - // Don't expose internal errors in production - const message = config.app.isProduction ? 'An internal server error occurred' : error.message; - - reply.code(500).send({ - error: { - message, - code: 'INTERNAL_SERVER_ERROR', - statusCode: 500, - }, - timestamp: new Date().toISOString(), - ...(config.app.isDevelopment && { stack: error.stack }), - }); - }); - - // Handle 404 errors - this.app.setNotFoundHandler((request: FastifyRequest, reply: FastifyReply) => { - loggers.api.warn( - { - method: request.method, - url: request.url, - ip: request.ip, - userId: request.user?.userId, - }, - 'Route not found' - ); - - reply.code(404).send({ - error: { - message: `Route ${request.method} ${request.url} not found`, - code: 'ROUTE_NOT_FOUND', - statusCode: 404, - }, - timestamp: new Date().toISOString(), - }); - }); - - logger.debug('Error handling setup complete'); - } - - /** - * Start the server - */ - async start(): Promise { - try { - const overallTimer = startupLogger.createTimer('API Server Startup'); - const services: Array<{ name: string; status: boolean; duration?: number }> = []; - - startupLogger.logStep('API Server Startup'); - - // Initialize database - const dbTimer = startupLogger.createTimer('Database'); - try { - await initializeDatabase(); - services.push({ name: 'Database', status: true, duration: dbTimer.end() }); - } catch (error) { - services.push({ name: 'Database', status: false, duration: dbTimer.end() }); - throw error; - } - - // Initialize Redis - const redisTimer = startupLogger.createTimer('Redis'); - try { - await redisManager.initialize(); - services.push({ name: 'Redis', status: true, duration: redisTimer.end() }); - } catch (error) { - services.push({ name: 'Redis', status: false, duration: redisTimer.end() }); - throw error; - } - - // Initialize WebSocket server - const wsTimer = startupLogger.createTimer('WebSocket Server'); - try { - // Get the HTTP server from Fastify after it starts listening - // We'll initialize Socket.IO after the server is listening - services.push({ name: 'WebSocket Server', status: true, duration: wsTimer.end() }); - } catch (error) { - services.push({ name: 'WebSocket Server', status: false, duration: wsTimer.end() }); - throw error; - } - - // Run migrations - const migrationTimer = startupLogger.createTimer('Database Migrations'); - try { - await runMigrations(); - services.push({ name: 'Database Migrations', status: true, duration: migrationTimer.end() }); - } catch (error) { - services.push({ name: 'Database Migrations', status: false, duration: migrationTimer.end() }); - throw error; - } - - // Run database seeding (only in development) - if (config.app.isDevelopment) { - const seedTimer = startupLogger.createTimer('Database Seeding'); - try { - logger.debug('Skipping database seeding (data already exists)'); - services.push({ name: 'Database Seeding', status: true, duration: seedTimer.end() }); - } catch (error) { - logger.warn({ error }, 'Database seeding failed, continuing without seed data'); - services.push({ name: 'Database Seeding', status: false, duration: seedTimer.end() }); - } - } - - // Initialize server - const serverTimer = startupLogger.createTimer('Server Configuration'); - try { - await this.initialize(); - services.push({ name: 'Server Configuration', status: true, duration: serverTimer.end() }); - } catch (error) { - services.push({ name: 'Server Configuration', status: false, duration: serverTimer.end() }); - throw error; - } - - // Start listening - const listenTimer = startupLogger.createTimer('Server Listen'); - try { - // Use Fastify's built-in listen method - const address = await this.app.listen({ - port: config.app.port, - host: config.app.host, - }); - - // Now initialize Socket.IO with Fastify's server - this.httpServer = this.app.server; - if (this.httpServer) { - await socketManager.initialize(this.httpServer); - } - - services.push({ name: 'Server Listen', status: true, duration: listenTimer.end() }); - - // Log startup summary - const totalDuration = overallTimer.end(); - services.push({ name: 'Total Startup Time', status: true, duration: totalDuration }); - startupLogger.logSummary(services); - - logger.info( - { - address, - port: config.app.port, - host: config.app.host, - environment: config.app.env, - }, - '🚀 API server running' - ); - } catch (error) { - services.push({ name: 'Server Listen', status: false, duration: listenTimer.end() }); - throw error; - } - - // Setup graceful shutdown - this.setupGracefulShutdown(); - } catch (error) { - logger.error({ error }, 'Failed to start API server'); - throw error; - } - } - - /** - * Setup graceful shutdown handlers - */ - private setupGracefulShutdown(): void { - const gracefulShutdown = async (signal: string) => { - logger.info({ signal }, 'Received shutdown signal, starting graceful shutdown...'); - - this.isShuttingDown = true; - - try { - // Stop accepting new connections - await this.app.close(); - logger.info('Server closed successfully'); - - // Close WebSocket server - logger.info('Closing WebSocket server...'); - await socketManager.close(); - - // Close Redis connections - logger.info('Closing Redis connections...'); - await redisManager.close(); - - // Close HTTP server - if (this.httpServer) { - this.httpServer.close(); - } - - // Exit process - process.exit(0); - } catch (error) { - logger.error({ error }, 'Error during graceful shutdown'); - process.exit(1); - } - }; - - // Handle different shutdown signals - process.on('SIGTERM', () => gracefulShutdown('SIGTERM')); - process.on('SIGINT', () => gracefulShutdown('SIGINT')); - - // Handle uncaught exceptions - process.on('uncaughtException', (error: Error) => { - logger.fatal({ error }, 'Uncaught exception occurred'); - process.exit(1); - }); - - // Handle unhandled promise rejections - process.on('unhandledRejection', (reason: unknown, promise: Promise) => { - logger.fatal({ reason, promise }, 'Unhandled promise rejection'); - process.exit(1); - }); - } - - /** - * Get Fastify instance for testing - */ - getApp(): FastifyInstance { - return this.app; - } -} - -// Extend Fastify types -declare module 'fastify' { - interface FastifyRequest { - startTime?: number; - user?: TokenPayload & { - isAuthenticated: boolean; - id: string; // Alias for userId for compatibility - }; - } - - interface FastifyInstance { - authenticate: typeof authenticate; - optionalAuthenticate: typeof optionalAuthenticate; - } -} - -// Export server class and create instance -export { APIServer }; -export const server = new APIServer(); - -// Start server if this file is run directly -if (require.main === module) { - console.log('Starting server directly...'); - server.start().then(() => { - console.log('Server started successfully, keeping process alive...'); - // Keep the process alive - setInterval(() => { - console.log('Server is running...', new Date().toISOString()); - }, 30000); // Log every 30 seconds - }).catch((error) => { - logger.fatal({ error }, 'Failed to start server'); - process.exit(1); - }); -} diff --git a/backend/src/services/CacheService.ts b/backend/src/services/CacheService.ts deleted file mode 100644 index f52a7c9..0000000 --- a/backend/src/services/CacheService.ts +++ /dev/null @@ -1,593 +0,0 @@ -import { redisManager } from '@config/redis'; -import { logger, loggers, performanceLogger } from '@utils/logger'; -import { CacheError } from '@utils/errors'; -import { config } from '@config/index'; - -/** - * High-performance caching service with Redis backend - * Enterprise-grade caching with compression, serialization, and TTL management - */ - -export interface CacheOptions { - ttl?: number; // Time to live in seconds - compress?: boolean; // Enable compression for large values - serialize?: boolean; // Enable JSON serialization - namespace?: string; // Cache namespace/prefix - tags?: string[]; // Cache tags for bulk invalidation -} - -export interface CacheSetOptions extends CacheOptions { - nx?: boolean; // Set only if key doesn't exist - ex?: number; // Expiration time in seconds (alternative to ttl) -} - -export interface CacheStats { - totalKeys: number; - memoryUsage: number; - hitRate: number; - operations: { - gets: number; - sets: number; - deletes: number; - hits: number; - misses: number; - }; -} - -class CacheService { - private defaultTTL = 3600; // 1 hour default - private compressionThreshold = 1024; // Compress values larger than 1KB - private maxKeyLength = 250; // Redis key length limit - private namespacePrefix = 'ceo-platform:'; - - // Cache namespaces for different data types - private namespaces = { - users: 'users:', - channels: 'channels:', - tasks: 'tasks:', - sessions: 'sessions:', - analytics: 'analytics:', - voice: 'voice:', - general: 'general:', - } as const; - - /** - * Generate cache key with namespace - */ - private generateKey(key: string, namespace?: string): string { - const prefix = this.namespacePrefix + (namespace || this.namespaces.general); - const fullKey = prefix + key; - - if (fullKey.length > this.maxKeyLength) { - // Use hash for very long keys - const crypto = require('crypto'); - const hash = crypto.createHash('sha256').update(fullKey).digest('hex').substring(0, 16); - return `${prefix}hashed:${hash}`; - } - - return fullKey; - } - - /** - * Serialize value for storage - */ - private serialize(value: any, options: CacheOptions = {}): string { - if (options.serialize === false) { - return value; - } - - try { - return JSON.stringify(value); - } catch (error) { - loggers.cache.error({ error, value }, 'Failed to serialize cache value'); - throw new CacheError('Cache serialization failed'); - } - } - - /** - * Deserialize value from storage - */ - private deserialize(value: string, options: CacheOptions = {}): any { - if (options.serialize === false) { - return value; - } - - try { - return JSON.parse(value); - } catch (error) { - loggers.cache.warn({ error, value }, 'Failed to deserialize cache value, returning raw'); - return value; - } - } - - /** - * Compress value if needed - */ - private async compress(value: string, options: CacheOptions = {}): Promise { - if (!options.compress || value.length < this.compressionThreshold) { - return value; - } - - try { - const zlib = require('zlib'); - const compressed = zlib.gzipSync(value).toString('base64'); - return `compressed:${compressed}`; - } catch (error) { - loggers.cache.warn({ error }, 'Failed to compress cache value'); - return value; - } - } - - /** - * Decompress value if needed - */ - private async decompress(value: string): Promise { - if (!value.startsWith('compressed:')) { - return value; - } - - try { - const zlib = require('zlib'); - const compressed = value.substring('compressed:'.length); - return zlib.gunzipSync(Buffer.from(compressed, 'base64')).toString(); - } catch (error) { - loggers.cache.warn({ error }, 'Failed to decompress cache value'); - return value; - } - } - - /** - * Get value from cache - */ - async get(key: string, options: CacheOptions = {}): Promise { - return await performanceLogger.trackAsyncOperation( - async () => { - try { - const client = redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - - const value = await client.get(cacheKey); - - if (value === null) { - redisManager.incrementMisses(); - loggers.cache.debug({ key: cacheKey }, 'Cache miss'); - return null; - } - - redisManager.incrementHits(); - loggers.cache.debug({ key: cacheKey, size: value.length }, 'Cache hit'); - - // Decompress and deserialize - const decompressed = await this.decompress(value); - const result = this.deserialize(decompressed, options); - - return result as T; - } catch (error) { - loggers.cache.error({ error, key }, 'Cache get operation failed'); - redisManager.incrementMisses(); - return null; - } - }, - 'cache_get', - { key, namespace: options.namespace } - ); - } - - /** - * Set value in cache - */ - async set(key: string, value: any, options: CacheSetOptions = {}): Promise { - return await performanceLogger.trackAsyncOperation( - async () => { - try { - const client = redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - - // Serialize and compress - const serialized = this.serialize(value, options); - const compressed = await this.compress(serialized, options); - - const ttl = options.ttl || options.ex || this.defaultTTL; - - let result: string | null; - if (options.nx) { - result = await client.set(cacheKey, compressed, 'EX', ttl, 'NX'); - } else { - result = await client.setex(cacheKey, ttl, compressed); - } - - const success = result === 'OK'; - - if (success) { - redisManager.incrementSets(); - loggers.cache.debug( - { - key: cacheKey, - ttl, - size: compressed.length, - compressed: compressed.startsWith('compressed:'), - }, - 'Cache set successful' - ); - - // Set tags for bulk invalidation if provided - if (options.tags && options.tags.length > 0) { - await this.tagKey(cacheKey, options.tags); - } - } else { - loggers.cache.warn({ key: cacheKey }, 'Cache set failed'); - } - - return success; - } catch (error) { - loggers.cache.error({ error, key }, 'Cache set operation failed'); - return false; - } - }, - 'cache_set', - { key, namespace: options.namespace, ttl: options.ttl } - ); - } - - /** - * Delete value from cache - */ - async delete(key: string, options: CacheOptions = {}): Promise { - try { - const client = redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - - const result = await client.del(cacheKey); - const deleted = result > 0; - - if (deleted) { - redisManager.incrementDeletes(); - loggers.cache.debug({ key: cacheKey }, 'Cache delete successful'); - } - - return deleted; - } catch (error) { - loggers.cache.error({ error, key }, 'Cache delete operation failed'); - return false; - } - } - - /** - * Check if key exists in cache - */ - async exists(key: string, options: CacheOptions = {}): Promise { - try { - const client = redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - - const result = await client.exists(cacheKey); - return result === 1; - } catch (error) { - loggers.cache.error({ error, key }, 'Cache exists check failed'); - return false; - } - } - - /** - * Set expiration time for existing key - */ - async expire(key: string, ttl: number, options: CacheOptions = {}): Promise { - try { - const client = redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - - const result = await client.expire(cacheKey, ttl); - return result === 1; - } catch (error) { - loggers.cache.error({ error, key, ttl }, 'Cache expire operation failed'); - return false; - } - } - - /** - * Get remaining TTL for key - */ - async ttl(key: string, options: CacheOptions = {}): Promise { - try { - const client = redisManager.getClient(); - const cacheKey = this.generateKey(key, options.namespace); - - return await client.ttl(cacheKey); - } catch (error) { - loggers.cache.error({ error, key }, 'Cache TTL check failed'); - return -2; // Key doesn't exist - } - } - - /** - * Get multiple values at once - */ - async mget(keys: string[], options: CacheOptions = {}): Promise<(T | null)[]> { - return await performanceLogger.trackAsyncOperation( - async () => { - try { - const client = redisManager.getClient(); - const cacheKeys = keys.map((key) => this.generateKey(key, options.namespace)); - - const values = await client.mget(...cacheKeys); - - const results = await Promise.all( - values.map(async (value, index) => { - if (value === null) { - redisManager.incrementMisses(); - return null; - } - - redisManager.incrementHits(); - const decompressed = await this.decompress(value); - return this.deserialize(decompressed, options) as T; - }) - ); - - loggers.cache.debug( - { - keys: cacheKeys, - hits: values.filter((v) => v !== null).length, - misses: values.filter((v) => v === null).length, - }, - 'Cache mget operation' - ); - - return results; - } catch (error) { - loggers.cache.error({ error, keys }, 'Cache mget operation failed'); - return keys.map(() => null); - } - }, - 'cache_mget', - { keyCount: keys.length, namespace: options.namespace } - ); - } - - /** - * Set multiple values at once - */ - async mset(keyValuePairs: Record, options: CacheOptions = {}): Promise { - return await performanceLogger.trackAsyncOperation( - async () => { - try { - const client = redisManager.getClient(); - const pipeline = client.pipeline(); - - const ttl = options.ttl || this.defaultTTL; - - for (const [key, value] of Object.entries(keyValuePairs)) { - const cacheKey = this.generateKey(key, options.namespace); - const serialized = this.serialize(value, options); - const compressed = await this.compress(serialized, options); - - pipeline.setex(cacheKey, ttl, compressed); - } - - const results = await pipeline.exec(); - const success = - results?.every(([err, result]) => err === null && result === 'OK') || false; - - if (success) { - redisManager.incrementSets(); - loggers.cache.debug( - { - keyCount: Object.keys(keyValuePairs).length, - ttl, - }, - 'Cache mset operation successful' - ); - } - - return success; - } catch (error) { - loggers.cache.error( - { error, keyCount: Object.keys(keyValuePairs).length }, - 'Cache mset operation failed' - ); - return false; - } - }, - 'cache_mset', - { keyCount: Object.keys(keyValuePairs).length, namespace: options.namespace } - ); - } - - /** - * Delete multiple keys at once - */ - async mdel(keys: string[], options: CacheOptions = {}): Promise { - try { - const client = redisManager.getClient(); - const cacheKeys = keys.map((key) => this.generateKey(key, options.namespace)); - - const result = await client.del(...cacheKeys); - - if (result > 0) { - redisManager.incrementDeletes(); - loggers.cache.debug({ keys: cacheKeys, deleted: result }, 'Cache mdel operation'); - } - - return result; - } catch (error) { - loggers.cache.error({ error, keys }, 'Cache mdel operation failed'); - return 0; - } - } - - /** - * Clear all cache keys with pattern - */ - async clear(pattern?: string, options: CacheOptions = {}): Promise { - try { - const client = redisManager.getClient(); - const searchPattern = pattern - ? this.generateKey(pattern, options.namespace) - : this.generateKey('*', options.namespace); - - const keys = await client.keys(searchPattern); - - if (keys.length === 0) { - return 0; - } - - const result = await client.del(...keys); - - loggers.cache.info({ pattern: searchPattern, deleted: result }, 'Cache clear operation'); - - return result; - } catch (error) { - loggers.cache.error({ error, pattern }, 'Cache clear operation failed'); - return 0; - } - } - - /** - * Tag a key for bulk invalidation - */ - private async tagKey(key: string, tags: string[]): Promise { - try { - const client = redisManager.getClient(); - const pipeline = client.pipeline(); - - for (const tag of tags) { - const tagKey = this.generateKey(`tag:${tag}`, 'general'); - pipeline.sadd(tagKey, key); - pipeline.expire(tagKey, this.defaultTTL * 2); // Tags live longer than data - } - - await pipeline.exec(); - } catch (error) { - loggers.cache.error({ error, key, tags }, 'Failed to tag cache key'); - } - } - - /** - * Invalidate all keys with specific tags - */ - async invalidateByTags(tags: string[]): Promise { - try { - const client = redisManager.getClient(); - let totalDeleted = 0; - - for (const tag of tags) { - const tagKey = this.generateKey(`tag:${tag}`, 'general'); - const keys = await client.smembers(tagKey); - - if (keys.length > 0) { - const deleted = await client.del(...keys); - totalDeleted += deleted; - } - - // Remove the tag itself - await client.del(tagKey); - } - - loggers.cache.info({ tags, deleted: totalDeleted }, 'Cache invalidation by tags'); - - return totalDeleted; - } catch (error) { - loggers.cache.error({ error, tags }, 'Cache invalidation by tags failed'); - return 0; - } - } - - /** - * Get cache statistics - */ - async getStats(): Promise { - try { - const client = redisManager.getClient(); - const info = await client.info('memory'); - const keyspace = await client.info('keyspace'); - const metrics = redisManager.getMetrics(); - - // Parse memory usage - const memoryMatch = info.match(/used_memory:(\d+)/); - const memoryUsage = memoryMatch && memoryMatch[1] ? parseInt(memoryMatch[1], 10) : 0; - - // Parse total keys - const keyspaceMatch = keyspace.match(/keys=(\d+)/); - const totalKeys = keyspaceMatch && keyspaceMatch[1] ? parseInt(keyspaceMatch[1], 10) : 0; - - return { - totalKeys, - memoryUsage, - hitRate: metrics.hitRate, - operations: { - gets: metrics.hits + metrics.misses, - sets: metrics.sets, - deletes: metrics.deletes, - hits: metrics.hits, - misses: metrics.misses, - }, - }; - } catch (error) { - loggers.cache.error({ error }, 'Failed to get cache stats'); - return { - totalKeys: 0, - memoryUsage: 0, - hitRate: 0, - operations: { gets: 0, sets: 0, deletes: 0, hits: 0, misses: 0 }, - }; - } - } - - /** - * Cache wrapper for functions - */ - async wrap(key: string, fn: () => Promise, options: CacheOptions = {}): Promise { - // Try to get from cache first - const cached = await this.get(key, options); - if (cached !== null) { - return cached; - } - - // Execute function and cache result - const result = await fn(); - await this.set(key, result, options); - - return result; - } - - // Namespace-specific helper methods - users = { - get: (key: string, options?: CacheOptions) => - this.get(key, { ...options, namespace: this.namespaces.users }), - set: (key: string, value: any, options?: CacheSetOptions) => - this.set(key, value, { ...options, namespace: this.namespaces.users }), - delete: (key: string) => this.delete(key, { namespace: this.namespaces.users }), - clear: (pattern?: string) => this.clear(pattern, { namespace: this.namespaces.users }), - }; - - channels = { - get: (key: string, options?: CacheOptions) => - this.get(key, { ...options, namespace: this.namespaces.channels }), - set: (key: string, value: any, options?: CacheSetOptions) => - this.set(key, value, { ...options, namespace: this.namespaces.channels }), - delete: (key: string) => this.delete(key, { namespace: this.namespaces.channels }), - clear: (pattern?: string) => this.clear(pattern, { namespace: this.namespaces.channels }), - }; - - tasks = { - get: (key: string, options?: CacheOptions) => - this.get(key, { ...options, namespace: this.namespaces.tasks }), - set: (key: string, value: any, options?: CacheSetOptions) => - this.set(key, value, { ...options, namespace: this.namespaces.tasks }), - delete: (key: string) => this.delete(key, { namespace: this.namespaces.tasks }), - clear: (pattern?: string) => this.clear(pattern, { namespace: this.namespaces.tasks }), - }; - - sessions = { - get: (key: string, options?: CacheOptions) => - this.get(key, { ...options, namespace: this.namespaces.sessions }), - set: (key: string, value: any, options?: CacheSetOptions) => - this.set(key, value, { ...options, namespace: this.namespaces.sessions }), - delete: (key: string) => this.delete(key, { namespace: this.namespaces.sessions }), - clear: (pattern?: string) => this.clear(pattern, { namespace: this.namespaces.sessions }), - }; -} - -// Export singleton instance -export const cacheService = new CacheService(); -export default cacheService; diff --git a/backend/src/services/EmailService.ts b/backend/src/services/EmailService.ts deleted file mode 100644 index 08ef02f..0000000 --- a/backend/src/services/EmailService.ts +++ /dev/null @@ -1,244 +0,0 @@ -import nodemailer from 'nodemailer'; -import { logger } from '@utils/logger'; - -export interface EmailOptions { - to: string; - subject: string; - html: string; - text?: string; -} - -export interface EmailVerificationData { - userEmail: string; - userName: string; - verificationToken: string; -} - -export interface PasswordResetData { - userEmail: string; - userName: string; - resetToken: string; -} - -class EmailService { - private transporter!: nodemailer.Transporter; - private readonly frontendUrl: string; - - constructor() { - this.frontendUrl = process.env.FRONTEND_URL || 'http://localhost:3000'; - this.setupTransporter(); - } - - private setupTransporter() { - if (process.env.NODE_ENV === 'development') { - // For development, log emails to console to avoid rate limiting - this.transporter = nodemailer.createTransport({ - streamTransport: true, - newline: 'unix', - buffer: true, - }); - } else { - // Production configuration - const emailConfig = { - host: process.env.SMTP_HOST || 'smtp.gmail.com', - port: parseInt(process.env.SMTP_PORT || '587'), - secure: process.env.SMTP_SECURE === 'true', - auth: { - user: process.env.SMTP_USER, - pass: process.env.SMTP_PASS, - }, - }; - - this.transporter = nodemailer.createTransport(emailConfig); - } - } - - async sendEmail(options: EmailOptions): Promise { - try { - const info = await this.transporter.sendMail({ - from: process.env.FROM_EMAIL || 'noreply@ceocommunication.com', - to: options.to, - subject: options.subject, - html: options.html, - text: options.text, - }); - - if (process.env.NODE_ENV === 'development') { - // In development, log the email content instead of sending - logger.info( - { - messageId: info.messageId, - to: options.to, - subject: options.subject, - emailContent: info.message.toString(), - }, - 'Email logged (development mode - avoiding rate limits)' - ); - } else { - logger.info( - { - messageId: info.messageId, - to: options.to, - subject: options.subject, - }, - 'Email sent' - ); - } - - return true; - } catch (error) { - logger.error({ error, to: options.to, subject: options.subject }, 'Failed to send email'); - return false; - } - } - - async sendEmailVerification(data: EmailVerificationData): Promise { - const verificationUrl = `${this.frontendUrl}/verify-email/${data.verificationToken}`; - - const html = ` - - - - - Verify Your Email - - - -
-
-

CEO Communication Platform

-
-
-

Welcome, ${data.userName}!

-

Thank you for registering with the CEO Communication Platform. To complete your registration, please verify your email address by clicking the button below:

- - - -

If the button doesn't work, you can copy and paste this link into your browser:

-

- ${verificationUrl} -

- -

Important: This verification link will expire in 24 hours for security reasons.

- -

If you didn't create an account with us, please ignore this email.

-
- -
- - - `; - - const text = ` -Welcome to CEO Communication Platform, ${data.userName}! - -Please verify your email address by visiting this link: -${verificationUrl} - -This link will expire in 24 hours. - -If you didn't create an account with us, please ignore this email. - `; - - return this.sendEmail({ - to: data.userEmail, - subject: 'Verify Your Email Address - CEO Communication Platform', - html, - text, - }); - } - - async sendPasswordReset(data: PasswordResetData): Promise { - const resetUrl = `${this.frontendUrl}/reset-password/${data.resetToken}`; - - const html = ` - - - - - Password Reset - - - -
-
-

CEO Communication Platform

-
-
-

Password Reset Request

-

Hello ${data.userName},

-

We received a request to reset your password for your CEO Communication Platform account. If you made this request, click the button below to reset your password:

- - - -

If the button doesn't work, you can copy and paste this link into your browser:

-

- ${resetUrl} -

- -
- Security Notice: -
    -
  • This password reset link will expire in 1 hour
  • -
  • If you didn't request this reset, please ignore this email
  • -
  • Your password will remain unchanged until you use this link
  • -
-
- -

For security reasons, if you continue to receive unwanted password reset emails, please contact our support team.

-
- -
- - - `; - - const text = ` -Password Reset Request - CEO Communication Platform - -Hello ${data.userName}, - -We received a request to reset your password. If you made this request, please visit this link to reset your password: - -${resetUrl} - -This link will expire in 1 hour for security reasons. - -If you didn't request this reset, please ignore this email. Your password will remain unchanged. - `; - - return this.sendEmail({ - to: data.userEmail, - subject: 'Password Reset Request - CEO Communication Platform', - html, - text, - }); - } -} - -export const emailService = new EmailService(); diff --git a/backend/src/services/index.ts b/backend/src/services/index.ts deleted file mode 100644 index 19a73f1..0000000 --- a/backend/src/services/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -/** - * Services Index - * Re-exports all services for easy importing - */ - -export { cacheService } from './CacheService'; -export * from './CacheService'; -export { emailService } from './EmailService'; -export * from './EmailService'; \ No newline at end of file diff --git a/backend/src/utils/cache-decorators.ts b/backend/src/utils/cache-decorators.ts deleted file mode 100644 index d177117..0000000 --- a/backend/src/utils/cache-decorators.ts +++ /dev/null @@ -1,426 +0,0 @@ -import { cacheService, CacheOptions } from '../services/CacheService'; -import { logger, loggers } from './logger'; - -/** - * Cache decorators for automatic caching of method results - * Enterprise-grade caching with TTL, invalidation, and error handling - */ - -export interface CacheableOptions extends CacheOptions { - keyGenerator?: (...args: any[]) => string; - condition?: (...args: any[]) => boolean; - invalidateOnError?: boolean; -} - -/** - * Decorator for caching method results - */ -export function Cacheable(options: CacheableOptions = {}) { - return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { - const originalMethod = descriptor.value; - - descriptor.value = async function (...args: any[]) { - const className = target.constructor.name; - const methodName = propertyKey; - - // Check condition if provided - if (options.condition && !options.condition.apply(this, args)) { - return await originalMethod.apply(this, args); - } - - // Generate cache key - const defaultKeyGenerator = (...args: any[]) => { - const serializedArgs = args - .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))) - .join(':'); - return `${className}:${methodName}:${serializedArgs}`; - }; - - const keyGenerator = options.keyGenerator || defaultKeyGenerator; - const cacheKey = keyGenerator.apply(this, args); - - try { - // Try to get from cache - const getOptions: CacheOptions = {}; - if (typeof options.namespace !== 'undefined') { - getOptions.namespace = options.namespace as string; - } - if (typeof options.serialize !== 'undefined') { - getOptions.serialize = options.serialize; - } - const cached = await cacheService.get(cacheKey, getOptions); - - if (cached !== null) { - loggers.cache.debug?.( - { - className, - methodName, - cacheKey, - hit: true, - }, - 'Cache hit for method' - ); - return cached; - } - - // Execute original method - const result = await originalMethod.apply(this, args); - - // Cache the result - const setOptions: any = {}; - if (typeof options.ttl !== 'undefined') setOptions.ttl = options.ttl; - if (typeof options.namespace !== 'undefined') setOptions.namespace = options.namespace; - if (typeof options.serialize !== 'undefined') setOptions.serialize = options.serialize; - if (typeof options.compress !== 'undefined') setOptions.compress = options.compress; - if (typeof options.tags !== 'undefined') setOptions.tags = options.tags; - - await cacheService.set(cacheKey, result, setOptions); - - loggers.cache.debug?.( - { - className, - methodName, - cacheKey, - hit: false, - ttl: options.ttl, - }, - 'Cache miss, result cached' - ); - - return result; - } catch (cacheError) { - loggers.cache.warn?.( - { - error: cacheError, - className, - methodName, - cacheKey, - }, - 'Cache operation failed, executing method directly' - ); - - // Fall back to executing the method directly - return await originalMethod.apply(this, args); - } - }; - - return descriptor; - }; -} - -/** - * Decorator for cache invalidation on method execution - */ -export function CacheEvict( - options: { - keys?: string | string[] | ((...args: any[]) => string | string[]); - allEntries?: boolean; - beforeInvocation?: boolean; - namespace?: string; - tags?: string[]; - condition?: (...args: any[]) => boolean; - } = {} -) { - return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { - const originalMethod = descriptor.value; - - descriptor.value = async function (...args: any[]) { - const className = target.constructor.name; - const methodName = propertyKey; - - // Check condition if provided - if (options.condition && !options.condition.apply(this, args)) { - return await originalMethod.apply(this, args); - } - - const evictCache = async () => { - try { - if (options.tags && options.tags.length > 0) { - // Invalidate by tags - const invalidated = await cacheService.invalidateByTags(options.tags); - loggers.cache.info?.( - { - className, - methodName, - tags: options.tags, - invalidated, - }, - 'Cache invalidated by tags' - ); - } else if (options.allEntries) { - // Clear all entries in namespace - const clearOptions: CacheOptions = {}; - if (typeof options.namespace !== 'undefined') { - clearOptions.namespace = options.namespace as string; - } - const cleared = await cacheService.clear(undefined, clearOptions); - loggers.cache.info?.( - { - className, - methodName, - namespace: options.namespace, - cleared, - }, - 'All cache entries cleared' - ); - } else if (options.keys) { - // Invalidate specific keys - const keysToEvict = - typeof options.keys === 'function' ? options.keys.apply(this, args) : options.keys; - - const keys = Array.isArray(keysToEvict) ? keysToEvict : [keysToEvict]; - - for (const key of keys) { - const deleteOptions: CacheOptions = {}; - if (typeof options.namespace !== 'undefined') { - deleteOptions.namespace = options.namespace as string; - } - await cacheService.delete(key, deleteOptions); - } - - loggers.cache.info?.( - { - className, - methodName, - keys, - namespace: options.namespace, - }, - 'Cache keys evicted' - ); - } - } catch (error) { - loggers.cache.error?.( - { - error, - className, - methodName, - }, - 'Cache eviction failed' - ); - } - }; - - if (options.beforeInvocation) { - await evictCache(); - } - - try { - const result = await originalMethod.apply(this, args); - - if (!options.beforeInvocation) { - await evictCache(); - } - - return result; - } catch (error) { - // If method fails and beforeInvocation is false, don't evict cache - if (options.beforeInvocation) { - loggers.cache.warn?.( - { - className, - methodName, - error, - }, - 'Method failed after cache eviction' - ); - } - throw error; - } - }; - - return descriptor; - }; -} - -/** - * Decorator for caching with automatic invalidation - */ -export function CachePut( - options: { - key?: string | ((...args: any[]) => string); - namespace?: string; - ttl?: number; - condition?: (...args: any[]) => boolean; - unless?: (result: any) => boolean; - tags?: string[]; - } = {} -) { - return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) { - const originalMethod = descriptor.value; - - descriptor.value = async function (...args: any[]) { - const className = target.constructor.name; - const methodName = propertyKey; - - // Check condition if provided - if (options.condition && !options.condition.apply(this, args)) { - return await originalMethod.apply(this, args); - } - - const result = await originalMethod.apply(this, args); - - // Check unless condition - if (options.unless && options.unless(result)) { - return result; - } - - try { - // Generate cache key - const defaultKey = `${className}:${methodName}:${args - .map((arg) => (typeof arg === 'object' ? JSON.stringify(arg) : String(arg))) - .join(':')}`; - - const cacheKey = options.key - ? typeof options.key === 'function' - ? options.key.apply(this, args) - : options.key - : defaultKey; - - // Cache the result - const setOptions: any = {}; - if (typeof options.ttl !== 'undefined') setOptions.ttl = options.ttl; - if (typeof options.namespace !== 'undefined') setOptions.namespace = options.namespace; - if (typeof options.tags !== 'undefined') setOptions.tags = options.tags; - await cacheService.set(cacheKey, result, setOptions); - - loggers.cache.debug?.( - { - className, - methodName, - cacheKey, - namespace: options.namespace, - ttl: options.ttl, - }, - 'Result cached via @CachePut' - ); - } catch (error) { - loggers.cache.warn?.( - { - error, - className, - methodName, - }, - 'Failed to cache result via @CachePut' - ); - } - - return result; - }; - - return descriptor; - }; -} - -/** - * Class-level cache configuration - */ -export function CacheConfig(config: { - namespace?: string; - defaultTTL?: number; - keyPrefix?: string; -}) { - return function (constructor: T) { - // Store cache configuration on the prototype - constructor.prototype._cacheConfig = config; - return constructor; - }; -} - -/** - * Utility functions for cache key generation - */ -export const CacheKeyUtils = { - /** - * Generate key from user ID - */ - userKey: (userId: string, suffix?: string) => `user:${userId}${suffix ? `:${suffix}` : ''}`, - - /** - * Generate key from channel ID - */ - channelKey: (channelId: string, suffix?: string) => - `channel:${channelId}${suffix ? `:${suffix}` : ''}`, - - /** - * Generate key from task ID - */ - taskKey: (taskId: string, suffix?: string) => `task:${taskId}${suffix ? `:${suffix}` : ''}`, - - /** - * Generate key from multiple parameters - */ - multiKey: (...parts: string[]) => parts.join(':'), - - /** - * Generate pagination key - */ - paginationKey: (baseKey: string, limit: number, offset: number) => - `${baseKey}:page:${limit}:${offset}`, - - /** - * Generate search key - */ - searchKey: (query: string, filters?: Record) => { - const filterKey = filters ? `:${JSON.stringify(filters)}` : ''; - return `search:${encodeURIComponent(query)}${filterKey}`; - }, -}; - -/** - * Cache warming utilities - */ -export class CacheWarmer { - /** - * Warm cache with user data - */ - static async warmUserCache(userId: string, userData: any): Promise { - try { - await cacheService.users.set( - CacheKeyUtils.userKey(userId), - userData, - { ttl: 3600 } // 1 hour - ); - } catch (error) { - loggers.cache.warn?.({ error, userId }, 'Failed to warm user cache'); - } - } - - /** - * Warm cache with channel data - */ - static async warmChannelCache(channelId: string, channelData: any): Promise { - try { - await cacheService.channels.set( - CacheKeyUtils.channelKey(channelId), - channelData, - { ttl: 1800 } // 30 minutes - ); - } catch (error) { - loggers.cache.warn?.({ error, channelId }, 'Failed to warm channel cache'); - } - } - - /** - * Warm cache with task data - */ - static async warmTaskCache(taskId: string, taskData: any): Promise { - try { - await cacheService.tasks.set( - CacheKeyUtils.taskKey(taskId), - taskData, - { ttl: 900 } // 15 minutes - ); - } catch (error) { - loggers.cache.warn?.({ error, taskId }, 'Failed to warm task cache'); - } - } -} - -export default { - Cacheable, - CacheEvict, - CachePut, - CacheConfig, - CacheKeyUtils, - CacheWarmer, -}; diff --git a/backend/src/utils/errors.ts b/backend/src/utils/errors.ts deleted file mode 100644 index e52c918..0000000 --- a/backend/src/utils/errors.ts +++ /dev/null @@ -1,292 +0,0 @@ -/** - * Custom error classes for the CEO Communication Platform - * Provides structured error handling with proper HTTP status codes - */ - -export abstract class BaseError extends Error { - abstract readonly statusCode: number; - abstract readonly code: string; - abstract readonly isOperational: boolean; - - constructor( - message: string, - public readonly context?: Record - ) { - super(message); - this.name = this.constructor.name; - - // Maintains proper stack trace for where our error was thrown - Error.captureStackTrace(this, this.constructor); - } - - toJSON() { - return { - name: this.name, - message: this.message, - statusCode: this.statusCode, - code: this.code, - context: this.context, - stack: this.stack, - }; - } -} - -// Authentication and Authorization Errors -export class AuthenticationError extends BaseError { - readonly statusCode = 401; - readonly code = 'AUTHENTICATION_FAILED'; - readonly isOperational = true; -} - -export class AuthorizationError extends BaseError { - readonly statusCode = 403; - readonly code = 'AUTHORIZATION_FAILED'; - readonly isOperational = true; -} - -export class TokenExpiredError extends BaseError { - readonly statusCode = 401; - readonly code = 'TOKEN_EXPIRED'; - readonly isOperational = true; -} - -export class InvalidTokenError extends BaseError { - readonly statusCode = 401; - readonly code = 'INVALID_TOKEN'; - readonly isOperational = true; -} - -// Validation Errors -export class ValidationError extends BaseError { - readonly statusCode = 400; - readonly code = 'VALIDATION_ERROR'; - readonly isOperational = true; - - constructor( - message: string, - public readonly validationErrors: Array<{ - field: string; - message: string; - value?: unknown; - }>, - context?: Record - ) { - super(message, context); - this.validationErrors = validationErrors; - } - - toJSON() { - return { - ...super.toJSON(), - validationErrors: this.validationErrors, - }; - } -} - -// Resource Errors -export class NotFoundError extends BaseError { - readonly statusCode = 404; - readonly code = 'RESOURCE_NOT_FOUND'; - readonly isOperational = true; -} - -export class ConflictError extends BaseError { - readonly statusCode = 409; - readonly code = 'RESOURCE_CONFLICT'; - readonly isOperational = true; -} - -export class DuplicateResourceError extends BaseError { - readonly statusCode = 409; - readonly code = 'DUPLICATE_RESOURCE'; - readonly isOperational = true; -} - -// Rate Limiting Errors -export class RateLimitError extends BaseError { - readonly statusCode = 429; - readonly code = 'RATE_LIMIT_EXCEEDED'; - readonly isOperational = true; - - constructor( - message: string, - public readonly retryAfter: number, - context?: Record - ) { - super(message, context); - } - - toJSON() { - return { - ...super.toJSON(), - retryAfter: this.retryAfter, - }; - } -} - -// Database Errors -export class DatabaseError extends BaseError { - readonly statusCode = 500; - readonly code = 'DATABASE_ERROR'; - readonly isOperational = true; -} - -export class DatabaseConnectionError extends BaseError { - readonly statusCode = 503; - readonly code = 'DATABASE_CONNECTION_ERROR'; - readonly isOperational = true; -} - -export class TransactionError extends BaseError { - readonly statusCode = 500; - readonly code = 'TRANSACTION_ERROR'; - readonly isOperational = true; -} - -// External Service Errors -export class ExternalServiceError extends BaseError { - readonly statusCode = 502; - readonly code = 'EXTERNAL_SERVICE_ERROR'; - readonly isOperational = true; - - constructor( - message: string, - public readonly service: string, - public readonly originalError?: Error, - context?: Record - ) { - super(message, context); - } - - toJSON() { - return { - ...super.toJSON(), - service: this.service, - originalError: this.originalError?.message, - }; - } -} - -// Configuration Errors -export class ConfigurationError extends BaseError { - readonly statusCode = 500; - readonly code = 'CONFIGURATION_ERROR'; - readonly isOperational = false; // Non-operational - requires restart/fix -} - -// Cache Errors -export class CacheError extends BaseError { - readonly statusCode = 500; - readonly code = 'CACHE_ERROR'; - readonly isOperational = true; -} - -// WebSocket Errors -export class WebSocketError extends BaseError { - readonly statusCode = 500; - readonly code = 'WEBSOCKET_ERROR'; - readonly isOperational = true; -} - -// Voice Processing Errors (for Phase 2) -export class VoiceProcessingError extends BaseError { - readonly statusCode = 422; - readonly code = 'VOICE_PROCESSING_ERROR'; - readonly isOperational = true; -} - -export class SpeechRecognitionError extends BaseError { - readonly statusCode = 422; - readonly code = 'SPEECH_RECOGNITION_ERROR'; - readonly isOperational = true; -} - -// Command Execution Errors (for Phase 3) -export class CommandExecutionError extends BaseError { - readonly statusCode = 422; - readonly code = 'COMMAND_EXECUTION_ERROR'; - readonly isOperational = true; -} - -export class CommandValidationError extends BaseError { - readonly statusCode = 400; - readonly code = 'COMMAND_VALIDATION_ERROR'; - readonly isOperational = true; -} - -// Business Logic Errors -export class BusinessLogicError extends BaseError { - readonly statusCode = 422; - readonly code = 'BUSINESS_LOGIC_ERROR'; - readonly isOperational = true; -} - -export class InsufficientPermissionsError extends BaseError { - readonly statusCode = 403; - readonly code = 'INSUFFICIENT_PERMISSIONS'; - readonly isOperational = true; -} - -// Utility functions for error handling -export const isOperationalError = (error: Error): boolean => { - return error instanceof BaseError && error.isOperational; -}; - -export const isAuthenticationError = (error: Error): boolean => { - return error instanceof AuthenticationError || - error instanceof AuthorizationError || - error instanceof TokenExpiredError || - error instanceof InvalidTokenError; -}; - -export const isDatabaseError = (error: Error): boolean => { - return error instanceof DatabaseError || - error instanceof DatabaseConnectionError || - error instanceof TransactionError; -}; - -// Error context helpers -export const createErrorContext = ( - req?: { - user?: { id: string; email: string; role: string }; - ip?: string; - method?: string; - url?: string; - headers?: Record; - } -): Record => { - if (!req) return {}; - - return { - userId: req.user?.id, - userEmail: req.user?.email, - userRole: req.user?.role, - ip: req.ip, - method: req.method, - url: req.url, - userAgent: req.headers?.['user-agent'], - timestamp: new Date().toISOString(), - }; -}; - -// Error response formatter -export const formatErrorResponse = (error: BaseError) => { - return { - error: { - name: error.name, - message: error.message, - code: error.code, - statusCode: error.statusCode, - ...(error instanceof ValidationError && { - validationErrors: error.validationErrors - }), - ...(error instanceof RateLimitError && { - retryAfter: error.retryAfter - }), - ...(error instanceof ExternalServiceError && { - service: error.service - }), - }, - timestamp: new Date().toISOString(), - }; -}; \ No newline at end of file diff --git a/backend/src/utils/logger.ts b/backend/src/utils/logger.ts deleted file mode 100644 index 3218364..0000000 --- a/backend/src/utils/logger.ts +++ /dev/null @@ -1,274 +0,0 @@ -import pino, { Logger } from 'pino'; -import { config } from '@config/index'; - -// Create logger configuration based on environment -const loggerConfig = { - level: config.logging.level, - formatters: { - level: (label: string) => ({ level: label }), - }, - timestamp: pino.stdTimeFunctions.isoTime, - ...(config.app.isDevelopment && config.logging.format === 'pretty' - ? { - transport: { - target: 'pino-pretty', - options: { - colorize: true, - translateTime: 'HH:MM:ss Z', - ignore: 'pid,hostname', - messageFormat: '{time} [{level}] {component}: {msg}', - hideObject: false, - singleLine: false, - }, - }, - } - : {}), - // Reduce noise from frequent operations - redact: { - paths: ['password', 'token', 'secret', 'key'], - censor: '[REDACTED]' - }, -}; - -// Create base logger -const baseLogger: Logger = pino(loggerConfig); - -// Logger interface with context support -export interface ContextLogger extends Logger { - withContext(context: Record): Logger; -} - -// Enhanced logger with context support -export const logger: ContextLogger = Object.assign(baseLogger, { - withContext: (context: Record) => baseLogger.child(context), -}); - -// Specialized loggers for different components -export const loggers = { - // Database operations - db: logger.child({ component: 'database' }), - - // Authentication and security - auth: logger.child({ component: 'auth' }), - - // API requests and responses - api: logger.child({ component: 'api' }), - - // WebSocket real-time communication - websocket: logger.child({ component: 'websocket' }), - - // Caching operations - cache: logger.child({ component: 'cache' }), - - // Voice processing (for Phase 2) - voice: logger.child({ component: 'voice' }), - - // AI operations (for Phase 2-3) - ai: logger.child({ component: 'ai' }), - - // Command execution (for Phase 3) - commands: logger.child({ component: 'commands' }), - - // Analytics and monitoring (for Phase 4) - analytics: logger.child({ component: 'analytics' }), - - // Performance monitoring - performance: logger.child({ component: 'performance' }), - - // Security events - security: logger.child({ component: 'security' }), -} as const; - -// Performance tracking utilities -export const performanceLogger = { - /** - * Track execution time of async operations - */ - trackAsyncOperation: async ( - operation: () => Promise, - operationName: string, - context?: Record - ): Promise => { - const startTime = Date.now(); - const operationLogger = logger.withContext({ operation: operationName, ...context }); - - try { - // Only log start for critical operations or when debug level is enabled - if (config.logging.level === 'debug' || operationName.includes('migration') || operationName.includes('startup')) { - operationLogger.debug('Operation started'); - } - - const result = await operation(); - const duration = Date.now() - startTime; - - // Only log completion for operations that take > 500ms or are critical - if (duration > 500 || operationName.includes('migration') || operationName.includes('startup')) { - operationLogger.info({ duration }, 'Operation completed'); - } else if (config.logging.level === 'debug') { - operationLogger.debug({ duration }, 'Operation completed'); - } - - // Log performance warning if operation takes too long - if (duration > 1000) { - loggers.performance.warn({ operation: operationName, duration }, 'Slow operation detected'); - } - - return result; - } catch (error) { - const duration = Date.now() - startTime; - operationLogger.error({ error, duration }, 'Operation failed'); - throw error; - } - }, - - /** - * Track sync operation performance - */ - trackSyncOperation: ( - operation: () => T, - operationName: string, - context?: Record - ): T => { - const startTime = Date.now(); - const operationLogger = logger.withContext({ operation: operationName, ...context }); - - try { - operationLogger.debug('Sync operation started'); - const result = operation(); - const duration = Date.now() - startTime; - - operationLogger.debug({ duration }, 'Sync operation completed'); - - // Log performance warning for slow sync operations - if (duration > 100) { - loggers.performance.warn({ operation: operationName, duration }, 'Slow sync operation'); - } - - return result; - } catch (error) { - const duration = Date.now() - startTime; - operationLogger.error({ error, duration }, 'Sync operation failed'); - throw error; - } - }, -}; - -// Security event logging -export const securityLogger = { - /** - * Log authentication events - */ - logAuthEvent: (event: 'login' | 'logout' | 'token_refresh' | 'failed_login' | 'login_attempt' | 'registration_attempt' | 'registration_success' | 'password_reset_requested' | 'password_reset' | 'email_verified' | 'token_generated' | 'token_refreshed' | 'authentication_success' | 'refresh_token_expired' | 'missing_token', context: { - userId?: string; - email?: string; - ip?: string; - userAgent?: string; - [key: string]: unknown; - }) => { - loggers.security.info({ event, ...context }, 'Authentication event'); - }, - - /** - * Log authorization events - */ - logAuthzEvent: (event: 'access_granted' | 'access_denied', context: { - userId: string; - resource: string; - action: string; - ip?: string; - [key: string]: unknown; - }) => { - loggers.security.info({ event, ...context }, 'Authorization event'); - }, - - /** - * Log security violations - */ - logSecurityViolation: (violation: string, context: Record) => { - loggers.security.error({ violation, ...context }, 'Security violation detected'); - }, -}; - -// Startup logging utilities to reduce verbosity during initialization -export const startupLogger = { - /** - * Log startup steps with grouped output - */ - logStep: (step: string, status: 'starting' | 'completed' | 'failed' = 'starting') => { - switch (status) { - case 'starting': - logger.info(`📋 ${step}...`); - break; - case 'completed': - logger.info(`✅ ${step}`); - break; - case 'failed': - logger.error(`❌ ${step}`); - break; - } - }, - - /** - * Log initialization summary - */ - logSummary: (services: Array<{ name: string; status: boolean; duration?: number }>) => { - logger.info('\n🚀 Server Initialization Summary:'); - services.forEach(service => { - const status = service.status ? '✅' : '❌'; - const duration = service.duration ? ` (${service.duration}ms)` : ''; - logger.info(` ${status} ${service.name}${duration}`); - }); - }, - - /** - * Create a startup timer - */ - createTimer: (name: string) => { - const start = Date.now(); - return { - end: () => Date.now() - start, - log: (status: 'completed' | 'failed' = 'completed') => { - const duration = Date.now() - start; - startupLogger.logStep(`${name} (${duration}ms)`, status); - return duration; - } - }; - } -}; - -// Utility to reduce metrics logging noise -export const metricsLogger = { - /** - * Log metrics only when significant changes occur - */ - logMetricsIfSignificant: ( - component: string, - currentMetrics: Record, - previousMetrics?: Record, - threshold = 0.1 - ) => { - if (!previousMetrics) { - loggers[component as keyof typeof loggers]?.info({ metrics: currentMetrics }, `${component} metrics`); - return; - } - - // Check if metrics have changed significantly - let hasSignificantChange = false; - for (const [key, value] of Object.entries(currentMetrics)) { - if (typeof value === 'number' && typeof previousMetrics[key] === 'number') { - const change = Math.abs(value - previousMetrics[key]) / (previousMetrics[key] || 1); - if (change > threshold) { - hasSignificantChange = true; - break; - } - } - } - - if (hasSignificantChange) { - loggers[component as keyof typeof loggers]?.info({ metrics: currentMetrics }, `${component} metrics`); - } - } -}; - -// Export default logger -export default logger; \ No newline at end of file diff --git a/backend/src/utils/performance.ts b/backend/src/utils/performance.ts deleted file mode 100644 index 2c4d8cd..0000000 --- a/backend/src/utils/performance.ts +++ /dev/null @@ -1,450 +0,0 @@ -import { performance } from 'perf_hooks'; -import { logger, loggers } from './logger'; - -/** - * Performance monitoring and metrics collection utilities - * Enterprise-grade performance tracking for the API - */ - -export interface PerformanceMetric { - name: string; - duration: number; - timestamp: number; - context?: Record; - tags?: string[]; -} - -export interface MemoryMetrics { - rss: number; - heapTotal: number; - heapUsed: number; - external: number; - arrayBuffers: number; -} - -export interface CPUMetrics { - user: number; - system: number; -} - -export class PerformanceMonitor { - private static instance: PerformanceMonitor; - private metrics: Map = new Map(); - private activeTimers: Map = new Map(); - private readonly maxMetricsPerType = 1000; // Prevent memory leaks - - private constructor() { - // Start periodic cleanup - setInterval(() => this.cleanup(), 60000); // Every minute - } - - static getInstance(): PerformanceMonitor { - if (!PerformanceMonitor.instance) { - PerformanceMonitor.instance = new PerformanceMonitor(); - } - return PerformanceMonitor.instance; - } - - /** - * Start timing an operation - */ - startTimer(operationName: string, context?: Record): string { - const timerId = `${operationName}_${Date.now()}_${Math.random()}`; - this.activeTimers.set(timerId, performance.now()); - - loggers.performance.debug?.( - { - operation: operationName, - timerId, - context, - }, - 'Performance timer started' - ); - - return timerId; - } - - /** - * End timing an operation and record metric - */ - endTimer(timerId: string, tags?: string[]): PerformanceMetric | null { - const startTime = this.activeTimers.get(timerId); - if (!startTime) { - loggers.performance.warn?.({ timerId }, 'Timer not found'); - return null; - } - - const endTime = performance.now(); - const duration = endTime - startTime; - this.activeTimers.delete(timerId); - - const operationName = timerId.split('_')[0] || 'unknown'; - const metric: PerformanceMetric = { - name: operationName, - duration, - timestamp: Date.now(), - ...(tags && { tags }), - }; - - this.recordMetric(metric); - - loggers.performance.debug?.( - { - operation: operationName, - duration, - timerId, - tags, - }, - 'Performance timer completed' - ); - - return metric; - } - - /** - * Time a synchronous operation - */ - timeSync(operationName: string, operation: () => T, context?: Record): T { - const startTime = performance.now(); - - try { - const result = operation(); - const duration = performance.now() - startTime; - - this.recordMetric({ - name: operationName, - duration, - timestamp: Date.now(), - ...(context && { context }), - }); - - loggers.performance.debug?.( - { - operation: operationName, - duration, - context, - }, - 'Sync operation timed' - ); - - return result; - } catch (error) { - const duration = performance.now() - startTime; - - loggers.performance.error( - { - operation: operationName, - duration, - error, - context, - }, - 'Sync operation failed' - ); - - throw error; - } - } - - /** - * Time an asynchronous operation - */ - async timeAsync( - operationName: string, - operation: () => Promise, - context?: Record - ): Promise { - const startTime = performance.now(); - - try { - const result = await operation(); - const duration = performance.now() - startTime; - - this.recordMetric({ - name: operationName, - duration, - timestamp: Date.now(), - ...(context && { context }), - }); - - loggers.performance.debug?.( - { - operation: operationName, - duration, - context, - }, - 'Async operation timed' - ); - - // Log slow operations - if (duration > 1000) { - loggers.performance.warn?.( - { - operation: operationName, - duration, - context, - }, - 'Slow async operation detected' - ); - } - - return result; - } catch (error) { - const duration = performance.now() - startTime; - - loggers.performance.error( - { - operation: operationName, - duration, - error, - context, - }, - 'Async operation failed' - ); - - throw error; - } - } - - /** - * Record a performance metric - */ - recordMetric(metric: PerformanceMetric): void { - if (!this.metrics.has(metric.name)) { - this.metrics.set(metric.name, []); - } - - const metrics = this.metrics.get(metric.name)!; - metrics.push(metric); - - // Prevent memory leaks by keeping only recent metrics - if (metrics.length > this.maxMetricsPerType) { - metrics.splice(0, metrics.length - this.maxMetricsPerType); - } - } - - /** - * Get performance statistics for an operation - */ - getStats(operationName: string): { - count: number; - min: number; - max: number; - avg: number; - p50: number; - p95: number; - p99: number; - } | null { - const metrics = this.metrics.get(operationName); - if (!metrics || metrics.length === 0) { - return null; - } - - const durations = metrics.map((m) => m.duration).sort((a, b) => a - b); - const count = durations.length; - - return { - count, - min: durations[0] ?? 0, - max: durations[count - 1] ?? 0, - avg: durations.length > 0 ? durations.reduce((sum, d) => sum + d, 0) / count : 0, - p50: durations.length > 0 ? this.percentile(durations, 50) : 0, - p95: durations.length > 0 ? this.percentile(durations, 95) : 0, - p99: durations.length > 0 ? this.percentile(durations, 99) : 0, - }; - } - - /** - * Get all performance statistics - */ - getAllStats(): Record> { - const stats: Record> = {}; - - for (const operationName of this.metrics.keys()) { - stats[operationName] = this.getStats(operationName); - } - - return stats; - } - - /** - * Get current memory usage - */ - getMemoryMetrics(): MemoryMetrics { - const memUsage = process.memoryUsage(); - return { - rss: Math.round(memUsage.rss / 1024 / 1024), // MB - heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024), // MB - heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024), // MB - external: Math.round(memUsage.external / 1024 / 1024), // MB - arrayBuffers: Math.round(memUsage.arrayBuffers / 1024 / 1024), // MB - }; - } - - /** - * Get current CPU usage - */ - getCPUMetrics(): CPUMetrics { - const cpuUsage = process.cpuUsage(); - return { - user: cpuUsage.user, - system: cpuUsage.system, - }; - } - - /** - * Get comprehensive system metrics - */ - getSystemMetrics(): { - memory: MemoryMetrics; - cpu: CPUMetrics; - uptime: number; - performance: Record< - string, - { - count: number; - min: number; - max: number; - avg: number; - p50: number; - p95: number; - p99: number; - } | null - >; - } { - return { - memory: this.getMemoryMetrics(), - cpu: this.getCPUMetrics(), - uptime: process.uptime(), - performance: this.getAllStats(), - }; - } - - /** - * Clear all metrics (useful for testing) - */ - clearMetrics(): void { - this.metrics.clear(); - this.activeTimers.clear(); - } - - /** - * Log system metrics at regular intervals - */ - startMetricsLogging(intervalMs: number = 30000): NodeJS.Timer { - return setInterval(() => { - const metrics = this.getSystemMetrics(); - - loggers.performance.info( - { - memory: metrics.memory, - cpu: metrics.cpu, - uptime: metrics.uptime, - activeTimers: this.activeTimers.size, - metricsCount: Array.from(this.metrics.values()).reduce((sum, arr) => sum + arr.length, 0), - }, - 'System metrics snapshot' - ); - - // Log performance warnings - if (metrics.memory.heapUsed > 512) { - // More than 512MB - loggers.performance.warn( - { - heapUsed: metrics.memory.heapUsed, - }, - 'High memory usage detected' - ); - } - }, intervalMs); - } - - private percentile(sortedArray: number[], percentile: number): number { - const index = (percentile / 100) * (sortedArray.length - 1); - const lower = Math.floor(index); - const upper = Math.ceil(index); - const weight = index % 1; - - if (lower === upper) { - return sortedArray[lower] ?? 0; - } - - const lowerValue = sortedArray[lower] ?? 0; - const upperValue = sortedArray[upper] ?? 0; - return lowerValue * (1 - weight) + upperValue * weight; - } - - private cleanup(): void { - const now = Date.now(); - const maxAge = 5 * 60 * 1000; // 5 minutes - - for (const [operationName, metrics] of this.metrics.entries()) { - const validMetrics = metrics.filter((metric) => now - metric.timestamp < maxAge); - - if (validMetrics.length === 0) { - this.metrics.delete(operationName); - } else if (validMetrics.length !== metrics.length) { - this.metrics.set(operationName, validMetrics); - } - } - - // Clean up orphaned timers (older than 1 hour) - for (const [timerId, startTime] of this.activeTimers.entries()) { - if (performance.now() - startTime > 3600000) { - // 1 hour - this.activeTimers.delete(timerId); - loggers.performance.warn({ timerId }, 'Orphaned timer cleaned up'); - } - } - } -} - -// Export singleton instance -export const performanceMonitor = PerformanceMonitor.getInstance(); - -// Decorator for timing class methods -export function Timed(operationName?: string) { - return function (target: any, propertyName: string, descriptor: PropertyDescriptor) { - const method = descriptor.value; - const opName = operationName || `${target.constructor.name}.${propertyName}`; - - descriptor.value = async function (...args: any[]) { - return await performanceMonitor.timeAsync(opName, () => method.apply(this, args), { - className: target.constructor.name, - methodName: propertyName, - }); - }; - }; -} - -// Utility functions -export const createPerformanceMiddleware = () => { - return async (request: any, reply: any, next: () => void) => { - const timerId = performanceMonitor.startTimer('api_request', { - method: request.method, - url: request.url, - userId: request.user?.userId, - }); - - try { - await next(); - } finally { - const metric = performanceMonitor.endTimer(timerId, ['api', 'request']); - - if (metric && metric.duration > 2000) { - // Log slow requests (>2s) - loggers.performance.warn( - { - method: request.method, - url: request.url, - duration: metric.duration, - userId: request.user?.userId, - }, - 'Slow API request detected' - ); - } - } - }; -}; - -// Export performance monitoring utilities -export default performanceMonitor; diff --git a/backend/src/utils/validation.ts b/backend/src/utils/validation.ts deleted file mode 100644 index a6ad02b..0000000 --- a/backend/src/utils/validation.ts +++ /dev/null @@ -1,281 +0,0 @@ -import { Type, Static, TSchema } from '@sinclair/typebox'; -import { Value } from '@sinclair/typebox/value'; -import { ValidationError } from './errors'; - -/** - * Common validation schemas and utilities - * TypeBox schemas for request/response validation - */ - -// Base schemas -export const UUIDSchema = Type.String({ - format: 'uuid', - description: 'UUID v4 string' -}); - -export const EmailSchema = Type.String({ - format: 'email', - maxLength: 255, - description: 'Valid email address' -}); - -export const TimestampSchema = Type.String({ - format: 'date-time', - description: 'ISO 8601 timestamp' -}); - -export const PaginationSchema = Type.Object({ - limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100, default: 20 })), - offset: Type.Optional(Type.Integer({ minimum: 0, default: 0 })), - sortBy: Type.Optional(Type.String()), - sortOrder: Type.Optional(Type.Union([Type.Literal('asc'), Type.Literal('desc')], { default: 'desc' })) -}); - -// User-related schemas -export const UserRoleSchema = Type.Union([ - Type.Literal('ceo'), - Type.Literal('manager'), - Type.Literal('staff') -]); - -export const UserStatusSchema = Type.Union([ - Type.Literal('active'), - Type.Literal('inactive'), - Type.Literal('suspended'), - Type.Literal('pending') -]); - -// Task-related schemas -export const TaskPrioritySchema = Type.Union([ - Type.Literal('low'), - Type.Literal('medium'), - Type.Literal('high'), - Type.Literal('urgent'), - Type.Literal('critical') -]); - -export const TaskStatusSchema = Type.Union([ - Type.Literal('pending'), - Type.Literal('in_progress'), - Type.Literal('review'), - Type.Literal('completed'), - Type.Literal('cancelled'), - Type.Literal('on_hold') -]); - -export const BusinessValueSchema = Type.Union([ - Type.Literal('low'), - Type.Literal('medium'), - Type.Literal('high'), - Type.Literal('critical') -]); - -// Channel-related schemas -export const ChannelTypeSchema = Type.Union([ - Type.Literal('general'), - Type.Literal('project'), - Type.Literal('department'), - Type.Literal('announcement'), - Type.Literal('initiative'), - Type.Literal('temporary') -]); - -export const ChannelPrivacySchema = Type.Union([ - Type.Literal('public'), - Type.Literal('private'), - Type.Literal('restricted') -]); - -// Common response schemas -export const SuccessResponseSchema = Type.Object({ - success: Type.Boolean({ default: true }), - message: Type.Optional(Type.String()), - timestamp: TimestampSchema -}); - -export const ErrorResponseSchema = Type.Object({ - error: Type.Object({ - message: Type.String(), - code: Type.String(), - statusCode: Type.Integer(), - details: Type.Optional(Type.Array(Type.Object({ - field: Type.String(), - message: Type.String(), - value: Type.Optional(Type.Any()) - }))), - retryAfter: Type.Optional(Type.Integer()) - }), - timestamp: TimestampSchema -}); - -export const PaginatedResponseSchema = (dataSchema: T) => Type.Object({ - success: Type.Boolean({ default: true }), - data: Type.Array(dataSchema), - pagination: Type.Object({ - total: Type.Integer(), - limit: Type.Integer(), - offset: Type.Integer(), - hasMore: Type.Boolean() - }), - timestamp: TimestampSchema -}); - -// Validation utilities -export class SchemaValidator { - /** - * Validate data against schema and throw ValidationError if invalid - */ - static validate(schema: T, data: unknown): Static { - const errors = [...Value.Errors(schema, data)]; - - if (errors.length > 0) { - const validationErrors = errors.map(error => ({ - field: error.path.replace('/', '.').replace(/^\./, '') || 'root', - message: error.message, - value: error.value - })); - - throw new ValidationError( - 'Validation failed', - validationErrors - ); - } - - return data as Static; - } - - /** - * Check if data is valid against schema - */ - static isValid(schema: T, data: unknown): boolean { - return Value.Check(schema, data); - } - - /** - * Clean data according to schema (remove extra properties) - */ - static clean(schema: T, data: unknown): Static { - return (Value as any).Clean(schema, data) as Static; - } - - /** - * Apply default values from schema - */ - static withDefaults(schema: T, data: unknown): Static { - return (Value as any).Default(schema, data) as Static; - } -} - -// Custom validators -export const CustomValidators = { - /** - * Validate password strength - */ - password: (password: string): boolean => { - if (password.length < 8) return false; - if (!/[A-Z]/.test(password)) return false; - if (!/[a-z]/.test(password)) return false; - if (!/[0-9]/.test(password)) return false; - if (!/[^A-Za-z0-9]/.test(password)) return false; - return true; - }, - - /** - * Validate phone number format - */ - phoneNumber: (phone: string): boolean => { - const phoneRegex = /^\+?[1-9]\d{1,14}$/; - return phoneRegex.test(phone.replace(/[\s\-\(\)]/g, '')); - }, - - /** - * Validate URL format - */ - url: (url: string): boolean => { - try { - new URL(url); - return true; - } catch { - return false; - } - }, - - /** - * Validate slug format (for channels, etc.) - */ - slug: (slug: string): boolean => { - const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/; - return slugRegex.test(slug) && slug.length >= 2 && slug.length <= 50; - }, - - /** - * Validate color hex code - */ - hexColor: (color: string): boolean => { - const hexRegex = /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/; - return hexRegex.test(color); - } -}; - -// Request validation middleware factory -export const validateRequest = (schema: T) => { - return (data: unknown) => { - return SchemaValidator.validate(schema, data); - }; -}; - -// Common validation patterns -export const ValidationPatterns = { - // Strong password requirements - StrongPasswordSchema: Type.String({ - minLength: 8, - maxLength: 128, - pattern: '^(?=.*[a-z])(?=.*[A-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]', - description: 'Password must contain at least 8 characters, including uppercase, lowercase, number, and special character' - }), - - // Phone number schema - PhoneNumberSchema: Type.String({ - pattern: '^\\+?[1-9]\\d{1,14}$', - description: 'Valid international phone number' - }), - - // URL schema - UrlSchema: Type.String({ - format: 'uri', - maxLength: 2048, - description: 'Valid HTTP(S) URL' - }), - - // Slug schema (for channels, etc.) - SlugSchema: Type.String({ - pattern: '^[a-z0-9]+(?:-[a-z0-9]+)*$', - minLength: 2, - maxLength: 50, - description: 'Valid slug (lowercase letters, numbers, and hyphens)' - }), - - // Hex color schema - HexColorSchema: Type.String({ - pattern: '^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$', - description: 'Valid hex color code' - }), - - // Tag schema - TagSchema: Type.String({ - minLength: 1, - maxLength: 50, - pattern: '^[a-zA-Z0-9_-]+$', - description: 'Valid tag name' - }), - - // Name schema (for users, channels, etc.) - NameSchema: Type.String({ - minLength: 1, - maxLength: 255, - pattern: '^[^\\s]+(\\s+[^\\s]+)*$', // No leading/trailing whitespace, no double spaces - description: 'Valid name (no leading/trailing spaces)' - }) -}; - -export type ValidationPattern = keyof typeof ValidationPatterns; \ No newline at end of file diff --git a/backend/src/voice/VoiceProcessingService.ts b/backend/src/voice/VoiceProcessingService.ts deleted file mode 100644 index c7af0c3..0000000 --- a/backend/src/voice/VoiceProcessingService.ts +++ /dev/null @@ -1,570 +0,0 @@ -/** - * Voice Processing Service - Phase 2 Integration Layer - * Orchestrates the complete voice processing pipeline - * - * Success Criteria Integration: - * - End-to-end voice command processing <2 seconds (simple) / <5 seconds (complex) - * - Coordinates audio stream, transcription, AI parsing, and execution - * - Provides comprehensive error handling and performance monitoring - */ - -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import { logger } from '../utils/logger'; -import { AudioStreamManager, AudioSegment } from './stream/AudioStreamManager'; -import { WhisperService } from './transcription/WhisperService'; -import { AICommandParser } from '../ai/commands/AICommandParser'; -import { MultiActionExecutor } from '../ai/execution/MultiActionExecutor'; -import { - UserContext, - ParsedCommand, - VoiceProcessingError, - ProcessingResult, - VoiceProcessingMetrics, -} from './types'; - -export interface VoiceProcessingOptions { - enableCaching?: boolean; - maxProcessingTime?: number; - qualityThreshold?: number; - streamingMode?: boolean; -} - -export interface VoiceProcessingResult { - success: boolean; - command?: ParsedCommand; - metrics: VoiceProcessingMetrics; - error?: string; -} - -export class VoiceProcessingService extends EventEmitter { - private audioStreamManager: AudioStreamManager; - private whisperService: WhisperService; - private aiCommandParser: AICommandParser; - private multiActionExecutor: MultiActionExecutor; - private performanceMetrics: VoiceProcessingMetrics[] = []; - - constructor() { - super(); - - this.audioStreamManager = new AudioStreamManager(); - this.whisperService = new WhisperService(); - this.aiCommandParser = new AICommandParser(); - this.multiActionExecutor = new MultiActionExecutor(); - - this.setupEventHandlers(); - - logger.info('Voice Processing Service initialized'); - } - - /** - * Process complete voice command from audio to parsed command - * This is the main integration point for the voice processing pipeline - */ - async processVoiceCommand( - audioBuffer: Buffer, - userContext: UserContext, - options: VoiceProcessingOptions = {} - ): Promise { - const startTime = performance.now(); - const commandId = `cmd-${Date.now()}-${Math.random().toString(36).substring(7)}`; - - logger.info('Starting voice command processing', { - commandId, - userId: userContext.userId, - audioSize: audioBuffer.length, - options, - }); - - try { - // Step 1: Audio preprocessing and validation - const preprocessingStart = performance.now(); - await this.validateAudioInput(audioBuffer); - const preprocessingTime = performance.now() - preprocessingStart; - - this.emit('preprocessing_complete', { - commandId, - processingTime: preprocessingTime, - }); - - // Step 2: Transcription - const transcriptionStart = performance.now(); - const transcriptOptions: any = {}; - if (userContext.language !== undefined) { - transcriptOptions.language = userContext.language; - } - const transcriptResult = await this.whisperService.transcribeAudio( - audioBuffer, - transcriptOptions - ); - const transcriptionTime = performance.now() - transcriptionStart; - - if (!transcriptResult.transcript || transcriptResult.transcript.trim().length === 0) { - throw new VoiceProcessingError('Empty transcription result'); - } - - this.emit('transcription_complete', { - commandId, - transcript: transcriptResult.transcript, - confidence: transcriptResult.confidence, - processingTime: transcriptionTime, - }); - - // Step 3: AI Command Parsing - const parsingStart = performance.now(); - const parsedCommand = await this.aiCommandParser.parseVoiceCommand( - transcriptResult.transcript, - userContext, - { - timeout: options.maxProcessingTime || 10000, - enhanceWithContext: true, - } - ); - const parsingTime = performance.now() - parsingStart; - - this.emit('parsing_complete', { - commandId, - intent: parsedCommand.intent, - actionCount: parsedCommand.actions.length, - confidence: parsedCommand.confidence, - processingTime: parsingTime, - }); - - // Calculate total processing time - const totalTime = performance.now() - startTime; - - // Create comprehensive metrics - const metrics: VoiceProcessingMetrics = { - transcriptionTime, - processingTime: parsingTime, - executionTime: 0, // Will be set when command is executed - totalTime, - accuracy: Math.min(transcriptResult.confidence, parsedCommand.confidence), - success: true, - commandId, - userId: userContext.userId, - timestamp: new Date().toISOString(), - }; - - // Record metrics - this.recordMetrics(metrics); - - // Check performance targets - this.validatePerformanceTargets(totalTime, parsedCommand.actions.length); - - logger.info('Voice command processing completed successfully', { - commandId, - totalTime: `${totalTime.toFixed(2)}ms`, - transcript: transcriptResult.transcript.substring(0, 100) + '...', - intent: parsedCommand.intent, - actionCount: parsedCommand.actions.length, - }); - - this.emit('processing_complete', { - commandId, - success: true, - metrics, - }); - - return { - success: true, - command: parsedCommand, - metrics, - }; - } catch (error) { - const totalTime = performance.now() - startTime; - - const metrics: VoiceProcessingMetrics = { - transcriptionTime: 0, - processingTime: 0, - executionTime: 0, - totalTime, - accuracy: 0, - success: false, - commandId, - userId: userContext.userId, - timestamp: new Date().toISOString(), - }; - - this.recordMetrics(metrics); - - logger.error('Voice command processing failed', { - commandId, - error: error instanceof Error ? error.message : String(error), - totalTime: `${totalTime.toFixed(2)}ms`, - userId: userContext.userId, - }); - - this.emit('processing_error', { - commandId, - error: error instanceof Error ? error.message : String(error), - metrics, - }); - - return { - success: false, - error: error instanceof Error ? error.message : String(error), - metrics, - }; - } - } - - /** - * Process and execute complete voice command from audio to final result - * This includes transcription, AI parsing, and multi-action execution - */ - async processAndExecuteVoiceCommand( - audioBuffer: Buffer, - userContext: UserContext, - options: VoiceProcessingOptions = {} - ): Promise<{ - success: boolean; - command?: ParsedCommand; - executionResult?: any; - metrics: VoiceProcessingMetrics; - error?: string; - }> { - const startTime = performance.now(); - const commandId = `cmd-${Date.now()}-${Math.random().toString(36).substring(7)}`; - - try { - // Step 1: Process voice command (transcription + AI parsing) - const processingResult = await this.processVoiceCommand(audioBuffer, userContext, options); - - if (!processingResult.success || !processingResult.command) { - return { - success: false, - error: processingResult.error || 'Voice command processing failed', - metrics: processingResult.metrics, - }; - } - - const parsedCommand = processingResult.command; - - // Step 2: Execute multi-action command if it has actions - let executionResult; - if (parsedCommand.actions && parsedCommand.actions.length > 0) { - logger.info('Executing multi-action command', { - commandId: parsedCommand.id, - actionCount: parsedCommand.actions.length, - userId: userContext.userId, - }); - - const executionStart = performance.now(); - executionResult = await this.multiActionExecutor.executeMultiActionCommand( - parsedCommand, - userContext, - { - transactionTimeout: options.maxProcessingTime || 30000, - auditLogging: true, - progressTracking: true, - } - ); - - const executionTime = performance.now() - executionStart; - - // Update metrics with execution time - processingResult.metrics.executionTime = executionTime; - processingResult.metrics.totalTime = performance.now() - startTime; - - this.emit('command_execution_complete', { - commandId: parsedCommand.id, - success: executionResult.success, - executionTime, - actionCount: executionResult.executedActions.length, - }); - - logger.info('Multi-action command execution completed', { - commandId: parsedCommand.id, - success: executionResult.success, - executionTime: `${executionTime.toFixed(2)}ms`, - successfulActions: executionResult.executedActions.length, - failedActions: executionResult.failedActions.length, - }); - } - - const totalTime = performance.now() - startTime; - processingResult.metrics.totalTime = totalTime; - - return executionResult && !executionResult.success - ? { - success: false, - command: parsedCommand, - executionResult, - metrics: processingResult.metrics, - error: 'Command execution failed', - } - : { - success: processingResult.success, - command: parsedCommand, - executionResult, - metrics: processingResult.metrics, - }; - } catch (error) { - const totalTime = performance.now() - startTime; - - const errorMetrics: VoiceProcessingMetrics = { - transcriptionTime: 0, - processingTime: 0, - executionTime: 0, - totalTime, - accuracy: 0, - success: false, - commandId, - userId: userContext.userId, - timestamp: new Date().toISOString(), - }; - - logger.error('Complete voice command processing failed', { - commandId, - error: error instanceof Error ? error.message : String(error), - totalTime: `${totalTime.toFixed(2)}ms`, - userId: userContext.userId, - }); - - return { - success: false, - error: error instanceof Error ? error.message : String(error), - metrics: errorMetrics, - }; - } - } - - /** - * Process streaming audio for real-time transcription - */ - async processStreamingAudio( - audioStream: AsyncGenerator, - userContext: UserContext, - options: VoiceProcessingOptions = {} - ): Promise>> { - const streamId = `stream-${Date.now()}`; - - logger.info('Starting streaming audio processing', { - streamId, - userId: userContext.userId, - }); - - return async function* (this: VoiceProcessingService) { - try { - const transcriptStream = await this.whisperService.transcribeStream(audioStream); - - for await (const partialTranscript of transcriptStream) { - if (partialTranscript.transcript && partialTranscript.transcript.trim().length > 0) { - try { - // Parse partial transcript - const partialCommand = await this.aiCommandParser.parseVoiceCommand( - partialTranscript.transcript, - userContext, - { timeout: 5000 } - ); - - yield { - id: partialCommand.id, - transcript: partialTranscript.transcript, - intent: partialCommand.intent, - confidence: Math.min( - partialTranscript.confidence || 0.5, - partialCommand.confidence - ), - actions: partialCommand.actions.slice(0, 3), // Limit actions for streaming - timestamp: new Date().toISOString(), - }; - } catch (error) { - logger.warn('Partial command parsing failed', { - streamId, - error: error instanceof Error ? error.message : String(error), - transcript: partialTranscript.transcript?.substring(0, 50), - }); - } - } - } - } catch (error) { - logger.error('Streaming audio processing failed', { - streamId, - error: error instanceof Error ? error.message : String(error), - }); - - yield { - error: error instanceof Error ? error.message : String(error), - timestamp: new Date().toISOString(), - }; - } - }.call(this); - } - - /** - * Get comprehensive service performance statistics - */ - getPerformanceStatistics(): { - overall: any; - audioProcessing: any; - transcription: any; - aiParsing: any; - } { - const overallStats = this.calculateOverallStats(); - - return { - overall: overallStats, - audioProcessing: this.audioStreamManager.getPerformanceMetrics(), - transcription: this.whisperService.getPerformanceStats(), - aiParsing: this.aiCommandParser.getPerformanceStats(), - }; - } - - /** - * Get service health status - */ - getHealthStatus(): { - status: 'healthy' | 'degraded' | 'unhealthy'; - components: Record; - metrics: any; - } { - const whisperHealth = this.whisperService.getHealthStatus(); - const overallStats = this.calculateOverallStats(); - - let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy'; - - // Check overall performance - if (overallStats.averageProcessingTime > 5000) { - status = 'unhealthy'; - } else if (overallStats.averageProcessingTime > 3000) { - status = 'degraded'; - } - - // Check success rate - if (overallStats.successRate < 0.9) { - status = 'unhealthy'; - } else if (overallStats.successRate < 0.95) { - status = 'degraded'; - } - - // Factor in component health - if (whisperHealth.status === 'unhealthy') { - status = 'unhealthy'; - } else if (whisperHealth.status === 'degraded' && status === 'healthy') { - status = 'degraded'; - } - - return { - status, - components: { - whisper: whisperHealth, - audioStream: this.audioStreamManager.getPerformanceMetrics(), - aiParsing: this.aiCommandParser.getPerformanceStats(), - }, - metrics: overallStats, - }; - } - - private async validateAudioInput(audioBuffer: Buffer): Promise { - if (!audioBuffer || audioBuffer.length === 0) { - throw new VoiceProcessingError('Empty audio buffer'); - } - - if (audioBuffer.length < 1000) { - throw new VoiceProcessingError('Audio buffer too small (minimum 1KB)'); - } - - if (audioBuffer.length > 50 * 1024 * 1024) { - throw new VoiceProcessingError('Audio buffer too large (maximum 50MB)'); - } - - // Basic audio format validation (check for WAV header) - if (audioBuffer.length > 12) { - const header = audioBuffer.toString('ascii', 0, 4); - if (header !== 'RIFF') { - logger.warn('Audio format validation: Not a standard WAV file'); - } - } - } - - private validatePerformanceTargets(totalTime: number, actionCount: number): void { - // Simple commands should complete in <2 seconds - if (actionCount <= 2 && totalTime > 2000) { - logger.warn('Performance target missed: Simple command exceeded 2 seconds', { - totalTime: `${totalTime.toFixed(2)}ms`, - actionCount, - target: '2000ms', - }); - } - - // Complex commands should complete in <5 seconds - if (actionCount > 2 && totalTime > 5000) { - logger.warn('Performance target missed: Complex command exceeded 5 seconds', { - totalTime: `${totalTime.toFixed(2)}ms`, - actionCount, - target: '5000ms', - }); - } - } - - private recordMetrics(metrics: VoiceProcessingMetrics): void { - this.performanceMetrics.push(metrics); - - // Keep only last 1000 metrics - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } - - private calculateOverallStats(): { - totalCommands: number; - successfulCommands: number; - successRate: number; - averageProcessingTime: number; - averageTranscriptionTime: number; - averageParsingTime: number; - p95ProcessingTime: number; - p99ProcessingTime: number; - } { - if (this.performanceMetrics.length === 0) { - return { - totalCommands: 0, - successfulCommands: 0, - successRate: 0, - averageProcessingTime: 0, - averageTranscriptionTime: 0, - averageParsingTime: 0, - p95ProcessingTime: 0, - p99ProcessingTime: 0, - }; - } - - const successfulMetrics = this.performanceMetrics.filter((m) => m.success); - const sortedTimes = this.performanceMetrics.map((m) => m.totalTime).sort((a, b) => a - b); - - return { - totalCommands: this.performanceMetrics.length, - successfulCommands: successfulMetrics.length, - successRate: successfulMetrics.length / this.performanceMetrics.length, - averageProcessingTime: - this.performanceMetrics.reduce((sum, m) => sum + m.totalTime, 0) / - this.performanceMetrics.length, - averageTranscriptionTime: - successfulMetrics.reduce((sum, m) => sum + m.transcriptionTime, 0) / - Math.max(successfulMetrics.length, 1), - averageParsingTime: - successfulMetrics.reduce((sum, m) => sum + m.processingTime, 0) / - Math.max(successfulMetrics.length, 1), - p95ProcessingTime: sortedTimes[Math.floor(sortedTimes.length * 0.95)] || 0, - p99ProcessingTime: sortedTimes[Math.floor(sortedTimes.length * 0.99)] || 0, - }; - } - - private setupEventHandlers(): void { - // Audio stream events - this.audioStreamManager.on('stream_error', (data) => { - this.emit('audio_stream_error', data); - }); - - this.audioStreamManager.on('audio_segment_ready', (segment: AudioSegment) => { - this.emit('audio_segment_ready', segment); - }); - - // Add error handling for uncaught events - this.on('error', (error) => { - logger.error('Voice processing service error', { error: error.message }); - }); - } -} diff --git a/backend/src/voice/stream/AudioPreprocessor.ts b/backend/src/voice/stream/AudioPreprocessor.ts deleted file mode 100644 index 0864a89..0000000 --- a/backend/src/voice/stream/AudioPreprocessor.ts +++ /dev/null @@ -1,458 +0,0 @@ -/** - * Audio Preprocessor - Phase 2 Voice Processing Pipeline - * Real-time audio enhancement and noise reduction - * - * Success Criteria: - * - Noise reduction improves transcription accuracy by 15%+ - * - Volume normalization prevents clipping - * - Processing adds <50ms latency - * - Output optimized for Whisper API format - */ - -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; - -export interface PreprocessorConfig { - noiseReduction: boolean; - volumeNormalization: boolean; - highPassFilter: boolean; - lowPassFilter: boolean; - agcEnabled: boolean; // Automatic Gain Control - targetRMS: number; // Target RMS level for normalization - noiseGateThreshold: number; // Noise gate threshold - highPassFreq: number; // High pass filter frequency - lowPassFreq: number; // Low pass filter frequency - sampleRate: number; -} - -export interface ProcessingResult { - processedAudio: Buffer; - processingTime: number; - applied: string[]; - metrics: { - inputRMS: number; - outputRMS: number; - noiseReduction: number; - clippingPrevented: boolean; - }; -} - -export class AudioPreprocessor { - private config: PreprocessorConfig; - private performanceMetrics: number[] = []; - - // State for running calculations - private runningRMS: number = 0; - private noiseProfile: Float32Array | null = null; - private agcGain: number = 1.0; - private dcOffset: number = 0; - - // Filter state (simple IIR filters) - private highPassState: { x1: number; x2: number; y1: number; y2: number } = { - x1: 0, - x2: 0, - y1: 0, - y2: 0, - }; - private lowPassState: { x1: number; x2: number; y1: number; y2: number } = { - x1: 0, - x2: 0, - y1: 0, - y2: 0, - }; - - constructor(config: Partial = {}) { - this.config = { - noiseReduction: true, - volumeNormalization: true, - highPassFilter: true, - lowPassFilter: false, - agcEnabled: true, - targetRMS: 0.1, // 10% of full scale - noiseGateThreshold: 0.01, // 1% of full scale - highPassFreq: 80, // Remove low frequency noise - lowPassFreq: 8000, // Remove high frequency noise above speech - sampleRate: 16000, - ...config, - }; - - logger.debug('Audio preprocessor initialized', { config: this.config }); - } - - /** - * Process audio buffer with all enabled preprocessing steps - * Target: <50ms processing time - */ - async process(audioBuffer: Buffer): Promise { - const startTime = performance.now(); - - try { - // Convert to float samples for processing - const samples = this.bufferToFloat32(audioBuffer); - const originalRMS = this.calculateRMS(samples); - - let processedSamples: Float32Array = new Float32Array(samples.length); - processedSamples.set(samples); - const appliedProcessing: string[] = []; - - // Step 1: DC offset removal - processedSamples = this.removeDCOffset(processedSamples) as Float32Array; - appliedProcessing.push('dc_removal'); - - // Step 2: High-pass filter (remove low frequency noise) - if (this.config.highPassFilter) { - processedSamples = this.applyHighPassFilter(processedSamples) as Float32Array; - appliedProcessing.push('high_pass_filter'); - } - - // Step 3: Low-pass filter (anti-aliasing) - if (this.config.lowPassFilter) { - processedSamples = this.applyLowPassFilter(processedSamples) as Float32Array; - appliedProcessing.push('low_pass_filter'); - } - - // Step 4: Noise gate - processedSamples = this.applyNoiseGate(processedSamples) as Float32Array; - appliedProcessing.push('noise_gate'); - - // Step 5: Noise reduction - let noiseReductionAmount = 0; - if (this.config.noiseReduction) { - const result = this.applyNoiseReduction(processedSamples); - processedSamples = result.samples as Float32Array; - noiseReductionAmount = result.reductionAmount; - appliedProcessing.push('noise_reduction'); - } - - // Step 6: Automatic Gain Control - if (this.config.agcEnabled) { - processedSamples = this.applyAutomaticGainControl(processedSamples) as Float32Array; - appliedProcessing.push('agc'); - } - - // Step 7: Volume normalization - let clippingPrevented = false; - if (this.config.volumeNormalization) { - const result = this.normalizeVolume(processedSamples); - processedSamples = result.samples as Float32Array; - clippingPrevented = result.clippingPrevented; - appliedProcessing.push('volume_normalization'); - } - - // Convert back to buffer - const processedBuffer = this.float32ToBuffer(processedSamples); - const processingTime = performance.now() - startTime; - - // Record performance - this.recordPerformance(processingTime); - - // Log slow processing - if (processingTime > 50) { - logger.warn('Slow audio preprocessing', { - processingTime: `${processingTime.toFixed(2)}ms`, - bufferSize: audioBuffer.length, - appliedProcessing, - }); - } - - const outputRMS = this.calculateRMS(processedSamples); - - logger.debug('Audio preprocessing complete', { - processingTime: `${processingTime.toFixed(2)}ms`, - inputRMS: originalRMS.toFixed(4), - outputRMS: outputRMS.toFixed(4), - appliedProcessing, - noiseReduction: noiseReductionAmount.toFixed(4), - }); - - return processedBuffer; - } catch (error: unknown) { - const processingTime = performance.now() - startTime; - const errorMessage = error instanceof Error ? error.message : String(error); - - logger.error('Audio preprocessing failed', { - error: errorMessage, - processingTime: `${processingTime.toFixed(2)}ms`, - bufferSize: audioBuffer.length, - }); - - // Return original buffer on error - return audioBuffer; - } - } - - /** - * Process with detailed results - */ - async processDetailed(audioBuffer: Buffer): Promise { - const startTime = performance.now(); - - const samples = this.bufferToFloat32(audioBuffer); - const inputRMS = this.calculateRMS(samples); - - const processedBuffer = await this.process(audioBuffer); - const processedSamples = this.bufferToFloat32(processedBuffer); - const outputRMS = this.calculateRMS(processedSamples); - - const processingTime = performance.now() - startTime; - - return { - processedAudio: processedBuffer, - processingTime, - applied: ['preprocessing_pipeline'], - metrics: { - inputRMS, - outputRMS, - noiseReduction: Math.max(0, inputRMS - outputRMS), - clippingPrevented: this.checkForClipping(processedSamples), - }, - }; - } - - /** - * Update noise profile for noise reduction - */ - updateNoiseProfile(noiseSample: Buffer): void { - const samples = this.bufferToFloat32(noiseSample); - - // Simple noise profiling - calculate spectral characteristics - this.noiseProfile = new Float32Array(256); // Frequency bins - - // Simple FFT-like analysis (simplified for real-time processing) - for (let i = 0; i < this.noiseProfile.length; i++) { - let sum = 0; - const freqBin = (i / this.noiseProfile.length) * (this.config.sampleRate / 2); - - // Calculate energy in this frequency bin - for (let j = 0; j < samples.length - 1; j++) { - const phase = (2 * Math.PI * freqBin * j) / this.config.sampleRate; - sum += samples[j]! * Math.cos(phase); - } - - this.noiseProfile[i] = Math.abs(sum) / samples.length; - } - - logger.info('Noise profile updated', { - sampleLength: samples.length, - profileBins: this.noiseProfile.length, - }); - } - - /** - * Get performance statistics - */ - getPerformanceStats(): { average: number; p95: number; p99: number; count: number } { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = - this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] ?? 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] ?? 0; - - return { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: this.performanceMetrics.length, - }; - } - - private bufferToFloat32(buffer: Buffer): Float32Array { - const samples = new Float32Array(buffer.length / 2); - for (let i = 0; i < samples.length; i++) { - samples[i] = buffer.readInt16LE(i * 2) / 32768.0; - } - return samples; - } - - private float32ToBuffer(samples: Float32Array): Buffer { - const buffer = Buffer.allocUnsafe(samples.length * 2); - for (let i = 0; i < samples.length; i++) { - const sample = Math.max(-1, Math.min(1, samples[i] ?? 0)); - buffer.writeInt16LE(Math.round(sample * 32767), i * 2); - } - return buffer; - } - - private calculateRMS(samples: Float32Array): number { - let sum = 0; - for (let i = 0; i < samples.length; i++) { - const sample = samples[i] ?? 0; - sum += sample * sample; - } - return Math.sqrt(sum / samples.length); - } - - private removeDCOffset(samples: Float32Array): Float32Array { - // Calculate DC offset - let sum = 0; - for (let i = 0; i < samples.length; i++) { - sum += samples[i] ?? 0; - } - const dcOffset = sum / samples.length; - - // Remove DC offset - const output = new Float32Array(samples.length); - for (let i = 0; i < samples.length; i++) { - output[i] = (samples[i] ?? 0) - dcOffset; - } - - this.dcOffset = dcOffset; - return output; - } - - private applyHighPassFilter(samples: Float32Array): Float32Array { - const output = new Float32Array(samples.length); - - // Simple high-pass filter (80Hz cutoff) - const fc = this.config.highPassFreq / this.config.sampleRate; - const alpha = 1 / (1 + 2 * Math.PI * fc); - - for (let i = 0; i < samples.length; i++) { - if (i === 0) { - output[i] = alpha * (samples[i] ?? 0); - } else { - output[i] = alpha * ((output[i - 1] ?? 0) + (samples[i] ?? 0) - (samples[i - 1] ?? 0)); - } - } - - return output; - } - - private applyLowPassFilter(samples: Float32Array): Float32Array { - const output = new Float32Array(samples.length); - - // Simple low-pass filter - const fc = this.config.lowPassFreq / this.config.sampleRate; - const alpha = (2 * Math.PI * fc) / (1 + 2 * Math.PI * fc); - - output[0] = samples[0] ?? 0; - for (let i = 1; i < samples.length; i++) { - output[i] = alpha * (samples[i] ?? 0) + (1 - alpha) * (output[i - 1] ?? 0); - } - - return output; - } - - private applyNoiseGate(samples: Float32Array): Float32Array { - const output = new Float32Array(samples.length); - const threshold = this.config.noiseGateThreshold; - - for (let i = 0; i < samples.length; i++) { - const sample = samples[i] ?? 0; - if (Math.abs(sample) > threshold) { - output[i] = sample; - } else { - output[i] = 0; // Gate the signal - } - } - - return output; - } - - private applyNoiseReduction(samples: Float32Array): { - samples: Float32Array; - reductionAmount: number; - } { - const output = new Float32Array(samples.length); - let reductionAmount = 0; - - if (!this.noiseProfile) { - // Simple spectral subtraction without profile - for (let i = 0; i < samples.length; i++) { - const sample = samples[i] ?? 0; - const originalMagnitude = Math.abs(sample); - const reducedMagnitude = Math.max(0.1 * originalMagnitude, originalMagnitude - 0.02); - output[i] = sample >= 0 ? reducedMagnitude : -reducedMagnitude; - reductionAmount += originalMagnitude - Math.abs(output[i] ?? 0); - } - } else { - // More sophisticated noise reduction would go here - // For now, use simple approach - for (let i = 0; i < samples.length; i++) { - const sample = samples[i] ?? 0; - output[i] = sample * 0.95; // Simple noise reduction - reductionAmount += Math.abs(sample) * 0.05; - } - } - - return { samples: output, reductionAmount: reductionAmount / samples.length }; - } - - private applyAutomaticGainControl(samples: Float32Array): Float32Array { - const output = new Float32Array(samples.length); - const currentRMS = this.calculateRMS(samples); - - if (currentRMS > 0) { - const targetGain = this.config.targetRMS / currentRMS; - - // Smooth gain changes to avoid artifacts - const maxGainChange = 1.1; // Max 10% gain change per frame - if (targetGain > this.agcGain * maxGainChange) { - this.agcGain *= maxGainChange; - } else if (targetGain < this.agcGain / maxGainChange) { - this.agcGain /= maxGainChange; - } else { - this.agcGain = targetGain; - } - - // Apply gain with limiting - for (let i = 0; i < samples.length; i++) { - output[i] = Math.max(-1, Math.min(1, (samples[i] ?? 0) * this.agcGain)); - } - } else { - // No signal, pass through - output.set(samples); - } - - return output; - } - - private normalizeVolume(samples: Float32Array): { - samples: Float32Array; - clippingPrevented: boolean; - } { - const output = new Float32Array(samples.length); - let clippingPrevented = false; - - // Find peak - let peak = 0; - for (let i = 0; i < samples.length; i++) { - peak = Math.max(peak, Math.abs(samples[i] ?? 0)); - } - - if (peak > 0.95) { - // Prevent clipping - const scaleFactor = 0.95 / peak; - for (let i = 0; i < samples.length; i++) { - output[i] = (samples[i] ?? 0) * scaleFactor; - } - clippingPrevented = true; - } else { - output.set(samples); - } - - return { samples: output, clippingPrevented }; - } - - private checkForClipping(samples: Float32Array): boolean { - for (let i = 0; i < samples.length; i++) { - if (Math.abs(samples[i] ?? 0) > 0.99) { - return true; - } - } - return false; - } - - private recordPerformance(time: number): void { - this.performanceMetrics.push(time); - - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} diff --git a/backend/src/voice/stream/AudioStreamManager.ts b/backend/src/voice/stream/AudioStreamManager.ts deleted file mode 100644 index a67681a..0000000 --- a/backend/src/voice/stream/AudioStreamManager.ts +++ /dev/null @@ -1,504 +0,0 @@ -/** - * Audio Stream Manager - Phase 2 Voice Processing Pipeline - * Handles WebRTC audio streaming with real-time processing - * - * Success Criteria: - * - WebRTC connection established with <100ms latency - * - Audio stream supports 16kHz, mono, PCM16 format - * - Stream handles 1KB chunks efficiently - * - Memory usage <50MB for 10 concurrent streams - */ - -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { VoiceActivityDetector } from './VoiceActivityDetector'; -import { AudioPreprocessor } from './AudioPreprocessor'; -import { CircularBuffer } from './CircularBuffer'; - -export interface AudioStreamConfig { - userId: string; - socketId: string; - sampleRate: number; - channels: number; - format: 'pcm16' | 'pcm32' | 'float32'; - chunkSize: number; - maxBufferSize?: number; - silenceThreshold?: number; - maxSegmentLength?: number; -} - -export interface AudioSegment { - id: string; - audioData: Buffer; - sampleRate: number; - channels: number; - format: string; - duration: number; - timestamp: string; - userId: string; - socketId: string; -} - -export interface ProcessingResult { - status: 'waiting' | 'processing' | 'segment_ready' | 'error'; - hasVoice?: boolean; - segment?: AudioSegment; - processingTime?: number; - error?: string; -} - -export interface StreamStatus { - isActive: boolean; - bufferLevel: number; - lastActivity: number; - totalProcessed: number; - memoryUsage: number; -} - -export class AudioStreamManager extends EventEmitter { - private activeStreams: Map = new Map(); - private vad: VoiceActivityDetector; - private preprocessor: AudioPreprocessor; - private performanceMetrics: Map = new Map(); - - constructor() { - super(); - this.vad = new VoiceActivityDetector({ - threshold: 0.5, - sensitivity: 0.8, - smoothing: 0.3, - }); - this.preprocessor = new AudioPreprocessor(); - - // Setup periodic cleanup - setInterval(() => this.cleanupInactiveStreams(), 30000); // Every 30 seconds - } - - /** - * Initialize a new audio stream - * Target: <100ms initialization time - */ - async initializeStream(config: AudioStreamConfig): Promise { - const startTime = performance.now(); - - try { - // Validate configuration - this.validateStreamConfig(config); - - // Check memory constraints (max 10 concurrent streams) - if (this.activeStreams.size >= 10) { - throw new Error('Maximum concurrent streams (10) reached'); - } - - // Create new audio stream - const stream = new AudioStream(config, this.vad, this.preprocessor); - await stream.initialize(); - - // Store stream - this.activeStreams.set(config.socketId, stream); - - // Setup stream event handlers - this.setupStreamEventHandlers(stream); - - const initTime = performance.now() - startTime; - - // Record performance metrics - this.recordMetric('stream_init_time', initTime); - - logger.info('Audio stream initialized', { - socketId: config.socketId, - userId: config.userId, - initTime: `${initTime.toFixed(2)}ms`, - activeStreams: this.activeStreams.size, - }); - - // Emit stream ready event - this.emit('stream_initialized', { - socketId: config.socketId, - userId: config.userId, - config, - initTime, - }); - - return stream; - } catch (error) { - const initTime = performance.now() - startTime; - - logger.error('Audio stream initialization failed', { - socketId: config.socketId, - error: error instanceof Error ? error.message : String(error), - initTime: `${initTime.toFixed(2)}ms`, - }); - - throw error; - } - } - - /** - * Process audio chunk for a specific stream - * Target: <10ms processing time per chunk - */ - async processAudioChunk(socketId: string, audioChunk: Buffer): Promise { - const startTime = performance.now(); - - try { - const stream = this.activeStreams.get(socketId); - if (!stream) { - throw new Error(`Audio stream not found: ${socketId}`); - } - - // Validate chunk size and format - if (audioChunk.length === 0) { - return { status: 'waiting' }; - } - - // Process audio chunk through the stream - const result = await stream.processChunk(audioChunk); - - const processingTime = performance.now() - startTime; - - // Record performance metrics - this.recordMetric('chunk_processing_time', processingTime); - - // Log slow processing - if (processingTime > 10) { - logger.warn('Slow audio chunk processing', { - socketId, - processingTime: `${processingTime.toFixed(2)}ms`, - chunkSize: audioChunk.length, - }); - } - - return { - ...result, - processingTime, - }; - } catch (error) { - const processingTime = performance.now() - startTime; - - logger.error('Audio chunk processing failed', { - socketId, - error: error instanceof Error ? error.message : String(error), - processingTime: `${processingTime.toFixed(2)}ms`, - chunkSize: audioChunk.length, - }); - - return { - status: 'error', - error: error instanceof Error ? error.message : String(error), - processingTime, - }; - } - } - - /** - * Close audio stream and cleanup resources - */ - async closeStream(socketId: string): Promise { - const stream = this.activeStreams.get(socketId); - if (!stream) { - return; - } - - try { - await stream.close(); - this.activeStreams.delete(socketId); - - logger.info('Audio stream closed', { - socketId, - remainingStreams: this.activeStreams.size, - }); - - this.emit('stream_closed', { socketId }); - } catch (error) { - logger.error('Error closing audio stream', { - socketId, - error: error instanceof Error ? error.message : String(error), - }); - } - } - - /** - * Get stream status information - */ - getStreamStatus(socketId: string): StreamStatus | null { - const stream = this.activeStreams.get(socketId); - if (!stream) { - return null; - } - - return stream.getStatus(); - } - - /** - * Get all active streams - */ - getActiveStreams(): string[] { - return Array.from(this.activeStreams.keys()); - } - - /** - * Get performance metrics - */ - getPerformanceMetrics(): Record { - const metrics: Record = {}; - - for (const [key, values] of this.performanceMetrics.entries()) { - if (values.length === 0) continue; - - const sorted = [...values].sort((a, b) => a - b); - const average = values.reduce((sum, val) => sum + val, 0) / values.length; - const p95Index = Math.floor(sorted.length * 0.95); - const p95 = sorted[p95Index] || sorted[sorted.length - 1]; - - metrics[key] = { - average: Math.round(average * 100) / 100, - p95: Math.round((p95 ?? 0) * 100) / 100, - count: values.length, - }; - } - - return metrics; - } - - /** - * Get memory usage for all active streams - */ - getMemoryUsage(): { total: number; perStream: Record } { - let total = 0; - const perStream: Record = {}; - - for (const [socketId, stream] of this.activeStreams.entries()) { - const streamMemory = stream.getMemoryUsage(); - total += streamMemory; - perStream[socketId] = streamMemory; - } - - return { total, perStream }; - } - - private validateStreamConfig(config: AudioStreamConfig): void { - if (!config.userId || !config.socketId) { - throw new Error('userId and socketId are required'); - } - - if (config.sampleRate !== 16000) { - throw new Error('Only 16kHz sample rate is supported'); - } - - if (config.channels !== 1) { - throw new Error('Only mono audio is supported'); - } - - if (config.format !== 'pcm16') { - throw new Error('Only PCM16 format is supported'); - } - - if (config.chunkSize < 512 || config.chunkSize > 8192) { - throw new Error('Chunk size must be between 512 and 8192 bytes'); - } - } - - private setupStreamEventHandlers(stream: AudioStream): void { - stream.on('chunk_processed', (data) => { - this.emit('stream_progress', { - socketId: stream.config.socketId, - userId: stream.config.userId, - ...data, - }); - }); - - stream.on('segment_complete', (segment) => { - this.emit('audio_segment_ready', segment); - }); - - stream.on('error', (error) => { - logger.error('Audio stream error', { - socketId: stream.config.socketId, - error: error.message, - }); - - this.emit('stream_error', { - socketId: stream.config.socketId, - error, - }); - }); - } - - private cleanupInactiveStreams(): void { - const now = Date.now(); - const inactiveThreshold = 5 * 60 * 1000; // 5 minutes - - for (const [socketId, stream] of this.activeStreams.entries()) { - if (now - stream.getLastActivity() > inactiveThreshold) { - logger.info('Cleaning up inactive stream', { socketId }); - this.closeStream(socketId).catch((error) => { - logger.error('Error during stream cleanup', { socketId, error: error.message }); - }); - } - } - } - - private recordMetric(key: string, value: number): void { - if (!this.performanceMetrics.has(key)) { - this.performanceMetrics.set(key, []); - } - - const metrics = this.performanceMetrics.get(key)!; - metrics.push(value); - - // Keep only last 1000 measurements - if (metrics.length > 1000) { - metrics.shift(); - } - } -} - -/** - * Individual Audio Stream Handler - */ -export class AudioStream extends EventEmitter { - private buffer: CircularBuffer; - private lastVoiceTime: number = 0; - private lastActivity: number = 0; - private isInitialized: boolean = false; - private totalProcessed: number = 0; - - constructor( - public config: AudioStreamConfig, - private vad: VoiceActivityDetector, - private preprocessor: AudioPreprocessor - ) { - super(); - - const bufferSize = config.maxBufferSize || config.sampleRate * 30; // 30 seconds default - this.buffer = new CircularBuffer(bufferSize); - this.lastActivity = Date.now(); - } - - async initialize(): Promise { - await this.buffer.initialize(); - this.lastVoiceTime = Date.now(); - this.isInitialized = true; - - logger.debug('Audio stream initialized', { - socketId: this.config.socketId, - bufferSize: this.buffer.getCapacity(), - }); - } - - async processChunk(chunk: Buffer): Promise { - if (!this.isInitialized) { - throw new Error('Stream not initialized'); - } - - this.lastActivity = Date.now(); - this.totalProcessed += chunk.length; - - try { - // Preprocess audio chunk - const preprocessed = await this.preprocessor.process(chunk); - - // Voice activity detection - const hasVoice = await this.vad.detectActivity(preprocessed); - - if (hasVoice) { - this.lastVoiceTime = Date.now(); - this.buffer.write(preprocessed); - - this.emit('chunk_processed', { - hasVoice: true, - chunkSize: chunk.length, - bufferLevel: this.buffer.getLevel(), - timestamp: new Date().toISOString(), - }); - } else { - // Still add silence to buffer for context - this.buffer.write(preprocessed); - - this.emit('chunk_processed', { - hasVoice: false, - chunkSize: chunk.length, - bufferLevel: this.buffer.getLevel(), - timestamp: new Date().toISOString(), - }); - } - - // Check if segment is complete - if (this.isSegmentComplete()) { - const segment = await this.createSegment(); - this.emit('segment_complete', segment); - return { status: 'segment_ready', segment, hasVoice }; - } - - return { status: hasVoice ? 'processing' : 'waiting', hasVoice }; - } catch (error) { - this.emit('error', error); - return { - status: 'error', - error: error instanceof Error ? error.message : String(error), - }; - } - } - - async close(): Promise { - this.isInitialized = false; - this.buffer.clear(); - this.removeAllListeners(); - } - - getStatus(): StreamStatus { - return { - isActive: this.isInitialized, - bufferLevel: this.buffer.getLevel(), - lastActivity: this.lastActivity, - totalProcessed: this.totalProcessed, - memoryUsage: this.getMemoryUsage(), - }; - } - - getLastActivity(): number { - return this.lastActivity; - } - - getMemoryUsage(): number { - return this.buffer.getMemoryUsage(); - } - - private isSegmentComplete(): boolean { - const silenceThreshold = this.config.silenceThreshold || 1500; // 1.5 seconds - const maxSegmentLength = this.config.maxSegmentLength || 30000; // 30 seconds - - const silenceDuration = Date.now() - this.lastVoiceTime; - const bufferDuration = this.buffer.getDurationMs(); - - return ( - (silenceDuration > silenceThreshold && bufferDuration > 1000) || - bufferDuration > maxSegmentLength - ); - } - - private async createSegment(): Promise { - const audioData = this.buffer.readAll(); - const duration = this.buffer.getDurationMs(); - - const segment: AudioSegment = { - id: crypto.randomUUID(), - audioData, - sampleRate: this.config.sampleRate, - channels: this.config.channels, - format: this.config.format, - duration, - timestamp: new Date().toISOString(), - userId: this.config.userId, - socketId: this.config.socketId, - }; - - // Clear buffer for next segment - this.buffer.clear(); - this.lastVoiceTime = Date.now(); - - return segment; - } -} diff --git a/backend/src/voice/stream/CircularBuffer.ts b/backend/src/voice/stream/CircularBuffer.ts deleted file mode 100644 index 2a3d0e3..0000000 --- a/backend/src/voice/stream/CircularBuffer.ts +++ /dev/null @@ -1,320 +0,0 @@ -/** - * Circular Buffer for Audio Data - * Memory-efficient buffer with automatic overflow protection - * - * Success Criteria: - * - Memory-efficient circular buffer implementation - * - Automatic segment detection based on silence - * - Handles variable-length audio segments - * - Buffer overflow protection - */ - -import { logger } from '../../utils/logger'; - -export class CircularBuffer { - private buffer: Buffer; - private writeIndex: number = 0; - private readIndex: number = 0; - private size: number = 0; - private capacity: number; - private sampleRate: number = 16000; // Default 16kHz - private bytesPerSample: number = 2; // 16-bit PCM - - constructor(capacityInSamples: number) { - this.capacity = capacityInSamples * this.bytesPerSample; - this.buffer = Buffer.allocUnsafe(this.capacity); - this.buffer.fill(0); - } - - async initialize(): Promise { - // Initialize buffer state - this.writeIndex = 0; - this.readIndex = 0; - this.size = 0; - this.buffer.fill(0); - - logger.debug('Circular buffer initialized', { - capacity: this.capacity, - capacityMs: this.getCapacityMs(), - }); - } - - /** - * Write audio data to buffer - */ - write(data: Buffer): number { - if (data.length === 0) { - return 0; - } - - const bytesToWrite = Math.min(data.length, this.capacity - this.size); - - if (bytesToWrite < data.length) { - // Buffer overflow - log warning but continue with partial write - logger.warn('Circular buffer overflow', { - requestedBytes: data.length, - availableBytes: bytesToWrite, - bufferSize: this.size, - capacity: this.capacity, - }); - } - - let written = 0; - - for (let i = 0; i < bytesToWrite; i++) { - this.buffer[this.writeIndex] = data[i] ?? 0; - this.writeIndex = (this.writeIndex + 1) % this.capacity; - written++; - - // Update size only if buffer not full - if (this.size < this.capacity) { - this.size++; - } else { - // If buffer is full, advance read index (overwrite oldest data) - this.readIndex = (this.readIndex + 1) % this.capacity; - } - } - - return written; - } - - /** - * Read specified number of bytes from buffer - */ - read(bytes: number): Buffer { - const bytesToRead = Math.min(bytes, this.size); - const result = Buffer.allocUnsafe(bytesToRead); - - for (let i = 0; i < bytesToRead; i++) { - result[i] = this.buffer[this.readIndex] ?? 0; - this.readIndex = (this.readIndex + 1) % this.capacity; - this.size--; - } - - return result; - } - - /** - * Read all available data from buffer - */ - readAll(): Buffer { - return this.read(this.size); - } - - /** - * Peek at data without consuming it - */ - peek(bytes: number): Buffer { - const bytesToPeek = Math.min(bytes, this.size); - const result = Buffer.allocUnsafe(bytesToPeek); - - let peekIndex = this.readIndex; - for (let i = 0; i < bytesToPeek; i++) { - result[i] = this.buffer[peekIndex] ?? 0; - peekIndex = (peekIndex + 1) % this.capacity; - } - - return result; - } - - /** - * Clear all data from buffer - */ - clear(): void { - this.writeIndex = 0; - this.readIndex = 0; - this.size = 0; - this.buffer.fill(0); - } - - /** - * Get current buffer level (0-1) - */ - getLevel(): number { - return this.size / this.capacity; - } - - /** - * Get available space in bytes - */ - getAvailableSpace(): number { - return this.capacity - this.size; - } - - /** - * Get current size in bytes - */ - getSize(): number { - return this.size; - } - - /** - * Get buffer capacity in bytes - */ - getCapacity(): number { - return this.capacity; - } - - /** - * Get buffer capacity in milliseconds - */ - getCapacityMs(): number { - const samples = this.capacity / this.bytesPerSample; - return (samples / this.sampleRate) * 1000; - } - - /** - * Get current buffer duration in milliseconds - */ - getDurationMs(): number { - const samples = this.size / this.bytesPerSample; - return (samples / this.sampleRate) * 1000; - } - - /** - * Get memory usage in bytes - */ - getMemoryUsage(): number { - return this.capacity + 32 * 4; // Buffer + overhead for indices - } - - /** - * Check if buffer is empty - */ - isEmpty(): boolean { - return this.size === 0; - } - - /** - * Check if buffer is full - */ - isFull(): boolean { - return this.size === this.capacity; - } - - /** - * Get buffer statistics - */ - getStats(): { - size: number; - capacity: number; - level: number; - durationMs: number; - capacityMs: number; - memoryUsage: number; - } { - return { - size: this.size, - capacity: this.capacity, - level: this.getLevel(), - durationMs: this.getDurationMs(), - capacityMs: this.getCapacityMs(), - memoryUsage: this.getMemoryUsage(), - }; - } - - /** - * Resize buffer (creates new buffer and copies data) - */ - resize(newCapacityInSamples: number): void { - const newCapacity = newCapacityInSamples * this.bytesPerSample; - const newBuffer = Buffer.allocUnsafe(newCapacity); - newBuffer.fill(0); - - // Copy existing data to new buffer - const existingData = this.readAll(); - - this.buffer = newBuffer; - this.capacity = newCapacity; - this.writeIndex = 0; - this.readIndex = 0; - this.size = 0; - - // Write existing data back - this.write(existingData); - - logger.debug('Circular buffer resized', { - oldCapacity: this.capacity, - newCapacity, - preservedBytes: existingData.length, - }); - } - - /** - * Get RMS energy level of buffer content (for VAD purposes) - */ - getRMSEnergy(): number { - if (this.size === 0) { - return 0; - } - - let sum = 0; - let index = this.readIndex; - const samples = this.size / this.bytesPerSample; - - for (let i = 0; i < samples; i++) { - // Read 16-bit sample (little-endian) - const sample = this.buffer.readInt16LE(index); - sum += sample * sample; - index = (index + this.bytesPerSample) % this.capacity; - } - - return Math.sqrt(sum / samples) / 32768.0; // Normalize to 0-1 - } - - /** - * Find silence periods in buffer (returns indices) - */ - findSilencePeriods( - threshold: number = 0.01, - minDurationMs: number = 500 - ): Array<{ start: number; end: number }> { - const silencePeriods: Array<{ start: number; end: number }> = []; - - if (this.size === 0) { - return silencePeriods; - } - - const samples = this.size / this.bytesPerSample; - const minSamples = (minDurationMs / 1000) * this.sampleRate; - - let silenceStart = -1; - let index = this.readIndex; - - for (let i = 0; i < samples; i++) { - const sample = Math.abs(this.buffer.readInt16LE(index)) / 32768.0; - - if (sample < threshold) { - if (silenceStart === -1) { - silenceStart = i; - } - } else { - if (silenceStart !== -1) { - const silenceDuration = i - silenceStart; - if (silenceDuration >= minSamples) { - silencePeriods.push({ - start: silenceStart, - end: i, - }); - } - silenceStart = -1; - } - } - - index = (index + this.bytesPerSample) % this.capacity; - } - - // Handle silence at end of buffer - if (silenceStart !== -1) { - const silenceDuration = samples - silenceStart; - if (silenceDuration >= minSamples) { - silencePeriods.push({ - start: silenceStart, - end: samples, - }); - } - } - - return silencePeriods; - } -} diff --git a/backend/src/voice/stream/VoiceActivityDetector.ts b/backend/src/voice/stream/VoiceActivityDetector.ts deleted file mode 100644 index 8007cf1..0000000 --- a/backend/src/voice/stream/VoiceActivityDetector.ts +++ /dev/null @@ -1,412 +0,0 @@ -/** - * Voice Activity Detector (VAD) - Phase 2 Voice Processing - * Real-time speech detection with background noise adaptation - * - * Success Criteria: - * - 95%+ accuracy in detecting speech vs silence - * - <10ms processing time per chunk - * - Adjustable sensitivity thresholds - * - Handles background noise effectively - */ - -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { start } from 'repl'; - -export interface VADConfig { - threshold: number; // Base energy threshold (0-1) - sensitivity: number; // Detection sensitivity (0-1) - smoothing: number; // Smoothing factor for background noise (0-1) - minSpeechDuration: number; // Minimum speech duration in ms - hangoverTime: number; // Time to continue detection after speech ends in ms -} - -export interface VADResult { - hasVoice: boolean; - confidence: number; - energy: number; - backgroundNoise: number; - processingTime: number; -} - -export class VoiceActivityDetector { - private config: VADConfig; - private energyHistory: number[] = []; - private backgroundNoise: number = 0; - private speechStart: number = 0; - private lastSpeechTime: number = 0; - private isInSpeech: boolean = false; - private adaptiveThreshold: number = 0; - private performanceMetrics: number[] = []; - - // Advanced features - private spectralCentroid: number = 0; - private zeroCrossingRate: number = 0; - private energyVariance: number = 0; - - constructor(config: Partial = {}) { - this.config = { - threshold: 0.02, // Base threshold - sensitivity: 0.8, // High sensitivity - smoothing: 0.95, // Slow adaptation to background noise - minSpeechDuration: 100, // 100ms minimum speech - hangoverTime: 200, // 200ms hangover - ...config, - }; - - this.adaptiveThreshold = this.config.threshold; - - logger.debug('VAD initialized', { config: this.config }); - } - - /** - * Detect voice activity in audio chunk - * Target: <10ms processing time - */ - async detectActivity(audioChunk: Buffer): Promise { - const startTime = performance.now(); - - try { - const result = this.analyzeAudio(audioChunk); - const processingTime = performance.now() - startTime; - - // Record performance - this.recordPerformance(processingTime); - - // Log if processing is slow - if (processingTime > 10) { - logger.warn('Slow VAD processing detected', { - processingTime: `${processingTime.toFixed(2)}ms`, - chunkSize: audioChunk.length, - }); - } - - return this.makeDecision(result, processingTime); - } catch (error) { - const processingTime = performance.now() - startTime; - - logger.error('VAD processing error', { - error: error instanceof Error ? error.message : String(error), - processingTime: `${processingTime.toFixed(2)}ms`, - chunkSize: audioChunk.length, - }); - - return false; // Default to no voice on error - } - } - - /** - * Get detailed VAD result with confidence and metrics - */ - async detectActivityDetailed(audioChunk: Buffer): Promise { - const startTime = performance.now(); - - const result = this.analyzeAudio(audioChunk); - const hasVoice = this.makeDecision(result, 0); - const processingTime = performance.now() - startTime; - - this.recordPerformance(processingTime); - - return { - hasVoice, - confidence: result.confidence, - energy: result.energy, - backgroundNoise: this.backgroundNoise, - processingTime, - }; - } - - /** - * Calibrate detector based on background noise sample - */ - calibrateThreshold(backgroundSample: Buffer): void { - logger.info('Starting VAD calibration'); - - const analysis = this.analyzeAudio(backgroundSample); - - // Set background noise level - this.backgroundNoise = analysis.energy; - - // Calculate adaptive threshold - this.adaptiveThreshold = Math.max( - this.config.threshold, - this.backgroundNoise * 2.0 // Threshold should be 2x background noise - ); - - logger.info('VAD calibration complete', { - backgroundNoise: this.backgroundNoise.toFixed(4), - adaptiveThreshold: this.adaptiveThreshold.toFixed(4), - sampleSize: backgroundSample.length, - }); - } - - /** - * Get confidence score for current audio chunk - */ - getConfidenceScore(audioChunk: Buffer): number { - const analysis = this.analyzeAudio(audioChunk); - return analysis.confidence; - } - - /** - * Update sensitivity settings - */ - updateSensitivity(sensitivity: number): void { - if (sensitivity < 0 || sensitivity > 1) { - throw new Error('Sensitivity must be between 0 and 1'); - } - - this.config.sensitivity = sensitivity; - logger.debug('VAD sensitivity updated', { sensitivity }); - } - - /** - * Get current performance statistics - */ - getPerformanceStats(): { average: number; p95: number; p99: number; count: number } { - if (this.performanceMetrics.length === 0) { - return { average: 0, p95: 0, p99: 0, count: 0 }; - } - - const sorted = [...this.performanceMetrics].sort((a, b) => a - b); - const average = - this.performanceMetrics.reduce((sum, time) => sum + time, 0) / this.performanceMetrics.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] ?? 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] ?? 0; - - return { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: this.performanceMetrics.length, - }; - } - - /** - * Reset VAD state - */ - reset(): void { - this.energyHistory = []; - this.backgroundNoise = 0; - this.speechStart = 0; - this.lastSpeechTime = 0; - this.isInSpeech = false; - this.adaptiveThreshold = this.config.threshold; - this.spectralCentroid = 0; - this.zeroCrossingRate = 0; - this.energyVariance = 0; - - logger.debug('VAD state reset'); - } - - private analyzeAudio(audioChunk: Buffer): { - energy: number; - confidence: number; - spectralCentroid: number; - zeroCrossingRate: number; - } { - // Convert buffer to samples - const samples = this.bufferToSamples(audioChunk); - - // Calculate RMS energy - const energy = this.calculateRMSEnergy(samples); - - // Calculate spectral features - const spectralCentroid = this.calculateSpectralCentroid(samples); - const zeroCrossingRate = this.calculateZeroCrossingRate(samples); - - // Update energy history for variance calculation - this.updateEnergyHistory(energy); - - // Calculate confidence based on multiple features - const confidence = this.calculateConfidence(energy, spectralCentroid, zeroCrossingRate); - - return { - energy, - confidence, - spectralCentroid, - zeroCrossingRate, - }; - } - - private makeDecision(analysis: any, processingTime: number): boolean { - const now = Date.now(); - - // Update background noise (slow adaptation) - this.updateBackgroundNoise(analysis.energy); - - // Calculate dynamic threshold - const dynamicThreshold = this.calculateDynamicThreshold(); - - // Primary decision based on energy - const energyDecision = analysis.energy > dynamicThreshold; - - // Enhanced decision with spectral features - const spectralDecision = this.makeSpectralDecision(analysis); - - // Combine decisions - let hasVoice = energyDecision && spectralDecision; - - // Apply temporal logic - if (hasVoice) { - if (!this.isInSpeech) { - // Start of potential speech - this.speechStart = now; - } - - this.lastSpeechTime = now; - this.isInSpeech = true; - } else { - // Check hangover time - if (this.isInSpeech && now - this.lastSpeechTime < this.config.hangoverTime) { - hasVoice = true; // Continue detection during hangover period - } else { - // Check minimum speech duration - if (this.isInSpeech && now - this.speechStart < this.config.minSpeechDuration) { - hasVoice = false; // Too short to be speech - } - - this.isInSpeech = false; - } - } - - return hasVoice; - } - - private bufferToSamples(buffer: Buffer): Float32Array { - const samples = new Float32Array(buffer.length / 2); - for (let i = 0; i < samples.length; i++) { - // Convert 16-bit PCM to float32 (-1 to 1) - samples[i] = buffer.readInt16LE(i * 2) / 32768.0; - } - return samples; - } - - private calculateRMSEnergy(samples: Float32Array): number { - let sum = 0; - for (let i = 0; i < samples.length; i++) { - if (typeof samples[i] !== 'undefined') { - sum += samples[i]! * samples[i]!; - } - } - return Math.sqrt(sum / samples.length); - } - - private calculateSpectralCentroid(samples: Float32Array): number { - // Simple approximation of spectral centroid - let weightedSum = 0; - let magnitudeSum = 0; - - for (let i = 1; i < samples.length; i++) { - const magnitude = Math.abs(samples[i]!); - weightedSum += i * magnitude; - magnitudeSum += magnitude; - } - - return magnitudeSum > 0 ? weightedSum / magnitudeSum : 0; - } - - private calculateZeroCrossingRate(samples: Float32Array): number { - let crossings = 0; - - for (let i = 1; i < samples.length; i++) { - if (samples[i]! >= 0 !== samples[i - 1]! >= 0) { - crossings++; - } - } - - return crossings / (samples.length - 1); - } - - private updateEnergyHistory(energy: number): void { - this.energyHistory.push(energy); - - // Keep only recent history (last 100 frames ~= 2 seconds at 50fps) - if (this.energyHistory.length > 100) { - this.energyHistory.shift(); - } - - // Calculate energy variance - if (this.energyHistory.length > 10) { - const mean = this.energyHistory.reduce((sum, e) => sum + e, 0) / this.energyHistory.length; - const variance = - this.energyHistory.reduce((sum, e) => sum + Math.pow(e - mean, 2), 0) / - this.energyHistory.length; - this.energyVariance = variance; - } - } - - private calculateConfidence( - energy: number, - spectralCentroid: number, - zeroCrossingRate: number - ): number { - // Base confidence from energy ratio - const energyRatio = - this.backgroundNoise > 0 ? energy / this.backgroundNoise : energy / this.adaptiveThreshold; - let confidence = Math.min(1.0, energyRatio / 3.0); // Normalize to 0-1 - - // Adjust based on spectral features - if (spectralCentroid > 0.1 && zeroCrossingRate > 0.05) { - confidence *= 1.2; // Boost confidence for speech-like spectral characteristics - } - - if (zeroCrossingRate > 0.3) { - confidence *= 0.8; // Reduce confidence for very high ZCR (likely noise) - } - - return Math.max(0, Math.min(1, confidence)); - } - - private updateBackgroundNoise(energy: number): void { - if (this.backgroundNoise === 0) { - this.backgroundNoise = energy; - } else { - // Slow adaptation using exponential moving average - this.backgroundNoise = - this.config.smoothing * this.backgroundNoise + (1 - this.config.smoothing) * energy; - } - } - - private calculateDynamicThreshold(): number { - // Base threshold adjusted by sensitivity - let threshold = this.adaptiveThreshold * (2.0 - this.config.sensitivity); - - // Adjust based on background noise - if (this.backgroundNoise > 0) { - threshold = Math.max(threshold, this.backgroundNoise * 1.5); - } - - // Adjust based on energy variance (more variable = lower threshold) - if (this.energyVariance > 0.001) { - threshold *= 0.9; - } - - return threshold; - } - - private makeSpectralDecision(analysis: any): boolean { - // Speech typically has: - // - Moderate spectral centroid (not too high, not too low) - // - Moderate zero crossing rate (not too high like noise, not too low like tones) - - const { spectralCentroid, zeroCrossingRate } = analysis; - - // Spectral centroid check (speech is usually in middle frequencies) - const spectralOk = spectralCentroid > 0.05 && spectralCentroid < 0.5; - - // Zero crossing rate check (speech has moderate ZCR) - const zcrOk = zeroCrossingRate > 0.02 && zeroCrossingRate < 0.25; - - return spectralOk && zcrOk; - } - - private recordPerformance(time: number): void { - this.performanceMetrics.push(time); - - // Keep only last 1000 measurements - if (this.performanceMetrics.length > 1000) { - this.performanceMetrics.shift(); - } - } -} diff --git a/backend/src/voice/transcription/TranscriptionCache.ts b/backend/src/voice/transcription/TranscriptionCache.ts deleted file mode 100644 index 3e14417..0000000 --- a/backend/src/voice/transcription/TranscriptionCache.ts +++ /dev/null @@ -1,343 +0,0 @@ -/** - * Transcription Cache - Phase 2 Voice Processing - * Redis-based caching for Whisper API responses - * - * Success Criteria: - * - Audio fingerprinting for cache keys - * - 1-hour cache TTL for transcripts - * - 30% cache hit rate after 1 week of usage - * - <10ms cache lookup time - */ - -import { createHash } from 'crypto'; -import { performance } from 'perf_hooks'; -import Redis from 'ioredis'; -import { logger } from '../../utils/logger'; -import { TranscriptResult, TranscriptionOptions } from '../types'; - -export interface CacheStats { - hits: number; - misses: number; - hitRate: number; - averageLookupTime: number; - totalKeys: number; - memoryUsage: number; -} - -export class TranscriptionCache { - private redis: Redis; - private keyPrefix = 'whisper:cache:'; - private defaultTTL = 3600; // 1 hour - - // Statistics - private hits = 0; - private misses = 0; - private lookupTimes: number[] = []; - - constructor() { - this.redis = new Redis({ - host: process.env.REDIS_HOST || 'localhost', - port: parseInt(process.env.REDIS_PORT || '6379'), - db: parseInt(process.env.REDIS_CACHE_DB || '1'), // Use separate DB for cache - keyPrefix: this.keyPrefix, - maxRetriesPerRequest: 3, - lazyConnect: true, - enableReadyCheck: true, - }); - - this.redis.on('connect', () => { - logger.info('Transcription cache connected to Redis'); - }); - - this.redis.on('error', (error) => { - logger.error('Transcription cache Redis error', { error: error.message }); - }); - - logger.debug('Transcription cache initialized'); - } - - /** - * Generate cache key from audio buffer and options - * Uses audio fingerprinting for consistent keys - */ - generateCacheKey(audioBuffer: Buffer, options: TranscriptionOptions): string { - // Create hash of audio content - const audioHash = this.generateAudioFingerprint(audioBuffer); - - // Include relevant options in key - const optionsKey = JSON.stringify({ - language: options.language || 'auto', - temperature: options.temperature || 0, - responseFormat: options.responseFormat || 'verbose_json', - }); - - const optionsHash = createHash('md5').update(optionsKey).digest('hex').substring(0, 8); - - return `${audioHash}:${optionsHash}`; - } - - /** - * Get cached transcription result - * Target: <10ms lookup time - */ - async get(key: string): Promise { - const startTime = performance.now(); - - try { - const cached = await this.redis.get(key); - const lookupTime = performance.now() - startTime; - - this.recordLookupTime(lookupTime); - - if (cached) { - this.hits++; - - // Extend TTL on cache hit - await this.redis.expire(key, this.defaultTTL); - - const result = JSON.parse(cached) as TranscriptResult; - - logger.debug('Cache hit', { - key, - lookupTime: `${lookupTime.toFixed(2)}ms`, - transcriptLength: result.transcript.length, - }); - - return result; - } else { - this.misses++; - - logger.debug('Cache miss', { - key, - lookupTime: `${lookupTime.toFixed(2)}ms`, - }); - - return null; - } - } catch (error) { - const lookupTime = performance.now() - startTime; - this.misses++; - this.recordLookupTime(lookupTime); - - logger.error('Cache lookup failed', { - key, - error: (error as Error).message, - lookupTime: `${lookupTime.toFixed(2)}ms`, - }); - - return null; - } - } - - /** - * Cache transcription result - */ - async set(key: string, result: TranscriptResult, ttl?: number): Promise { - try { - const cacheData = { - ...result, - cachedAt: new Date().toISOString(), - // Remove processingTime from cache (will be recalculated) - processingTime: undefined, - }; - - await this.redis.setex(key, ttl || this.defaultTTL, JSON.stringify(cacheData)); - - logger.debug('Result cached', { - key, - ttl: ttl || this.defaultTTL, - transcriptLength: result.transcript.length, - confidence: result.confidence, - }); - } catch (error) { - logger.error('Cache set failed', { - key, - error: (error as Error).message, - }); - } - } - - /** - * Delete cached result - */ - async delete(key: string): Promise { - try { - await this.redis.del(key); - logger.debug('Cache entry deleted', { key }); - } catch (error) { - logger.error('Cache delete failed', { - key, - error: (error as Error).message, - }); - } - } - - /** - * Clear all cached results - */ - async clear(): Promise { - try { - const keys = await this.redis.keys('*'); - if (keys.length > 0) { - await this.redis.del(...keys); - logger.info('Cache cleared', { deletedKeys: keys.length }); - } - } catch (error) { - logger.error('Cache clear failed', { - error: error instanceof Error ? error.message : String(error), - }); - } - } - - /** - * Get cache statistics - */ - getStats(): CacheStats { - const totalRequests = this.hits + this.misses; - const hitRate = totalRequests > 0 ? (this.hits / totalRequests) * 100 : 0; - - const avgLookupTime = - this.lookupTimes.length > 0 - ? this.lookupTimes.reduce((sum, time) => sum + time, 0) / this.lookupTimes.length - : 0; - - return { - hits: this.hits, - misses: this.misses, - hitRate: Math.round(hitRate * 100) / 100, - averageLookupTime: Math.round(avgLookupTime * 100) / 100, - totalKeys: 0, // Will be populated by getTotalKeys() if needed - memoryUsage: 0, // Will be populated by getMemoryUsage() if needed - }; - } - - /** - * Get total number of cached keys - */ - async getTotalKeys(): Promise { - try { - const keys = await this.redis.keys('*'); - return keys.length; - } catch (error) { - logger.error('Failed to get total keys', { - error: error instanceof Error ? error.message : String(error), - }); - return 0; - } - } - - /** - * Get memory usage information - */ - async getMemoryUsage(): Promise { - try { - const info = await this.redis.memory('USAGE', this.keyPrefix + '*'); - return info || 0; - } catch (error) { - logger.error('Failed to get memory usage', { - error: error instanceof Error ? error.message : String(error), - }); - return 0; - } - } - - /** - * Preload cache with common transcriptions - */ - async preload(commonPhrases: Array<{ audio: Buffer; transcript: string }>): Promise { - logger.info('Starting cache preload', { phrasesCount: commonPhrases.length }); - - const preloadPromises = commonPhrases.map(async ({ audio, transcript }) => { - const key = this.generateCacheKey(audio, {}); - const result: TranscriptResult = { - transcript, - confidence: 0.95, // High confidence for preloaded data - language: 'en', - processingTime: 0, - segments: [], - }; - - await this.set(key, result, 86400); // Cache for 24 hours - }); - - await Promise.allSettled(preloadPromises); - logger.info('Cache preload completed'); - } - - /** - * Clean up expired entries (manual cleanup) - */ - async cleanup(): Promise { - try { - // Redis automatically handles TTL cleanup, but we can do manual cleanup if needed - const keys = await this.redis.keys('*'); - let cleanedCount = 0; - - for (const key of keys) { - const ttl = await this.redis.ttl(key); - if (ttl === -1) { - // No TTL set - await this.redis.expire(key, this.defaultTTL); - cleanedCount++; - } - } - - logger.info('Cache cleanup completed', { - totalKeys: keys.length, - cleanedKeys: cleanedCount, - }); - } catch (error) { - logger.error('Cache cleanup failed', { - error: error instanceof Error ? error.message : String(error), - }); - } - } - - /** - * Close Redis connection - */ - async close(): Promise { - await this.redis.quit(); - logger.info('Transcription cache connection closed'); - } - - private generateAudioFingerprint(audioBuffer: Buffer): string { - // Generate a more sophisticated fingerprint based on audio characteristics - const hash = createHash('sha256'); - - // Add buffer content - hash.update(audioBuffer); - - // Add buffer size - hash.update(audioBuffer.length.toString()); - - // Simple audio characteristics (RMS energy in chunks) - const chunkSize = Math.max(1, Math.floor(audioBuffer.length / 10)); - for (let i = 0; i < audioBuffer.length; i += chunkSize) { - const chunk = audioBuffer.subarray(i, i + chunkSize); - let rms = 0; - - // Calculate RMS for this chunk - for (let j = 0; j < chunk.length; j += 2) { - if (j + 1 < chunk.length) { - const sample = chunk.readInt16LE(j); - rms += sample * sample; - } - } - - rms = Math.sqrt(rms / (chunk.length / 2)); - hash.update(Math.floor(rms).toString()); - } - - return hash.digest('hex').substring(0, 16); // First 16 characters - } - - private recordLookupTime(time: number): void { - this.lookupTimes.push(time); - - // Keep only last 1000 measurements - if (this.lookupTimes.length > 1000) { - this.lookupTimes.shift(); - } - } -} diff --git a/backend/src/voice/transcription/WhisperConnectionPool.ts b/backend/src/voice/transcription/WhisperConnectionPool.ts deleted file mode 100644 index fe55d45..0000000 --- a/backend/src/voice/transcription/WhisperConnectionPool.ts +++ /dev/null @@ -1,439 +0,0 @@ -/** - * Whisper Connection Pool - Phase 2 Voice Processing - * Pre-warmed connection pool for OpenAI Whisper API - * - * Success Criteria: - * - 5 pre-warmed connections maintained - * - Connection health monitoring - * - Automatic connection recovery - * - <50ms connection acquisition time - */ - -import axios, { AxiosInstance, AxiosError } from 'axios'; -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; - -export interface PoolStats { - totalConnections: number; - availableConnections: number; - activeConnections: number; - healthyConnections: number; - totalRequests: number; - failedRequests: number; - averageAcquisitionTime: number; -} - -export class WhisperConnectionPool { - private connections: AxiosInstance[] = []; - private availableConnections: AxiosInstance[] = []; - private activeConnections: Set = new Set(); - private connectionHealth: Map< - AxiosInstance, - { healthy: boolean; lastCheck: number; failures: number } - > = new Map(); - private healthCheckInterval: NodeJS.Timeout | null = null; - private warmupInterval: NodeJS.Timeout | null = null; - - // Statistics - private totalRequests = 0; - private failedRequests = 0; - private acquisitionTimes: number[] = []; - - constructor(private poolSize: number = 5) { - this.initializePool(); - this.startHealthMonitoring(); - this.startConnectionWarming(); - - logger.info('Whisper connection pool initialized', { - poolSize: this.poolSize, - apiVersion: 'v1', - }); - } - - /** - * Get a connection from the pool - * Target: <50ms acquisition time - */ - async getConnection(): Promise { - const startTime = performance.now(); - this.totalRequests++; - - try { - // Try to get a healthy available connection - const connection = await this.acquireHealthyConnection(); - - const acquisitionTime = performance.now() - startTime; - this.recordAcquisitionTime(acquisitionTime); - - // Log slow acquisition - if (acquisitionTime > 50) { - logger.warn('Slow connection acquisition', { - acquisitionTime: `${acquisitionTime.toFixed(2)}ms`, - availableConnections: this.availableConnections.length, - activeConnections: this.activeConnections.size, - }); - } - - // Mark as active - this.activeConnections.add(connection); - - return connection; - } catch (error) { - this.failedRequests++; - logger.error('Failed to acquire connection', { - error: error instanceof Error ? error.message : String(error), - availableConnections: this.availableConnections.length, - healthyConnections: this.getHealthyConnectionCount(), - }); - - throw error; - } - } - - /** - * Release a connection back to the pool - */ - releaseConnection(connection: AxiosInstance): void { - if (!this.connections.includes(connection)) { - logger.warn('Attempting to release unknown connection'); - return; - } - - // Remove from active set - this.activeConnections.delete(connection); - - // Add back to available pool if healthy - const health = this.connectionHealth.get(connection); - if (health?.healthy !== false) { - this.availableConnections.push(connection); - } - - logger.debug('Connection released', { - availableConnections: this.availableConnections.length, - activeConnections: this.activeConnections.size, - }); - } - - /** - * Get pool statistics - */ - getStats(): PoolStats { - const avgAcquisitionTime = - this.acquisitionTimes.length > 0 - ? this.acquisitionTimes.reduce((sum, time) => sum + time, 0) / this.acquisitionTimes.length - : 0; - - return { - totalConnections: this.connections.length, - availableConnections: this.availableConnections.length, - activeConnections: this.activeConnections.size, - healthyConnections: this.getHealthyConnectionCount(), - totalRequests: this.totalRequests, - failedRequests: this.failedRequests, - averageAcquisitionTime: Math.round(avgAcquisitionTime * 100) / 100, - }; - } - - /** - * Force health check on all connections - */ - async checkAllConnectionsHealth(): Promise { - logger.info('Starting manual health check for all connections'); - - const healthChecks = this.connections.map((conn) => this.checkConnectionHealth(conn)); - await Promise.allSettled(healthChecks); - - const stats = this.getStats(); - logger.info('Manual health check completed', stats); - } - - /** - * Shut down the connection pool - */ - async shutdown(): Promise { - logger.info('Shutting down Whisper connection pool'); - - if (this.healthCheckInterval) { - clearInterval(this.healthCheckInterval); - } - - if (this.warmupInterval) { - clearInterval(this.warmupInterval); - } - - // Clear all arrays and maps - this.connections = []; - this.availableConnections = []; - this.activeConnections.clear(); - this.connectionHealth.clear(); - - logger.info('Whisper connection pool shut down'); - } - - private initializePool(): void { - for (let i = 0; i < this.poolSize; i++) { - const connection = this.createConnection(); - this.connections.push(connection); - this.availableConnections.push(connection); - this.connectionHealth.set(connection, { - healthy: true, - lastCheck: Date.now(), - failures: 0, - }); - } - - logger.debug('Connection pool initialized', { - totalConnections: this.connections.length, - availableConnections: this.availableConnections.length, - }); - } - - private createConnection(): AxiosInstance { - const connection = axios.create({ - baseURL: 'https://api.openai.com/v1', - timeout: 30000, // 30 second timeout for uploads - headers: { - Authorization: `Bearer ${process.env.OPENAI_API_KEY}`, - 'User-Agent': 'CEO-Platform/1.0', - 'Keep-Alive': 'timeout=30, max=100', - }, - httpAgent: new (require('http').Agent)({ - keepAlive: true, - maxSockets: 10, - timeout: 30000, - }), - httpsAgent: new (require('https').Agent)({ - keepAlive: true, - maxSockets: 10, - timeout: 30000, - rejectUnauthorized: true, - }), - maxRedirects: 3, - validateStatus: (status) => status >= 200 && status < 300, - }); - - // Add request interceptor for logging - connection.interceptors.request.use( - (config) => { - logger.debug('Whisper API request', { - method: config.method?.toUpperCase(), - url: config.url, - timeout: config.timeout, - }); - return config; - }, - (error) => { - logger.error('Whisper API request error', { error: error.message }); - return Promise.reject(error); - } - ); - - // Add response interceptor for error handling - connection.interceptors.response.use( - (response) => { - // Mark connection as healthy on successful response - const health = this.connectionHealth.get(connection); - if (health) { - health.healthy = true; - health.failures = 0; - health.lastCheck = Date.now(); - } - - return response; - }, - (error: AxiosError) => { - // Mark connection as potentially unhealthy on error - const health = this.connectionHealth.get(connection); - if (health) { - health.failures++; - health.lastCheck = Date.now(); - - // Mark as unhealthy after 3 consecutive failures - if (health.failures >= 3) { - health.healthy = false; - logger.warn('Connection marked as unhealthy', { - failures: health.failures, - errorStatus: error.response?.status, - errorMessage: error.message, - }); - } - } - - logger.error('Whisper API response error', { - status: error.response?.status, - message: error.message, - code: error.code, - }); - - return Promise.reject(error); - } - ); - - return connection; - } - - private async acquireHealthyConnection(): Promise { - const maxAttempts = 10; - let attempts = 0; - - while (attempts < maxAttempts) { - // Get next available connection - const connection = this.availableConnections.shift(); - - if (!connection) { - // No available connections - wait for one to be released - await this.waitForConnection(); - attempts++; - continue; - } - - // Check if connection is healthy - const health = this.connectionHealth.get(connection); - if (health?.healthy !== false) { - return connection; - } - - // Connection is unhealthy - try to replace it - logger.warn('Replacing unhealthy connection'); - this.replaceConnection(connection); - attempts++; - } - - throw new Error('Unable to acquire healthy connection after maximum attempts'); - } - - private async waitForConnection(): Promise { - return new Promise((resolve) => { - const checkInterval = setInterval(() => { - if (this.availableConnections.length > 0) { - clearInterval(checkInterval); - resolve(); - } - }, 10); // Check every 10ms - - // Timeout after 5 seconds - setTimeout(() => { - clearInterval(checkInterval); - resolve(); - }, 5000); - }); - } - - private replaceConnection(oldConnection: AxiosInstance): void { - // Remove from all collections - const connectionIndex = this.connections.indexOf(oldConnection); - if (connectionIndex !== -1) { - this.connections[connectionIndex] = this.createConnection(); - this.availableConnections.push(this.connections[connectionIndex]); - this.connectionHealth.set(this.connections[connectionIndex], { - healthy: true, - lastCheck: Date.now(), - failures: 0, - }); - } - - this.connectionHealth.delete(oldConnection); - this.activeConnections.delete(oldConnection); - } - - private startHealthMonitoring(): void { - this.healthCheckInterval = setInterval(async () => { - await this.performHealthCheck(); - }, 60000); // Check every minute - } - - private startConnectionWarming(): void { - this.warmupInterval = setInterval(async () => { - await this.warmupConnections(); - }, 300000); // Warm up every 5 minutes - } - - private async performHealthCheck(): Promise { - logger.debug('Starting connection health check'); - - const healthPromises = this.connections.map((conn) => this.checkConnectionHealth(conn)); - - await Promise.allSettled(healthPromises); - - const stats = this.getStats(); - logger.debug('Health check completed', { - healthyConnections: stats.healthyConnections, - totalConnections: stats.totalConnections, - }); - - // Replace unhealthy connections - const unhealthyConnections = this.connections.filter((conn) => { - const health = this.connectionHealth.get(conn); - return health?.healthy === false; - }); - - for (const conn of unhealthyConnections) { - this.replaceConnection(conn); - } - } - - private async checkConnectionHealth(connection: AxiosInstance): Promise { - try { - // Make a lightweight request to check health - await connection.get('/models', { - timeout: 5000, - params: { limit: 1 }, - }); - - // Mark as healthy - const health = this.connectionHealth.get(connection); - if (health) { - health.healthy = true; - health.failures = 0; - health.lastCheck = Date.now(); - } - } catch (error) { - // Mark as potentially unhealthy - const health = this.connectionHealth.get(connection); - if (health) { - health.failures++; - health.lastCheck = Date.now(); - - if (health.failures >= 2) { - health.healthy = false; - logger.warn('Connection health check failed', { - failures: health.failures, - error: error instanceof Error ? error.message : String(error), - }); - } - } - } - } - - private async warmupConnections(): Promise { - // Warm up a subset of available connections - const connectionsToWarm = this.availableConnections.slice(0, 2); - - const warmupPromises = connectionsToWarm.map(async (conn) => { - try { - await conn.get('/models', { timeout: 5000 }); - logger.debug('Connection warmed up successfully'); - } catch (error) { - logger.warn('Connection warmup failed', { - error: error instanceof Error ? error.message : String(error), - }); - } - }); - - await Promise.allSettled(warmupPromises); - } - - private getHealthyConnectionCount(): number { - return Array.from(this.connectionHealth.values()).filter((health) => health.healthy !== false) - .length; - } - - private recordAcquisitionTime(time: number): void { - this.acquisitionTimes.push(time); - - // Keep only last 100 measurements - if (this.acquisitionTimes.length > 100) { - this.acquisitionTimes.shift(); - } - } -} diff --git a/backend/src/voice/transcription/WhisperService.ts b/backend/src/voice/transcription/WhisperService.ts deleted file mode 100644 index 1467caa..0000000 --- a/backend/src/voice/transcription/WhisperService.ts +++ /dev/null @@ -1,453 +0,0 @@ -/** - * Whisper Transcription Service - Phase 2 Voice Processing - * OpenAI Whisper API integration with connection pooling and caching - * - * Success Criteria: - * - Single audio transcription in <1.5 seconds - * - Batch processing for multiple segments - * - Error handling with automatic retry - * - 95%+ transcription accuracy for clear speech - */ - -import axios, { AxiosInstance } from 'axios'; -import FormData from 'form-data'; -import { performance } from 'perf_hooks'; -import { logger } from '../../utils/logger'; -import { - VoiceProcessingError, - TranscriptResult, - TranscriptionOptions, - AudioSegment, -} from '../types'; -import { WhisperConnectionPool } from './WhisperConnectionPool'; -import { TranscriptionCache } from './TranscriptionCache'; - -export class WhisperService { - private connectionPool: WhisperConnectionPool; - private cache: TranscriptionCache; - private performanceMetrics: Map = new Map(); - private requestCount = 0; - - constructor() { - this.connectionPool = new WhisperConnectionPool(5); - this.cache = new TranscriptionCache(); - - logger.info('Whisper service initialized', { - poolSize: 5, - cacheEnabled: true, - }); - } - - /** - * Transcribe audio buffer to text - * Target: <1.5 seconds processing time - */ - async transcribeAudio( - audioBuffer: Buffer, - options: TranscriptionOptions = {} - ): Promise { - const startTime = performance.now(); - const requestId = `whisper-${++this.requestCount}-${Date.now()}`; - - try { - // Validate input - this.validateAudioBuffer(audioBuffer); - - // Check cache first - const cacheKey = this.cache.generateCacheKey(audioBuffer, options); - const cachedResult = await this.cache.get(cacheKey); - - if (cachedResult) { - logger.debug('Whisper cache hit', { requestId, cacheKey }); - return { - ...cachedResult, - processingTime: performance.now() - startTime, - }; - } - - // Get connection from pool - const connection = await this.connectionPool.getConnection(); - - try { - // Prepare form data - const formData = this.prepareFormData(audioBuffer, options); - - // Make API request - const response = await connection.post('/audio/transcriptions', formData, { - headers: formData.getHeaders(), - timeout: 15000, // 15 second timeout - maxContentLength: 25 * 1024 * 1024, // 25MB max - }); - - const processingTime = performance.now() - startTime; - - // Process response - const result = this.processWhisperResponse(response.data, processingTime, requestId); - - // Cache successful results - if (result.confidence > 0.7) { - await this.cache.set(cacheKey, result, 3600); // Cache for 1 hour - } - - // Record metrics - this.recordPerformanceMetric('transcription_time', processingTime); - this.recordPerformanceMetric('transcription_success', 1); - - logger.info('Whisper transcription successful', { - requestId, - processingTime: `${processingTime.toFixed(2)}ms`, - transcriptLength: result.transcript.length, - confidence: result.confidence, - language: result.language, - }); - - return result; - } finally { - // Always release connection back to pool - this.connectionPool.releaseConnection(connection); - } - } catch (error) { - const processingTime = performance.now() - startTime; - - this.recordPerformanceMetric('transcription_time', processingTime); - this.recordPerformanceMetric('transcription_error', 1); - - logger.error('Whisper transcription failed', { - requestId, - error: error instanceof Error ? error.message : String(error), - processingTime: `${processingTime.toFixed(2)}ms`, - audioSize: audioBuffer.length, - options, - }); - - throw new VoiceProcessingError('Transcription failed', { - requestId, - processingTime, - originalError: error instanceof Error ? error.message : String(error), - audioSize: audioBuffer.length, - }); - } - } - - /** - * Transcribe multiple audio segments in parallel - * Optimized for complex multi-segment commands - */ - async transcribeBatch(audioSegments: AudioSegment[]): Promise { - if (audioSegments.length === 0) { - return []; - } - - const startTime = performance.now(); - const batchId = `batch-${Date.now()}`; - - logger.info('Starting batch transcription', { - batchId, - segmentCount: audioSegments.length, - }); - - try { - // Process segments in parallel (max 3 concurrent to avoid rate limiting) - const concurrencyLimit = 3; - const results: TranscriptResult[] = []; - - for (let i = 0; i < audioSegments.length; i += concurrencyLimit) { - const batch = audioSegments.slice(i, i + concurrencyLimit); - const batchPromises = batch.map((segment, index) => - this.transcribeAudio(segment.audioData, { - language: segment.language ?? '', - prompt: segment.context ?? '', - }).catch( - (error) => - ({ - transcript: '', - confidence: 0, - language: 'unknown', - processingTime: 0, - error: error instanceof Error ? error.message : String(error), - }) as TranscriptResult - ) - ); - - const batchResults = await Promise.all(batchPromises); - results.push(...batchResults); - - // Small delay between batches to respect rate limits - if (i + concurrencyLimit < audioSegments.length) { - await new Promise((resolve) => setTimeout(resolve, 100)); - } - } - - const totalTime = performance.now() - startTime; - const successfulResults = results.filter((r) => !r.error); - - logger.info('Batch transcription completed', { - batchId, - totalTime: `${totalTime.toFixed(2)}ms`, - totalSegments: audioSegments.length, - successfulTranscriptions: successfulResults.length, - failedTranscriptions: results.length - successfulResults.length, - }); - - return results; - } catch (error) { - const totalTime = performance.now() - startTime; - - logger.error('Batch transcription failed', { - batchId, - error: error instanceof Error ? error.message : String(error), - totalTime: `${totalTime.toFixed(2)}ms`, - segmentCount: audioSegments.length, - }); - - throw new VoiceProcessingError('Batch transcription failed', { - batchId, - totalTime, - segmentCount: audioSegments.length, - originalError: error instanceof Error ? error.message : String(error), - }); - } - } - - /** - * Transcribe with streaming support for real-time processing - */ - async transcribeStream( - audioStream: AsyncGenerator - ): Promise>> { - const streamId = `stream-${Date.now()}`; - - logger.info('Starting stream transcription', { streamId }); - - return async function* (this: WhisperService) { - const bufferChunks: Buffer[] = []; - let segmentBuffer = Buffer.alloc(0); - - try { - for await (const chunk of audioStream) { - segmentBuffer = Buffer.concat([segmentBuffer, chunk]); - - // Process when we have enough data (2 seconds of audio) - const minSize = 16000 * 2 * 2; // 2 seconds at 16kHz, 16-bit - - if (segmentBuffer.length >= minSize) { - try { - const result = await this.transcribeAudio(segmentBuffer); - - yield { - transcript: result.transcript, - confidence: result.confidence, - language: result.language, - processingTime: result.processingTime, - }; - - // Reset buffer - segmentBuffer = Buffer.alloc(0); - } catch (error) { - yield { - error: error instanceof Error ? error.message : String(error), - processingTime: 0, - }; - } - } - } - - // Process remaining buffer - if (segmentBuffer.length > 0) { - try { - const result = await this.transcribeAudio(segmentBuffer); - yield result; - } catch (error) { - yield { - error: error instanceof Error ? error.message : String(error), - processingTime: 0, - }; - } - } - } catch (error) { - logger.error('Stream transcription error', { - streamId, - error: error instanceof Error ? error.message : String(error), - }); - - yield { - error: error instanceof Error ? error.message : String(error), - processingTime: 0, - }; - } - }.call(this); - } - - /** - * Get service performance statistics - */ - getPerformanceStats(): Record< - string, - { average: number; p95: number; p99: number; count: number } - > { - const stats: Record = {}; - - for (const [metric, values] of this.performanceMetrics.entries()) { - if (values.length === 0) continue; - - const sorted = [...values].sort((a, b) => a - b); - const average = values.reduce((sum, val) => sum + val, 0) / values.length; - const p95 = sorted[Math.floor(sorted.length * 0.95)] || 0; - const p99 = sorted[Math.floor(sorted.length * 0.99)] || 0; - - stats[metric] = { - average: Math.round(average * 100) / 100, - p95: Math.round(p95 * 100) / 100, - p99: Math.round(p99 * 100) / 100, - count: values.length, - }; - } - - return stats; - } - - /** - * Get service health status - */ - getHealthStatus(): { - status: 'healthy' | 'degraded' | 'unhealthy'; - metrics: Record; - connectionPool: any; - cache: any; - } { - const stats = this.getPerformanceStats(); - const poolStats = this.connectionPool.getStats(); - const cacheStats = this.cache.getStats(); - - let status: 'healthy' | 'degraded' | 'unhealthy' = 'healthy'; - - // Check if average transcription time is too high - const avgTime = stats.transcription_time?.average || 0; - if (avgTime > 3000) { - status = 'unhealthy'; - } else if (avgTime > 2000) { - status = 'degraded'; - } - - // Check error rate - const successCount = stats.transcription_success?.count || 0; - const errorCount = stats.transcription_error?.count || 0; - const errorRate = errorCount / (successCount + errorCount) || 0; - - if (errorRate > 0.1) { - status = 'unhealthy'; - } else if (errorRate > 0.05) { - status = 'degraded'; - } - - return { - status, - metrics: stats, - connectionPool: poolStats, - cache: cacheStats, - }; - } - - private validateAudioBuffer(buffer: Buffer): void { - if (!buffer || buffer.length === 0) { - throw new Error('Audio buffer is empty'); - } - - if (buffer.length > 25 * 1024 * 1024) { - // 25MB limit - throw new Error('Audio buffer too large (>25MB)'); - } - - if (buffer.length < 1000) { - // Minimum reasonable size - throw new Error('Audio buffer too small (<1KB)'); - } - } - - private prepareFormData(audioBuffer: Buffer, options: TranscriptionOptions): FormData { - const formData = new FormData(); - - // Add audio file - formData.append('file', audioBuffer, { - filename: 'audio.wav', - contentType: 'audio/wav', - }); - - // Add model - formData.append('model', 'whisper-1'); - - // Add response format - formData.append('response_format', options.responseFormat || 'verbose_json'); - - // Add optional parameters - if (options.language) { - formData.append('language', options.language); - } - - if (options.prompt) { - formData.append('prompt', options.prompt); - } - - if (options.temperature !== undefined) { - formData.append('temperature', options.temperature.toString()); - } - - return formData; - } - - private processWhisperResponse( - responseData: any, - processingTime: number, - requestId: string - ): TranscriptResult { - const transcript = responseData.text || ''; - const language = responseData.language || 'en'; - - // Calculate confidence score from segments if available - let confidence = 0.9; // Default confidence - if (responseData.segments && responseData.segments.length > 0) { - const totalConfidence = responseData.segments.reduce( - (sum: number, segment: any) => sum + (segment.confidence || 0.9), - 0 - ); - confidence = Math.max(0.1, Math.min(1.0, totalConfidence / responseData.segments.length)); - } - - // Validate transcript quality - if (transcript.length === 0) { - confidence = 0; - } else if (transcript.length < 3) { - confidence *= 0.5; // Reduce confidence for very short transcripts - } - - logger.debug('Whisper response processed', { - requestId, - transcriptLength: transcript.length, - confidence, - language, - segmentCount: responseData.segments?.length || 0, - }); - - return { - transcript, - confidence, - language, - processingTime, - segments: responseData.segments || [], - }; - } - - private recordPerformanceMetric(key: string, value: number): void { - if (!this.performanceMetrics.has(key)) { - this.performanceMetrics.set(key, []); - } - - const metrics = this.performanceMetrics.get(key)!; - metrics.push(value); - - // Keep only last 1000 measurements - if (metrics.length > 1000) { - metrics.shift(); - } - } -} diff --git a/backend/src/voice/types.ts b/backend/src/voice/types.ts deleted file mode 100644 index 6d51de4..0000000 --- a/backend/src/voice/types.ts +++ /dev/null @@ -1,410 +0,0 @@ -/** - * Voice Processing Types and Interfaces - Phase 2 - * Central type definitions for voice processing pipeline - */ - -// Audio Stream Types -export interface AudioStreamConfig { - userId: string; - socketId: string; - sampleRate: number; - channels: number; - format: 'pcm16' | 'pcm32' | 'float32'; - chunkSize: number; - maxBufferSize?: number; - silenceThreshold?: number; - maxSegmentLength?: number; -} - -export interface AudioSegment { - id: string; - audioData: Buffer; - sampleRate: number; - channels: number; - format: string; - duration: number; - timestamp: string; - userId: string; - socketId: string; - language?: string; - context?: string; -} - -export interface ProcessingResult { - status: 'waiting' | 'processing' | 'segment_ready' | 'error'; - hasVoice?: boolean; - segment?: AudioSegment; - processingTime?: number; - error?: string; -} - -export interface StreamStatus { - isActive: boolean; - bufferLevel: number; - lastActivity: number; - totalProcessed: number; - memoryUsage: number; -} - -// Voice Activity Detection Types -export interface VADConfig { - threshold: number; - sensitivity: number; - smoothing: number; - minSpeechDuration: number; - hangoverTime: number; -} - -export interface VADResult { - hasVoice: boolean; - confidence: number; - energy: number; - backgroundNoise: number; - processingTime: number; -} - -// Transcription Types -export interface TranscriptionOptions { - language?: string; - prompt?: string; - temperature?: number; - responseFormat?: 'json' | 'text' | 'verbose_json'; -} - -export interface TranscriptResult { - transcript: string; - confidence: number; - language: string; - processingTime: number; - segments?: TranscriptSegment[]; - error?: string; -} - -export interface TranscriptSegment { - id: string; - start: number; - end: number; - text: string; - confidence: number; - words?: TranscriptWord[]; -} - -export interface TranscriptWord { - word: string; - start: number; - end: number; - confidence: number; -} - -// AI Command Processing Types -export interface UserContext { - userId: string; - organizationId: string; - sessionId?: string; - language?: string; - timezone?: string; -} - -export interface ContextData { - user: { - id: string; - name: string; - role: string; - email: string; - }; - organization: { - id: string; - name: string; - timezone: string; - }; - activeChannels: Array<{ - id: string; - name: string; - type: string; - memberCount: number; - }>; - recentTasks: Array<{ - id: string; - title: string; - status: string; - assignedTo: string[]; - dueDate?: string; - }>; - teamMembers: Array<{ - id: string; - name: string; - role: string; - status: 'online' | 'offline' | 'busy'; - }>; - temporal: { - currentTime: string; - timezone: string; - businessHours: { - start: string; - end: string; - }; - }; - conversation?: { - recentCommands: VoiceCommand[]; - context: string[]; - }; -} - -export interface ParsedCommand { - id: string; - userId: string; - transcript: string; - originalTranscript: string; - intent: string; - confidence: number; - actions: CommandAction[]; - entities: ResolvedEntities; - contextReferences?: { - pronouns: string[]; - temporalContext: string[]; - implicitEntities: string[]; - }; - processingTime: number; - timestamp: string; -} - -export interface CommandAction { - id?: string; - type: ActionType; - parameters: Record; - priority: number; - dependencies: string[]; - estimatedDuration?: string; - critical?: boolean; - order?: number; - validated?: boolean; -} - -export type ActionType = - | 'CREATE_CHANNEL' - | 'CREATE_TASK' - | 'ASSIGN_USERS' - | 'SEND_MESSAGE' - | 'UPLOAD_FILE' - | 'SET_DEADLINE' - | 'CREATE_DEPENDENCY' - | 'UPDATE_STATUS' - | 'SCHEDULE_MEETING' - | 'GENERATE_REPORT'; - -export interface ResolvedEntities { - users: Array<{ - name: string; - resolved_id: string; - confidence: number; - }>; - channels: Array<{ - name: string; - resolved_id: string; - confidence: number; - }>; - tasks: Array<{ - title: string; - resolved_id: string; - confidence: number; - }>; - dates: Array<{ - text: string; - resolved_date: string; - confidence: number; - }>; - files: Array<{ - name: string; - resolved_id?: string; - confidence: number; - }>; -} - -// Command Execution Types -export interface ExecutionPlan { - id: string; - steps: ExecutionStep[]; - totalActions: number; - estimatedTime: number; - createdAt: string; -} - -export interface ExecutionStep { - id: string; - actions: CommandAction[]; - parallel: boolean; - dependencies: string[]; - estimatedTime: number; -} - -export interface ExecutionResult { - id: string; - commandId: string; - success: boolean; - totalActions: number; - successfulActions: number; - failedActions: number; - results: ActionResult[]; - error?: string; - executionTime: number; - rollbackApplied?: boolean; - timestamp: string; -} - -export interface ActionResult { - actionId: string; - actionType: string; - success: boolean; - data?: any; - error?: string; - executionTime: number; - timestamp: string; -} - -// Voice Command History -export interface VoiceCommand { - id: string; - userId: string; - transcript: string; - processedTranscript?: string; - intentAnalysis?: any; - actionsPlanned?: CommandAction[]; - actionsExecuted?: ActionResult[]; - executionStatus: 'pending' | 'processing' | 'completed' | 'failed' | 'partial'; - processingTime: number; - errorDetails?: any; - createdAt: string; -} - -// File Management Types -export interface VoiceFileUpload { - userId: string; - fileName: string; - contentType: string; - fileSize?: number; - description?: string; - targetChannels?: string[]; - targetTasks?: string[]; - transcript: string; -} - -export interface FileUploadResult { - success: boolean; - fileId: string; - uploadUrl: string; - finalUrl: string; - expiresAt: string; -} - -export interface FileOperationResult { - success: boolean; - operation: string; - result: any; -} - -// Real-time Broadcasting Types -export interface BroadcastEvent { - type: string; - data: any; - timestamp: string; - source: string; -} - -export interface BroadcastTarget { - type: 'user' | 'channel' | 'room'; - id: string; -} - -// Performance Monitoring Types -export interface PerformanceMetrics { - component: string; - operation: string; - duration: number; - success: boolean; - metadata?: Record; - timestamp: string; -} - -export interface VoiceProcessingMetrics { - transcriptionTime: number; - processingTime: number; - executionTime: number; - totalTime: number; - accuracy: number; - success: boolean; - commandId: string; - userId: string; - timestamp: string; -} - -// Error Types -export class VoiceProcessingError extends Error { - constructor( - message: string, - public context?: Record - ) { - super(message); - this.name = 'VoiceProcessingError'; - } -} - -export class AIProcessingError extends Error { - constructor( - message: string, - public context?: Record - ) { - super(message); - this.name = 'AIProcessingError'; - } -} - -export class ExecutionError extends Error { - constructor( - message: string, - public actionResult?: ActionResult - ) { - super(message); - this.name = 'ExecutionError'; - } -} - -export class FileUploadError extends Error { - constructor( - message: string, - public context?: Record - ) { - super(message); - this.name = 'FileUploadError'; - } -} - -// Additional types for Multi-Action Execution System - -export interface ExecutionSummary { - totalActions: number; - successfulActions: number; - failedActions: number; - totalExecutionTime: number; - averageActionTime: number; - affectedEntities: string[]; - actionBreakdown: Record; -} - -export interface AuditEntry { - id: string; - commandId: string; - userId: string; - organizationId: string; - sessionId: string; - timestamp: string; - originalTranscript: string; - parsedIntent: string; - executionSummary: ExecutionSummary; - success: boolean; - error?: string | undefined; - metadata: { - userAgent: string; - clientVersion: string; - processingTime: number; - }; -} \ No newline at end of file diff --git a/backend/src/websocket/EnhancedSocketManager.ts b/backend/src/websocket/EnhancedSocketManager.ts deleted file mode 100644 index 1d5f4ae..0000000 --- a/backend/src/websocket/EnhancedSocketManager.ts +++ /dev/null @@ -1,632 +0,0 @@ -/** - * Enhanced Socket Manager - Phase 2 Real-Time WebSocket Management - * Advanced WebSocket management with voice command event handling - * - * Success Criteria: - * - Connection management with automatic reconnection - * - Event routing and middleware support - * - Voice command event handling - * - Connection persistence and recovery - */ - -import { Server as SocketIOServer, Socket } from 'socket.io'; -import { EventEmitter } from 'events'; -import { performance } from 'perf_hooks'; -import { logger } from '../utils/logger'; -import { ISocketManager } from './types'; -import { ExecutionEventManager } from '../realtime/broadcasting/ExecutionEventManager'; -import { ProgressBroadcaster } from '../realtime/broadcasting/ProgressBroadcaster'; -import { EntityUpdateBroadcaster } from '../realtime/broadcasting/EntityUpdateBroadcaster'; - -export interface SocketConnection { - socketId: string; - userId: string; - organizationId: string; - connectionTime: number; - lastActivity: number; - subscriptions: Set; - metadata: Record; - isActive: boolean; - reconnectCount: number; -} - -export interface ConnectionMetrics { - totalConnections: number; - activeConnections: number; - averageConnectionDuration: number; - reconnectionRate: number; - eventThroughput: number; - errorRate: number; -} - -export interface VoiceCommandEvent { - eventType: 'command_start' | 'command_progress' | 'command_complete' | 'command_error'; - commandId: string; - userId: string; - organizationId: string; - data: any; - timestamp: string; -} - -export class EnhancedSocketManager extends EventEmitter implements ISocketManager { - private io: SocketIOServer; - private connections: Map = new Map(); - private userConnections: Map> = new Map(); - private executionEventManager: ExecutionEventManager; - private progressBroadcaster: ProgressBroadcaster; - private entityUpdateBroadcaster: EntityUpdateBroadcaster; - private connectionMetrics: number[] = []; - private eventMetrics: number[] = []; - private errorCount = 0; - private readonly heartbeatInterval = 30000; // 30 seconds - private readonly connectionTimeout = 300000; // 5 minutes - private heartbeatTimer?: NodeJS.Timeout; - - constructor(io: SocketIOServer) { - super(); - - this.io = io; - - // Initialize broadcasting components - this.executionEventManager = new ExecutionEventManager(this); - this.progressBroadcaster = new ProgressBroadcaster(this); - this.entityUpdateBroadcaster = new EntityUpdateBroadcaster(this); - - this.setupSocketHandlers(); - this.startHeartbeat(); - this.startMetricsCollection(); - - logger.info('Enhanced Socket Manager initialized'); - } - - /** - * Setup socket event handlers - */ - private setupSocketHandlers(): void { - this.io.on('connection', (socket: Socket) => { - this.handleConnection(socket); - }); - } - - /** - * Handle new socket connection - */ - private async handleConnection(socket: Socket): Promise { - const startTime = performance.now(); - - try { - // Extract user info from socket (assuming it's added by auth middleware) - const userId = socket.data.userId; - const organizationId = socket.data.organizationId; - - if (!userId || !organizationId) { - logger.warn('Socket connection without proper authentication', { - socketId: socket.id, - }); - socket.disconnect(); - return; - } - - // Create connection record - const connection: SocketConnection = { - socketId: socket.id, - userId, - organizationId, - connectionTime: startTime, - lastActivity: startTime, - subscriptions: new Set(), - metadata: socket.data.metadata || {}, - isActive: true, - reconnectCount: socket.data.reconnectCount || 0, - }; - - this.connections.set(socket.id, connection); - - // Track user connections - if (!this.userConnections.has(userId)) { - this.userConnections.set(userId, new Set()); - } - this.userConnections.get(userId)!.add(socket.id); - - // Setup socket event handlers - this.setupSocketEventHandlers(socket); - - // Join organization room - socket.join(`org:${organizationId}`); - socket.join(`user:${userId}`); - - logger.info('Socket connected', { - socketId: socket.id, - userId, - organizationId, - reconnectCount: connection.reconnectCount, - }); - - this.emit('connection_established', { - socketId: socket.id, - userId, - organizationId, - connectionTime: startTime, - }); - - // Send connection acknowledgment - socket.emit('connection_ack', { - socketId: socket.id, - serverTime: new Date().toISOString(), - features: ['voice_commands', 'real_time_updates', 'progress_tracking'], - }); - } catch (error: any) { - this.errorCount++; - logger.error('Error handling socket connection', { - socketId: socket.id, - error: error.message, - }); - socket.disconnect(); - } - } - - /** - * Setup event handlers for a socket - */ - private setupSocketEventHandlers(socket: Socket): void { - const connection = this.connections.get(socket.id); - if (!connection) return; - - // Voice command events - socket.on('voice_command_start', (data) => { - this.handleVoiceCommandEvent(socket, 'command_start', data); - }); - - socket.on('voice_command_progress', (data) => { - this.handleVoiceCommandEvent(socket, 'command_progress', data); - }); - - socket.on('voice_command_complete', (data) => { - this.handleVoiceCommandEvent(socket, 'command_complete', data); - }); - - socket.on('voice_command_error', (data) => { - this.handleVoiceCommandEvent(socket, 'command_error', data); - }); - - // Subscription management - socket.on('subscribe', (channels: string[]) => { - this.handleSubscription(socket, channels); - }); - - socket.on('unsubscribe', (channels: string[]) => { - this.handleUnsubscription(socket, channels); - }); - - // Progress tracking - socket.on('request_progress', (commandId: string) => { - this.handleProgressRequest(socket, commandId); - }); - - // Entity updates - socket.on('request_entity_updates', (entityIds: string[]) => { - this.handleEntityUpdateRequest(socket, entityIds); - }); - - // Heartbeat - socket.on('ping', () => { - connection.lastActivity = performance.now(); - socket.emit('pong', { serverTime: Date.now() }); - }); - - // Disconnection - socket.on('disconnect', (reason) => { - this.handleDisconnection(socket, reason); - }); - - // Error handling - socket.on('error', (error) => { - this.handleSocketError(socket, error); - }); - } - - /** - * Handle voice command events - */ - private async handleVoiceCommandEvent( - socket: Socket, - eventType: VoiceCommandEvent['eventType'], - data: any - ): Promise { - const connection = this.connections.get(socket.id); - if (!connection) return; - - const startTime = performance.now(); - - try { - const voiceCommandEvent: VoiceCommandEvent = { - eventType, - commandId: data.commandId, - userId: connection.userId, - organizationId: connection.organizationId, - data, - timestamp: new Date().toISOString(), - }; - - // Update last activity - connection.lastActivity = performance.now(); - - // Route to appropriate handler based on event type - switch (eventType) { - case 'command_start': - await this.executionEventManager.broadcastCommandStart( - data.commandId, - connection.userId, - connection.organizationId, - data.affectedUsers || [], - data // Pass the commandData as the fifth argument - ); - break; - - case 'command_progress': - if (data.sessionId && data.stepId) { - await this.progressBroadcaster.updateStepProgress( - data.sessionId, - data.stepId, - data.progress, - data.status, - data.result, - data.error - ); - } - break; - - case 'command_complete': - await this.executionEventManager.broadcastCommandComplete( - data.commandId, - connection.userId, - connection.organizationId, - data.result, - data.affectedUsers || [] - ); - break; - - case 'command_error': - await this.executionEventManager.broadcastCommandError( - data.commandId, - connection.userId, - connection.organizationId, - data.error, - data.affectedUsers || [] - ); - break; - } - - const processingTime = performance.now() - startTime; - this.recordEventMetrics(processingTime); - - logger.debug('Voice command event processed', { - eventType, - commandId: data.commandId, - userId: connection.userId, - processingTime: `${processingTime.toFixed(2)}ms`, - }); - - this.emit('voice_command_event', voiceCommandEvent); - } catch (error: any) { - this.errorCount++; - logger.error('Error handling voice command event', { - socketId: socket.id, - eventType, - error: error.message, - }); - - socket.emit('event_error', { - eventType, - error: error.message, - timestamp: new Date().toISOString(), - }); - } - } - - /** - * Handle channel subscription - */ - private handleSubscription(socket: Socket, channels: string[]): void { - const connection = this.connections.get(socket.id); - if (!connection) return; - - for (const channel of channels) { - connection.subscriptions.add(channel); - socket.join(channel); - } - - logger.debug('Socket subscribed to channels', { - socketId: socket.id, - channels, - totalSubscriptions: connection.subscriptions.size, - }); - } - - /** - * Handle channel unsubscription - */ - private handleUnsubscription(socket: Socket, channels: string[]): void { - const connection = this.connections.get(socket.id); - if (!connection) return; - - for (const channel of channels) { - connection.subscriptions.delete(channel); - socket.leave(channel); - } - - logger.debug('Socket unsubscribed from channels', { - socketId: socket.id, - channels, - totalSubscriptions: connection.subscriptions.size, - }); - } - - /** - * Handle progress request - */ - private handleProgressRequest(socket: Socket, commandId: string): void { - const connection = this.connections.get(socket.id); - if (!connection) return; - - const sessions = this.progressBroadcaster.getUserSessions(connection.userId); - const commandSession = sessions.find((s) => s.commandId === commandId); - - if (commandSession) { - socket.emit('progress_update', { - sessionId: commandSession.sessionId, - commandId: commandSession.commandId, - // ... include full progress data - }); - } else { - socket.emit('progress_not_found', { commandId }); - } - } - - /** - * Handle entity update request - */ - private handleEntityUpdateRequest(socket: Socket, entityIds: string[]): void { - const connection = this.connections.get(socket.id); - if (!connection) return; - - // Get recent batches for organization - const recentBatches = this.entityUpdateBroadcaster.getRecentBatches( - connection.organizationId, - 5 - ); - - // Filter updates for requested entities - const relevantUpdates = recentBatches.flatMap((batch) => - batch.updates.filter( - (update) => - entityIds.includes(update.entityId) && update.affectedUsers.includes(connection.userId) - ) - ); - - socket.emit('entity_updates_history', { - entityIds, - updates: relevantUpdates, - timestamp: new Date().toISOString(), - }); - } - - /** - * Handle socket disconnection - */ - private handleDisconnection(socket: Socket, reason: string): void { - const connection = this.connections.get(socket.id); - if (!connection) return; - - const connectionDuration = performance.now() - connection.connectionTime; - this.recordConnectionMetrics(connectionDuration); - - // Remove from user connections - const userSockets = this.userConnections.get(connection.userId); - if (userSockets) { - userSockets.delete(socket.id); - if (userSockets.size === 0) { - this.userConnections.delete(connection.userId); - } - } - - // Remove connection - this.connections.delete(socket.id); - - logger.info('Socket disconnected', { - socketId: socket.id, - userId: connection.userId, - reason, - connectionDuration: `${connectionDuration.toFixed(2)}ms`, - reconnectCount: connection.reconnectCount, - }); - - this.emit('connection_closed', { - socketId: socket.id, - userId: connection.userId, - reason, - connectionDuration, - }); - } - - /** - * Handle socket errors - */ - private handleSocketError(socket: Socket, error: any): void { - this.errorCount++; - - logger.error('Socket error', { - socketId: socket.id, - error: error.message || error, - }); - - this.emit('socket_error', { - socketId: socket.id, - error: error.message || error, - }); - } - - /** - * Check if user is connected - */ - isUserConnected(userId: string): boolean { - const userSockets = this.userConnections.get(userId); - return userSockets ? userSockets.size > 0 : false; - } - - /** - * Emit event to specific user - */ - async emitToUser(userId: string, event: string, data: any): Promise { - const userSockets = this.userConnections.get(userId); - if (!userSockets) return; - - const emitPromises = Array.from(userSockets).map(async (socketId) => { - const socket = this.io.sockets.sockets.get(socketId); - if (socket) { - socket.emit(event, data); - } - }); - - await Promise.allSettled(emitPromises); - } - - /** - * Emit event to organization - */ - async emitToOrganization(organizationId: string, event: string, data: any): Promise { - this.io.to(`org:${organizationId}`).emit(event, data); - } - - /** - * Start heartbeat monitoring - */ - private startHeartbeat(): void { - this.heartbeatTimer = setInterval(() => { - this.checkConnections(); - }, this.heartbeatInterval); - } - - /** - * Check and cleanup inactive connections - */ - private checkConnections(): void { - const now = performance.now(); - let inactiveCount = 0; - - for (const [socketId, connection] of this.connections.entries()) { - const inactiveDuration = now - connection.lastActivity; - - if (inactiveDuration > this.connectionTimeout) { - const socket = this.io.sockets.sockets.get(socketId); - if (socket) { - socket.disconnect(true); - inactiveCount++; - } - } - } - - if (inactiveCount > 0) { - logger.debug('Cleaned up inactive connections', { count: inactiveCount }); - } - } - - /** - * Start metrics collection - */ - private startMetricsCollection(): void { - setInterval(() => { - this.emit('metrics_update', this.getConnectionMetrics()); - }, 60000); // Every minute - } - - /** - * Record connection duration metrics - */ - private recordConnectionMetrics(duration: number): void { - this.connectionMetrics.push(duration); - - // Keep only last 1000 measurements - if (this.connectionMetrics.length > 1000) { - this.connectionMetrics.shift(); - } - } - - /** - * Record event processing metrics - */ - private recordEventMetrics(time: number): void { - this.eventMetrics.push(time); - - // Keep only last 1000 measurements - if (this.eventMetrics.length > 1000) { - this.eventMetrics.shift(); - } - } - - /** - * Get connection metrics - */ - getConnectionMetrics(): ConnectionMetrics { - const totalConnections = this.connectionMetrics.length; - const activeConnections = this.connections.size; - - const averageConnectionDuration = - totalConnections > 0 - ? this.connectionMetrics.reduce((sum, duration) => sum + duration, 0) / totalConnections - : 0; - - const reconnectCount = Array.from(this.connections.values()).reduce( - (sum, conn) => sum + conn.reconnectCount, - 0 - ); - - const reconnectionRate = activeConnections > 0 ? reconnectCount / activeConnections : 0; - - const eventThroughput = this.eventMetrics.length; - const errorRate = totalConnections > 0 ? this.errorCount / totalConnections : 0; - - return { - totalConnections: totalConnections + activeConnections, - activeConnections, - averageConnectionDuration: Math.round(averageConnectionDuration * 100) / 100, - reconnectionRate: Math.round(reconnectionRate * 10000) / 10000, - eventThroughput, - errorRate: Math.round(errorRate * 10000) / 10000, - }; - } - - /** - * Get all active connections - */ - getActiveConnections(): SocketConnection[] { - return Array.from(this.connections.values()); - } - - /** - * Get connections for specific user - */ - getUserConnections(userId: string): SocketConnection[] { - return Array.from(this.connections.values()).filter((conn) => conn.userId === userId); - } - - /** - * Cleanup and destroy - */ - destroy(): void { - if (this.heartbeatTimer) { - clearInterval(this.heartbeatTimer); - } - - // Cleanup broadcasting components - this.progressBroadcaster.destroy(); - this.entityUpdateBroadcaster.destroy(); - - // Disconnect all sockets - this.io.disconnectSockets(true); - - this.removeAllListeners(); - - logger.info('Enhanced Socket Manager destroyed'); - } -} diff --git a/backend/src/websocket/SocketManager.ts b/backend/src/websocket/SocketManager.ts deleted file mode 100644 index afa2a8d..0000000 --- a/backend/src/websocket/SocketManager.ts +++ /dev/null @@ -1,806 +0,0 @@ -import { Server as SocketIOServer, Socket } from 'socket.io'; -import { createAdapter } from '@socket.io/redis-adapter'; -import { Server as HTTPServer } from 'http'; -import { redisManager } from '@config/redis'; -import { config } from '@config/index'; -import { logger, loggers, performanceLogger } from '@utils/logger'; -import { jwtService } from '@auth/jwt'; -import { userRepository } from '@db/index'; -import { WebSocketError, AuthenticationError, AuthorizationError } from '@utils/errors'; -import { ISocketManager } from './types'; - -/** - * WebSocket Manager for Real-Time Communication - * Enterprise-grade WebSocket handling with Redis clustering support - */ - -export interface AuthenticatedSocket extends Socket { - userId?: string; - userRole?: 'ceo' | 'manager' | 'staff'; - userEmail?: string; - userName?: string; - channelIds?: string[]; - lastActivity?: Date; -} - -export interface SocketEventData { - type: string; - payload: any; - timestamp: Date; - userId?: string; - channelId?: string; - taskId?: string; -} - -export interface RoomInfo { - name: string; - type: 'channel' | 'task' | 'user' | 'global'; - memberCount: number; - members: string[]; -} - -class SocketManager implements ISocketManager { - private io: SocketIOServer | null = null; - private httpServer: HTTPServer | null = null; - private connectedUsers = new Map(); - private userChannels = new Map>(); - private channelMembers = new Map>(); - - // Event metrics - private metrics = { - connections: 0, - disconnections: 0, - events: 0, - errors: 0, - rooms: 0, - totalUsers: 0, - }; - - /** - * Initialize WebSocket server - */ - async initialize(httpServer: HTTPServer): Promise { - try { - logger.info('Initializing WebSocket server...'); - - this.httpServer = httpServer; - - // Create Socket.IO server - this.io = new SocketIOServer(httpServer, { - path: '/socket.io', - cors: { - origin: config.api.cors.origin, - credentials: config.api.cors.credentials, - }, - pingTimeout: 60000, - pingInterval: 25000, - upgradeTimeout: 10000, - maxHttpBufferSize: 1e6, // 1MB - allowEIO3: true, - transports: ['websocket', 'polling'], - }); - - // Setup Redis adapter for clustering - if (redisManager.isRedisConnected()) { - const pubClient = redisManager.getPublisher(); - const subClient = redisManager.getSubscriber(); - - this.io.adapter(createAdapter(pubClient, subClient)); - logger.info('Redis adapter configured for Socket.IO clustering'); - } - - // Setup authentication middleware - this.setupAuthentication(); - - // Setup connection handlers - this.setupConnectionHandlers(); - - // Setup error handling - this.setupErrorHandling(); - - // Start metrics collection - this.startMetricsCollection(); - - logger.info('WebSocket server initialized'); - } catch (error) { - logger.error({ error }, 'Failed to initialize WebSocket server'); - throw new WebSocketError('WebSocket initialization failed'); - } - } - - /** - * Setup authentication middleware - */ - private setupAuthentication(): void { - if (!this.io) return; - - this.io.use(async (socket: AuthenticatedSocket, next) => { - try { - // Extract token from auth header or query parameter - const token = socket.handshake.auth?.token || socket.handshake.query?.token; - - if (!token) { - loggers.websocket.warn?.( - { - socketId: socket.id, - ip: socket.handshake.address, - }, - 'WebSocket connection attempt without token' - ); - return next(new AuthenticationError('Authentication token required')); - } - - // Verify JWT token - const payload = await jwtService.verifyAccessToken(token as string); - - // Get user details - const user = await userRepository.findById(payload.userId); - if (!user || user.deleted_at) { - loggers.websocket.warn?.( - { - socketId: socket.id, - userId: payload.userId, - ip: socket.handshake.address, - }, - 'WebSocket authentication failed - user not found' - ); - return next(new AuthenticationError('User account not found')); - } - - // Attach user information to socket - socket.userId = user.id; - socket.userRole = user.role; - socket.userEmail = user.email; - socket.userName = user.name; - socket.lastActivity = new Date(); - - // Get user's channels for room management - // Note: This would need to be implemented in a channel service - socket.channelIds = []; // TODO: Get from channel service - - loggers.websocket.info?.( - { - socketId: socket.id, - userId: user.id, - userRole: user.role, - ip: socket.handshake.address, - }, - 'WebSocket authentication successful' - ); - - next(); - } catch (error) { - loggers.websocket.error?.( - { - error, - socketId: socket.id, - ip: socket.handshake.address, - }, - 'WebSocket authentication error' - ); - - if (error instanceof AuthenticationError) { - next(error); - } else { - next(new AuthenticationError('Authentication failed')); - } - } - }); - } - - /** - * Setup connection handlers - */ - private setupConnectionHandlers(): void { - if (!this.io) return; - - this.io.on('connection', (socket: AuthenticatedSocket) => { - this.handleConnection(socket); - }); - } - - /** - * Handle new socket connection - */ - private async handleConnection(socket: AuthenticatedSocket): Promise { - try { - this.metrics.connections++; - this.metrics.totalUsers = this.connectedUsers.size + 1; - - // Store connected user - if (socket.userId) { - this.connectedUsers.set(socket.userId, socket); - - // Update last active - userRepository.updateLastActive(socket.userId).catch((error) => { - loggers.websocket.warn?.( - { error, userId: socket.userId }, - 'Failed to update user last active' - ); - }); - } - - // Join user to their personal room - if (socket.userId) { - await socket.join(`user:${socket.userId}`); - } - - // Join user to their channels - if (socket.channelIds) { - for (const channelId of socket.channelIds) { - await socket.join(`channel:${channelId}`); - - // Track channel membership - if (!this.channelMembers.has(channelId)) { - this.channelMembers.set(channelId, new Set()); - } - this.channelMembers.get(channelId)!.add(socket.userId!); - } - } - - loggers.websocket.info?.( - { - socketId: socket.id, - userId: socket.userId, - userRole: socket.userRole, - channelCount: socket.channelIds?.length || 0, - totalConnections: this.metrics.connections, - }, - 'WebSocket client connected' - ); - - // Setup event handlers for this socket - this.setupSocketEventHandlers(socket); - - // Emit connection success - socket.emit('connected', { - userId: socket.userId, - socketId: socket.id, - timestamp: new Date().toISOString(), - }); - - // Notify others about user online status - if (socket.userId) { - this.broadcastUserStatus(socket.userId, 'online'); - } - } catch (error) { - loggers.websocket.error?.({ error, socketId: socket.id }, 'Error handling socket connection'); - socket.disconnect(true); - } - } - - /** - * Setup event handlers for individual socket - */ - private setupSocketEventHandlers(socket: AuthenticatedSocket): void { - // Handle disconnection - socket.on('disconnect', (reason) => { - this.handleDisconnection(socket, reason); - }); - - // Handle joining channels - socket.on('join_channel', async (data: { channelId: string }) => { - await this.handleJoinChannel(socket, data.channelId); - }); - - // Handle leaving channels - socket.on('leave_channel', async (data: { channelId: string }) => { - await this.handleLeaveChannel(socket, data.channelId); - }); - - // Handle chat messages - socket.on( - 'chat_message', - async (data: { channelId: string; message: string; type?: 'text' | 'file' | 'voice' }) => { - await this.handleChatMessage(socket, data); - } - ); - - // Handle task updates - socket.on( - 'task_update', - async (data: { - taskId: string; - updates: any; - action: 'create' | 'update' | 'delete' | 'assign' | 'complete'; - }) => { - await this.handleTaskUpdate(socket, data); - } - ); - - // Handle voice command events (for Phase 2) - socket.on( - 'voice_command', - async (data: { audioData?: Buffer; command?: string; channelId?: string }) => { - await this.handleVoiceCommand(socket, data); - } - ); - - // Handle typing indicators - socket.on('typing_start', (data: { channelId: string }) => { - this.handleTyping(socket, data.channelId, true); - }); - - socket.on('typing_stop', (data: { channelId: string }) => { - this.handleTyping(socket, data.channelId, false); - }); - - // Handle presence updates - socket.on('presence_update', (data: { status: 'online' | 'away' | 'busy' | 'offline' }) => { - this.handlePresenceUpdate(socket, data.status); - }); - - // Generic event handler with rate limiting - socket.use(([event, ...args], next) => { - this.metrics.events++; - - // Update last activity - socket.lastActivity = new Date(); - - // TODO: Implement rate limiting per user/socket - - next(); - }); - } - - /** - * Handle socket disconnection - */ - private handleDisconnection(socket: AuthenticatedSocket, reason: string): void { - this.metrics.disconnections++; - - // Remove from connected users - if (socket.userId) { - this.connectedUsers.delete(socket.userId); - - // Remove from channel membership tracking - for (const [channelId, members] of this.channelMembers.entries()) { - if (members.has(socket.userId)) { - members.delete(socket.userId); - if (members.size === 0) { - this.channelMembers.delete(channelId); - } - } - } - - // Broadcast user offline status - this.broadcastUserStatus(socket.userId, 'offline'); - } - - this.metrics.totalUsers = this.connectedUsers.size; - - loggers.websocket.info?.( - { - socketId: socket.id, - userId: socket.userId, - reason, - totalConnections: this.connectedUsers.size, - }, - 'WebSocket client disconnected' - ); - } - - /** - * Handle joining a channel - */ - private async handleJoinChannel(socket: AuthenticatedSocket, channelId: string): Promise { - try { - // TODO: Verify user has access to channel - // const hasAccess = await channelService.canUserAccess(channelId, socket.userId); - // if (!hasAccess) { - // socket.emit('error', { message: 'Access denied to channel' }); - // return; - // } - - await socket.join(`channel:${channelId}`); - - // Track membership - if (!this.channelMembers.has(channelId)) { - this.channelMembers.set(channelId, new Set()); - } - this.channelMembers.get(channelId)!.add(socket.userId!); - - // Notify channel members - socket.to(`channel:${channelId}`).emit('user_joined_channel', { - channelId, - userId: socket.userId, - userName: socket.userName, - timestamp: new Date().toISOString(), - }); - - // Acknowledge join - socket.emit('channel_joined', { - channelId, - memberCount: this.channelMembers.get(channelId)?.size || 0, - }); - - loggers.websocket.debug?.( - { - socketId: socket.id, - userId: socket.userId, - channelId, - }, - 'User joined channel' - ); - } catch (error) { - loggers.websocket.error?.( - { error, socketId: socket.id, channelId }, - 'Failed to join channel' - ); - socket.emit('error', { message: 'Failed to join channel' }); - } - } - - /** - * Handle leaving a channel - */ - private async handleLeaveChannel(socket: AuthenticatedSocket, channelId: string): Promise { - try { - await socket.leave(`channel:${channelId}`); - - // Update membership tracking - const members = this.channelMembers.get(channelId); - if (members) { - members.delete(socket.userId!); - if (members.size === 0) { - this.channelMembers.delete(channelId); - } - } - - // Notify channel members - socket.to(`channel:${channelId}`).emit('user_left_channel', { - channelId, - userId: socket.userId, - userName: socket.userName, - timestamp: new Date().toISOString(), - }); - - socket.emit('channel_left', { channelId }); - - loggers.websocket.debug?.( - { - socketId: socket.id, - userId: socket.userId, - channelId, - }, - 'User left channel' - ); - } catch (error) { - loggers.websocket.error?.( - { error, socketId: socket.id, channelId }, - 'Failed to leave channel' - ); - } - } - - /** - * Handle chat messages - */ - private async handleChatMessage( - socket: AuthenticatedSocket, - data: { - channelId: string; - message: string; - type?: 'text' | 'file' | 'voice'; - } - ): Promise { - try { - // TODO: Validate message content and user permissions - - const messageEvent: SocketEventData = { - type: 'chat_message', - payload: { - channelId: data.channelId, - message: data.message, - messageType: data.type || 'text', - userId: socket.userId || 'unknown', - userName: socket.userName || 'Unknown User', - userRole: socket.userRole || 'staff', - }, - timestamp: new Date(), - userId: socket.userId || 'unknown', - channelId: data.channelId, - }; - - // Broadcast to channel members - this.io?.to(`channel:${data.channelId}`).emit('chat_message', messageEvent.payload); - - // TODO: Store message in database - // await messageService.createMessage(messageEvent.payload); - - loggers.websocket.debug?.( - { - userId: socket.userId, - channelId: data.channelId, - messageType: data.type, - }, - 'Chat message sent' - ); - } catch (error) { - loggers.websocket.error?.({ error, userId: socket.userId }, 'Failed to handle chat message'); - socket.emit('error', { message: 'Failed to send message' }); - } - } - - /** - * Handle task updates - */ - private async handleTaskUpdate( - socket: AuthenticatedSocket, - data: { - taskId: string; - updates: any; - action: 'create' | 'update' | 'delete' | 'assign' | 'complete'; - } - ): Promise { - try { - // TODO: Validate user permissions for task updates - - const taskEvent: SocketEventData = { - type: 'task_update', - payload: { - taskId: data.taskId, - action: data.action, - updates: data.updates, - userId: socket.userId ?? 'unknown', - userName: socket.userName, - }, - timestamp: new Date(), - userId: socket.userId ?? 'unknown', - taskId: data.taskId, - }; - - // Broadcast to task watchers and assignees - // TODO: Get task details to determine who should receive updates - this.io?.emit('task_update', taskEvent.payload); - - loggers.websocket.debug?.( - { - userId: socket.userId, - taskId: data.taskId, - action: data.action, - }, - 'Task update broadcasted' - ); - } catch (error) { - loggers.websocket.error?.({ error, userId: socket.userId }, 'Failed to handle task update'); - socket.emit('error', { message: 'Failed to update task' }); - } - } - - /** - * Handle voice commands (for Phase 2) - */ - private async handleVoiceCommand( - socket: AuthenticatedSocket, - data: { - audioData?: Buffer; - command?: string; - channelId?: string; - } - ): Promise { - try { - // Only CEO can use voice commands - if (socket.userRole !== 'ceo') { - socket.emit('error', { message: 'Voice commands are only available to CEO' }); - return; - } - - // TODO: Process voice command - // This will be implemented in Phase 2 with OpenAI Whisper integration - - loggers.websocket.info?.( - { - userId: socket.userId, - channelId: data.channelId, - hasAudioData: !!data.audioData, - hasTextCommand: !!data.command, - }, - 'Voice command received' - ); - - socket.emit('voice_command_received', { - status: 'processing', - timestamp: new Date().toISOString(), - }); - } catch (error) { - loggers.websocket.error?.({ error, userId: socket.userId }, 'Failed to handle voice command'); - socket.emit('error', { message: 'Failed to process voice command' }); - } - } - - /** - * Handle typing indicators - */ - private handleTyping(socket: AuthenticatedSocket, channelId: string, isTyping: boolean): void { - socket.to(`channel:${channelId}`).emit('typing_indicator', { - channelId, - userId: socket.userId, - userName: socket.userName, - isTyping, - timestamp: new Date().toISOString(), - }); - } - - /** - * Handle presence updates - */ - private handlePresenceUpdate(socket: AuthenticatedSocket, status: string): void { - this.broadcastUserStatus(socket.userId!, status); - } - - /** - * Broadcast user status to relevant users - */ - private broadcastUserStatus(userId: string, status: string): void { - // TODO: Determine which users should receive this status update - // For now, broadcast to all connected users - this.io?.emit('user_status_update', { - userId, - status, - timestamp: new Date().toISOString(), - }); - } - - /** - * Setup error handling - */ - private setupErrorHandling(): void { - if (!this.io) return; - - this.io.on('error', (error: Error) => { - this.metrics.errors++; - loggers.websocket.error?.({ error }, 'Socket.IO server error'); - }); - - this.io.engine.on('connection_error', (err: any) => { - this.metrics.errors++; - loggers.websocket.error?.(err, 'Socket.IO connection error'); - }); - } - - /** - * Start metrics collection - */ - private startMetricsCollection(): void { - // Log metrics every 5 minutes - setInterval( - () => { - const roomCount = this.io?.sockets.adapter.rooms.size || 0; - this.metrics.rooms = roomCount; - - loggers.websocket.info?.( - { - metrics: this.metrics, - roomCount, - connectedUsers: this.connectedUsers.size, - channels: this.channelMembers.size, - }, - 'WebSocket server metrics' - ); - }, - 5 * 60 * 1000 - ); - } - - // Public API methods - - /** - * Send message to specific user - */ - sendToUser(userId: string, event: string, data: any): boolean { - const socket = this.connectedUsers.get(userId); - if (socket) { - socket.emit(event, data); - return true; - } - - // Try sending to user room if socket not directly available - if (this.io) { - this.io.to(`user:${userId}`).emit(event, data); - return true; - } - - return false; - } - - /** - * Send message to channel - */ - sendToChannel(channelId: string, event: string, data: any): void { - this.io?.to(`channel:${channelId}`).emit(event, data); - } - - /** - * Broadcast to all connected users - */ - broadcast(event: string, data: any): void { - this.io?.emit(event, data); - } - - /** - * Get connected users count - */ - getConnectedUsersCount(): number { - return this.connectedUsers.size; - } - - /** - * Get channel member count - */ - getChannelMemberCount(channelId: string): number { - return this.channelMembers.get(channelId)?.size || 0; - } - - /** - * Get all connected users in channel - */ - getChannelMembers(channelId: string): string[] { - return Array.from(this.channelMembers.get(channelId) || []); - } - - /** - * Get server metrics - */ - getMetrics(): typeof this.metrics { - return { ...this.metrics }; - } - - /** - * Check if user is online - */ - isUserOnline(userId: string): boolean { - return this.connectedUsers.has(userId); - } - - /** - * Check if user is connected (alias for interface compatibility) - */ - isUserConnected(userId: string): boolean { - return this.isUserOnline(userId); - } - - /** - * Emit to user (async version for interface compatibility) - */ - async emitToUser(userId: string, event: string, data: any): Promise { - this.sendToUser(userId, event, data); - } - - /** - * Get Socket.IO server instance - */ - getServer(): SocketIOServer | null { - return this.io; - } - - /** - * Close WebSocket server - */ - async close(): Promise { - if (this.io) { - logger.info('Closing WebSocket server...'); - - // Disconnect all clients - this.io.disconnectSockets(true); - - // Close server - this.io.close(); - - this.io = null; - this.httpServer = null; - this.connectedUsers.clear(); - this.channelMembers.clear(); - this.userChannels.clear(); - - logger.info('WebSocket server closed'); - } - } -} - -// Export class for type declarations -export { SocketManager }; - -// Export singleton instance -export const socketManager = new SocketManager(); -export default socketManager; diff --git a/backend/src/websocket/index.ts b/backend/src/websocket/index.ts deleted file mode 100644 index e129f79..0000000 --- a/backend/src/websocket/index.ts +++ /dev/null @@ -1,17 +0,0 @@ -/** - * WebSocket module exports - * Real-time communication infrastructure for the CEO Communication Platform - */ - -import { socketManager } from './SocketManager'; -import { WebSocketUtils, EventBuilder } from './utils'; - -export { socketManager, SocketManager } from './SocketManager'; -export { WebSocketUtils, EventBuilder } from './utils'; -export * from './types'; - -// Re-export for easy access - only public API -export default { - WebSocketUtils, - EventBuilder, -}; \ No newline at end of file diff --git a/backend/src/websocket/types.ts b/backend/src/websocket/types.ts deleted file mode 100644 index 5100fc7..0000000 --- a/backend/src/websocket/types.ts +++ /dev/null @@ -1,388 +0,0 @@ -/** - * WebSocket Event Types and Interfaces - * Comprehensive type definitions for real-time communication - */ - -// Base event structure -export interface BaseSocketEvent { - type: string; - timestamp: string; - userId: string; - userName: string; - userRole: 'ceo' | 'manager' | 'staff'; -} - -// Connection events -export interface ConnectionEvent extends BaseSocketEvent { - type: 'user_connected' | 'user_disconnected'; - socketId: string; - channelIds: string[]; -} - -export interface UserStatusEvent extends BaseSocketEvent { - type: 'user_status_update'; - status: 'online' | 'away' | 'busy' | 'offline'; - lastSeen?: string; -} - -// Channel events -export interface ChannelEvent extends BaseSocketEvent { - channelId: string; -} - -export interface ChannelJoinEvent extends ChannelEvent { - type: 'user_joined_channel' | 'user_left_channel'; - memberCount: number; -} - -export interface ChannelUpdateEvent extends ChannelEvent { - type: 'channel_updated' | 'channel_created' | 'channel_deleted'; - updates: { - name?: string; - description?: string; - privacy?: 'public' | 'private' | 'restricted'; - settings?: Record; - }; -} - -// Message events -export interface ChatMessageEvent extends ChannelEvent { - type: 'chat_message'; - messageId: string; - message: string; - messageType: 'text' | 'file' | 'voice' | 'system'; - attachments?: Array<{ - id: string; - name: string; - type: string; - url: string; - size: number; - }>; - replyTo?: string; - edited?: boolean; - editedAt?: string; -} - -export interface MessageUpdateEvent extends ChannelEvent { - type: 'message_updated' | 'message_deleted'; - messageId: string; - updates?: Partial; -} - -export interface TypingEvent extends ChannelEvent { - type: 'typing_indicator'; - isTyping: boolean; -} - -// Task events -export interface TaskEvent extends BaseSocketEvent { - taskId: string; - channelId?: string; -} - -export interface TaskUpdateEvent extends TaskEvent { - type: 'task_created' | 'task_updated' | 'task_deleted' | 'task_completed'; - task: { - id: string; - title: string; - description?: string; - status: 'pending' | 'in_progress' | 'review' | 'completed' | 'cancelled' | 'on_hold'; - priority: 'low' | 'medium' | 'high' | 'urgent' | 'critical'; - assignedTo: string[]; - dueDate?: string; - progress: number; - tags: string[]; - }; - action: 'create' | 'update' | 'delete' | 'assign' | 'unassign' | 'complete' | 'reopen'; - changes?: Record; -} - -export interface TaskAssignmentEvent extends TaskEvent { - type: 'task_assigned' | 'task_unassigned'; - assignedTo: string[]; - assignedBy: string; - previousAssignees?: string[]; -} - -export interface TaskCommentEvent extends TaskEvent { - type: 'task_comment_added' | 'task_comment_updated' | 'task_comment_deleted'; - commentId: string; - comment: string; - attachments?: Array<{ - id: string; - name: string; - url: string; - }>; -} - -// Voice command events (Phase 2) -export interface VoiceCommandEvent extends BaseSocketEvent { - type: 'voice_command_received' | 'voice_command_processed' | 'voice_command_failed'; - commandId: string; - channelId?: string; - audioData?: string; // Base64 encoded audio - transcription?: string; - intent?: { - action: string; - entities: Record; - confidence: number; - }; - result?: { - success: boolean; - message: string; - actions: Array<{ - type: string; - target: string; - data: any; - }>; - }; - error?: string; -} - -// Notification events -export interface NotificationEvent extends BaseSocketEvent { - type: 'notification'; - notificationId: string; - title: string; - message: string; - category: 'task' | 'channel' | 'mention' | 'system' | 'voice'; - priority: 'low' | 'medium' | 'high' | 'urgent'; - actionUrl?: string; - actionText?: string; - data?: Record; - readAt?: string; - expiresAt?: string; -} - -// Analytics events (Phase 4) -export interface AnalyticsEvent extends BaseSocketEvent { - type: 'analytics_update'; - metric: string; - value: number | string | Record; - filters?: Record; - timeRange?: { - start: string; - end: string; - }; -} - -// Error events -export interface ErrorEvent { - type: 'error'; - code: string; - message: string; - details?: Record; - timestamp: string; - retryable: boolean; -} - -// System events -export interface SystemEvent extends BaseSocketEvent { - type: 'system_announcement' | 'system_maintenance' | 'system_update'; - title: string; - message: string; - level: 'info' | 'warning' | 'critical'; - scheduledAt?: string; - affectedServices?: string[]; -} - -// Union type for all events -export type SocketEvent = - | ConnectionEvent - | UserStatusEvent - | ChannelJoinEvent - | ChannelUpdateEvent - | ChatMessageEvent - | MessageUpdateEvent - | TypingEvent - | TaskUpdateEvent - | TaskAssignmentEvent - | TaskCommentEvent - | VoiceCommandEvent - | NotificationEvent - | AnalyticsEvent - | ErrorEvent - | SystemEvent; - -// Client-to-server event types -export interface ClientToServerEvents { - // Authentication - authenticate: (token: string) => void; - - // Channel operations - join_channel: (data: { channelId: string }) => void; - leave_channel: (data: { channelId: string }) => void; - - // Messaging - chat_message: (data: { - channelId: string; - message: string; - type?: 'text' | 'file' | 'voice'; - replyTo?: string; - attachments?: Array<{ - name: string; - type: string; - data: string; // Base64 - }>; - }) => void; - - // Task operations - task_update: (data: { - taskId: string; - updates: Record; - action: 'create' | 'update' | 'delete' | 'assign' | 'complete'; - }) => void; - - task_comment: (data: { - taskId: string; - comment: string; - attachments?: Array<{ - name: string; - data: string; - }>; - }) => void; - - // Voice commands (Phase 2) - voice_command: (data: { - audioData?: string; // Base64 encoded audio - command?: string; // Text command - channelId?: string; - }) => void; - - // Presence and status - typing_start: (data: { channelId: string }) => void; - typing_stop: (data: { channelId: string }) => void; - presence_update: (data: { - status: 'online' | 'away' | 'busy' | 'offline' - }) => void; - - // Notifications - mark_notification_read: (data: { notificationId: string }) => void; - - // Subscriptions - subscribe_to_analytics: (data: { - metrics: string[]; - filters?: Record - }) => void; -} - -// Server-to-client event types -export interface ServerToClientEvents { - // Connection - connected: (data: { - userId: string; - socketId: string; - timestamp: string; - }) => void; - - // Channel events - channel_joined: (data: { - channelId: string; - memberCount: number; - }) => void; - - channel_left: (data: { - channelId: string; - }) => void; - - user_joined_channel: (data: ChannelJoinEvent) => void; - user_left_channel: (data: ChannelJoinEvent) => void; - channel_updated: (data: ChannelUpdateEvent) => void; - - // Messages - chat_message: (data: ChatMessageEvent) => void; - message_updated: (data: MessageUpdateEvent) => void; - message_deleted: (data: MessageUpdateEvent) => void; - typing_indicator: (data: TypingEvent) => void; - - // Tasks - task_update: (data: TaskUpdateEvent) => void; - task_assigned: (data: TaskAssignmentEvent) => void; - task_comment_added: (data: TaskCommentEvent) => void; - - // Voice - voice_command_received: (data: { - status: 'processing' | 'completed' | 'failed'; - timestamp: string; - commandId?: string; - }) => void; - voice_command_result: (data: VoiceCommandEvent) => void; - - // User status - user_status_update: (data: UserStatusEvent) => void; - - // Notifications - notification: (data: NotificationEvent) => void; - - // Analytics - analytics_update: (data: AnalyticsEvent) => void; - - // System - system_announcement: (data: SystemEvent) => void; - - // Error handling - error: (data: ErrorEvent) => void; -} - -// Room types -export type RoomType = 'user' | 'channel' | 'task' | 'global' | 'voice_session'; - -// Permission types for WebSocket operations -export interface SocketPermissions { - canJoinChannel: (channelId: string, userId: string) => Promise; - canSendMessage: (channelId: string, userId: string) => Promise; - canUpdateTask: (taskId: string, userId: string, action: string) => Promise; - canUseVoiceCommands: (userId: string, userRole: string) => boolean; - canViewAnalytics: (userId: string, userRole: string) => boolean; -} - -// Rate limiting configuration -export interface RateLimitConfig { - messages: { - maxPerMinute: number; - maxPerHour: number; - }; - voiceCommands: { - maxPerMinute: number; - maxPerHour: number; - }; - taskUpdates: { - maxPerMinute: number; - }; -} - -// WebSocket middleware types -export interface SocketMiddleware { - name: string; - handler: (socket: any, next: (err?: Error) => void) => void; -} - -export interface SocketEventHandler { - event: string; - handler: (socket: any, data: T) => Promise | void; - permissions?: string[]; - rateLimit?: { - maxRequests: number; - windowMs: number; - }; -} - -/** - * Common interface for socket managers - * Used by broadcasting components to interact with socket managers - */ -export interface ISocketManager { - // Event emitter capabilities (for EventEmitter compatibility) - on?(event: string, listener: (...args: any[]) => void): any; - emit?(event: string, ...args: any[]): boolean; - - // User connection methods - isUserConnected(userId: string): boolean; - emitToUser(userId: string, event: string, data: any): Promise; - - // Optional methods that may be needed - emitToChannel?(channelId: string, event: string, data: any): Promise; - getUserConnections?(userId: string): any[]; -} - -// All types are exported individually, no default export needed for types \ No newline at end of file diff --git a/backend/src/websocket/utils.ts b/backend/src/websocket/utils.ts deleted file mode 100644 index ea2e97b..0000000 --- a/backend/src/websocket/utils.ts +++ /dev/null @@ -1,508 +0,0 @@ -import { socketManager } from './SocketManager'; -import { logger, loggers } from '@utils/logger'; -import { cacheService } from '../services/CacheService'; -import { - SocketEvent, - NotificationEvent, - TaskUpdateEvent, - ChatMessageEvent, - UserStatusEvent -} from './types'; - -/** - * WebSocket utility functions for real-time communication - * High-level helpers for common WebSocket operations - */ - -export class WebSocketUtils { - /** - * Send notification to user - */ - static async notifyUser(userId: string, notification: Omit): Promise { - try { - // TODO: Get user details for complete notification - const notificationEvent: Partial = { - ...notification, - type: 'notification', - timestamp: new Date().toISOString(), - userId, // This would need to be the sender's userId - }; - - const success = socketManager.sendToUser(userId, 'notification', notificationEvent); - - if (success) { - loggers.websocket.debug?.({ - userId, - notificationId: notification.notificationId, - category: notification.category, - }, 'Notification sent via WebSocket'); - } - - return success; - } catch (error) { - loggers.websocket.error?.({ error, userId }, 'Failed to send notification via WebSocket'); - return false; - } - } - - /** - * Broadcast task update to relevant users - */ - static async broadcastTaskUpdate(taskUpdate: Omit): Promise { - try { - const event: TaskUpdateEvent = { - ...taskUpdate, - timestamp: new Date().toISOString(), - }; - - // TODO: Determine who should receive this update based on: - // - Task assignees - // - Task watchers - // - Channel members if task is in a channel - // - Managers/CEO for important tasks - - // For now, broadcast to all connected users - socketManager.broadcast('task_update', event); - - // Also send to specific channel if task is in a channel - if (taskUpdate.channelId) { - socketManager.sendToChannel(taskUpdate.channelId, 'task_update', event); - } - - // Cache the task update for offline users - await this.cacheEventForOfflineUsers('task_update', event); - - loggers.websocket.debug?.({ - taskId: taskUpdate.taskId, - action: taskUpdate.action, - userId: taskUpdate.userId, - }, 'Task update broadcasted'); - - } catch (error) { - loggers.websocket.error?.({ error, taskId: taskUpdate.taskId }, 'Failed to broadcast task update'); - } - } - - /** - * Broadcast message to channel - */ - static async broadcastChannelMessage(message: Omit): Promise { - try { - const event: ChatMessageEvent = { - ...message, - timestamp: new Date().toISOString(), - }; - - // Send to channel members - socketManager.sendToChannel(message.channelId, 'chat_message', event); - - // Cache for offline channel members - await this.cacheEventForOfflineUsers('chat_message', event, message.channelId); - - loggers.websocket.debug?.({ - channelId: message.channelId, - userId: message.userId, - messageType: message.messageType, - }, 'Channel message broadcasted'); - - } catch (error) { - loggers.websocket.error?.({ error, channelId: message.channelId }, 'Failed to broadcast channel message'); - } - } - - /** - * Update user online status - */ - static async updateUserStatus(userId: string, status: 'online' | 'away' | 'busy' | 'offline'): Promise { - try { - // TODO: Get user details - const statusEvent: Partial = { - type: 'user_status_update', - userId, - status, - timestamp: new Date().toISOString(), - lastSeen: new Date().toISOString(), // Always provide a value - }; - - // Broadcast to all users (or just contacts/team members) - socketManager.broadcast('user_status_update', statusEvent); - - // Cache the status - await cacheService.users.set( - `status:${userId}`, - { status, lastSeen: statusEvent.lastSeen }, - { ttl: 3600 } // 1 hour - ); - - loggers.websocket.debug?.({ userId, status }, 'User status updated'); - - } catch (error) { - loggers.websocket.error?.({ error, userId, status }, 'Failed to update user status'); - } - } - - /** - * Send system announcement to all users - */ - static async sendSystemAnnouncement(announcement: { - title: string; - message: string; - level: 'info' | 'warning' | 'critical'; - scheduledAt?: Date; - affectedServices?: string[]; - }): Promise { - try { - const systemEvent = { - type: 'system_announcement', - ...announcement, - timestamp: new Date().toISOString(), - userId: 'system', - userName: 'System', - userRole: 'system' as any, - scheduledAt: announcement.scheduledAt?.toISOString(), - }; - - socketManager.broadcast('system_announcement', systemEvent); - - // Cache for offline users - await cacheService.set( - `system:announcement:${Date.now()}`, - systemEvent, - { ttl: 86400, tags: ['system', 'announcements'] } - ); - - loggers.websocket.info?.({ - title: announcement.title, - level: announcement.level, - affectedServices: announcement.affectedServices, - }, 'System announcement broadcasted'); - - } catch (error) { - loggers.websocket.error?.({ error, announcement }, 'Failed to send system announcement'); - } - } - - /** - * Send event to all users in a channel - */ - static async sendToChannel(channelId: string, eventType: string, data: any): Promise { - try { - const event = { - type: eventType, - channelId, - data, - timestamp: new Date().toISOString(), - }; - - // Use socket manager to broadcast to channel - socketManager.sendToChannel(channelId, eventType, event); - - // Cache for offline users - await this.cacheEventForOfflineUsers(eventType, event as any, channelId); - - loggers.websocket.info?.({ - eventType, - channelId, - dataKeys: Object.keys(data), - }, 'Event sent to channel'); - - } catch (error) { - loggers.websocket.error?.({ - error, - eventType, - channelId - }, 'Failed to send event to channel'); - } - } - - /** - * Cache events for offline users - */ - private static async cacheEventForOfflineUsers( - eventType: string, - event: SocketEvent, - channelId?: string - ): Promise { - try { - // TODO: Get list of users who should receive this event but are offline - // For now, just cache the event with a general key - - const cacheKey = `offline_events:${eventType}:${Date.now()}`; - await cacheService.set(cacheKey, event, { - ttl: 86400, // 24 hours - tags: ['offline_events', eventType, channelId].filter(Boolean) as string[] - }); - - } catch (error) { - loggers.websocket.warn?.({ error, eventType }, 'Failed to cache event for offline users'); - } - } - - /** - * Get offline events for user when they come online - */ - static async getOfflineEventsForUser(userId: string): Promise { - try { - // TODO: Implement offline event retrieval based on user's channels, tasks, etc. - // This would require a more sophisticated caching strategy - - return []; - } catch (error) { - loggers.websocket.error?.({ error, userId }, 'Failed to get offline events for user'); - return []; - } - } - - /** - * Check if user is online - */ - static isUserOnline(userId: string): boolean { - return socketManager.isUserOnline(userId); - } - - /** - * Get online users count - */ - static getOnlineUsersCount(): number { - return socketManager.getConnectedUsersCount(); - } - - /** - * Get channel member count - */ - static getChannelMemberCount(channelId: string): number { - return socketManager.getChannelMemberCount(channelId); - } - - /** - * Get active channel members - */ - static getChannelMembers(channelId: string): string[] { - return socketManager.getChannelMembers(channelId); - } - - /** - * Send typing indicator - */ - static sendTypingIndicator(channelId: string, userId: string, isTyping: boolean): void { - socketManager.sendToChannel(channelId, 'typing_indicator', { - type: 'typing_indicator', - channelId, - userId, - isTyping, - timestamp: new Date().toISOString(), - }); - } - - /** - * Get WebSocket server metrics - */ - static getMetrics(): { - connections: number; - disconnections: number; - events: number; - errors: number; - rooms: number; - totalUsers: number; - } { - return socketManager.getMetrics(); - } - - /** - * Send bulk notifications - */ - static async sendBulkNotifications( - userIds: string[], - notification: Omit - ): Promise<{ sent: number; failed: number }> { - let sent = 0; - let failed = 0; - - await Promise.allSettled( - userIds.map(async (userId) => { - try { - const success = await this.notifyUser(userId, notification); - if (success) { - sent++; - } else { - failed++; - } - } catch (error) { - failed++; - loggers.websocket.warn?.({ error, userId }, 'Failed to send bulk notification'); - } - }) - ); - - loggers.websocket.info?.({ - notificationId: notification.notificationId, - totalUsers: userIds.length, - sent, - failed, - }, 'Bulk notifications sent'); - - return { sent, failed }; - } - - /** - * Create and send notification - */ - static async createAndSendNotification( - userId: string, - notification: { - title: string; - message: string; - category: 'task' | 'channel' | 'mention' | 'system' | 'voice'; - priority?: 'low' | 'medium' | 'high' | 'urgent'; - actionUrl?: string; - actionText?: string; - data?: Record; - expiresAt?: Date; - } - ): Promise { - const notificationEvent: Omit = { - notificationId: `notification_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - priority: notification.priority || 'medium', - ...notification, - expiresAt: notification.expiresAt?.toISOString() || new Date(Date.now() + 86400000).toISOString(), // Default to 24 hours - }; - - return await this.notifyUser(userId, notificationEvent); - } - - /** - * Voice command helpers (for Phase 2) - */ - static async sendVoiceCommandResult( - userId: string, - commandId: string, - result: { - success: boolean; - message: string; - actions?: Array<{ - type: string; - target: string; - data: any; - }>; - } - ): Promise { - const voiceResult = { - type: 'voice_command_result', - commandId, - result, - timestamp: new Date().toISOString(), - userId, - }; - - return socketManager.sendToUser(userId, 'voice_command_result', voiceResult); - } - - /** - * Analytics update helpers (for Phase 4) - */ - static async broadcastAnalyticsUpdate( - metric: string, - value: number | string | Record, - filters?: Record - ): Promise { - const analyticsEvent = { - type: 'analytics_update', - metric, - value, - filters, - timestamp: new Date().toISOString(), - }; - - // Only send to users who have subscribed to analytics updates - // TODO: Implement analytics subscription management - socketManager.broadcast('analytics_update', analyticsEvent); - } -} - -/** - * WebSocket event builders for common scenarios - */ -export class EventBuilder { - /** - * Build task creation event - */ - static taskCreated(task: any, createdBy: string): TaskUpdateEvent { - return { - type: 'task_created', - taskId: task.id, - channelId: task.channel_id, - task: { - id: task.id, - title: task.title, - description: task.description, - status: task.status, - priority: task.priority, - assignedTo: task.assigned_to, - dueDate: task.due_date, - progress: task.progress_percentage, - tags: task.tags, - }, - action: 'create', - userId: createdBy, - userName: '', // TODO: Get from user data - userRole: 'staff', // TODO: Get from user data - timestamp: new Date().toISOString(), - }; - } - - /** - * Build task assignment event - */ - static taskAssigned(taskId: string, assignedTo: string[], assignedBy: string): TaskUpdateEvent { - return { - type: 'task_updated', - taskId, - task: {} as any, // Minimal data for assignment event - action: 'assign', - changes: { assigned_to: assignedTo }, - userId: assignedBy, - userName: '', // TODO: Get from user data - userRole: 'manager', // TODO: Get from user data - timestamp: new Date().toISOString(), - }; - } - - /** - * Build channel message event - */ - static channelMessage( - channelId: string, - message: string, - userId: string, - messageType: 'text' | 'file' | 'voice' = 'text' - ): ChatMessageEvent { - return { - type: 'chat_message', - channelId, - messageId: `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`, - message, - messageType, - userId, - userName: '', // TODO: Get from user data - userRole: 'staff', // TODO: Get from user data - timestamp: new Date().toISOString(), - }; - } - - /** - * Build user status event - */ - static userStatusUpdate(userId: string, status: 'online' | 'away' | 'busy' | 'offline'): UserStatusEvent { - return { - type: 'user_status_update', - status, - lastSeen: new Date().toISOString(), // Always provide a value to satisfy strict types - userId, - userName: '', // TODO: Get from user data - userRole: 'staff', // TODO: Get from user data - timestamp: new Date().toISOString(), - }; - } -} - -export default { WebSocketUtils, EventBuilder }; \ No newline at end of file diff --git a/backend/start-dev.sh b/backend/start-dev.sh deleted file mode 100755 index 7a331db..0000000 --- a/backend/start-dev.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash - -# Development startup script for CEO Communication Platform Backend - -echo "🚀 Starting CEO Communication Platform Backend..." - -# Check if .env exists -if [ ! -f ".env" ]; then - echo "⚠️ .env file not found. Creating from .env.example..." - cp .env.example .env - echo "📝 Please update .env with your database credentials and run again." - echo "💡 Common database URLs:" - echo " PostgreSQL: postgresql://username:password@localhost:5432/database_name" - echo " For Docker: postgresql://postgres:root@localhost:5432/ceo_platform_dev" - exit 1 -fi - -# Check if PostgreSQL is running (basic check) -if ! nc -z localhost 5432; then - echo "⚠️ PostgreSQL doesn't seem to be running on localhost:5432" - echo "💡 Start your database service or update DATABASE_URL in .env" - echo " For Docker: docker-compose -f docker/docker-compose.dev.yml up -d" -fi - -# Check if Redis is running (basic check) -if ! nc -z localhost 6379; then - echo "⚠️ Redis doesn't seem to be running on localhost:6379" - echo "💡 Start Redis or update REDIS_* settings in .env" -fi - -echo "📦 Installing dependencies..." -npm install - -echo "🗃️ Running migrations..." -npm run db:migrate - -echo "🌱 Starting development server with auto-seeding..." -npm run dev \ No newline at end of file diff --git a/backend/tests/api/routes/TaskRoutes.test.d.ts b/backend/tests/api/routes/TaskRoutes.test.d.ts deleted file mode 100644 index 2892e48..0000000 --- a/backend/tests/api/routes/TaskRoutes.test.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Task Routes API Tests - * Comprehensive tests for task management endpoints with performance validation - */ -export {}; -//# sourceMappingURL=TaskRoutes.test.d.ts.map \ No newline at end of file diff --git a/backend/tests/api/routes/TaskRoutes.test.d.ts.map b/backend/tests/api/routes/TaskRoutes.test.d.ts.map deleted file mode 100644 index f095b63..0000000 --- a/backend/tests/api/routes/TaskRoutes.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TaskRoutes.test.d.ts","sourceRoot":"","sources":["TaskRoutes.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"} \ No newline at end of file diff --git a/backend/tests/api/routes/TaskRoutes.test.js b/backend/tests/api/routes/TaskRoutes.test.js deleted file mode 100644 index 1ae332b..0000000 --- a/backend/tests/api/routes/TaskRoutes.test.js +++ /dev/null @@ -1,627 +0,0 @@ -"use strict"; -/** - * Task Routes API Tests - * Comprehensive tests for task management endpoints with performance validation - */ -Object.defineProperty(exports, "__esModule", { value: true }); -const globals_1 = require("@jest/globals"); -const setup_1 = require("../../setup"); -// Mock dependencies -globals_1.jest.mock('../../../src/repositories/TaskRepository'); -globals_1.jest.mock('../../../src/websocket/SocketManager'); -globals_1.jest.mock('../../../src/services/CacheService'); -(0, globals_1.describe)('Task Routes API', () => { - let app; - let mockTaskRepository; - let mockSocketManager; - let testUser; - let testTask; - (0, globals_1.beforeEach)(async () => { - // Setup test data - testUser = (0, setup_1.createTestUser)({ - id: 'task-test-user', - role: 'staff', - permissions: ['tasks:read', 'tasks:create', 'tasks:update'] - }); - testTask = (0, setup_1.createTestTask)({ - id: 'task-test-123', - title: 'Test Task', - assigned_to: [testUser.id], - created_by: testUser.id, - }); - // Setup mocks - mockTaskRepository = require('../../../src/repositories/TaskRepository').TaskRepository; - mockSocketManager = require('../../../src/websocket/SocketManager').socketManager; - mockTaskRepository.findById = globals_1.jest.fn(); - mockTaskRepository.findMany = globals_1.jest.fn(); - mockTaskRepository.create = globals_1.jest.fn(); - mockTaskRepository.update = globals_1.jest.fn(); - mockTaskRepository.delete = globals_1.jest.fn(); - mockTaskRepository.getTaskStats = globals_1.jest.fn(); - mockTaskRepository.assignUsers = globals_1.jest.fn(); - mockTaskRepository.updateStatus = globals_1.jest.fn(); - mockSocketManager.sendToUser = globals_1.jest.fn(); - mockSocketManager.sendToChannel = globals_1.jest.fn(); - mockSocketManager.broadcast = globals_1.jest.fn(); - // Setup Fastify instance with routes - const { setupTestApp } = require('../../helpers/testApp'); - app = await setupTestApp(); - }); - (0, globals_1.afterEach)(async () => { - await (0, setup_1.cleanupTestData)(); - globals_1.jest.clearAllMocks(); - await app?.close(); - }); - (0, globals_1.describe)('GET /api/v1/tasks', () => { - (0, globals_1.it)('should list tasks with pagination and filtering', async () => { - const mockTasks = { - data: [testTask, (0, setup_1.createTestTask)({ id: 'task-2' })], - total: 2, - page: 1, - limit: 10, - totalPages: 1, - }; - mockTaskRepository.findMany.mockResolvedValueOnce(mockTasks); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return app.inject({ - method: 'GET', - url: '/api/v1/tasks?page=1&limit=10&status=pending', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - }); - setup_1.validateSuccessCriteria.realTimeUpdate(duration); - (0, globals_1.expect)(result.statusCode).toBe(200); - const responseBody = JSON.parse(result.body); - (0, globals_1.expect)(responseBody.data).toHaveLength(2); - (0, globals_1.expect)(responseBody.total).toBe(2); - (0, globals_1.expect)(responseBody.page).toBe(1); - (0, globals_1.expect)(mockTaskRepository.findMany).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - status: 'pending', - }), globals_1.expect.objectContaining({ - page: 1, - limit: 10, - })); - }); - (0, globals_1.it)('should support advanced filtering options', async () => { - const mockFilteredTasks = { - data: [testTask], - total: 1, - page: 1, - limit: 10, - totalPages: 1, - }; - mockTaskRepository.findMany.mockResolvedValueOnce(mockFilteredTasks); - const response = await app.inject({ - method: 'GET', - url: '/api/v1/tasks?assignedTo=user-123&priority=high&dueBefore=2024-12-31&tags=urgent,important', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - (0, globals_1.expect)(response.statusCode).toBe(200); - (0, globals_1.expect)(mockTaskRepository.findMany).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - assigned_to: ['user-123'], - priority: 'high', - due_date_before: globals_1.expect.any(Date), - tags: ['urgent', 'important'], - }), globals_1.expect.any(Object)); - }); - (0, globals_1.it)('should require authentication', async () => { - const response = await app.inject({ - method: 'GET', - url: '/api/v1/tasks', - }); - (0, globals_1.expect)(response.statusCode).toBe(401); - (0, globals_1.expect)(mockTaskRepository.findMany).not.toHaveBeenCalled(); - }); - (0, globals_1.it)('should respect user permissions for task visibility', async () => { - const limitedUser = (0, setup_1.createTestUser)({ - role: 'staff', - permissions: ['tasks:read:own'], // Can only see own tasks - }); - mockTaskRepository.findMany.mockResolvedValueOnce({ - data: [testTask], - total: 1, - page: 1, - limit: 10, - totalPages: 1, - }); - const response = await app.inject({ - method: 'GET', - url: '/api/v1/tasks', - headers: { - authorization: `Bearer ${await generateTestToken(limitedUser)}`, - }, - }); - (0, globals_1.expect)(response.statusCode).toBe(200); - (0, globals_1.expect)(mockTaskRepository.findMany).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - assigned_to: [limitedUser.id], // Filtered to user's own tasks - }), globals_1.expect.any(Object)); - }); - }); - (0, globals_1.describe)('GET /api/v1/tasks/:id', () => { - (0, globals_1.it)('should get task by ID successfully', async () => { - mockTaskRepository.findById.mockResolvedValueOnce(testTask); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return app.inject({ - method: 'GET', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - }); - setup_1.validateSuccessCriteria.realTimeUpdate(duration); - (0, globals_1.expect)(result.statusCode).toBe(200); - const responseBody = JSON.parse(result.body); - (0, globals_1.expect)(responseBody.id).toBe(testTask.id); - (0, globals_1.expect)(responseBody.title).toBe(testTask.title); - (0, globals_1.expect)(mockTaskRepository.findById).toHaveBeenCalledWith(testTask.id); - }); - (0, globals_1.it)('should return 404 for non-existent task', async () => { - mockTaskRepository.findById.mockResolvedValueOnce(null); - const response = await app.inject({ - method: 'GET', - url: '/api/v1/tasks/non-existent-id', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - (0, globals_1.expect)(response.statusCode).toBe(404); - const responseBody = JSON.parse(response.body); - (0, globals_1.expect)(responseBody.error.code).toBe('TASK_NOT_FOUND'); - }); - (0, globals_1.it)('should enforce task access permissions', async () => { - const restrictedTask = (0, setup_1.createTestTask)({ - id: 'restricted-task', - assigned_to: ['other-user-id'], // Not assigned to test user - privacy: 'private', - }); - mockTaskRepository.findById.mockResolvedValueOnce(restrictedTask); - const response = await app.inject({ - method: 'GET', - url: `/api/v1/tasks/${restrictedTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - (0, globals_1.expect)(response.statusCode).toBe(403); - const responseBody = JSON.parse(response.body); - (0, globals_1.expect)(responseBody.error.code).toBe('ACCESS_DENIED'); - }); - }); - (0, globals_1.describe)('POST /api/v1/tasks', () => { - (0, globals_1.it)('should create task successfully', async () => { - const newTaskData = { - title: 'New Task via API', - description: 'Task created through API endpoint', - priority: 'high', - due_date: '2024-12-31T23:59:59Z', - assigned_to: [testUser.id], - }; - const createdTask = (0, setup_1.createTestTask)({ - ...newTaskData, - id: 'new-task-id', - created_by: testUser.id, - }); - mockTaskRepository.create.mockResolvedValueOnce(createdTask); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return app.inject({ - method: 'POST', - url: '/api/v1/tasks', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: newTaskData, - }); - }); - // Task creation should be fast for good UX - setup_1.validateSuccessCriteria.simpleCommandSpeed(duration); - (0, globals_1.expect)(result.statusCode).toBe(201); - const responseBody = JSON.parse(result.body); - (0, globals_1.expect)(responseBody.id).toBe(createdTask.id); - (0, globals_1.expect)(responseBody.title).toBe(newTaskData.title); - (0, globals_1.expect)(mockTaskRepository.create).toHaveBeenCalledWith(globals_1.expect.objectContaining(newTaskData), testUser.id); - // Should send real-time notifications - (0, globals_1.expect)(mockSocketManager.sendToUser).toHaveBeenCalledWith(testUser.id, 'task_assigned', globals_1.expect.objectContaining({ - task: createdTask, - })); - }); - (0, globals_1.it)('should validate required task fields', async () => { - const invalidTaskData = { - description: 'Task without title', // Missing required title - priority: 'high', - }; - const response = await app.inject({ - method: 'POST', - url: '/api/v1/tasks', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: invalidTaskData, - }); - (0, globals_1.expect)(response.statusCode).toBe(400); - const responseBody = JSON.parse(response.body); - (0, globals_1.expect)(responseBody.error.code).toBe('VALIDATION_ERROR'); - (0, globals_1.expect)(responseBody.error.details).toContain('title'); - (0, globals_1.expect)(mockTaskRepository.create).not.toHaveBeenCalled(); - }); - (0, globals_1.it)('should enforce task creation permissions', async () => { - const unauthorizedUser = (0, setup_1.createTestUser)({ - role: 'staff', - permissions: ['tasks:read'], // No create permission - }); - const response = await app.inject({ - method: 'POST', - url: '/api/v1/tasks', - headers: { - authorization: `Bearer ${await generateTestToken(unauthorizedUser)}`, - 'content-type': 'application/json', - }, - payload: { - title: 'Unauthorized Task', - description: 'Should not be created', - }, - }); - (0, globals_1.expect)(response.statusCode).toBe(403); - (0, globals_1.expect)(mockTaskRepository.create).not.toHaveBeenCalled(); - }); - (0, globals_1.it)('should support voice-created task metadata', async () => { - const voiceTaskData = { - title: 'Voice Created Task', - description: 'Created via voice command', - voice_created: true, - voice_command_id: 'voice-cmd-123', - voice_instructions: 'Create a task for project review due Friday', - }; - const voiceTask = (0, setup_1.createTestTask)({ - ...voiceTaskData, - id: 'voice-task-id', - }); - mockTaskRepository.create.mockResolvedValueOnce(voiceTask); - const response = await app.inject({ - method: 'POST', - url: '/api/v1/tasks', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: voiceTaskData, - }); - (0, globals_1.expect)(response.statusCode).toBe(201); - const responseBody = JSON.parse(response.body); - (0, globals_1.expect)(responseBody.voice_created).toBe(true); - (0, globals_1.expect)(responseBody.voice_command_id).toBe('voice-cmd-123'); - }); - }); - (0, globals_1.describe)('PUT /api/v1/tasks/:id', () => { - (0, globals_1.it)('should update task successfully', async () => { - const updateData = { - title: 'Updated Task Title', - priority: 'urgent', - status: 'in_progress', - }; - const updatedTask = { ...testTask, ...updateData, version: 2 }; - mockTaskRepository.update.mockResolvedValueOnce(updatedTask); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return app.inject({ - method: 'PUT', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: { ...updateData, version: 1 }, - }); - }); - setup_1.validateSuccessCriteria.realTimeUpdate(duration); - (0, globals_1.expect)(result.statusCode).toBe(200); - const responseBody = JSON.parse(result.body); - (0, globals_1.expect)(responseBody.title).toBe(updateData.title); - (0, globals_1.expect)(responseBody.priority).toBe(updateData.priority); - (0, globals_1.expect)(responseBody.version).toBe(2); - (0, globals_1.expect)(mockTaskRepository.update).toHaveBeenCalledWith(testTask.id, globals_1.expect.objectContaining(updateData), testUser.id, 1); - }); - (0, globals_1.it)('should handle version conflicts', async () => { - mockTaskRepository.update.mockRejectedValueOnce(new Error('Version conflict or record not found')); - const response = await app.inject({ - method: 'PUT', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: { - title: 'Conflicted Update', - version: 1, // Stale version - }, - }); - (0, globals_1.expect)(response.statusCode).toBe(409); - const responseBody = JSON.parse(response.body); - (0, globals_1.expect)(responseBody.error.code).toBe('VERSION_CONFLICT'); - }); - (0, globals_1.it)('should broadcast task updates to relevant users', async () => { - const updatedTask = { ...testTask, status: 'completed', version: 2 }; - mockTaskRepository.update.mockResolvedValueOnce(updatedTask); - await app.inject({ - method: 'PUT', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: { status: 'completed', version: 1 }, - }); - // Should broadcast to all assigned users - (0, globals_1.expect)(mockSocketManager.sendToUser).toHaveBeenCalledWith(testUser.id, 'task_updated', globals_1.expect.objectContaining({ - task: updatedTask, - changes: globals_1.expect.objectContaining({ - status: { old: 'pending', new: 'completed' }, - }), - })); - // Should also send to watchers if any - if (updatedTask.watchers && updatedTask.watchers.length > 0) { - updatedTask.watchers.forEach((watcherId) => { - (0, globals_1.expect)(mockSocketManager.sendToUser).toHaveBeenCalledWith(watcherId, 'watched_task_updated', globals_1.expect.any(Object)); - }); - } - }); - }); - (0, globals_1.describe)('PATCH /api/v1/tasks/:id/status', () => { - (0, globals_1.it)('should update task status efficiently', async () => { - const statusUpdate = { status: 'completed', progress_percentage: 100 }; - const updatedTask = { ...testTask, ...statusUpdate }; - mockTaskRepository.updateStatus.mockResolvedValueOnce(updatedTask); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return app.inject({ - method: 'PATCH', - url: `/api/v1/tasks/${testTask.id}/status`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: statusUpdate, - }); - }); - // Status updates should be very fast for real-time UX - (0, globals_1.expect)(duration).toBeLessThan(50); - (0, globals_1.expect)(result.statusCode).toBe(200); - const responseBody = JSON.parse(result.body); - (0, globals_1.expect)(responseBody.status).toBe('completed'); - (0, globals_1.expect)(responseBody.progress_percentage).toBe(100); - (0, globals_1.expect)(mockTaskRepository.updateStatus).toHaveBeenCalledWith(testTask.id, statusUpdate, testUser.id); - }); - (0, globals_1.it)('should validate status transitions', async () => { - const invalidStatusUpdate = { status: 'invalid_status' }; - const response = await app.inject({ - method: 'PATCH', - url: `/api/v1/tasks/${testTask.id}/status`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: invalidStatusUpdate, - }); - (0, globals_1.expect)(response.statusCode).toBe(400); - const responseBody = JSON.parse(response.body); - (0, globals_1.expect)(responseBody.error.code).toBe('INVALID_STATUS'); - }); - }); - (0, globals_1.describe)('POST /api/v1/tasks/:id/assign', () => { - (0, globals_1.it)('should assign users to task', async () => { - const assignmentData = { - user_ids: ['user-1', 'user-2'], - notify: true, - }; - const assignedTask = { - ...testTask, - assigned_to: [...testTask.assigned_to, ...assignmentData.user_ids], - }; - mockTaskRepository.assignUsers.mockResolvedValueOnce(assignedTask); - const response = await app.inject({ - method: 'POST', - url: `/api/v1/tasks/${testTask.id}/assign`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: assignmentData, - }); - (0, globals_1.expect)(response.statusCode).toBe(200); - const responseBody = JSON.parse(response.body); - (0, globals_1.expect)(responseBody.assigned_to).toContain('user-1'); - (0, globals_1.expect)(responseBody.assigned_to).toContain('user-2'); - // Should notify newly assigned users - assignmentData.user_ids.forEach(userId => { - (0, globals_1.expect)(mockSocketManager.sendToUser).toHaveBeenCalledWith(userId, 'task_assigned', globals_1.expect.objectContaining({ - task: assignedTask, - })); - }); - }); - (0, globals_1.it)('should enforce assignment permissions', async () => { - const unauthorizedUser = (0, setup_1.createTestUser)({ - role: 'staff', - permissions: ['tasks:read', 'tasks:update'], // No assign permission - }); - const response = await app.inject({ - method: 'POST', - url: `/api/v1/tasks/${testTask.id}/assign`, - headers: { - authorization: `Bearer ${await generateTestToken(unauthorizedUser)}`, - 'content-type': 'application/json', - }, - payload: { user_ids: ['user-1'] }, - }); - (0, globals_1.expect)(response.statusCode).toBe(403); - (0, globals_1.expect)(mockTaskRepository.assignUsers).not.toHaveBeenCalled(); - }); - }); - (0, globals_1.describe)('GET /api/v1/tasks/stats', () => { - (0, globals_1.it)('should return task statistics', async () => { - const mockStats = { - total_tasks: 150, - pending_tasks: 45, - in_progress_tasks: 60, - completed_tasks: 40, - overdue_tasks: 5, - by_priority: { - low: 20, - medium: 80, - high: 35, - urgent: 10, - critical: 5, - }, - by_assignee: { - [testUser.id]: { - total: 25, - pending: 8, - in_progress: 12, - completed: 5, - }, - }, - completion_rate: 0.73, - average_completion_time: 4.2, // days - }; - mockTaskRepository.getTaskStats.mockResolvedValueOnce(mockStats); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return app.inject({ - method: 'GET', - url: '/api/v1/tasks/stats', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - }); - // Statistics should load quickly for dashboard performance - setup_1.validateSuccessCriteria.realTimeUpdate(duration); - (0, globals_1.expect)(result.statusCode).toBe(200); - const responseBody = JSON.parse(result.body); - (0, globals_1.expect)(responseBody.total_tasks).toBe(150); - (0, globals_1.expect)(responseBody.completion_rate).toBe(0.73); - (0, globals_1.expect)(responseBody.by_priority).toBeDefined(); - (0, globals_1.expect)(responseBody.by_assignee).toBeDefined(); - }); - (0, globals_1.it)('should support date range filtering for stats', async () => { - const dateRangeStats = { - total_tasks: 25, - completed_tasks: 20, - completion_rate: 0.80, - }; - mockTaskRepository.getTaskStats.mockResolvedValueOnce(dateRangeStats); - const response = await app.inject({ - method: 'GET', - url: '/api/v1/tasks/stats?startDate=2024-01-01&endDate=2024-01-31', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - (0, globals_1.expect)(response.statusCode).toBe(200); - (0, globals_1.expect)(mockTaskRepository.getTaskStats).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - start_date: globals_1.expect.any(Date), - end_date: globals_1.expect.any(Date), - })); - }); - }); - (0, globals_1.describe)('DELETE /api/v1/tasks/:id', () => { - (0, globals_1.it)('should delete task successfully', async () => { - mockTaskRepository.delete.mockResolvedValueOnce(testTask); - const response = await app.inject({ - method: 'DELETE', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - (0, globals_1.expect)(response.statusCode).toBe(200); - const responseBody = JSON.parse(response.body); - (0, globals_1.expect)(responseBody.message).toBe('Task deleted successfully'); - (0, globals_1.expect)(mockTaskRepository.delete).toHaveBeenCalledWith(testTask.id, testUser.id, undefined, // version - false // soft delete by default - ); - }); - (0, globals_1.it)('should enforce deletion permissions', async () => { - const unauthorizedUser = (0, setup_1.createTestUser)({ - role: 'staff', - permissions: ['tasks:read', 'tasks:update'], // No delete permission - }); - const response = await app.inject({ - method: 'DELETE', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(unauthorizedUser)}`, - }, - }); - (0, globals_1.expect)(response.statusCode).toBe(403); - (0, globals_1.expect)(mockTaskRepository.delete).not.toHaveBeenCalled(); - }); - (0, globals_1.it)('should prevent deletion of tasks with dependencies', async () => { - const taskWithDependents = (0, setup_1.createTestTask)({ - id: 'parent-task', - dependent_tasks: ['child-task-1', 'child-task-2'], - }); - mockTaskRepository.delete.mockRejectedValueOnce(new Error('Cannot delete task with active dependencies')); - const response = await app.inject({ - method: 'DELETE', - url: `/api/v1/tasks/${taskWithDependents.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - (0, globals_1.expect)(response.statusCode).toBe(400); - const responseBody = JSON.parse(response.body); - (0, globals_1.expect)(responseBody.error.code).toBe('TASK_HAS_DEPENDENCIES'); - }); - }); - (0, globals_1.describe)('Performance and Load Testing', () => { - (0, globals_1.it)('should handle concurrent task operations', async () => { - const concurrentOperations = Array.from({ length: 50 }, (_, i) => ({ - method: 'GET', - url: `/api/v1/tasks/task-${i}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - })); - mockTaskRepository.findById.mockResolvedValue(testTask); - const { duration } = await (0, setup_1.measureExecutionTime)(async () => { - const promises = concurrentOperations.map(operation => app.inject(operation)); - await Promise.all(promises); - }); - // 50 concurrent requests should complete within 2 seconds - (0, globals_1.expect)(duration).toBeLessThan(2000); - }); - (0, globals_1.it)('should maintain performance under high load', async () => { - const loadTestOperations = Array.from({ length: 100 }, () => ({ - method: 'GET', - url: '/api/v1/tasks?page=1&limit=10', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - })); - const mockTaskList = { - data: Array.from({ length: 10 }, () => testTask), - total: 100, - page: 1, - limit: 10, - totalPages: 10, - }; - mockTaskRepository.findMany.mockResolvedValue(mockTaskList); - const { duration } = await (0, setup_1.measureExecutionTime)(async () => { - const promises = loadTestOperations.map(operation => app.inject(operation)); - await Promise.all(promises); - }); - // 100 list operations should complete within 3 seconds - (0, globals_1.expect)(duration).toBeLessThan(3000); - }); - }); -}); -// Helper function to generate test JWT tokens -async function generateTestToken(user) { - const { generateTokens } = require('../../../src/auth/jwt'); - const tokens = await generateTokens(user); - return tokens.accessToken; -} -//# sourceMappingURL=TaskRoutes.test.js.map \ No newline at end of file diff --git a/backend/tests/api/routes/TaskRoutes.test.js.map b/backend/tests/api/routes/TaskRoutes.test.js.map deleted file mode 100644 index c59b528..0000000 --- a/backend/tests/api/routes/TaskRoutes.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"TaskRoutes.test.js","sourceRoot":"","sources":["TaskRoutes.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/tests/api/routes/TaskRoutes.test.ts b/backend/tests/api/routes/TaskRoutes.test.ts deleted file mode 100644 index 92e8a1d..0000000 --- a/backend/tests/api/routes/TaskRoutes.test.ts +++ /dev/null @@ -1,801 +0,0 @@ -/** - * Task Routes API Tests - * Comprehensive tests for task management endpoints with performance validation - */ - -import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals'; -import { FastifyInstance } from 'fastify'; -import { - createTestUser, - createTestTask, - createMockRequest, - validateSuccessCriteria, - measureExecutionTime, - cleanupTestData -} from '../../setup'; - -// Mock dependencies -jest.mock('../../../src/repositories/TaskRepository'); -jest.mock('../../../src/websocket/SocketManager'); -jest.mock('../../../src/services/CacheService'); - -describe('Task Routes API', () => { - let app: FastifyInstance; - let mockTaskRepository: any; - let mockSocketManager: any; - let testUser: any; - let testTask: any; - - beforeEach(async () => { - // Setup test data - testUser = createTestUser({ - id: 'task-test-user', - role: 'staff', - permissions: ['tasks:read', 'tasks:create', 'tasks:update'] - }); - - testTask = createTestTask({ - id: 'task-test-123', - title: 'Test Task', - assigned_to: [testUser.id], - created_by: testUser.id, - }); - - // Setup mocks - mockTaskRepository = require('../../../src/repositories/TaskRepository').TaskRepository; - mockSocketManager = require('../../../src/websocket/SocketManager').socketManager; - - mockTaskRepository.findById = jest.fn(); - mockTaskRepository.findMany = jest.fn(); - mockTaskRepository.create = jest.fn(); - mockTaskRepository.update = jest.fn(); - mockTaskRepository.delete = jest.fn(); - mockTaskRepository.getTaskStats = jest.fn(); - mockTaskRepository.assignUsers = jest.fn(); - mockTaskRepository.updateStatus = jest.fn(); - - mockSocketManager.sendToUser = jest.fn(); - mockSocketManager.sendToChannel = jest.fn(); - mockSocketManager.broadcast = jest.fn(); - - // Setup Fastify instance with routes - const { setupTestApp } = require('../../helpers/testApp'); - app = await setupTestApp(); - }); - - afterEach(async () => { - await cleanupTestData(); - jest.clearAllMocks(); - await app?.close(); - }); - - describe('GET /api/v1/tasks', () => { - it('should list tasks with pagination and filtering', async () => { - const mockTasks = { - data: [testTask, createTestTask({ id: 'task-2' })], - total: 2, - page: 1, - limit: 10, - totalPages: 1, - }; - - mockTaskRepository.findMany.mockResolvedValueOnce(mockTasks); - - const { result, duration } = await measureExecutionTime(async () => { - return app.inject({ - method: 'GET', - url: '/api/v1/tasks?page=1&limit=10&status=pending', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - }); - - validateSuccessCriteria.realTimeUpdate(duration); - - expect(result.statusCode).toBe(200); - const responseBody = JSON.parse(result.body); - expect(responseBody.data).toHaveLength(2); - expect(responseBody.total).toBe(2); - expect(responseBody.page).toBe(1); - - expect(mockTaskRepository.findMany).toHaveBeenCalledWith( - expect.objectContaining({ - status: 'pending', - }), - expect.objectContaining({ - page: 1, - limit: 10, - }) - ); - }); - - it('should support advanced filtering options', async () => { - const mockFilteredTasks = { - data: [testTask], - total: 1, - page: 1, - limit: 10, - totalPages: 1, - }; - - mockTaskRepository.findMany.mockResolvedValueOnce(mockFilteredTasks); - - const response = await app.inject({ - method: 'GET', - url: '/api/v1/tasks?assignedTo=user-123&priority=high&dueBefore=2024-12-31&tags=urgent,important', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - - expect(response.statusCode).toBe(200); - expect(mockTaskRepository.findMany).toHaveBeenCalledWith( - expect.objectContaining({ - assigned_to: ['user-123'], - priority: 'high', - due_date_before: expect.any(Date), - tags: ['urgent', 'important'], - }), - expect.any(Object) - ); - }); - - it('should require authentication', async () => { - const response = await app.inject({ - method: 'GET', - url: '/api/v1/tasks', - }); - - expect(response.statusCode).toBe(401); - expect(mockTaskRepository.findMany).not.toHaveBeenCalled(); - }); - - it('should respect user permissions for task visibility', async () => { - const limitedUser = createTestUser({ - role: 'staff', - permissions: ['tasks:read:own'], // Can only see own tasks - }); - - mockTaskRepository.findMany.mockResolvedValueOnce({ - data: [testTask], - total: 1, - page: 1, - limit: 10, - totalPages: 1, - }); - - const response = await app.inject({ - method: 'GET', - url: '/api/v1/tasks', - headers: { - authorization: `Bearer ${await generateTestToken(limitedUser)}`, - }, - }); - - expect(response.statusCode).toBe(200); - expect(mockTaskRepository.findMany).toHaveBeenCalledWith( - expect.objectContaining({ - assigned_to: [limitedUser.id], // Filtered to user's own tasks - }), - expect.any(Object) - ); - }); - }); - - describe('GET /api/v1/tasks/:id', () => { - it('should get task by ID successfully', async () => { - mockTaskRepository.findById.mockResolvedValueOnce(testTask); - - const { result, duration } = await measureExecutionTime(async () => { - return app.inject({ - method: 'GET', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - }); - - validateSuccessCriteria.realTimeUpdate(duration); - - expect(result.statusCode).toBe(200); - const responseBody = JSON.parse(result.body); - expect(responseBody.id).toBe(testTask.id); - expect(responseBody.title).toBe(testTask.title); - - expect(mockTaskRepository.findById).toHaveBeenCalledWith(testTask.id); - }); - - it('should return 404 for non-existent task', async () => { - mockTaskRepository.findById.mockResolvedValueOnce(null); - - const response = await app.inject({ - method: 'GET', - url: '/api/v1/tasks/non-existent-id', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - - expect(response.statusCode).toBe(404); - const responseBody = JSON.parse(response.body); - expect(responseBody.error.code).toBe('TASK_NOT_FOUND'); - }); - - it('should enforce task access permissions', async () => { - const restrictedTask = createTestTask({ - id: 'restricted-task', - assigned_to: ['other-user-id'], // Not assigned to test user - privacy: 'private', - }); - - mockTaskRepository.findById.mockResolvedValueOnce(restrictedTask); - - const response = await app.inject({ - method: 'GET', - url: `/api/v1/tasks/${restrictedTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - - expect(response.statusCode).toBe(403); - const responseBody = JSON.parse(response.body); - expect(responseBody.error.code).toBe('ACCESS_DENIED'); - }); - }); - - describe('POST /api/v1/tasks', () => { - it('should create task successfully', async () => { - const newTaskData = { - title: 'New Task via API', - description: 'Task created through API endpoint', - priority: 'high', - due_date: '2024-12-31T23:59:59Z', - assigned_to: [testUser.id], - }; - - const createdTask = createTestTask({ - ...newTaskData, - id: 'new-task-id', - created_by: testUser.id, - }); - - mockTaskRepository.create.mockResolvedValueOnce(createdTask); - - const { result, duration } = await measureExecutionTime(async () => { - return app.inject({ - method: 'POST', - url: '/api/v1/tasks', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: newTaskData, - }); - }); - - // Task creation should be fast for good UX - validateSuccessCriteria.simpleCommandSpeed(duration); - - expect(result.statusCode).toBe(201); - const responseBody = JSON.parse(result.body); - expect(responseBody.id).toBe(createdTask.id); - expect(responseBody.title).toBe(newTaskData.title); - - expect(mockTaskRepository.create).toHaveBeenCalledWith( - expect.objectContaining(newTaskData), - testUser.id - ); - - // Should send real-time notifications - expect(mockSocketManager.sendToUser).toHaveBeenCalledWith( - testUser.id, - 'task_assigned', - expect.objectContaining({ - task: createdTask, - }) - ); - }); - - it('should validate required task fields', async () => { - const invalidTaskData = { - description: 'Task without title', // Missing required title - priority: 'high', - }; - - const response = await app.inject({ - method: 'POST', - url: '/api/v1/tasks', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: invalidTaskData, - }); - - expect(response.statusCode).toBe(400); - const responseBody = JSON.parse(response.body); - expect(responseBody.error.code).toBe('VALIDATION_ERROR'); - expect(responseBody.error.details).toContain('title'); - - expect(mockTaskRepository.create).not.toHaveBeenCalled(); - }); - - it('should enforce task creation permissions', async () => { - const unauthorizedUser = createTestUser({ - role: 'staff', - permissions: ['tasks:read'], // No create permission - }); - - const response = await app.inject({ - method: 'POST', - url: '/api/v1/tasks', - headers: { - authorization: `Bearer ${await generateTestToken(unauthorizedUser)}`, - 'content-type': 'application/json', - }, - payload: { - title: 'Unauthorized Task', - description: 'Should not be created', - }, - }); - - expect(response.statusCode).toBe(403); - expect(mockTaskRepository.create).not.toHaveBeenCalled(); - }); - - it('should support voice-created task metadata', async () => { - const voiceTaskData = { - title: 'Voice Created Task', - description: 'Created via voice command', - voice_created: true, - voice_command_id: 'voice-cmd-123', - voice_instructions: 'Create a task for project review due Friday', - }; - - const voiceTask = createTestTask({ - ...voiceTaskData, - id: 'voice-task-id', - }); - - mockTaskRepository.create.mockResolvedValueOnce(voiceTask); - - const response = await app.inject({ - method: 'POST', - url: '/api/v1/tasks', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: voiceTaskData, - }); - - expect(response.statusCode).toBe(201); - const responseBody = JSON.parse(response.body); - expect(responseBody.voice_created).toBe(true); - expect(responseBody.voice_command_id).toBe('voice-cmd-123'); - }); - }); - - describe('PUT /api/v1/tasks/:id', () => { - it('should update task successfully', async () => { - const updateData = { - title: 'Updated Task Title', - priority: 'urgent', - status: 'in_progress', - }; - - const updatedTask = { ...testTask, ...updateData, version: 2 }; - mockTaskRepository.update.mockResolvedValueOnce(updatedTask); - - const { result, duration } = await measureExecutionTime(async () => { - return app.inject({ - method: 'PUT', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: { ...updateData, version: 1 }, - }); - }); - - validateSuccessCriteria.realTimeUpdate(duration); - - expect(result.statusCode).toBe(200); - const responseBody = JSON.parse(result.body); - expect(responseBody.title).toBe(updateData.title); - expect(responseBody.priority).toBe(updateData.priority); - expect(responseBody.version).toBe(2); - - expect(mockTaskRepository.update).toHaveBeenCalledWith( - testTask.id, - expect.objectContaining(updateData), - testUser.id, - 1 - ); - }); - - it('should handle version conflicts', async () => { - mockTaskRepository.update.mockRejectedValueOnce( - new Error('Version conflict or record not found') - ); - - const response = await app.inject({ - method: 'PUT', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: { - title: 'Conflicted Update', - version: 1, // Stale version - }, - }); - - expect(response.statusCode).toBe(409); - const responseBody = JSON.parse(response.body); - expect(responseBody.error.code).toBe('VERSION_CONFLICT'); - }); - - it('should broadcast task updates to relevant users', async () => { - const updatedTask = { ...testTask, status: 'completed', version: 2 }; - mockTaskRepository.update.mockResolvedValueOnce(updatedTask); - - await app.inject({ - method: 'PUT', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: { status: 'completed', version: 1 }, - }); - - // Should broadcast to all assigned users - expect(mockSocketManager.sendToUser).toHaveBeenCalledWith( - testUser.id, - 'task_updated', - expect.objectContaining({ - task: updatedTask, - changes: expect.objectContaining({ - status: { old: 'pending', new: 'completed' }, - }), - }) - ); - - // Should also send to watchers if any - if (updatedTask.watchers && updatedTask.watchers.length > 0) { - updatedTask.watchers.forEach((watcherId: string) => { - expect(mockSocketManager.sendToUser).toHaveBeenCalledWith( - watcherId, - 'watched_task_updated', - expect.any(Object) - ); - }); - } - }); - }); - - describe('PATCH /api/v1/tasks/:id/status', () => { - it('should update task status efficiently', async () => { - const statusUpdate = { status: 'completed', progress_percentage: 100 }; - const updatedTask = { ...testTask, ...statusUpdate }; - - mockTaskRepository.updateStatus.mockResolvedValueOnce(updatedTask); - - const { result, duration } = await measureExecutionTime(async () => { - return app.inject({ - method: 'PATCH', - url: `/api/v1/tasks/${testTask.id}/status`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: statusUpdate, - }); - }); - - // Status updates should be very fast for real-time UX - expect(duration).toBeLessThan(50); - - expect(result.statusCode).toBe(200); - const responseBody = JSON.parse(result.body); - expect(responseBody.status).toBe('completed'); - expect(responseBody.progress_percentage).toBe(100); - - expect(mockTaskRepository.updateStatus).toHaveBeenCalledWith( - testTask.id, - statusUpdate, - testUser.id - ); - }); - - it('should validate status transitions', async () => { - const invalidStatusUpdate = { status: 'invalid_status' }; - - const response = await app.inject({ - method: 'PATCH', - url: `/api/v1/tasks/${testTask.id}/status`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: invalidStatusUpdate, - }); - - expect(response.statusCode).toBe(400); - const responseBody = JSON.parse(response.body); - expect(responseBody.error.code).toBe('INVALID_STATUS'); - }); - }); - - describe('POST /api/v1/tasks/:id/assign', () => { - it('should assign users to task', async () => { - const assignmentData = { - user_ids: ['user-1', 'user-2'], - notify: true, - }; - - const assignedTask = { - ...testTask, - assigned_to: [...testTask.assigned_to, ...assignmentData.user_ids], - }; - - mockTaskRepository.assignUsers.mockResolvedValueOnce(assignedTask); - - const response = await app.inject({ - method: 'POST', - url: `/api/v1/tasks/${testTask.id}/assign`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - 'content-type': 'application/json', - }, - payload: assignmentData, - }); - - expect(response.statusCode).toBe(200); - const responseBody = JSON.parse(response.body); - expect(responseBody.assigned_to).toContain('user-1'); - expect(responseBody.assigned_to).toContain('user-2'); - - // Should notify newly assigned users - assignmentData.user_ids.forEach(userId => { - expect(mockSocketManager.sendToUser).toHaveBeenCalledWith( - userId, - 'task_assigned', - expect.objectContaining({ - task: assignedTask, - }) - ); - }); - }); - - it('should enforce assignment permissions', async () => { - const unauthorizedUser = createTestUser({ - role: 'staff', - permissions: ['tasks:read', 'tasks:update'], // No assign permission - }); - - const response = await app.inject({ - method: 'POST', - url: `/api/v1/tasks/${testTask.id}/assign`, - headers: { - authorization: `Bearer ${await generateTestToken(unauthorizedUser)}`, - 'content-type': 'application/json', - }, - payload: { user_ids: ['user-1'] }, - }); - - expect(response.statusCode).toBe(403); - expect(mockTaskRepository.assignUsers).not.toHaveBeenCalled(); - }); - }); - - describe('GET /api/v1/tasks/stats', () => { - it('should return task statistics', async () => { - const mockStats = { - total_tasks: 150, - pending_tasks: 45, - in_progress_tasks: 60, - completed_tasks: 40, - overdue_tasks: 5, - by_priority: { - low: 20, - medium: 80, - high: 35, - urgent: 10, - critical: 5, - }, - by_assignee: { - [testUser.id]: { - total: 25, - pending: 8, - in_progress: 12, - completed: 5, - }, - }, - completion_rate: 0.73, - average_completion_time: 4.2, // days - }; - - mockTaskRepository.getTaskStats.mockResolvedValueOnce(mockStats); - - const { result, duration } = await measureExecutionTime(async () => { - return app.inject({ - method: 'GET', - url: '/api/v1/tasks/stats', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - }); - - // Statistics should load quickly for dashboard performance - validateSuccessCriteria.realTimeUpdate(duration); - - expect(result.statusCode).toBe(200); - const responseBody = JSON.parse(result.body); - expect(responseBody.total_tasks).toBe(150); - expect(responseBody.completion_rate).toBe(0.73); - expect(responseBody.by_priority).toBeDefined(); - expect(responseBody.by_assignee).toBeDefined(); - }); - - it('should support date range filtering for stats', async () => { - const dateRangeStats = { - total_tasks: 25, - completed_tasks: 20, - completion_rate: 0.80, - }; - - mockTaskRepository.getTaskStats.mockResolvedValueOnce(dateRangeStats); - - const response = await app.inject({ - method: 'GET', - url: '/api/v1/tasks/stats?startDate=2024-01-01&endDate=2024-01-31', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - - expect(response.statusCode).toBe(200); - expect(mockTaskRepository.getTaskStats).toHaveBeenCalledWith( - expect.objectContaining({ - start_date: expect.any(Date), - end_date: expect.any(Date), - }) - ); - }); - }); - - describe('DELETE /api/v1/tasks/:id', () => { - it('should delete task successfully', async () => { - mockTaskRepository.delete.mockResolvedValueOnce(testTask); - - const response = await app.inject({ - method: 'DELETE', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - - expect(response.statusCode).toBe(200); - const responseBody = JSON.parse(response.body); - expect(responseBody.message).toBe('Task deleted successfully'); - - expect(mockTaskRepository.delete).toHaveBeenCalledWith( - testTask.id, - testUser.id, - undefined, // version - false // soft delete by default - ); - }); - - it('should enforce deletion permissions', async () => { - const unauthorizedUser = createTestUser({ - role: 'staff', - permissions: ['tasks:read', 'tasks:update'], // No delete permission - }); - - const response = await app.inject({ - method: 'DELETE', - url: `/api/v1/tasks/${testTask.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(unauthorizedUser)}`, - }, - }); - - expect(response.statusCode).toBe(403); - expect(mockTaskRepository.delete).not.toHaveBeenCalled(); - }); - - it('should prevent deletion of tasks with dependencies', async () => { - const taskWithDependents = createTestTask({ - id: 'parent-task', - dependent_tasks: ['child-task-1', 'child-task-2'], - }); - - mockTaskRepository.delete.mockRejectedValueOnce( - new Error('Cannot delete task with active dependencies') - ); - - const response = await app.inject({ - method: 'DELETE', - url: `/api/v1/tasks/${taskWithDependents.id}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - }); - - expect(response.statusCode).toBe(400); - const responseBody = JSON.parse(response.body); - expect(responseBody.error.code).toBe('TASK_HAS_DEPENDENCIES'); - }); - }); - - describe('Performance and Load Testing', () => { - it('should handle concurrent task operations', async () => { - const concurrentOperations = Array.from({ length: 50 }, (_, i) => ({ - method: 'GET' as const, - url: `/api/v1/tasks/task-${i}`, - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - })); - - mockTaskRepository.findById.mockResolvedValue(testTask); - - const { duration } = await measureExecutionTime(async () => { - const promises = concurrentOperations.map(operation => - app.inject(operation) - ); - await Promise.all(promises); - }); - - // 50 concurrent requests should complete within 2 seconds - expect(duration).toBeLessThan(2000); - }); - - it('should maintain performance under high load', async () => { - const loadTestOperations = Array.from({ length: 100 }, () => ({ - method: 'GET' as const, - url: '/api/v1/tasks?page=1&limit=10', - headers: { - authorization: `Bearer ${await generateTestToken(testUser)}`, - }, - })); - - const mockTaskList = { - data: Array.from({ length: 10 }, () => testTask), - total: 100, - page: 1, - limit: 10, - totalPages: 10, - }; - - mockTaskRepository.findMany.mockResolvedValue(mockTaskList); - - const { duration } = await measureExecutionTime(async () => { - const promises = loadTestOperations.map(operation => - app.inject(operation) - ); - await Promise.all(promises); - }); - - // 100 list operations should complete within 3 seconds - expect(duration).toBeLessThan(3000); - }); - }); -}); - -// Helper function to generate test JWT tokens -async function generateTestToken(user: any): Promise { - const { generateTokens } = require('../../../src/auth/jwt'); - const tokens = await generateTokens(user); - return tokens.accessToken; -} \ No newline at end of file diff --git a/backend/tests/auth/jwt.test.d.ts b/backend/tests/auth/jwt.test.d.ts deleted file mode 100644 index f3186b7..0000000 --- a/backend/tests/auth/jwt.test.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * JWT Authentication Service Tests - * Comprehensive tests for JWT token generation, verification, and user authentication - */ -export {}; -//# sourceMappingURL=jwt.test.d.ts.map \ No newline at end of file diff --git a/backend/tests/auth/jwt.test.d.ts.map b/backend/tests/auth/jwt.test.d.ts.map deleted file mode 100644 index cbb9b32..0000000 --- a/backend/tests/auth/jwt.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"jwt.test.d.ts","sourceRoot":"","sources":["jwt.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"} \ No newline at end of file diff --git a/backend/tests/auth/jwt.test.js b/backend/tests/auth/jwt.test.js deleted file mode 100644 index e4123bb..0000000 --- a/backend/tests/auth/jwt.test.js +++ /dev/null @@ -1,231 +0,0 @@ -"use strict"; -/** - * JWT Authentication Service Tests - * Comprehensive tests for JWT token generation, verification, and user authentication - */ -Object.defineProperty(exports, "__esModule", { value: true }); -const globals_1 = require("@jest/globals"); -const jwt_1 = require("../../src/auth/jwt"); -const setup_1 = require("../setup"); -(0, globals_1.describe)('JWT Authentication Service', () => { - let testUser; - (0, globals_1.beforeEach)(() => { - testUser = (0, setup_1.createTestUser)({ - id: 'test-user-123', - email: 'test@ceocomm.com', - name: 'Test User', - role: 'staff', - }); - }); - (0, globals_1.describe)('Token Generation', () => { - (0, globals_1.it)('should generate valid access and refresh tokens', async () => { - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return jwt_1.jwtService.generateTokens(testUser); - }); - // Validate performance benchmark - setup_1.validateSuccessCriteria.simpleCommandSpeed(duration); - (0, globals_1.expect)(result).toHaveProperty('accessToken'); - (0, globals_1.expect)(result).toHaveProperty('refreshToken'); - (0, globals_1.expect)(result).toHaveProperty('expiresIn'); - (0, globals_1.expect)(result).toHaveProperty('refreshExpiresIn'); - (0, globals_1.expect)(typeof result.accessToken).toBe('string'); - (0, globals_1.expect)(typeof result.refreshToken).toBe('string'); - (0, globals_1.expect)(result.accessToken.split('.')).toHaveLength(3); // JWT format - (0, globals_1.expect)(result.refreshToken.split('.')).toHaveLength(3); - }); - (0, globals_1.it)('should include correct user data in token payload', async () => { - const tokens = await jwt_1.jwtService.generateTokens(testUser); - const payload = await jwt_1.jwtService.verifyAccessToken(tokens.accessToken); - (0, globals_1.expect)(payload.userId).toBe(testUser.id); - (0, globals_1.expect)(payload.email).toBe(testUser.email); - (0, globals_1.expect)(payload.role).toBe(testUser.role); - (0, globals_1.expect)(payload.name).toBe(testUser.name); - (0, globals_1.expect)(payload.type).toBe('access'); - }); - (0, globals_1.it)('should generate different tokens for different users', async () => { - const user1 = (0, setup_1.createTestUser)({ id: 'user-1', email: 'user1@test.com' }); - const user2 = (0, setup_1.createTestUser)({ id: 'user-2', email: 'user2@test.com' }); - const tokens1 = await jwt_1.jwtService.generateTokens(user1); - const tokens2 = await jwt_1.jwtService.generateTokens(user2); - (0, globals_1.expect)(tokens1.accessToken).not.toBe(tokens2.accessToken); - (0, globals_1.expect)(tokens1.refreshToken).not.toBe(tokens2.refreshToken); - }); - (0, globals_1.it)('should generate tokens with appropriate expiry times', async () => { - const tokens = await jwt_1.jwtService.generateTokens(testUser); - const now = Math.floor(Date.now() / 1000); // JWT uses seconds - (0, globals_1.expect)(tokens.expiresIn).toBeGreaterThan(0); - (0, globals_1.expect)(tokens.refreshExpiresIn).toBeGreaterThan(0); - (0, globals_1.expect)(tokens.refreshExpiresIn).toBeGreaterThan(tokens.expiresIn); - }); - }); - (0, globals_1.describe)('Token Verification', () => { - let validTokens; - (0, globals_1.beforeEach)(async () => { - validTokens = await jwt_1.jwtService.generateTokens(testUser); - }); - (0, globals_1.it)('should verify valid access tokens', async () => { - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return jwt_1.jwtService.verifyAccessToken(validTokens.accessToken); - }); - // Should be very fast for security checks - (0, globals_1.expect)(duration).toBeLessThan(100); - (0, globals_1.expect)(result.userId).toBe(testUser.id); - (0, globals_1.expect)(result.email).toBe(testUser.email); - (0, globals_1.expect)(result.role).toBe(testUser.role); - (0, globals_1.expect)(result.type).toBe('access'); - }); - (0, globals_1.it)('should verify valid refresh tokens', async () => { - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return jwt_1.jwtService.verifyRefreshToken(validTokens.refreshToken); - }); - (0, globals_1.expect)(duration).toBeLessThan(100); - (0, globals_1.expect)(result.userId).toBe(testUser.id); - (0, globals_1.expect)(result.email).toBe(testUser.email); - (0, globals_1.expect)(result.role).toBe(testUser.role); - (0, globals_1.expect)(result.type).toBe('refresh'); - }); - (0, globals_1.it)('should reject invalid tokens', async () => { - const invalidToken = 'invalid.token.here'; - await (0, globals_1.expect)(jwt_1.jwtService.verifyAccessToken(invalidToken)) - .rejects - .toThrow(); - await (0, globals_1.expect)(jwt_1.jwtService.verifyRefreshToken(invalidToken)) - .rejects - .toThrow(); - }); - (0, globals_1.it)('should reject expired tokens', async () => { - // This test would require creating expired tokens which is complex to mock - // In real scenarios, the JWT library handles expiration - const expiredToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ0ZXN0IiwiZXhwIjoxfQ.invalid'; - await (0, globals_1.expect)(jwt_1.jwtService.verifyAccessToken(expiredToken)) - .rejects - .toThrow(); - await (0, globals_1.expect)(jwt_1.jwtService.verifyRefreshToken(expiredToken)) - .rejects - .toThrow(); - }); - (0, globals_1.it)('should reject tokens with wrong type', async () => { - // Try to verify refresh token as access token and vice versa - await (0, globals_1.expect)(jwt_1.jwtService.verifyAccessToken(validTokens.refreshToken)) - .rejects - .toThrow(); - await (0, globals_1.expect)(jwt_1.jwtService.verifyRefreshToken(validTokens.accessToken)) - .rejects - .toThrow(); - }); - }); - (0, globals_1.describe)('Token Decoding', () => { - let validTokens; - (0, globals_1.beforeEach)(async () => { - validTokens = await jwt_1.jwtService.generateTokens(testUser); - }); - (0, globals_1.it)('should decode token without verification', async () => { - const userData = jwt_1.jwtService.decodeToken(validTokens.accessToken); - (0, globals_1.expect)(userData).toEqual(globals_1.expect.objectContaining({ - userId: testUser.id, - email: testUser.email, - role: testUser.role, - name: testUser.name, - permissions: globals_1.expect.any(Array), - sessionId: globals_1.expect.any(String), - type: 'access', - })); - }); - (0, globals_1.it)('should handle invalid tokens gracefully', async () => { - const invalidToken = 'invalid.token'; - const userData = jwt_1.jwtService.decodeToken(invalidToken); - (0, globals_1.expect)(userData).toBeNull(); - }); - }); - (0, globals_1.describe)('Security Features', () => { - (0, globals_1.it)('should generate cryptographically secure session IDs', async () => { - const tokens1 = await jwt_1.jwtService.generateTokens(testUser); - const tokens2 = await jwt_1.jwtService.generateTokens(testUser); - const payload1 = await jwt_1.jwtService.verifyAccessToken(tokens1.accessToken); - const payload2 = await jwt_1.jwtService.verifyAccessToken(tokens2.accessToken); - (0, globals_1.expect)(payload1.sessionId).not.toBe(payload2.sessionId); - (0, globals_1.expect)(payload1.sessionId.length).toBeGreaterThanOrEqual(32); - }); - (0, globals_1.it)('should include role-based permissions', async () => { - const ceoUser = (0, setup_1.createTestUser)({ role: 'ceo' }); - const managerUser = (0, setup_1.createTestUser)({ role: 'manager' }); - const staffUser = (0, setup_1.createTestUser)({ role: 'staff' }); - const ceoTokens = await jwt_1.jwtService.generateTokens(ceoUser); - const managerTokens = await jwt_1.jwtService.generateTokens(managerUser); - const staffTokens = await jwt_1.jwtService.generateTokens(staffUser); - const ceoPayload = await jwt_1.jwtService.verifyAccessToken(ceoTokens.accessToken); - const managerPayload = await jwt_1.jwtService.verifyAccessToken(managerTokens.accessToken); - const staffPayload = await jwt_1.jwtService.verifyAccessToken(staffTokens.accessToken); - (0, globals_1.expect)(ceoPayload.permissions.length).toBeGreaterThan(managerPayload.permissions.length); // CEO has more permissions - (0, globals_1.expect)(managerPayload.permissions.length).toBeGreaterThan(staffPayload.permissions.length); - }); - (0, globals_1.it)('should handle malformed tokens securely', async () => { - const malformedTokens = [ - '', // Empty token - 'not.a.token', // Invalid format - 'header.payload', // Missing signature - 'too.many.parts.in.token.here', // Too many parts - ]; - for (const token of malformedTokens) { - await (0, globals_1.expect)(jwt_1.jwtService.verifyAccessToken(token)) - .rejects - .toThrow(); - } - }); - }); - (0, globals_1.describe)('Performance Requirements', () => { - (0, globals_1.it)('should meet token generation performance benchmarks', async () => { - const { duration } = await (0, setup_1.measureExecutionTime)(async () => { - const promises = Array.from({ length: 10 }, () => jwt_1.jwtService.generateTokens(testUser)); - await Promise.all(promises); - }); - // 10 token generations should complete in under 1 second - (0, globals_1.expect)(duration).toBeLessThan(1000); - }); - (0, globals_1.it)('should meet token verification performance benchmarks', async () => { - const tokens = await jwt_1.jwtService.generateTokens(testUser); - const { duration } = await (0, setup_1.measureExecutionTime)(async () => { - const promises = Array.from({ length: 100 }, () => jwt_1.jwtService.verifyAccessToken(tokens.accessToken)); - await Promise.all(promises); - }); - // 100 verifications should complete in under 500ms - (0, globals_1.expect)(duration).toBeLessThan(500); - }); - }); - (0, globals_1.describe)('Edge Cases', () => { - (0, globals_1.it)('should handle users with special characters in data', async () => { - const specialUser = (0, setup_1.createTestUser)({ - name: "John O'Connor-Smith", - email: 'user+test@domain.co.uk', - }); - const tokens = await jwt_1.jwtService.generateTokens(specialUser); - const payload = await jwt_1.jwtService.verifyAccessToken(tokens.accessToken); - (0, globals_1.expect)(payload.name).toBe(specialUser.name); - (0, globals_1.expect)(payload.email).toBe(specialUser.email); - }); - (0, globals_1.it)('should handle users with minimal data', async () => { - const minimalUser = { - id: 'min-user-123', - email: 'min@test.com', - role: 'staff', - name: '', - }; - const tokens = await jwt_1.jwtService.generateTokens(minimalUser); - (0, globals_1.expect)(tokens.accessToken).toBeTruthy(); - (0, globals_1.expect)(tokens.refreshToken).toBeTruthy(); - }); - (0, globals_1.it)('should validate required user fields', async () => { - const incompleteUsers = [ - { email: 'test@test.com', role: 'staff', name: 'Test' }, // Missing id - { id: 'test-123', role: 'staff', name: 'Test' }, // Missing email - { id: 'test-123', email: 'test@test.com', name: 'Test' }, // Missing role - ]; - for (const user of incompleteUsers) { - await (0, globals_1.expect)(jwt_1.jwtService.generateTokens(user)) - .rejects - .toThrow(); - } - }); - }); -}); -//# sourceMappingURL=jwt.test.js.map \ No newline at end of file diff --git a/backend/tests/auth/jwt.test.js.map b/backend/tests/auth/jwt.test.js.map deleted file mode 100644 index fe6fdd2..0000000 --- a/backend/tests/auth/jwt.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"jwt.test.js","sourceRoot":"","sources":["jwt.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/tests/auth/jwt.test.ts b/backend/tests/auth/jwt.test.ts deleted file mode 100644 index c9876eb..0000000 --- a/backend/tests/auth/jwt.test.ts +++ /dev/null @@ -1,288 +0,0 @@ -/** - * JWT Authentication Service Tests - * Comprehensive tests for JWT token generation, verification, and user authentication - */ - -import { describe, it, expect, beforeEach, jest } from '@jest/globals'; -import { jwtService, TokenPayload } from '../../src/auth/jwt'; -import { config } from '../../src/config'; -import { createTestUser, validateSuccessCriteria, measureExecutionTime } from '../setup'; - -describe('JWT Authentication Service', () => { - let testUser: any; - - beforeEach(() => { - testUser = createTestUser({ - id: 'test-user-123', - email: 'test@ceocomm.com', - name: 'Test User', - role: 'staff', - }); - }); - - describe('Token Generation', () => { - it('should generate valid access and refresh tokens', async () => { - const { result, duration } = await measureExecutionTime(async () => { - return jwtService.generateTokens(testUser); - }); - - // Validate performance benchmark - validateSuccessCriteria.simpleCommandSpeed(duration); - - expect(result).toHaveProperty('accessToken'); - expect(result).toHaveProperty('refreshToken'); - expect(result).toHaveProperty('expiresIn'); - expect(result).toHaveProperty('refreshExpiresIn'); - expect(typeof result.accessToken).toBe('string'); - expect(typeof result.refreshToken).toBe('string'); - expect(result.accessToken.split('.')).toHaveLength(3); // JWT format - expect(result.refreshToken.split('.')).toHaveLength(3); - }); - - it('should include correct user data in token payload', async () => { - const tokens = await jwtService.generateTokens(testUser); - const payload = await jwtService.verifyAccessToken(tokens.accessToken); - - expect(payload.userId).toBe(testUser.id); - expect(payload.email).toBe(testUser.email); - expect(payload.role).toBe(testUser.role); - expect(payload.name).toBe(testUser.name); - expect(payload.type).toBe('access'); - }); - - it('should generate different tokens for different users', async () => { - const user1 = createTestUser({ id: 'user-1', email: 'user1@test.com' }); - const user2 = createTestUser({ id: 'user-2', email: 'user2@test.com' }); - - const tokens1 = await jwtService.generateTokens(user1); - const tokens2 = await jwtService.generateTokens(user2); - - expect(tokens1.accessToken).not.toBe(tokens2.accessToken); - expect(tokens1.refreshToken).not.toBe(tokens2.refreshToken); - }); - - it('should generate tokens with appropriate expiry times', async () => { - const tokens = await jwtService.generateTokens(testUser); - const now = Math.floor(Date.now() / 1000); // JWT uses seconds - - expect(tokens.expiresIn).toBeGreaterThan(0); - expect(tokens.refreshExpiresIn).toBeGreaterThan(0); - expect(tokens.refreshExpiresIn).toBeGreaterThan(tokens.expiresIn); - }); - }); - - describe('Token Verification', () => { - let validTokens: any; - - beforeEach(async () => { - validTokens = await jwtService.generateTokens(testUser); - }); - - it('should verify valid access tokens', async () => { - const { result, duration } = await measureExecutionTime(async () => { - return jwtService.verifyAccessToken(validTokens.accessToken); - }); - - // Should be very fast for security checks - expect(duration).toBeLessThan(100); - - expect(result.userId).toBe(testUser.id); - expect(result.email).toBe(testUser.email); - expect(result.role).toBe(testUser.role); - expect(result.type).toBe('access'); - }); - - it('should verify valid refresh tokens', async () => { - const { result, duration } = await measureExecutionTime(async () => { - return jwtService.verifyRefreshToken(validTokens.refreshToken); - }); - - expect(duration).toBeLessThan(100); - - expect(result.userId).toBe(testUser.id); - expect(result.email).toBe(testUser.email); - expect(result.role).toBe(testUser.role); - expect(result.type).toBe('refresh'); - }); - - it('should reject invalid tokens', async () => { - const invalidToken = 'invalid.token.here'; - - await expect(jwtService.verifyAccessToken(invalidToken)) - .rejects - .toThrow(); - - await expect(jwtService.verifyRefreshToken(invalidToken)) - .rejects - .toThrow(); - }); - - it('should reject expired tokens', async () => { - // This test would require creating expired tokens which is complex to mock - // In real scenarios, the JWT library handles expiration - const expiredToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ0ZXN0IiwiZXhwIjoxfQ.invalid'; - - await expect(jwtService.verifyAccessToken(expiredToken)) - .rejects - .toThrow(); - - await expect(jwtService.verifyRefreshToken(expiredToken)) - .rejects - .toThrow(); - }); - - it('should reject tokens with wrong type', async () => { - // Try to verify refresh token as access token and vice versa - await expect(jwtService.verifyAccessToken(validTokens.refreshToken)) - .rejects - .toThrow(); - - await expect(jwtService.verifyRefreshToken(validTokens.accessToken)) - .rejects - .toThrow(); - }); - }); - - describe('Token Decoding', () => { - let validTokens: any; - - beforeEach(async () => { - validTokens = await jwtService.generateTokens(testUser); - }); - - it('should decode token without verification', async () => { - const userData = jwtService.decodeToken(validTokens.accessToken); - - expect(userData).toEqual(expect.objectContaining({ - userId: testUser.id, - email: testUser.email, - role: testUser.role, - name: testUser.name, - permissions: expect.any(Array), - sessionId: expect.any(String), - type: 'access', - })); - }); - - it('should handle invalid tokens gracefully', async () => { - const invalidToken = 'invalid.token'; - - const userData = jwtService.decodeToken(invalidToken); - - expect(userData).toBeNull(); - }); - }); - - describe('Security Features', () => { - it('should generate cryptographically secure session IDs', async () => { - const tokens1 = await jwtService.generateTokens(testUser); - const tokens2 = await jwtService.generateTokens(testUser); - - const payload1 = await jwtService.verifyAccessToken(tokens1.accessToken); - const payload2 = await jwtService.verifyAccessToken(tokens2.accessToken); - - expect(payload1.sessionId).not.toBe(payload2.sessionId); - expect(payload1.sessionId.length).toBeGreaterThanOrEqual(32); - }); - - it('should include role-based permissions', async () => { - const ceoUser = createTestUser({ role: 'ceo' }); - const managerUser = createTestUser({ role: 'manager' }); - const staffUser = createTestUser({ role: 'staff' }); - - const ceoTokens = await jwtService.generateTokens(ceoUser); - const managerTokens = await jwtService.generateTokens(managerUser); - const staffTokens = await jwtService.generateTokens(staffUser); - - const ceoPayload = await jwtService.verifyAccessToken(ceoTokens.accessToken); - const managerPayload = await jwtService.verifyAccessToken(managerTokens.accessToken); - const staffPayload = await jwtService.verifyAccessToken(staffTokens.accessToken); - - expect(ceoPayload.permissions.length).toBeGreaterThan(managerPayload.permissions.length); // CEO has more permissions - expect(managerPayload.permissions.length).toBeGreaterThan(staffPayload.permissions.length); - }); - - it('should handle malformed tokens securely', async () => { - const malformedTokens = [ - '', // Empty token - 'not.a.token', // Invalid format - 'header.payload', // Missing signature - 'too.many.parts.in.token.here', // Too many parts - ]; - - for (const token of malformedTokens) { - await expect(jwtService.verifyAccessToken(token)) - .rejects - .toThrow(); - } - }); - }); - - describe('Performance Requirements', () => { - it('should meet token generation performance benchmarks', async () => { - const { duration } = await measureExecutionTime(async () => { - const promises = Array.from({ length: 10 }, () => jwtService.generateTokens(testUser)); - await Promise.all(promises); - }); - - // 10 token generations should complete in under 1 second - expect(duration).toBeLessThan(1000); - }); - - it('should meet token verification performance benchmarks', async () => { - const tokens = await jwtService.generateTokens(testUser); - - const { duration } = await measureExecutionTime(async () => { - const promises = Array.from({ length: 100 }, () => - jwtService.verifyAccessToken(tokens.accessToken) - ); - await Promise.all(promises); - }); - - // 100 verifications should complete in under 500ms - expect(duration).toBeLessThan(500); - }); - }); - - describe('Edge Cases', () => { - it('should handle users with special characters in data', async () => { - const specialUser = createTestUser({ - name: "John O'Connor-Smith", - email: 'user+test@domain.co.uk', - }); - - const tokens = await jwtService.generateTokens(specialUser); - const payload = await jwtService.verifyAccessToken(tokens.accessToken); - - expect(payload.name).toBe(specialUser.name); - expect(payload.email).toBe(specialUser.email); - }); - - it('should handle users with minimal data', async () => { - const minimalUser = { - id: 'min-user-123', - email: 'min@test.com', - role: 'staff', - name: '', - }; - - const tokens = await jwtService.generateTokens(minimalUser); - expect(tokens.accessToken).toBeTruthy(); - expect(tokens.refreshToken).toBeTruthy(); - }); - - it('should validate required user fields', async () => { - const incompleteUsers = [ - { email: 'test@test.com', role: 'staff', name: 'Test' }, // Missing id - { id: 'test-123', role: 'staff', name: 'Test' }, // Missing email - { id: 'test-123', email: 'test@test.com', name: 'Test' }, // Missing role - ]; - - for (const user of incompleteUsers) { - await expect(jwtService.generateTokens(user as any)) - .rejects - .toThrow(); - } - }); - }); -}); \ No newline at end of file diff --git a/backend/tests/auth/middleware.test.d.ts b/backend/tests/auth/middleware.test.d.ts deleted file mode 100644 index 0322b4b..0000000 --- a/backend/tests/auth/middleware.test.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Authentication and Authorization Middleware Tests - * Tests for JWT middleware, rate limiting, and security features - */ -export {}; -//# sourceMappingURL=middleware.test.d.ts.map \ No newline at end of file diff --git a/backend/tests/auth/middleware.test.d.ts.map b/backend/tests/auth/middleware.test.d.ts.map deleted file mode 100644 index a6417fb..0000000 --- a/backend/tests/auth/middleware.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"middleware.test.d.ts","sourceRoot":"","sources":["middleware.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"} \ No newline at end of file diff --git a/backend/tests/auth/middleware.test.js b/backend/tests/auth/middleware.test.js deleted file mode 100644 index 8fe79f5..0000000 --- a/backend/tests/auth/middleware.test.js +++ /dev/null @@ -1,315 +0,0 @@ -"use strict"; -/** - * Authentication and Authorization Middleware Tests - * Tests for JWT middleware, rate limiting, and security features - */ -Object.defineProperty(exports, "__esModule", { value: true }); -const globals_1 = require("@jest/globals"); -const middleware_1 = require("../../src/auth/middleware"); -const jwt_1 = require("../../src/auth/jwt"); -const setup_1 = require("../setup"); -(0, globals_1.describe)('Authentication Middleware', () => { - let testUser; - let mockRequest; - let mockReply; - let validTokens; - (0, globals_1.beforeEach)(async () => { - testUser = (0, setup_1.createTestUser)({ - id: 'auth-test-user', - email: 'auth@test.com', - role: 'staff', - name: 'Auth Test User', - }); - validTokens = await jwt_1.jwtService.generateTokens(testUser); - mockRequest = (0, setup_1.createMockRequest)(); - mockReply = (0, setup_1.createMockReply)(); - }); - (0, globals_1.afterEach)(() => { - globals_1.jest.clearAllMocks(); - }); - (0, globals_1.describe)('authenticate middleware', () => { - (0, globals_1.it)('should authenticate valid bearer token', async () => { - mockRequest.headers.authorization = `Bearer ${validTokens.accessToken}`; - const { duration } = await (0, setup_1.measureExecutionTime)(async () => { - await (0, middleware_1.authenticate)(mockRequest, mockReply); - }); - // Authentication should be fast - setup_1.validateSuccessCriteria.realTimeUpdate(duration); - (0, globals_1.expect)(mockRequest.user).toBeDefined(); - (0, globals_1.expect)(mockRequest.user.userId).toBe(testUser.id); - (0, globals_1.expect)(mockRequest.user.email).toBe(testUser.email); - (0, globals_1.expect)(mockRequest.user.role).toBe(testUser.role); - (0, globals_1.expect)(mockRequest.user.isAuthenticated).toBe(true); - }); - (0, globals_1.it)('should reject request without authorization header', async () => { - // No authorization header - delete mockRequest.headers.authorization; - await (0, middleware_1.authenticate)(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).toHaveBeenCalledWith(401); - (0, globals_1.expect)(mockReply.send).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - error: globals_1.expect.objectContaining({ - message: 'Authentication required', - code: 'AUTHENTICATION_REQUIRED', - statusCode: 401, - }), - })); - }); - (0, globals_1.it)('should reject malformed authorization header', async () => { - const malformedHeaders = [ - 'InvalidFormat', // No Bearer prefix - 'Bearer', // No token - 'Bearer ', // Empty token - 'Basic dGVzdDp0ZXN0', // Wrong auth type - ]; - for (const authHeader of malformedHeaders) { - mockRequest.headers.authorization = authHeader; - mockReply.code.mockClear(); - mockReply.send.mockClear(); - await (0, middleware_1.authenticate)(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).toHaveBeenCalledWith(401); - (0, globals_1.expect)(mockReply.send).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - error: globals_1.expect.objectContaining({ - code: 'INVALID_TOKEN_FORMAT', - }), - })); - } - }); - (0, globals_1.it)('should reject expired tokens', async () => { - // Create expired token (this would need a helper to create expired tokens) - const expiredToken = 'expired.jwt.token'; - mockRequest.headers.authorization = `Bearer ${expiredToken}`; - await (0, middleware_1.authenticate)(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).toHaveBeenCalledWith(401); - (0, globals_1.expect)(mockReply.send).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - error: globals_1.expect.objectContaining({ - code: 'TOKEN_EXPIRED', - }), - })); - }); - (0, globals_1.it)('should handle authentication errors gracefully', async () => { - mockRequest.headers.authorization = 'Bearer invalid.token.here'; - await (0, middleware_1.authenticate)(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).toHaveBeenCalledWith(401); - (0, globals_1.expect)(mockReply.send).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - error: globals_1.expect.objectContaining({ - code: 'INVALID_TOKEN', - }), - })); - }); - }); - (0, globals_1.describe)('optionalAuthenticate middleware', () => { - (0, globals_1.it)('should authenticate when valid token is provided', async () => { - mockRequest.headers.authorization = `Bearer ${validTokens.accessToken}`; - await (0, middleware_1.optionalAuthenticate)(mockRequest, mockReply); - (0, globals_1.expect)(mockRequest.user).toBeDefined(); - (0, globals_1.expect)(mockRequest.user.isAuthenticated).toBe(true); - }); - (0, globals_1.it)('should continue without authentication when no token provided', async () => { - delete mockRequest.headers.authorization; - await (0, middleware_1.optionalAuthenticate)(mockRequest, mockReply); - (0, globals_1.expect)(mockRequest.user).toBeUndefined(); - (0, globals_1.expect)(mockReply.code).not.toHaveBeenCalled(); - (0, globals_1.expect)(mockReply.send).not.toHaveBeenCalled(); - }); - (0, globals_1.it)('should continue without authentication for invalid tokens', async () => { - mockRequest.headers.authorization = 'Bearer invalid.token'; - await (0, middleware_1.optionalAuthenticate)(mockRequest, mockReply); - (0, globals_1.expect)(mockRequest.user).toBeUndefined(); - (0, globals_1.expect)(mockReply.code).not.toHaveBeenCalled(); - (0, globals_1.expect)(mockReply.send).not.toHaveBeenCalled(); - }); - }); - (0, globals_1.describe)('authorize middleware', () => { - (0, globals_1.beforeEach)(async () => { - mockRequest.headers.authorization = `Bearer ${validTokens.accessToken}`; - await (0, middleware_1.authenticate)(mockRequest, mockReply); - mockReply.code.mockClear(); - mockReply.send.mockClear(); - }); - (0, globals_1.it)('should authorize user with required permissions', async () => { - const staffUser = (0, setup_1.createTestUser)({ role: 'staff' }); - mockRequest.user = { - ...staffUser, - permissions: ['tasks:read', 'channels:read'], - isAuthenticated: true, - }; - const authorizeMiddleware = (0, middleware_1.authorize)('tasks:read'); - await authorizeMiddleware(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).not.toHaveBeenCalled(); - (0, globals_1.expect)(mockReply.send).not.toHaveBeenCalled(); - }); - (0, globals_1.it)('should reject user without required permissions', async () => { - mockRequest.user = { - ...testUser, - permissions: ['tasks:read'], - isAuthenticated: true, - }; - const authorizeMiddleware = (0, middleware_1.authorize)('users:admin'); - await authorizeMiddleware(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).toHaveBeenCalledWith(403); - (0, globals_1.expect)(mockReply.send).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - error: globals_1.expect.objectContaining({ - code: 'INSUFFICIENT_PERMISSIONS', - }), - })); - }); - (0, globals_1.it)('should handle CEO role with wildcard permissions', async () => { - const ceoUser = (0, setup_1.createTestUser)({ role: 'ceo' }); - mockRequest.user = { - ...ceoUser, - permissions: ['*'], // CEO has all permissions - isAuthenticated: true, - }; - const authorizeMiddleware = (0, middleware_1.authorize)('users:admin', 'channels:delete'); - await authorizeMiddleware(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).not.toHaveBeenCalled(); - (0, globals_1.expect)(mockReply.send).not.toHaveBeenCalled(); - }); - (0, globals_1.it)('should reject unauthenticated requests', async () => { - delete mockRequest.user; // No authenticated user - const authorizeMiddleware = (0, middleware_1.authorize)('tasks:read'); - await authorizeMiddleware(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).toHaveBeenCalledWith(401); - (0, globals_1.expect)(mockReply.send).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - error: globals_1.expect.objectContaining({ - code: 'AUTHENTICATION_REQUIRED', - }), - })); - }); - (0, globals_1.it)('should handle multiple required permissions', async () => { - mockRequest.user = { - ...testUser, - permissions: ['tasks:read', 'tasks:write', 'channels:read'], - isAuthenticated: true, - }; - const authorizeMiddleware = (0, middleware_1.authorize)('tasks:read', 'tasks:write'); - await authorizeMiddleware(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).not.toHaveBeenCalled(); - (0, globals_1.expect)(mockReply.send).not.toHaveBeenCalled(); - }); - }); - (0, globals_1.describe)('audit logging', () => { - (0, globals_1.it)('should log authentication events', async () => { - const logSpy = globals_1.jest.spyOn(console, 'log').mockImplementation(() => { }); - mockRequest.headers.authorization = `Bearer ${validTokens.accessToken}`; - await (0, middleware_1.authenticate)(mockRequest, mockReply); - (0, globals_1.expect)(logSpy).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - event: 'auth_success', - userId: testUser.id, - email: testUser.email, - })); - logSpy.mockRestore(); - }); - (0, globals_1.it)('should log authorization failures', async () => { - const logSpy = globals_1.jest.spyOn(console, 'log').mockImplementation(() => { }); - mockRequest.user = { - ...testUser, - permissions: ['limited:access'], - isAuthenticated: true, - }; - const authorizeMiddleware = (0, middleware_1.authorize)(['admin:access']); - await authorizeMiddleware(mockRequest, mockReply); - (0, globals_1.expect)(logSpy).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - event: 'authorization_denied', - userId: testUser.id, - requiredPermissions: ['admin:access'], - })); - logSpy.mockRestore(); - }); - (0, globals_1.it)('should log security violations', async () => { - const logSpy = globals_1.jest.spyOn(console, 'log').mockImplementation(() => { }); - mockRequest.headers.authorization = 'Bearer malicious.token.attempt'; - await (0, middleware_1.authenticate)(mockRequest, mockReply); - (0, globals_1.expect)(logSpy).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - event: 'security_violation', - type: 'invalid_token', - ip: mockRequest.ip, - })); - logSpy.mockRestore(); - }); - }); - (0, globals_1.describe)('performance requirements', () => { - (0, globals_1.it)('should meet authentication performance benchmarks', async () => { - mockRequest.headers.authorization = `Bearer ${validTokens.accessToken}`; - const { duration } = await (0, setup_1.measureExecutionTime)(async () => { - const promises = Array.from({ length: 100 }, () => (0, middleware_1.authenticate)((0, setup_1.createMockRequest)({ - headers: { authorization: `Bearer ${validTokens.accessToken}` } - }), (0, setup_1.createMockReply)())); - await Promise.all(promises); - }); - // 100 authentications should complete in under 500ms - (0, globals_1.expect)(duration).toBeLessThan(500); - }); - (0, globals_1.it)('should meet real-time update requirements for authorization', async () => { - mockRequest.user = { - ...testUser, - permissions: ['tasks:read', 'channels:read'], - isAuthenticated: true, - }; - const { duration } = await (0, setup_1.measureExecutionTime)(async () => { - const authorizeMiddleware = (0, middleware_1.authorize)(['tasks:read']); - await authorizeMiddleware(mockRequest, mockReply); - }); - // Authorization should be under 100ms for real-time requirements - setup_1.validateSuccessCriteria.realTimeUpdate(duration); - }); - }); - (0, globals_1.describe)('security edge cases', () => { - (0, globals_1.it)('should handle JWT header manipulation attempts', async () => { - const maliciousTokens = [ - 'Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VySWQiOiJhZG1pbiJ9.', // None algorithm - 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJhZG1pbiIsImV4cCI6OTk5OTk5OTk5OX0.malicious', // Manipulated payload - ]; - for (const token of maliciousTokens) { - mockRequest.headers.authorization = token; - mockReply.code.mockClear(); - mockReply.send.mockClear(); - await (0, middleware_1.authenticate)(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).toHaveBeenCalledWith(401); - (0, globals_1.expect)(mockReply.send).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - error: globals_1.expect.objectContaining({ - code: globals_1.expect.stringMatching(/INVALID_TOKEN|SECURITY_VIOLATION/), - }), - })); - } - }); - (0, globals_1.it)('should prevent privilege escalation attempts', async () => { - // User with staff role trying to access admin endpoints - const staffUser = (0, setup_1.createTestUser)({ role: 'staff' }); - mockRequest.user = { - ...staffUser, - permissions: ['tasks:read', 'channels:read'], - isAuthenticated: true, - }; - const adminMiddleware = (0, middleware_1.authorize)(['users:admin', 'system:config']); - await adminMiddleware(mockRequest, mockReply); - (0, globals_1.expect)(mockReply.code).toHaveBeenCalledWith(403); - (0, globals_1.expect)(mockReply.send).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - error: globals_1.expect.objectContaining({ - code: 'INSUFFICIENT_PERMISSIONS', - }), - })); - }); - (0, globals_1.it)('should rate limit authentication attempts', async () => { - const rateLimitedRequest = (0, setup_1.createMockRequest)({ - ip: '192.168.1.100', // Same IP for rate limiting - }); - // Simulate multiple failed authentication attempts - for (let i = 0; i < 10; i++) { - rateLimitedRequest.headers.authorization = `Bearer invalid-token-${i}`; - await (0, middleware_1.authenticate)(rateLimitedRequest, (0, setup_1.createMockReply)()); - } - // Next attempt should be rate limited - rateLimitedRequest.headers.authorization = 'Bearer another-invalid-token'; - const rateLimitedReply = (0, setup_1.createMockReply)(); - await (0, middleware_1.authenticate)(rateLimitedRequest, rateLimitedReply); - (0, globals_1.expect)(rateLimitedReply.code).toHaveBeenCalledWith(429); - (0, globals_1.expect)(rateLimitedReply.send).toHaveBeenCalledWith(globals_1.expect.objectContaining({ - error: globals_1.expect.objectContaining({ - code: 'RATE_LIMIT_EXCEEDED', - }), - })); - }); - }); -}); -//# sourceMappingURL=middleware.test.js.map \ No newline at end of file diff --git a/backend/tests/auth/middleware.test.js.map b/backend/tests/auth/middleware.test.js.map deleted file mode 100644 index 200d16f..0000000 --- a/backend/tests/auth/middleware.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"middleware.test.js","sourceRoot":"","sources":["middleware.test.ts"],"names":[],"mappings":";AAAA;;;GAGG;;AAEH,2CAAkF;AAClF,0DAAoG;AACpG,4CAAgD;AAChD,oCAMkB;AAElB,IAAA,kBAAQ,EAAC,2BAA2B,EAAE,GAAG,EAAE;IACzC,IAAI,QAAa,CAAC;IAClB,IAAI,WAAgB,CAAC;IACrB,IAAI,SAAc,CAAC;IACnB,IAAI,WAAgB,CAAC;IAErB,IAAA,oBAAU,EAAC,KAAK,IAAI,EAAE;QACpB,QAAQ,GAAG,IAAA,sBAAc,EAAC;YACxB,EAAE,EAAE,gBAAgB;YACpB,KAAK,EAAE,eAAe;YACtB,IAAI,EAAE,OAAO;YACb,IAAI,EAAE,gBAAgB;SACvB,CAAC,CAAC;QAEH,WAAW,GAAG,MAAM,gBAAU,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACxD,WAAW,GAAG,IAAA,yBAAiB,GAAE,CAAC;QAClC,SAAS,GAAG,IAAA,uBAAe,GAAE,CAAC;IAChC,CAAC,CAAC,CAAC;IAEH,IAAA,mBAAS,EAAC,GAAG,EAAE;QACb,cAAI,CAAC,aAAa,EAAE,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,IAAA,kBAAQ,EAAC,yBAAyB,EAAE,GAAG,EAAE;QACvC,IAAA,YAAE,EAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,UAAU,WAAW,CAAC,WAAW,EAAE,CAAC;YAExE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAA,4BAAoB,EAAC,KAAK,IAAI,EAAE;gBACzD,MAAM,IAAA,yBAAY,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAC7C,CAAC,CAAC,CAAC;YAEH,gCAAgC;YAChC,+BAAuB,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YAEjD,IAAA,gBAAM,EAAC,WAAW,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YACvC,IAAA,gBAAM,EAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;YAClD,IAAA,gBAAM,EAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACpD,IAAA,gBAAM,EAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;YAClD,IAAA,gBAAM,EAAC,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,oDAAoD,EAAE,KAAK,IAAI,EAAE;YAClE,0BAA0B;YAC1B,OAAO,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC;YAEzC,MAAM,IAAA,yBAAY,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAE3C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;YACjD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACzC,gBAAM,CAAC,gBAAgB,CAAC;gBACtB,KAAK,EAAE,gBAAM,CAAC,gBAAgB,CAAC;oBAC7B,OAAO,EAAE,yBAAyB;oBAClC,IAAI,EAAE,yBAAyB;oBAC/B,UAAU,EAAE,GAAG;iBAChB,CAAC;aACH,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;YAC5D,MAAM,gBAAgB,GAAG;gBACvB,eAAe,EAAE,mBAAmB;gBACpC,QAAQ,EAAE,WAAW;gBACrB,SAAS,EAAE,cAAc;gBACzB,oBAAoB,EAAE,kBAAkB;aACzC,CAAC;YAEF,KAAK,MAAM,UAAU,IAAI,gBAAgB,EAAE,CAAC;gBAC1C,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,UAAU,CAAC;gBAC/C,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC3B,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBAE3B,MAAM,IAAA,yBAAY,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;gBAE3C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;gBACjD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACzC,gBAAM,CAAC,gBAAgB,CAAC;oBACtB,KAAK,EAAE,gBAAM,CAAC,gBAAgB,CAAC;wBAC7B,IAAI,EAAE,sBAAsB;qBAC7B,CAAC;iBACH,CAAC,CACH,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;YAC5C,2EAA2E;YAC3E,MAAM,YAAY,GAAG,mBAAmB,CAAC;YACzC,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,UAAU,YAAY,EAAE,CAAC;YAE7D,MAAM,IAAA,yBAAY,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAE3C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;YACjD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACzC,gBAAM,CAAC,gBAAgB,CAAC;gBACtB,KAAK,EAAE,gBAAM,CAAC,gBAAgB,CAAC;oBAC7B,IAAI,EAAE,eAAe;iBACtB,CAAC;aACH,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,2BAA2B,CAAC;YAEhE,MAAM,IAAA,yBAAY,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAE3C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;YACjD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACzC,gBAAM,CAAC,gBAAgB,CAAC;gBACtB,KAAK,EAAE,gBAAM,CAAC,gBAAgB,CAAC;oBAC7B,IAAI,EAAE,eAAe;iBACtB,CAAC;aACH,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,kBAAQ,EAAC,iCAAiC,EAAE,GAAG,EAAE;QAC/C,IAAA,YAAE,EAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;YAChE,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,UAAU,WAAW,CAAC,WAAW,EAAE,CAAC;YAExE,MAAM,IAAA,iCAAoB,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAEnD,IAAA,gBAAM,EAAC,WAAW,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YACvC,IAAA,gBAAM,EAAC,WAAW,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;YAC7E,OAAO,WAAW,CAAC,OAAO,CAAC,aAAa,CAAC;YAEzC,MAAM,IAAA,iCAAoB,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAEnD,IAAA,gBAAM,EAAC,WAAW,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;YACzC,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAC9C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;YACzE,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,sBAAsB,CAAC;YAE3D,MAAM,IAAA,iCAAoB,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAEnD,IAAA,gBAAM,EAAC,WAAW,CAAC,IAAI,CAAC,CAAC,aAAa,EAAE,CAAC;YACzC,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAC9C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,kBAAQ,EAAC,sBAAsB,EAAE,GAAG,EAAE;QACpC,IAAA,oBAAU,EAAC,KAAK,IAAI,EAAE;YACpB,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,UAAU,WAAW,CAAC,WAAW,EAAE,CAAC;YACxE,MAAM,IAAA,yBAAY,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAC3C,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YAC3B,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;QAC7B,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YAC/D,MAAM,SAAS,GAAG,IAAA,sBAAc,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YACpD,WAAW,CAAC,IAAI,GAAG;gBACjB,GAAG,SAAS;gBACZ,WAAW,EAAE,CAAC,YAAY,EAAE,eAAe,CAAC;gBAC5C,eAAe,EAAE,IAAI;aACtB,CAAC;YAEF,MAAM,mBAAmB,GAAG,IAAA,sBAAS,EAAC,YAAY,CAAC,CAAC;YACpD,MAAM,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAElD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAC9C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YAC/D,WAAW,CAAC,IAAI,GAAG;gBACjB,GAAG,QAAQ;gBACX,WAAW,EAAE,CAAC,YAAY,CAAC;gBAC3B,eAAe,EAAE,IAAI;aACtB,CAAC;YAEF,MAAM,mBAAmB,GAAG,IAAA,sBAAS,EAAC,aAAa,CAAC,CAAC;YACrD,MAAM,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAElD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;YACjD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACzC,gBAAM,CAAC,gBAAgB,CAAC;gBACtB,KAAK,EAAE,gBAAM,CAAC,gBAAgB,CAAC;oBAC7B,IAAI,EAAE,0BAA0B;iBACjC,CAAC;aACH,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;YAChE,MAAM,OAAO,GAAG,IAAA,sBAAc,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;YAChD,WAAW,CAAC,IAAI,GAAG;gBACjB,GAAG,OAAO;gBACV,WAAW,EAAE,CAAC,GAAG,CAAC,EAAE,0BAA0B;gBAC9C,eAAe,EAAE,IAAI;aACtB,CAAC;YAEF,MAAM,mBAAmB,GAAG,IAAA,sBAAS,EAAC,aAAa,EAAE,iBAAiB,CAAC,CAAC;YACxE,MAAM,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAElD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAC9C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAChD,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;YACtD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,wBAAwB;YAEjD,MAAM,mBAAmB,GAAG,IAAA,sBAAS,EAAC,YAAY,CAAC,CAAC;YACpD,MAAM,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAElD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;YACjD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACzC,gBAAM,CAAC,gBAAgB,CAAC;gBACtB,KAAK,EAAE,gBAAM,CAAC,gBAAgB,CAAC;oBAC7B,IAAI,EAAE,yBAAyB;iBAChC,CAAC;aACH,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,6CAA6C,EAAE,KAAK,IAAI,EAAE;YAC3D,WAAW,CAAC,IAAI,GAAG;gBACjB,GAAG,QAAQ;gBACX,WAAW,EAAE,CAAC,YAAY,EAAE,aAAa,EAAE,eAAe,CAAC;gBAC3D,eAAe,EAAE,IAAI;aACtB,CAAC;YAEF,MAAM,mBAAmB,GAAG,IAAA,sBAAS,EAAC,YAAY,EAAE,aAAa,CAAC,CAAC;YACnE,MAAM,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAElD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;YAC9C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,kBAAQ,EAAC,eAAe,EAAE,GAAG,EAAE;QAC7B,IAAA,YAAE,EAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;YAChD,MAAM,MAAM,GAAG,cAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACvE,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,UAAU,WAAW,CAAC,WAAW,EAAE,CAAC;YAExE,MAAM,IAAA,yBAAY,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAE3C,IAAA,gBAAM,EAAC,MAAM,CAAC,CAAC,oBAAoB,CACjC,gBAAM,CAAC,gBAAgB,CAAC;gBACtB,KAAK,EAAE,cAAc;gBACrB,MAAM,EAAE,QAAQ,CAAC,EAAE;gBACnB,KAAK,EAAE,QAAQ,CAAC,KAAK;aACtB,CAAC,CACH,CAAC;YAEF,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;YACjD,MAAM,MAAM,GAAG,cAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACvE,WAAW,CAAC,IAAI,GAAG;gBACjB,GAAG,QAAQ;gBACX,WAAW,EAAE,CAAC,gBAAgB,CAAC;gBAC/B,eAAe,EAAE,IAAI;aACtB,CAAC;YAEF,MAAM,mBAAmB,GAAG,IAAA,sBAAS,EAAC,CAAC,cAAc,CAAC,CAAC,CAAC;YACxD,MAAM,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAElD,IAAA,gBAAM,EAAC,MAAM,CAAC,CAAC,oBAAoB,CACjC,gBAAM,CAAC,gBAAgB,CAAC;gBACtB,KAAK,EAAE,sBAAsB;gBAC7B,MAAM,EAAE,QAAQ,CAAC,EAAE;gBACnB,mBAAmB,EAAE,CAAC,cAAc,CAAC;aACtC,CAAC,CACH,CAAC;YAEF,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,MAAM,GAAG,cAAI,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,kBAAkB,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;YACvE,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,gCAAgC,CAAC;YAErE,MAAM,IAAA,yBAAY,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAE3C,IAAA,gBAAM,EAAC,MAAM,CAAC,CAAC,oBAAoB,CACjC,gBAAM,CAAC,gBAAgB,CAAC;gBACtB,KAAK,EAAE,oBAAoB;gBAC3B,IAAI,EAAE,eAAe;gBACrB,EAAE,EAAE,WAAW,CAAC,EAAE;aACnB,CAAC,CACH,CAAC;YAEF,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,kBAAQ,EAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,IAAA,YAAE,EAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,UAAU,WAAW,CAAC,WAAW,EAAE,CAAC;YAExE,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAA,4BAAoB,EAAC,KAAK,IAAI,EAAE;gBACzD,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,GAAG,EAAE,CAChD,IAAA,yBAAY,EAAC,IAAA,yBAAiB,EAAC;oBAC7B,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,WAAW,CAAC,WAAW,EAAE,EAAE;iBAChE,CAAC,EAAE,IAAA,uBAAe,GAAE,CAAC,CACvB,CAAC;gBACF,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;YAEH,qDAAqD;YACrD,IAAA,gBAAM,EAAC,QAAQ,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC;QACrC,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;YAC3E,WAAW,CAAC,IAAI,GAAG;gBACjB,GAAG,QAAQ;gBACX,WAAW,EAAE,CAAC,YAAY,EAAE,eAAe,CAAC;gBAC5C,eAAe,EAAE,IAAI;aACtB,CAAC;YAEF,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAA,4BAAoB,EAAC,KAAK,IAAI,EAAE;gBACzD,MAAM,mBAAmB,GAAG,IAAA,sBAAS,EAAC,CAAC,YAAY,CAAC,CAAC,CAAC;gBACtD,MAAM,mBAAmB,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YACpD,CAAC,CAAC,CAAC;YAEH,iEAAiE;YACjE,+BAAuB,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,IAAA,kBAAQ,EAAC,qBAAqB,EAAE,GAAG,EAAE;QACnC,IAAA,YAAE,EAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,MAAM,eAAe,GAAG;gBACtB,sEAAsE,EAAE,iBAAiB;gBACzF,uGAAuG,EAAE,sBAAsB;aAChI,CAAC;YAEF,KAAK,MAAM,KAAK,IAAI,eAAe,EAAE,CAAC;gBACpC,WAAW,CAAC,OAAO,CAAC,aAAa,GAAG,KAAK,CAAC;gBAC1C,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBAC3B,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;gBAE3B,MAAM,IAAA,yBAAY,EAAC,WAAW,EAAE,SAAS,CAAC,CAAC;gBAE3C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;gBACjD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACzC,gBAAM,CAAC,gBAAgB,CAAC;oBACtB,KAAK,EAAE,gBAAM,CAAC,gBAAgB,CAAC;wBAC7B,IAAI,EAAE,gBAAM,CAAC,cAAc,CAAC,kCAAkC,CAAC;qBAChE,CAAC;iBACH,CAAC,CACH,CAAC;YACJ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,8CAA8C,EAAE,KAAK,IAAI,EAAE;YAC5D,wDAAwD;YACxD,MAAM,SAAS,GAAG,IAAA,sBAAc,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC;YACpD,WAAW,CAAC,IAAI,GAAG;gBACjB,GAAG,SAAS;gBACZ,WAAW,EAAE,CAAC,YAAY,EAAE,eAAe,CAAC;gBAC5C,eAAe,EAAE,IAAI;aACtB,CAAC;YAEF,MAAM,eAAe,GAAG,IAAA,sBAAS,EAAC,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC,CAAC;YACpE,MAAM,eAAe,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;YAE9C,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;YACjD,IAAA,gBAAM,EAAC,SAAS,CAAC,IAAI,CAAC,CAAC,oBAAoB,CACzC,gBAAM,CAAC,gBAAgB,CAAC;gBACtB,KAAK,EAAE,gBAAM,CAAC,gBAAgB,CAAC;oBAC7B,IAAI,EAAE,0BAA0B;iBACjC,CAAC;aACH,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAA,YAAE,EAAC,2CAA2C,EAAE,KAAK,IAAI,EAAE;YACzD,MAAM,kBAAkB,GAAG,IAAA,yBAAiB,EAAC;gBAC3C,EAAE,EAAE,eAAe,EAAE,4BAA4B;aAClD,CAAC,CAAC;YAEH,mDAAmD;YACnD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;gBAC5B,kBAAkB,CAAC,OAAO,CAAC,aAAa,GAAG,wBAAwB,CAAC,EAAE,CAAC;gBACvE,MAAM,IAAA,yBAAY,EAAC,kBAAkB,EAAE,IAAA,uBAAe,GAAE,CAAC,CAAC;YAC5D,CAAC;YAED,sCAAsC;YACtC,kBAAkB,CAAC,OAAO,CAAC,aAAa,GAAG,8BAA8B,CAAC;YAC1E,MAAM,gBAAgB,GAAG,IAAA,uBAAe,GAAE,CAAC;YAC3C,MAAM,IAAA,yBAAY,EAAC,kBAAkB,EAAE,gBAAgB,CAAC,CAAC;YAEzD,IAAA,gBAAM,EAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC;YACxD,IAAA,gBAAM,EAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC,oBAAoB,CAChD,gBAAM,CAAC,gBAAgB,CAAC;gBACtB,KAAK,EAAE,gBAAM,CAAC,gBAAgB,CAAC;oBAC7B,IAAI,EAAE,qBAAqB;iBAC5B,CAAC;aACH,CAAC,CACH,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"} \ No newline at end of file diff --git a/backend/tests/auth/middleware.test.ts b/backend/tests/auth/middleware.test.ts deleted file mode 100644 index 80660d3..0000000 --- a/backend/tests/auth/middleware.test.ts +++ /dev/null @@ -1,420 +0,0 @@ -/** - * Authentication and Authorization Middleware Tests - * Tests for JWT middleware, rate limiting, and security features - */ - -import { describe, it, expect, beforeEach, jest, afterEach } from '@jest/globals'; -import { authenticate, authorize, optionalAuthenticate, auditLog } from '../../src/auth/middleware'; -import { jwtService } from '../../src/auth/jwt'; -import { - createTestUser, - createMockRequest, - createMockReply, - validateSuccessCriteria, - measureExecutionTime -} from '../setup'; - -describe('Authentication Middleware', () => { - let testUser: any; - let mockRequest: any; - let mockReply: any; - let validTokens: any; - - beforeEach(async () => { - testUser = createTestUser({ - id: 'auth-test-user', - email: 'auth@test.com', - role: 'staff', - name: 'Auth Test User', - }); - - validTokens = await jwtService.generateTokens(testUser); - mockRequest = createMockRequest(); - mockReply = createMockReply(); - }); - - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('authenticate middleware', () => { - it('should authenticate valid bearer token', async () => { - mockRequest.headers.authorization = `Bearer ${validTokens.accessToken}`; - - const { duration } = await measureExecutionTime(async () => { - await authenticate(mockRequest, mockReply); - }); - - // Authentication should be fast - validateSuccessCriteria.realTimeUpdate(duration); - - expect(mockRequest.user).toBeDefined(); - expect(mockRequest.user.userId).toBe(testUser.id); - expect(mockRequest.user.email).toBe(testUser.email); - expect(mockRequest.user.role).toBe(testUser.role); - expect(mockRequest.user.isAuthenticated).toBe(true); - }); - - it('should reject request without authorization header', async () => { - // No authorization header - delete mockRequest.headers.authorization; - - await authenticate(mockRequest, mockReply); - - expect(mockReply.code).toHaveBeenCalledWith(401); - expect(mockReply.send).toHaveBeenCalledWith( - expect.objectContaining({ - error: expect.objectContaining({ - message: 'Authentication required', - code: 'AUTHENTICATION_REQUIRED', - statusCode: 401, - }), - }) - ); - }); - - it('should reject malformed authorization header', async () => { - const malformedHeaders = [ - 'InvalidFormat', // No Bearer prefix - 'Bearer', // No token - 'Bearer ', // Empty token - 'Basic dGVzdDp0ZXN0', // Wrong auth type - ]; - - for (const authHeader of malformedHeaders) { - mockRequest.headers.authorization = authHeader; - mockReply.code.mockClear(); - mockReply.send.mockClear(); - - await authenticate(mockRequest, mockReply); - - expect(mockReply.code).toHaveBeenCalledWith(401); - expect(mockReply.send).toHaveBeenCalledWith( - expect.objectContaining({ - error: expect.objectContaining({ - code: 'INVALID_TOKEN_FORMAT', - }), - }) - ); - } - }); - - it('should reject expired tokens', async () => { - // Create expired token (this would need a helper to create expired tokens) - const expiredToken = 'expired.jwt.token'; - mockRequest.headers.authorization = `Bearer ${expiredToken}`; - - await authenticate(mockRequest, mockReply); - - expect(mockReply.code).toHaveBeenCalledWith(401); - expect(mockReply.send).toHaveBeenCalledWith( - expect.objectContaining({ - error: expect.objectContaining({ - code: 'TOKEN_EXPIRED', - }), - }) - ); - }); - - it('should handle authentication errors gracefully', async () => { - mockRequest.headers.authorization = 'Bearer invalid.token.here'; - - await authenticate(mockRequest, mockReply); - - expect(mockReply.code).toHaveBeenCalledWith(401); - expect(mockReply.send).toHaveBeenCalledWith( - expect.objectContaining({ - error: expect.objectContaining({ - code: 'INVALID_TOKEN', - }), - }) - ); - }); - }); - - describe('optionalAuthenticate middleware', () => { - it('should authenticate when valid token is provided', async () => { - mockRequest.headers.authorization = `Bearer ${validTokens.accessToken}`; - - await optionalAuthenticate(mockRequest, mockReply); - - expect(mockRequest.user).toBeDefined(); - expect(mockRequest.user.isAuthenticated).toBe(true); - }); - - it('should continue without authentication when no token provided', async () => { - delete mockRequest.headers.authorization; - - await optionalAuthenticate(mockRequest, mockReply); - - expect(mockRequest.user).toBeUndefined(); - expect(mockReply.code).not.toHaveBeenCalled(); - expect(mockReply.send).not.toHaveBeenCalled(); - }); - - it('should continue without authentication for invalid tokens', async () => { - mockRequest.headers.authorization = 'Bearer invalid.token'; - - await optionalAuthenticate(mockRequest, mockReply); - - expect(mockRequest.user).toBeUndefined(); - expect(mockReply.code).not.toHaveBeenCalled(); - expect(mockReply.send).not.toHaveBeenCalled(); - }); - }); - - describe('authorize middleware', () => { - beforeEach(async () => { - mockRequest.headers.authorization = `Bearer ${validTokens.accessToken}`; - await authenticate(mockRequest, mockReply); - mockReply.code.mockClear(); - mockReply.send.mockClear(); - }); - - it('should authorize user with required permissions', async () => { - const staffUser = createTestUser({ role: 'staff' }); - mockRequest.user = { - ...staffUser, - permissions: ['tasks:read', 'channels:read'], - isAuthenticated: true, - }; - - const authorizeMiddleware = authorize('tasks:read'); - await authorizeMiddleware(mockRequest, mockReply); - - expect(mockReply.code).not.toHaveBeenCalled(); - expect(mockReply.send).not.toHaveBeenCalled(); - }); - - it('should reject user without required permissions', async () => { - mockRequest.user = { - ...testUser, - permissions: ['tasks:read'], - isAuthenticated: true, - }; - - const authorizeMiddleware = authorize('users:admin'); - await authorizeMiddleware(mockRequest, mockReply); - - expect(mockReply.code).toHaveBeenCalledWith(403); - expect(mockReply.send).toHaveBeenCalledWith( - expect.objectContaining({ - error: expect.objectContaining({ - code: 'INSUFFICIENT_PERMISSIONS', - }), - }) - ); - }); - - it('should handle CEO role with wildcard permissions', async () => { - const ceoUser = createTestUser({ role: 'ceo' }); - mockRequest.user = { - ...ceoUser, - permissions: ['*'], // CEO has all permissions - isAuthenticated: true, - }; - - const authorizeMiddleware = authorize('users:admin', 'channels:delete'); - await authorizeMiddleware(mockRequest, mockReply); - - expect(mockReply.code).not.toHaveBeenCalled(); - expect(mockReply.send).not.toHaveBeenCalled(); - }); - - it('should reject unauthenticated requests', async () => { - delete mockRequest.user; // No authenticated user - - const authorizeMiddleware = authorize('tasks:read'); - await authorizeMiddleware(mockRequest, mockReply); - - expect(mockReply.code).toHaveBeenCalledWith(401); - expect(mockReply.send).toHaveBeenCalledWith( - expect.objectContaining({ - error: expect.objectContaining({ - code: 'AUTHENTICATION_REQUIRED', - }), - }) - ); - }); - - it('should handle multiple required permissions', async () => { - mockRequest.user = { - ...testUser, - permissions: ['tasks:read', 'tasks:write', 'channels:read'], - isAuthenticated: true, - }; - - const authorizeMiddleware = authorize('tasks:read', 'tasks:write'); - await authorizeMiddleware(mockRequest, mockReply); - - expect(mockReply.code).not.toHaveBeenCalled(); - expect(mockReply.send).not.toHaveBeenCalled(); - }); - }); - - describe('audit logging', () => { - it('should log authentication events', async () => { - const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); - mockRequest.headers.authorization = `Bearer ${validTokens.accessToken}`; - - await authenticate(mockRequest, mockReply); - - expect(logSpy).toHaveBeenCalledWith( - expect.objectContaining({ - event: 'auth_success', - userId: testUser.id, - email: testUser.email, - }) - ); - - logSpy.mockRestore(); - }); - - it('should log authorization failures', async () => { - const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); - mockRequest.user = { - ...testUser, - permissions: ['limited:access'], - isAuthenticated: true, - }; - - const authorizeMiddleware = authorize(['admin:access']); - await authorizeMiddleware(mockRequest, mockReply); - - expect(logSpy).toHaveBeenCalledWith( - expect.objectContaining({ - event: 'authorization_denied', - userId: testUser.id, - requiredPermissions: ['admin:access'], - }) - ); - - logSpy.mockRestore(); - }); - - it('should log security violations', async () => { - const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {}); - mockRequest.headers.authorization = 'Bearer malicious.token.attempt'; - - await authenticate(mockRequest, mockReply); - - expect(logSpy).toHaveBeenCalledWith( - expect.objectContaining({ - event: 'security_violation', - type: 'invalid_token', - ip: mockRequest.ip, - }) - ); - - logSpy.mockRestore(); - }); - }); - - describe('performance requirements', () => { - it('should meet authentication performance benchmarks', async () => { - mockRequest.headers.authorization = `Bearer ${validTokens.accessToken}`; - - const { duration } = await measureExecutionTime(async () => { - const promises = Array.from({ length: 100 }, () => - authenticate(createMockRequest({ - headers: { authorization: `Bearer ${validTokens.accessToken}` } - }), createMockReply()) - ); - await Promise.all(promises); - }); - - // 100 authentications should complete in under 500ms - expect(duration).toBeLessThan(500); - }); - - it('should meet real-time update requirements for authorization', async () => { - mockRequest.user = { - ...testUser, - permissions: ['tasks:read', 'channels:read'], - isAuthenticated: true, - }; - - const { duration } = await measureExecutionTime(async () => { - const authorizeMiddleware = authorize(['tasks:read']); - await authorizeMiddleware(mockRequest, mockReply); - }); - - // Authorization should be under 100ms for real-time requirements - validateSuccessCriteria.realTimeUpdate(duration); - }); - }); - - describe('security edge cases', () => { - it('should handle JWT header manipulation attempts', async () => { - const maliciousTokens = [ - 'Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJ1c2VySWQiOiJhZG1pbiJ9.', // None algorithm - 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJhZG1pbiIsImV4cCI6OTk5OTk5OTk5OX0.malicious', // Manipulated payload - ]; - - for (const token of maliciousTokens) { - mockRequest.headers.authorization = token; - mockReply.code.mockClear(); - mockReply.send.mockClear(); - - await authenticate(mockRequest, mockReply); - - expect(mockReply.code).toHaveBeenCalledWith(401); - expect(mockReply.send).toHaveBeenCalledWith( - expect.objectContaining({ - error: expect.objectContaining({ - code: expect.stringMatching(/INVALID_TOKEN|SECURITY_VIOLATION/), - }), - }) - ); - } - }); - - it('should prevent privilege escalation attempts', async () => { - // User with staff role trying to access admin endpoints - const staffUser = createTestUser({ role: 'staff' }); - mockRequest.user = { - ...staffUser, - permissions: ['tasks:read', 'channels:read'], - isAuthenticated: true, - }; - - const adminMiddleware = authorize(['users:admin', 'system:config']); - await adminMiddleware(mockRequest, mockReply); - - expect(mockReply.code).toHaveBeenCalledWith(403); - expect(mockReply.send).toHaveBeenCalledWith( - expect.objectContaining({ - error: expect.objectContaining({ - code: 'INSUFFICIENT_PERMISSIONS', - }), - }) - ); - }); - - it('should rate limit authentication attempts', async () => { - const rateLimitedRequest = createMockRequest({ - ip: '192.168.1.100', // Same IP for rate limiting - }); - - // Simulate multiple failed authentication attempts - for (let i = 0; i < 10; i++) { - rateLimitedRequest.headers.authorization = `Bearer invalid-token-${i}`; - await authenticate(rateLimitedRequest, createMockReply()); - } - - // Next attempt should be rate limited - rateLimitedRequest.headers.authorization = 'Bearer another-invalid-token'; - const rateLimitedReply = createMockReply(); - await authenticate(rateLimitedRequest, rateLimitedReply); - - expect(rateLimitedReply.code).toHaveBeenCalledWith(429); - expect(rateLimitedReply.send).toHaveBeenCalledWith( - expect.objectContaining({ - error: expect.objectContaining({ - code: 'RATE_LIMIT_EXCEEDED', - }), - }) - ); - }); - }); -}); \ No newline at end of file diff --git a/backend/tests/db/BaseRepository.test.d.ts b/backend/tests/db/BaseRepository.test.d.ts deleted file mode 100644 index d3cd21c..0000000 --- a/backend/tests/db/BaseRepository.test.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Base Repository Tests - * Tests for the core database repository pattern with caching, soft deletes, and performance monitoring - */ -export {}; -//# sourceMappingURL=BaseRepository.test.d.ts.map \ No newline at end of file diff --git a/backend/tests/db/BaseRepository.test.d.ts.map b/backend/tests/db/BaseRepository.test.d.ts.map deleted file mode 100644 index 4f006ac..0000000 --- a/backend/tests/db/BaseRepository.test.d.ts.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"BaseRepository.test.d.ts","sourceRoot":"","sources":["BaseRepository.test.ts"],"names":[],"mappings":"AAAA;;;GAGG"} \ No newline at end of file diff --git a/backend/tests/db/BaseRepository.test.js b/backend/tests/db/BaseRepository.test.js deleted file mode 100644 index 22d5fd1..0000000 --- a/backend/tests/db/BaseRepository.test.js +++ /dev/null @@ -1,403 +0,0 @@ -"use strict"; -/** - * Base Repository Tests - * Tests for the core database repository pattern with caching, soft deletes, and performance monitoring - */ -Object.defineProperty(exports, "__esModule", { value: true }); -const globals_1 = require("@jest/globals"); -const BaseRepository_1 = require("../../src/db/BaseRepository"); -const errors_1 = require("../../src/utils/errors"); -const setup_1 = require("../setup"); -// Mock database and cache dependencies -globals_1.jest.mock('../../src/config/database', () => ({ - pool: { - query: globals_1.jest.fn(), - connect: globals_1.jest.fn().mockResolvedValue({ - query: globals_1.jest.fn(), - release: globals_1.jest.fn(), - }), - }, -})); -globals_1.jest.mock('../../src/services/CacheService', () => ({ - cacheService: { - get: globals_1.jest.fn(), - set: globals_1.jest.fn(), - delete: globals_1.jest.fn(), - invalidateByTag: globals_1.jest.fn(), - clear: globals_1.jest.fn(), - }, -})); -// Test implementation of BaseRepository -class TestRepository extends BaseRepository_1.BaseRepository { - constructor() { - super('test_table', 'test_cache'); - } - validateCreate(data) { - if (!data.name) { - throw new errors_1.ValidationError('Name is required'); - } - } - validateUpdate(data) { - if (data.name === '') { - throw new errors_1.ValidationError('Name cannot be empty'); - } - } -} -(0, globals_1.describe)('BaseRepository', () => { - let repository; - let mockPool; - let mockCache; - (0, globals_1.beforeEach)(() => { - repository = new TestRepository(); - mockPool = require('../../src/config/database').pool; - mockCache = require('../../src/services/CacheService').cacheService; - // Reset mocks - globals_1.jest.clearAllMocks(); - }); - (0, globals_1.afterEach)(async () => { - await (0, setup_1.cleanupTestData)(); - }); - (0, globals_1.describe)('Create Operations', () => { - (0, globals_1.it)('should create a new record successfully', async () => { - const testData = { name: 'Test Record', description: 'Test description' }; - const mockResult = { - rows: [{ - id: 'test-id-123', - ...testData, - created_at: new Date(), - updated_at: new Date(), - version: 1 - }] - }; - mockPool.query.mockResolvedValueOnce(mockResult); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return repository.create(testData, 'test-user-id'); - }); - // Should meet database operation performance requirements - setup_1.validateSuccessCriteria.realTimeUpdate(duration); - (0, globals_1.expect)(result).toEqual(mockResult.rows[0]); - (0, globals_1.expect)(mockPool.query).toHaveBeenCalledWith(globals_1.expect.stringContaining('INSERT INTO test_table'), globals_1.expect.any(Array)); - (0, globals_1.expect)(mockCache.invalidateByTag).toHaveBeenCalledWith('test_cache'); - }); - (0, globals_1.it)('should validate data before creation', async () => { - const invalidData = { description: 'Missing required name' }; - await (0, globals_1.expect)(repository.create(invalidData, 'test-user-id')) - .rejects - .toThrow(errors_1.ValidationError); - (0, globals_1.expect)(mockPool.query).not.toHaveBeenCalled(); - }); - (0, globals_1.it)('should handle database errors during creation', async () => { - const testData = { name: 'Test Record' }; - mockPool.query.mockRejectedValueOnce(new Error('Database connection failed')); - await (0, globals_1.expect)(repository.create(testData, 'test-user-id')) - .rejects - .toThrow(errors_1.DatabaseError); - }); - (0, globals_1.it)('should include audit fields in creation', async () => { - const testData = { name: 'Audit Test' }; - const mockResult = { rows: [{ id: 'audit-test-id', ...testData }] }; - mockPool.query.mockResolvedValueOnce(mockResult); - await repository.create(testData, 'audit-user-id'); - const queryCall = mockPool.query.mock.calls[0]; - const queryParams = queryCall[1]; - (0, globals_1.expect)(queryParams).toContain('audit-user-id'); // created_by - (0, globals_1.expect)(queryParams).toContain('audit-user-id'); // updated_by - }); - }); - (0, globals_1.describe)('Read Operations', () => { - (0, globals_1.it)('should find record by ID with caching', async () => { - const mockRecord = { - id: 'cached-record-id', - name: 'Cached Record', - created_at: new Date(), - deleted_at: null - }; - // First call - cache miss, database hit - mockCache.get.mockResolvedValueOnce(null); - mockPool.query.mockResolvedValueOnce({ rows: [mockRecord] }); - const { result: result1, duration: duration1 } = await (0, setup_1.measureExecutionTime)(async () => { - return repository.findById('cached-record-id'); - }); - (0, globals_1.expect)(result1).toEqual(mockRecord); - (0, globals_1.expect)(mockPool.query).toHaveBeenCalledWith(globals_1.expect.stringContaining('SELECT * FROM test_table WHERE id = $1'), ['cached-record-id']); - (0, globals_1.expect)(mockCache.set).toHaveBeenCalledWith('test_cache:cached-record-id', mockRecord, globals_1.expect.any(Object)); - // Second call - cache hit - mockCache.get.mockResolvedValueOnce(mockRecord); - const { result: result2, duration: duration2 } = await (0, setup_1.measureExecutionTime)(async () => { - return repository.findById('cached-record-id'); - }); - (0, globals_1.expect)(result2).toEqual(mockRecord); - // Cache hit should be much faster - (0, globals_1.expect)(duration2).toBeLessThan(duration1); - (0, globals_1.expect)(mockPool.query).toHaveBeenCalledTimes(1); // No additional DB call - }); - (0, globals_1.it)('should return null for non-existent records', async () => { - mockCache.get.mockResolvedValueOnce(null); - mockPool.query.mockResolvedValueOnce({ rows: [] }); - const result = await repository.findById('non-existent-id'); - (0, globals_1.expect)(result).toBeNull(); - }); - (0, globals_1.it)('should exclude soft-deleted records by default', async () => { - const deletedRecord = { - id: 'deleted-id', - name: 'Deleted Record', - deleted_at: new Date() - }; - mockCache.get.mockResolvedValueOnce(null); - mockPool.query.mockResolvedValueOnce({ rows: [deletedRecord] }); - const result = await repository.findById('deleted-id'); - (0, globals_1.expect)(result).toBeNull(); - (0, globals_1.expect)(mockPool.query).toHaveBeenCalledWith(globals_1.expect.stringContaining('AND deleted_at IS NULL'), globals_1.expect.any(Array)); - }); - (0, globals_1.it)('should support finding with filters and pagination', async () => { - const mockResults = { - rows: [ - { id: 'result-1', name: 'Result 1' }, - { id: 'result-2', name: 'Result 2' }, - ], - count: 10, - }; - mockPool.query.mockResolvedValueOnce({ rows: [{ count: '10' }] }); // Count query - mockPool.query.mockResolvedValueOnce(mockResults); // Results query - const filters = { status: 'active' }; - const options = { page: 1, limit: 10 }; - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return repository.findMany(filters, options); - }); - setup_1.validateSuccessCriteria.realTimeUpdate(duration); - (0, globals_1.expect)(result).toEqual({ - data: mockResults.rows, - total: 10, - page: 1, - limit: 10, - totalPages: 1, - }); - }); - }); - (0, globals_1.describe)('Update Operations', () => { - (0, globals_1.it)('should update record with optimistic locking', async () => { - const updateData = { name: 'Updated Name' }; - const mockResult = { - rows: [{ - id: 'update-test-id', - ...updateData, - version: 2, - updated_at: new Date() - }] - }; - mockPool.query.mockResolvedValueOnce(mockResult); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return repository.update('update-test-id', updateData, 'updater-user-id', 1); - }); - setup_1.validateSuccessCriteria.realTimeUpdate(duration); - (0, globals_1.expect)(result).toEqual(mockResult.rows[0]); - (0, globals_1.expect)(mockPool.query).toHaveBeenCalledWith(globals_1.expect.stringContaining('WHERE id = $1 AND version = $2'), globals_1.expect.arrayContaining(['update-test-id', 1])); - (0, globals_1.expect)(mockCache.delete).toHaveBeenCalledWith('test_cache:update-test-id'); - }); - (0, globals_1.it)('should handle version conflicts in optimistic locking', async () => { - mockPool.query.mockResolvedValueOnce({ rows: [] }); // No rows updated - await (0, globals_1.expect)(repository.update('conflict-id', { name: 'Updated' }, 'user-id', 1)).rejects.toThrow('Version conflict or record not found'); - }); - (0, globals_1.it)('should validate update data', async () => { - const invalidData = { name: '' }; // Empty name should fail validation - await (0, globals_1.expect)(repository.update('test-id', invalidData, 'user-id', 1)).rejects.toThrow(errors_1.ValidationError); - (0, globals_1.expect)(mockPool.query).not.toHaveBeenCalled(); - }); - }); - (0, globals_1.describe)('Delete Operations', () => { - (0, globals_1.it)('should perform soft delete by default', async () => { - const mockResult = { - rows: [{ - id: 'soft-delete-id', - name: 'Soft Deleted Record', - deleted_at: new Date(), - version: 2 - }] - }; - mockPool.query.mockResolvedValueOnce(mockResult); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return repository.delete('soft-delete-id', 'deleter-user-id', 1); - }); - setup_1.validateSuccessCriteria.realTimeUpdate(duration); - (0, globals_1.expect)(result).toEqual(mockResult.rows[0]); - (0, globals_1.expect)(mockPool.query).toHaveBeenCalledWith(globals_1.expect.stringContaining('SET deleted_at = NOW()'), globals_1.expect.any(Array)); - (0, globals_1.expect)(mockCache.delete).toHaveBeenCalledWith('test_cache:soft-delete-id'); - }); - (0, globals_1.it)('should perform hard delete when specified', async () => { - mockPool.query.mockResolvedValueOnce({ rowCount: 1 }); - await repository.delete('hard-delete-id', 'deleter-user-id', 1, true); - (0, globals_1.expect)(mockPool.query).toHaveBeenCalledWith(globals_1.expect.stringContaining('DELETE FROM test_table'), globals_1.expect.any(Array)); - }); - (0, globals_1.it)('should handle delete conflicts', async () => { - mockPool.query.mockResolvedValueOnce({ rows: [] }); - await (0, globals_1.expect)(repository.delete('conflict-id', 'user-id', 1)).rejects.toThrow('Version conflict or record not found'); - }); - }); - (0, globals_1.describe)('Bulk Operations', () => { - (0, globals_1.it)('should support bulk creation with transaction', async () => { - const bulkData = [ - { name: 'Bulk Record 1' }, - { name: 'Bulk Record 2' }, - { name: 'Bulk Record 3' }, - ]; - const mockResults = { - rows: bulkData.map((data, index) => ({ - id: `bulk-${index}`, - ...data, - created_at: new Date(), - })) - }; - const mockClient = { - query: globals_1.jest.fn().mockResolvedValue(mockResults), - release: globals_1.jest.fn(), - }; - mockPool.connect.mockResolvedValueOnce(mockClient); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return repository.createMany(bulkData, 'bulk-user-id'); - }); - // Bulk operations should still be performant - (0, globals_1.expect)(duration).toBeLessThan(1000); // Under 1 second for bulk ops - (0, globals_1.expect)(result).toHaveLength(3); - (0, globals_1.expect)(mockClient.query).toHaveBeenCalledWith('BEGIN'); - (0, globals_1.expect)(mockClient.query).toHaveBeenCalledWith('COMMIT'); - (0, globals_1.expect)(mockClient.release).toHaveBeenCalled(); - }); - (0, globals_1.it)('should rollback bulk creation on error', async () => { - const bulkData = [ - { name: 'Valid Record' }, - { description: 'Invalid - missing name' }, // Should fail validation - ]; - const mockClient = { - query: globals_1.jest.fn().mockRejectedValue(new Error('Validation failed')), - release: globals_1.jest.fn(), - }; - mockPool.connect.mockResolvedValueOnce(mockClient); - await (0, globals_1.expect)(repository.createMany(bulkData, 'bulk-user-id')).rejects.toThrow(); - (0, globals_1.expect)(mockClient.query).toHaveBeenCalledWith('ROLLBACK'); - (0, globals_1.expect)(mockClient.release).toHaveBeenCalled(); - }); - }); - (0, globals_1.describe)('Performance and Caching', () => { - (0, globals_1.it)('should meet performance benchmarks for read operations', async () => { - const testRecord = { id: 'perf-test', name: 'Performance Test' }; - mockCache.get.mockResolvedValue(testRecord); - const { duration } = await (0, setup_1.measureExecutionTime)(async () => { - const promises = Array.from({ length: 100 }, () => repository.findById('perf-test')); - await Promise.all(promises); - }); - // 100 cached reads should complete in under 100ms - (0, globals_1.expect)(duration).toBeLessThan(100); - }); - (0, globals_1.it)('should invalidate cache on mutations', async () => { - const mockResult = { rows: [{ id: 'cache-test', name: 'Updated' }] }; - mockPool.query.mockResolvedValueOnce(mockResult); - await repository.update('cache-test', { name: 'Updated' }, 'user-id', 1); - (0, globals_1.expect)(mockCache.delete).toHaveBeenCalledWith('test_cache:cache-test'); - (0, globals_1.expect)(mockCache.invalidateByTag).toHaveBeenCalledWith('test_cache'); - }); - (0, globals_1.it)('should handle cache failures gracefully', async () => { - const testRecord = { id: 'cache-fail-test', name: 'Cache Fail Test' }; - // Cache throws error but DB works - mockCache.get.mockRejectedValueOnce(new Error('Cache unavailable')); - mockPool.query.mockResolvedValueOnce({ rows: [testRecord] }); - const result = await repository.findById('cache-fail-test'); - (0, globals_1.expect)(result).toEqual(testRecord); - (0, globals_1.expect)(mockPool.query).toHaveBeenCalled(); // Still queries database - }); - }); - (0, globals_1.describe)('Advanced Features', () => { - (0, globals_1.it)('should support complex queries with joins', async () => { - const mockResults = { - rows: [ - { - id: 'join-test-1', - name: 'Record 1', - related_name: 'Related Record 1' - } - ] - }; - mockPool.query.mockResolvedValueOnce(mockResults); - const customQuery = ` - SELECT t.*, r.name as related_name - FROM test_table t - LEFT JOIN related_table r ON t.related_id = r.id - WHERE t.status = $1 - `; - const result = await repository.customQuery(customQuery, ['active']); - (0, globals_1.expect)(result).toEqual(mockResults.rows); - (0, globals_1.expect)(mockPool.query).toHaveBeenCalledWith(customQuery, ['active']); - }); - (0, globals_1.it)('should support search with full-text indexing', async () => { - const searchTerm = 'important document'; - const mockResults = { - rows: [ - { - id: 'search-1', - name: 'Important Document 1', - rank: 0.95 - } - ] - }; - mockPool.query.mockResolvedValueOnce(mockResults); - const result = await repository.search(searchTerm, { - fields: ['name', 'description'], - limit: 10 - }); - (0, globals_1.expect)(result).toEqual(mockResults.rows); - (0, globals_1.expect)(mockPool.query).toHaveBeenCalledWith(globals_1.expect.stringContaining('ts_rank'), globals_1.expect.arrayContaining([searchTerm])); - }); - (0, globals_1.it)('should support aggregation queries', async () => { - const mockAggregation = { - rows: [{ - total_count: 150, - active_count: 120, - avg_score: 4.2, - }] - }; - mockPool.query.mockResolvedValueOnce(mockAggregation); - const result = await repository.aggregate({ - count: '*', - countWhere: { status: 'active' }, - avg: 'score', - }); - (0, globals_1.expect)(result).toEqual(mockAggregation.rows[0]); - }); - }); - (0, globals_1.describe)('Error Handling and Edge Cases', () => { - (0, globals_1.it)('should handle concurrent update conflicts', async () => { - // Simulate concurrent update scenario - mockPool.query.mockResolvedValueOnce({ rows: [] }); // Version conflict - await (0, globals_1.expect)(repository.update('concurrent-test', { name: 'Updated' }, 'user-1', 1)).rejects.toThrow('Version conflict or record not found'); - }); - (0, globals_1.it)('should validate input sanitization', async () => { - const maliciousData = { - name: "'; DROP TABLE test_table; --", - description: '' - }; - // Should not throw error (parameterized queries handle this) - const mockResult = { rows: [{ id: 'sanitized-test', ...maliciousData }] }; - mockPool.query.mockResolvedValueOnce(mockResult); - const result = await repository.create(maliciousData, 'test-user'); - (0, globals_1.expect)(result).toBeDefined(); - // Verify parameterized query was used - (0, globals_1.expect)(mockPool.query).toHaveBeenCalledWith(globals_1.expect.any(String), globals_1.expect.arrayContaining([maliciousData.name, maliciousData.description])); - }); - (0, globals_1.it)('should handle large result sets efficiently', async () => { - const largeResultSet = { - rows: Array.from({ length: 1000 }, (_, i) => ({ - id: `large-result-${i}`, - name: `Record ${i}`, - data: 'Large data payload'.repeat(100), - })) - }; - mockPool.query.mockResolvedValueOnce({ rows: [{ count: '1000' }] }); - mockPool.query.mockResolvedValueOnce(largeResultSet); - const { result, duration } = await (0, setup_1.measureExecutionTime)(async () => { - return repository.findMany({}, { page: 1, limit: 1000 }); - }); - (0, globals_1.expect)(result.data).toHaveLength(1000); - (0, globals_1.expect)(duration).toBeLessThan(2000); // Should handle large sets under 2 seconds - }); - }); -}); -//# sourceMappingURL=BaseRepository.test.js.map \ No newline at end of file diff --git a/backend/tests/db/BaseRepository.test.js.map b/backend/tests/db/BaseRepository.test.js.map deleted file mode 100644 index eead86c..0000000 --- a/backend/tests/db/BaseRepository.test.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"BaseRepository.test.js","sourceRoot":"","sources":["BaseRepository.test.ts"],"names":[],"mappings":"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"} \ No newline at end of file diff --git a/backend/tests/db/BaseRepository.test.ts b/backend/tests/db/BaseRepository.test.ts deleted file mode 100644 index f332027..0000000 --- a/backend/tests/db/BaseRepository.test.ts +++ /dev/null @@ -1,552 +0,0 @@ -/** - * Base Repository Tests - * Tests for the core database repository pattern with caching, soft deletes, and performance monitoring - */ - -import { describe, it, expect, beforeEach, afterEach, jest } from '@jest/globals'; -import { BaseRepository } from '../../src/db/BaseRepository'; -import { DatabaseError, NotFoundError, ValidationError } from '../../src/utils/errors'; -import { - createTestUser, - validateSuccessCriteria, - measureExecutionTime, - cleanupTestData -} from '../setup'; - -// Mock database and cache dependencies -jest.mock('../../src/config/database', () => ({ - pool: { - query: jest.fn(), - connect: jest.fn().mockResolvedValue({ - query: jest.fn(), - release: jest.fn(), - }), - }, -})); - -jest.mock('../../src/services/CacheService', () => ({ - cacheService: { - get: jest.fn(), - set: jest.fn(), - delete: jest.fn(), - invalidateByTag: jest.fn(), - clear: jest.fn(), - }, -})); - -// Test implementation of BaseRepository -class TestRepository extends BaseRepository { - constructor() { - super('test_table', 'test_cache'); - } - - protected validateCreate(data: any): void { - if (!data.name) { - throw new ValidationError('Name is required'); - } - } - - protected validateUpdate(data: any): void { - if (data.name === '') { - throw new ValidationError('Name cannot be empty'); - } - } -} - -describe('BaseRepository', () => { - let repository: TestRepository; - let mockPool: any; - let mockCache: any; - - beforeEach(() => { - repository = new TestRepository(); - mockPool = require('../../src/config/database').pool; - mockCache = require('../../src/services/CacheService').cacheService; - - // Reset mocks - jest.clearAllMocks(); - }); - - afterEach(async () => { - await cleanupTestData(); - }); - - describe('Create Operations', () => { - it('should create a new record successfully', async () => { - const testData = { name: 'Test Record', description: 'Test description' }; - const mockResult = { - rows: [{ - id: 'test-id-123', - ...testData, - created_at: new Date(), - updated_at: new Date(), - version: 1 - }] - }; - - mockPool.query.mockResolvedValueOnce(mockResult); - - const { result, duration } = await measureExecutionTime(async () => { - return repository.create(testData, 'test-user-id'); - }); - - // Should meet database operation performance requirements - validateSuccessCriteria.realTimeUpdate(duration); - - expect(result).toEqual(mockResult.rows[0]); - expect(mockPool.query).toHaveBeenCalledWith( - expect.stringContaining('INSERT INTO test_table'), - expect.any(Array) - ); - expect(mockCache.invalidateByTag).toHaveBeenCalledWith('test_cache'); - }); - - it('should validate data before creation', async () => { - const invalidData = { description: 'Missing required name' }; - - await expect(repository.create(invalidData, 'test-user-id')) - .rejects - .toThrow(ValidationError); - - expect(mockPool.query).not.toHaveBeenCalled(); - }); - - it('should handle database errors during creation', async () => { - const testData = { name: 'Test Record' }; - mockPool.query.mockRejectedValueOnce(new Error('Database connection failed')); - - await expect(repository.create(testData, 'test-user-id')) - .rejects - .toThrow(DatabaseError); - }); - - it('should include audit fields in creation', async () => { - const testData = { name: 'Audit Test' }; - const mockResult = { rows: [{ id: 'audit-test-id', ...testData }] }; - mockPool.query.mockResolvedValueOnce(mockResult); - - await repository.create(testData, 'audit-user-id'); - - const queryCall = mockPool.query.mock.calls[0]; - const queryParams = queryCall[1]; - - expect(queryParams).toContain('audit-user-id'); // created_by - expect(queryParams).toContain('audit-user-id'); // updated_by - }); - }); - - describe('Read Operations', () => { - it('should find record by ID with caching', async () => { - const mockRecord = { - id: 'cached-record-id', - name: 'Cached Record', - created_at: new Date(), - deleted_at: null - }; - - // First call - cache miss, database hit - mockCache.get.mockResolvedValueOnce(null); - mockPool.query.mockResolvedValueOnce({ rows: [mockRecord] }); - - const { result: result1, duration: duration1 } = await measureExecutionTime(async () => { - return repository.findById('cached-record-id'); - }); - - expect(result1).toEqual(mockRecord); - expect(mockPool.query).toHaveBeenCalledWith( - expect.stringContaining('SELECT * FROM test_table WHERE id = $1'), - ['cached-record-id'] - ); - expect(mockCache.set).toHaveBeenCalledWith( - 'test_cache:cached-record-id', - mockRecord, - expect.any(Object) - ); - - // Second call - cache hit - mockCache.get.mockResolvedValueOnce(mockRecord); - - const { result: result2, duration: duration2 } = await measureExecutionTime(async () => { - return repository.findById('cached-record-id'); - }); - - expect(result2).toEqual(mockRecord); - // Cache hit should be much faster - expect(duration2).toBeLessThan(duration1); - expect(mockPool.query).toHaveBeenCalledTimes(1); // No additional DB call - }); - - it('should return null for non-existent records', async () => { - mockCache.get.mockResolvedValueOnce(null); - mockPool.query.mockResolvedValueOnce({ rows: [] }); - - const result = await repository.findById('non-existent-id'); - - expect(result).toBeNull(); - }); - - it('should exclude soft-deleted records by default', async () => { - const deletedRecord = { - id: 'deleted-id', - name: 'Deleted Record', - deleted_at: new Date() - }; - - mockCache.get.mockResolvedValueOnce(null); - mockPool.query.mockResolvedValueOnce({ rows: [deletedRecord] }); - - const result = await repository.findById('deleted-id'); - - expect(result).toBeNull(); - expect(mockPool.query).toHaveBeenCalledWith( - expect.stringContaining('AND deleted_at IS NULL'), - expect.any(Array) - ); - }); - - it('should support finding with filters and pagination', async () => { - const mockResults = { - rows: [ - { id: 'result-1', name: 'Result 1' }, - { id: 'result-2', name: 'Result 2' }, - ], - count: 10, - }; - - mockPool.query.mockResolvedValueOnce({ rows: [{ count: '10' }] }); // Count query - mockPool.query.mockResolvedValueOnce(mockResults); // Results query - - const filters = { status: 'active' }; - const options = { page: 1, limit: 10 }; - - const { result, duration } = await measureExecutionTime(async () => { - return repository.findMany(filters, options); - }); - - validateSuccessCriteria.realTimeUpdate(duration); - - expect(result).toEqual({ - data: mockResults.rows, - total: 10, - page: 1, - limit: 10, - totalPages: 1, - }); - }); - }); - - describe('Update Operations', () => { - it('should update record with optimistic locking', async () => { - const updateData = { name: 'Updated Name' }; - const mockResult = { - rows: [{ - id: 'update-test-id', - ...updateData, - version: 2, - updated_at: new Date() - }] - }; - - mockPool.query.mockResolvedValueOnce(mockResult); - - const { result, duration } = await measureExecutionTime(async () => { - return repository.update('update-test-id', updateData, 'updater-user-id', 1); - }); - - validateSuccessCriteria.realTimeUpdate(duration); - - expect(result).toEqual(mockResult.rows[0]); - expect(mockPool.query).toHaveBeenCalledWith( - expect.stringContaining('WHERE id = $1 AND version = $2'), - expect.arrayContaining(['update-test-id', 1]) - ); - expect(mockCache.delete).toHaveBeenCalledWith('test_cache:update-test-id'); - }); - - it('should handle version conflicts in optimistic locking', async () => { - mockPool.query.mockResolvedValueOnce({ rows: [] }); // No rows updated - - await expect( - repository.update('conflict-id', { name: 'Updated' }, 'user-id', 1) - ).rejects.toThrow('Version conflict or record not found'); - }); - - it('should validate update data', async () => { - const invalidData = { name: '' }; // Empty name should fail validation - - await expect( - repository.update('test-id', invalidData, 'user-id', 1) - ).rejects.toThrow(ValidationError); - - expect(mockPool.query).not.toHaveBeenCalled(); - }); - }); - - describe('Delete Operations', () => { - it('should perform soft delete by default', async () => { - const mockResult = { - rows: [{ - id: 'soft-delete-id', - name: 'Soft Deleted Record', - deleted_at: new Date(), - version: 2 - }] - }; - - mockPool.query.mockResolvedValueOnce(mockResult); - - const { result, duration } = await measureExecutionTime(async () => { - return repository.delete('soft-delete-id', 'deleter-user-id', 1); - }); - - validateSuccessCriteria.realTimeUpdate(duration); - - expect(result).toEqual(mockResult.rows[0]); - expect(mockPool.query).toHaveBeenCalledWith( - expect.stringContaining('SET deleted_at = NOW()'), - expect.any(Array) - ); - expect(mockCache.delete).toHaveBeenCalledWith('test_cache:soft-delete-id'); - }); - - it('should perform hard delete when specified', async () => { - mockPool.query.mockResolvedValueOnce({ rowCount: 1 }); - - await repository.delete('hard-delete-id', 'deleter-user-id', 1, true); - - expect(mockPool.query).toHaveBeenCalledWith( - expect.stringContaining('DELETE FROM test_table'), - expect.any(Array) - ); - }); - - it('should handle delete conflicts', async () => { - mockPool.query.mockResolvedValueOnce({ rows: [] }); - - await expect( - repository.delete('conflict-id', 'user-id', 1) - ).rejects.toThrow('Version conflict or record not found'); - }); - }); - - describe('Bulk Operations', () => { - it('should support bulk creation with transaction', async () => { - const bulkData = [ - { name: 'Bulk Record 1' }, - { name: 'Bulk Record 2' }, - { name: 'Bulk Record 3' }, - ]; - - const mockResults = { - rows: bulkData.map((data, index) => ({ - id: `bulk-${index}`, - ...data, - created_at: new Date(), - })) - }; - - const mockClient = { - query: jest.fn().mockResolvedValue(mockResults), - release: jest.fn(), - }; - mockPool.connect.mockResolvedValueOnce(mockClient); - - const { result, duration } = await measureExecutionTime(async () => { - return repository.createMany(bulkData, 'bulk-user-id'); - }); - - // Bulk operations should still be performant - expect(duration).toBeLessThan(1000); // Under 1 second for bulk ops - - expect(result).toHaveLength(3); - expect(mockClient.query).toHaveBeenCalledWith('BEGIN'); - expect(mockClient.query).toHaveBeenCalledWith('COMMIT'); - expect(mockClient.release).toHaveBeenCalled(); - }); - - it('should rollback bulk creation on error', async () => { - const bulkData = [ - { name: 'Valid Record' }, - { description: 'Invalid - missing name' }, // Should fail validation - ]; - - const mockClient = { - query: jest.fn().mockRejectedValue(new Error('Validation failed')), - release: jest.fn(), - }; - mockPool.connect.mockResolvedValueOnce(mockClient); - - await expect( - repository.createMany(bulkData, 'bulk-user-id') - ).rejects.toThrow(); - - expect(mockClient.query).toHaveBeenCalledWith('ROLLBACK'); - expect(mockClient.release).toHaveBeenCalled(); - }); - }); - - describe('Performance and Caching', () => { - it('should meet performance benchmarks for read operations', async () => { - const testRecord = { id: 'perf-test', name: 'Performance Test' }; - mockCache.get.mockResolvedValue(testRecord); - - const { duration } = await measureExecutionTime(async () => { - const promises = Array.from({ length: 100 }, () => - repository.findById('perf-test') - ); - await Promise.all(promises); - }); - - // 100 cached reads should complete in under 100ms - expect(duration).toBeLessThan(100); - }); - - it('should invalidate cache on mutations', async () => { - const mockResult = { rows: [{ id: 'cache-test', name: 'Updated' }] }; - mockPool.query.mockResolvedValueOnce(mockResult); - - await repository.update('cache-test', { name: 'Updated' }, 'user-id', 1); - - expect(mockCache.delete).toHaveBeenCalledWith('test_cache:cache-test'); - expect(mockCache.invalidateByTag).toHaveBeenCalledWith('test_cache'); - }); - - it('should handle cache failures gracefully', async () => { - const testRecord = { id: 'cache-fail-test', name: 'Cache Fail Test' }; - - // Cache throws error but DB works - mockCache.get.mockRejectedValueOnce(new Error('Cache unavailable')); - mockPool.query.mockResolvedValueOnce({ rows: [testRecord] }); - - const result = await repository.findById('cache-fail-test'); - - expect(result).toEqual(testRecord); - expect(mockPool.query).toHaveBeenCalled(); // Still queries database - }); - }); - - describe('Advanced Features', () => { - it('should support complex queries with joins', async () => { - const mockResults = { - rows: [ - { - id: 'join-test-1', - name: 'Record 1', - related_name: 'Related Record 1' - } - ] - }; - - mockPool.query.mockResolvedValueOnce(mockResults); - - const customQuery = ` - SELECT t.*, r.name as related_name - FROM test_table t - LEFT JOIN related_table r ON t.related_id = r.id - WHERE t.status = $1 - `; - - const result = await repository.customQuery(customQuery, ['active']); - - expect(result).toEqual(mockResults.rows); - expect(mockPool.query).toHaveBeenCalledWith(customQuery, ['active']); - }); - - it('should support search with full-text indexing', async () => { - const searchTerm = 'important document'; - const mockResults = { - rows: [ - { - id: 'search-1', - name: 'Important Document 1', - rank: 0.95 - } - ] - }; - - mockPool.query.mockResolvedValueOnce(mockResults); - - const result = await repository.search(searchTerm, { - fields: ['name', 'description'], - limit: 10 - }); - - expect(result).toEqual(mockResults.rows); - expect(mockPool.query).toHaveBeenCalledWith( - expect.stringContaining('ts_rank'), - expect.arrayContaining([searchTerm]) - ); - }); - - it('should support aggregation queries', async () => { - const mockAggregation = { - rows: [{ - total_count: 150, - active_count: 120, - avg_score: 4.2, - }] - }; - - mockPool.query.mockResolvedValueOnce(mockAggregation); - - const result = await repository.aggregate({ - count: '*', - countWhere: { status: 'active' }, - avg: 'score', - }); - - expect(result).toEqual(mockAggregation.rows[0]); - }); - }); - - describe('Error Handling and Edge Cases', () => { - it('should handle concurrent update conflicts', async () => { - // Simulate concurrent update scenario - mockPool.query.mockResolvedValueOnce({ rows: [] }); // Version conflict - - await expect( - repository.update('concurrent-test', { name: 'Updated' }, 'user-1', 1) - ).rejects.toThrow('Version conflict or record not found'); - }); - - it('should validate input sanitization', async () => { - const maliciousData = { - name: "'; DROP TABLE test_table; --", - description: '' - }; - - // Should not throw error (parameterized queries handle this) - const mockResult = { rows: [{ id: 'sanitized-test', ...maliciousData }] }; - mockPool.query.mockResolvedValueOnce(mockResult); - - const result = await repository.create(maliciousData, 'test-user'); - - expect(result).toBeDefined(); - // Verify parameterized query was used - expect(mockPool.query).toHaveBeenCalledWith( - expect.any(String), - expect.arrayContaining([maliciousData.name, maliciousData.description]) - ); - }); - - it('should handle large result sets efficiently', async () => { - const largeResultSet = { - rows: Array.from({ length: 1000 }, (_, i) => ({ - id: `large-result-${i}`, - name: `Record ${i}`, - data: 'Large data payload'.repeat(100), - })) - }; - - mockPool.query.mockResolvedValueOnce({ rows: [{ count: '1000' }] }); - mockPool.query.mockResolvedValueOnce(largeResultSet); - - const { result, duration } = await measureExecutionTime(async () => { - return repository.findMany({}, { page: 1, limit: 1000 }); - }); - - expect(result.data).toHaveLength(1000); - expect(duration).toBeLessThan(2000); // Should handle large sets under 2 seconds - }); - }); -}); \ No newline at end of file diff --git a/backend/tests/integration/VoiceCommandIntegration.test.ts b/backend/tests/integration/VoiceCommandIntegration.test.ts deleted file mode 100644 index d6dfa41..0000000 --- a/backend/tests/integration/VoiceCommandIntegration.test.ts +++ /dev/null @@ -1,740 +0,0 @@ -/** - * Voice Command Integration Tests - Phase 2 Quality Assurance - * Comprehensive integration tests for the complete voice command processing pipeline - * - * Test Coverage: - * - End-to-end voice command processing - * - Multi-step command execution - * - Real-time broadcasting - * - File upload workflows - * - Error handling and recovery - */ - -import { describe, beforeAll, afterAll, beforeEach, afterEach, test, expect, jest } from '@jest/globals'; -import supertest from 'supertest'; -import { Server } from 'http'; -import { io as Client } from 'socket.io-client'; -import fs from 'fs'; -import path from 'path'; - -// Import services to be tested -import { WhisperService } from '../../src/services/ai/WhisperService'; -import { OpenAIService } from '../../src/services/ai/OpenAIService'; -import { MultiActionExecutor } from '../../src/ai/execution/MultiActionExecutor'; -import { ExecutionEventManager } from '../../src/realtime/broadcasting/ExecutionEventManager'; -import { ProgressBroadcaster } from '../../src/realtime/broadcasting/ProgressBroadcaster'; -import { VoiceFileUploadService } from '../../src/files/upload/VoiceFileUploadService'; -import { SecurityManager } from '../../src/security/SecurityManager'; -import { PerformanceMonitor } from '../../src/performance/monitoring/PerformanceMonitor'; - -// Test utilities -interface TestContext { - server: Server; - request: supertest.SuperTest; - socketClient: any; - whisperService: WhisperService; - openAIService: OpenAIService; - multiActionExecutor: MultiActionExecutor; - executionEventManager: ExecutionEventManager; - progressBroadcaster: ProgressBroadcaster; - fileUploadService: VoiceFileUploadService; - securityManager: SecurityManager; - performanceMonitor: PerformanceMonitor; -} - -interface TestUser { - id: string; - organizationId: string; - token: string; - permissions: string[]; -} - -interface TestVoiceCommand { - audioBuffer: Buffer; - expectedTranscription: string; - expectedActions: string[]; - expectedEntities: string[]; -} - -describe('Voice Command Integration Tests', () => { - let context: TestContext; - let testUser: TestUser; - let testCommands: TestVoiceCommand[]; - - // Test constants - const TEST_TIMEOUT = 30000; - const WEBSOCKET_URL = 'http://localhost:3001'; - const API_BASE_URL = '/api/v1'; - - beforeAll(async () => { - jest.setTimeout(TEST_TIMEOUT); - - // Initialize test context - context = await initializeTestContext(); - - // Setup test user - testUser = await createTestUser(); - - // Prepare test voice commands - testCommands = await prepareTestVoiceCommands(); - - console.log('✓ Integration test environment initialized'); - }, TEST_TIMEOUT); - - afterAll(async () => { - await cleanupTestContext(context); - console.log('✓ Integration test environment cleaned up'); - }); - - beforeEach(async () => { - // Reset metrics and clear any pending operations - await context.performanceMonitor.recordCustomMetric('test_start', Date.now()); - }); - - afterEach(async () => { - // Cleanup any test data - await cleanupTestData(); - }); - - describe('Complete Voice Command Pipeline', () => { - test('should process simple task creation command end-to-end', async () => { - const startTime = Date.now(); - const command = testCommands.find(cmd => cmd.expectedActions.includes('create_task')); - expect(command).toBeDefined(); - - // Step 1: Audio transcription - const transcriptionResult = await context.whisperService.transcribeAudio( - command!.audioBuffer, - { - userId: testUser.id, - organizationId: testUser.organizationId, - language: 'en' - } - ); - - expect(transcriptionResult).toBeDefined(); - expect(transcriptionResult.transcription).toContain('create task'); - expect(transcriptionResult.confidence).toBeGreaterThan(0.8); - - // Step 2: AI command processing - const aiResult = await context.openAIService.processVoiceCommand( - transcriptionResult.transcription, - { - userId: testUser.id, - organizationId: testUser.organizationId, - conversationHistory: [] - } - ); - - expect(aiResult).toBeDefined(); - expect(aiResult.actions).toContainEqual( - expect.objectContaining({ - type: 'create_task', - parameters: expect.objectContaining({ - title: expect.any(String), - description: expect.any(String) - }) - }) - ); - - // Step 3: Security authorization - const securityCheck = await context.securityManager.authorizeVoiceCommand( - testUser.id, - testUser.organizationId, - 'create_task', - testUser.permissions, - aiResult.actions[0].parameters - ); - - expect(securityCheck.approved).toBe(true); - - // Step 4: Multi-action execution with progress tracking - const executionResult = await context.multiActionExecutor.executeActions( - aiResult.actions, - { - userId: testUser.id, - organizationId: testUser.organizationId, - sessionId: 'test-session', - commandId: securityCheck.commandId - } - ); - - expect(executionResult).toBeDefined(); - expect(executionResult.success).toBe(true); - expect(executionResult.results).toHaveLength(aiResult.actions.length); - - // Verify performance metrics - const processingTime = Date.now() - startTime; - expect(processingTime).toBeLessThan(5000); // Under 5 seconds - - console.log(`✓ Task creation command processed in ${processingTime}ms`); - }); - - test('should handle multi-step file upload with entity linking', async () => { - const startTime = Date.now(); - const command = testCommands.find(cmd => cmd.expectedActions.includes('upload_file')); - expect(command).toBeDefined(); - - // Create mock file data - const mockFile = { - fileName: 'test-document.pdf', - fileSize: 1024 * 1024, // 1MB - contentType: 'application/pdf', - buffer: Buffer.from('mock pdf content'), - entityLinks: [ - { type: 'task', id: 'task-123' }, - { type: 'channel', id: 'channel-456' } - ] - }; - - // Step 1: Voice command transcription - const transcriptionResult = await context.whisperService.transcribeAudio( - command!.audioBuffer, - { - userId: testUser.id, - organizationId: testUser.organizationId, - language: 'en' - } - ); - - expect(transcriptionResult.transcription).toContain('upload'); - - // Step 2: AI processing with file context - const aiResult = await context.openAIService.processVoiceCommand( - transcriptionResult.transcription, - { - userId: testUser.id, - organizationId: testUser.organizationId, - conversationHistory: [], - contextData: { - availableFiles: [mockFile.fileName], - currentChannel: 'channel-456', - activeTasks: ['task-123'] - } - } - ); - - expect(aiResult.actions).toContainEqual( - expect.objectContaining({ - type: 'upload_file', - parameters: expect.objectContaining({ - fileName: expect.stringContaining('test-document'), - linkedEntities: expect.arrayContaining([ - expect.objectContaining({ type: 'task' }) - ]) - }) - }) - ); - - // Step 3: File upload workflow - const uploadResult = await context.fileUploadService.initiateVoiceUpload({ - fileName: mockFile.fileName, - fileSize: mockFile.fileSize, - contentType: mockFile.contentType, - userId: testUser.id, - organizationId: testUser.organizationId, - linkedEntities: mockFile.entityLinks, - description: 'Voice uploaded test document' - }); - - expect(uploadResult).toBeDefined(); - expect(uploadResult.uploadUrl).toBeDefined(); - expect(uploadResult.fileId).toBeDefined(); - expect(uploadResult.linkedEntities).toHaveLength(2); - - // Step 4: Verify real-time progress broadcasting - const progressEvents: any[] = []; - - const socketPromise = new Promise((resolve) => { - context.socketClient.on('progress_update', (data: any) => { - progressEvents.push(data); - if (data.status === 'completed') { - resolve(data); - } - }); - }); - - // Simulate file upload completion - setTimeout(async () => { - await context.progressBroadcaster.updateStepProgress( - uploadResult.sessionId || 'test-session', - 'file_upload', - 100, - 'completed', - { fileId: uploadResult.fileId } - ); - }, 100); - - const finalProgress = await socketPromise; - expect(finalProgress).toBeDefined(); - expect(progressEvents.length).toBeGreaterThan(0); - - const processingTime = Date.now() - startTime; - expect(processingTime).toBeLessThan(10000); // Under 10 seconds - - console.log(`✓ File upload workflow completed in ${processingTime}ms`); - }); - - test('should handle error scenarios gracefully', async () => { - const command = testCommands.find(cmd => cmd.expectedActions.includes('delete_task')); - expect(command).toBeDefined(); - - // Step 1: Process command that will fail due to permissions - const transcriptionResult = await context.whisperService.transcribeAudio( - command!.audioBuffer, - { - userId: testUser.id, - organizationId: testUser.organizationId, - language: 'en' - } - ); - - const aiResult = await context.openAIService.processVoiceCommand( - transcriptionResult.transcription, - { - userId: testUser.id, - organizationId: testUser.organizationId, - conversationHistory: [] - } - ); - - // Step 2: Security check should fail (user doesn't have delete permissions) - const securityCheck = await context.securityManager.authorizeVoiceCommand( - testUser.id, - testUser.organizationId, - 'delete_task', - ['tasks:read', 'tasks:write'], // Missing 'tasks:delete' - aiResult.actions[0].parameters - ); - - expect(securityCheck.approved).toBe(false); - expect(securityCheck.reason).toBe('Insufficient permissions'); - - // Step 3: Verify error is properly logged and broadcasted - const errorEvents: any[] = []; - - context.socketClient.on('command_error', (data: any) => { - errorEvents.push(data); - }); - - await context.executionEventManager.broadcastCommandError( - securityCheck.commandId, - testUser.id, - testUser.organizationId, - 'Authorization failed: ' + securityCheck.reason, - [testUser.id] - ); - - // Wait for error broadcast - await new Promise(resolve => setTimeout(resolve, 100)); - - expect(errorEvents.length).toBeGreaterThan(0); - expect(errorEvents[0]).toMatchObject({ - commandId: securityCheck.commandId, - error: expect.stringContaining('Authorization failed') - }); - - console.log('✓ Error handling workflow completed successfully'); - }); - }); - - describe('Real-time Broadcasting System', () => { - test('should broadcast command execution events to affected users', async () => { - const commandId = 'test-cmd-' + Date.now(); - const affectedUsers = [testUser.id, 'user-2', 'user-3']; - - const broadcastEvents: any[] = []; - - context.socketClient.on('command_start', (data: any) => { - broadcastEvents.push({ type: 'start', data }); - }); - - context.socketClient.on('command_complete', (data: any) => { - broadcastEvents.push({ type: 'complete', data }); - }); - - // Start command execution - const startResult = await context.executionEventManager.broadcastCommandStart( - commandId, - testUser.id, - testUser.organizationId, - affectedUsers - ); - - expect(startResult.successful).toBeGreaterThan(0); - - // Wait for broadcast - await new Promise(resolve => setTimeout(resolve, 100)); - - // Complete command execution - await context.executionEventManager.broadcastCommandComplete( - commandId, - testUser.id, - testUser.organizationId, - { success: true, resultCount: 1 }, - affectedUsers - ); - - // Wait for broadcast - await new Promise(resolve => setTimeout(resolve, 100)); - - expect(broadcastEvents).toHaveLength(2); - expect(broadcastEvents[0].type).toBe('start'); - expect(broadcastEvents[1].type).toBe('complete'); - - console.log('✓ Real-time broadcasting system working correctly'); - }); - - test('should track and broadcast step-by-step progress', async () => { - const steps = [ - { id: 'step1', name: 'Parse command', estimatedDuration: 500 }, - { id: 'step2', name: 'Validate data', estimatedDuration: 300 }, - { id: 'step3', name: 'Execute action', estimatedDuration: 1000 }, - { id: 'step4', name: 'Send notifications', estimatedDuration: 200 } - ]; - - const progressUpdates: any[] = []; - - context.socketClient.on('progress_update', (data: any) => { - progressUpdates.push(data); - }); - - // Initialize progress tracking - const session = context.progressBroadcaster.initializeProgressTracking( - 'test-command-' + Date.now(), - testUser.id, - testUser.organizationId, - steps, - [testUser.id] - ); - - // Simulate step progression - for (let i = 0; i < steps.length; i++) { - const step = steps[i]; - - // Start step - await context.progressBroadcaster.updateStepProgress( - session.sessionId, - step.id, - 0, - 'running' - ); - - // Progress through step - for (let progress = 25; progress <= 100; progress += 25) { - await context.progressBroadcaster.updateStepProgress( - session.sessionId, - step.id, - progress, - progress === 100 ? 'completed' : 'running' - ); - - await new Promise(resolve => setTimeout(resolve, 50)); - } - } - - // Wait for final broadcasts - await new Promise(resolve => setTimeout(resolve, 200)); - - expect(progressUpdates.length).toBeGreaterThan(steps.length); - - const finalUpdate = progressUpdates[progressUpdates.length - 1]; - expect(finalUpdate.totalProgress).toBe(100); - expect(finalUpdate.status).toBe('completed'); - - console.log(`✓ Step-by-step progress tracking completed (${progressUpdates.length} updates)`); - }); - }); - - describe('Performance and Security Validation', () => { - test('should maintain sub-100ms response times for cached operations', async () => { - const iterations = 50; - const responseTimes: number[] = []; - - for (let i = 0; i < iterations; i++) { - const startTime = performance.now(); - - // Simulate cached operation (context retrieval) - await context.openAIService.getConversationContext(testUser.id); - - const endTime = performance.now(); - responseTimes.push(endTime - startTime); - } - - const averageTime = responseTimes.reduce((sum, time) => sum + time, 0) / responseTimes.length; - const p95Time = responseTimes.sort((a, b) => a - b)[Math.floor(responseTimes.length * 0.95)]; - - expect(averageTime).toBeLessThan(100); - expect(p95Time).toBeLessThan(150); - - console.log(`✓ Cache performance: avg=${averageTime.toFixed(2)}ms, p95=${p95Time.toFixed(2)}ms`); - }); - - test('should enforce rate limiting for voice commands', async () => { - const rateLimitResults: any[] = []; - - // Attempt to exceed rate limit - for (let i = 0; i < 25; i++) { // Assuming limit is 20 per minute - const result = await context.securityManager.checkRateLimit(testUser.id, 'voice_command'); - rateLimitResults.push(result); - } - - const allowedRequests = rateLimitResults.filter(r => r.allowed).length; - const blockedRequests = rateLimitResults.filter(r => !r.allowed).length; - - expect(allowedRequests).toBeLessThan(25); - expect(blockedRequests).toBeGreaterThan(0); - - // Verify rate limit information - const lastBlocked = rateLimitResults.find(r => !r.allowed); - expect(lastBlocked).toBeDefined(); - expect(lastBlocked.retryAfter).toBeGreaterThan(0); - - console.log(`✓ Rate limiting: ${allowedRequests} allowed, ${blockedRequests} blocked`); - }); - - test('should validate and sanitize input data', async () => { - const testInputs = [ - { - input: { title: 'Task Title', description: 'Normal description' }, - schema: { - title: { required: true, type: 'string', maxLength: 100 }, - description: { required: true, type: 'string', maxLength: 500 } - } - }, - { - input: { title: '', description: 'Description without title' }, - schema: { - title: { required: true, type: 'string', minLength: 1 }, - description: { required: true, type: 'string' } - } - } - ]; - - for (const testCase of testInputs) { - const result = context.securityManager.validateAndSanitizeInput( - testCase.input, - testCase.schema - ); - - if (testCase.input.title === '') { - expect(result.valid).toBe(false); - expect(result.errors).toContain('title is required'); - } else { - expect(result.valid).toBe(true); - expect(result.sanitized?.title).not.toContain(' with SQL injection\'; DROP TABLE tasks; --'; - - mockOpenAI.chat.completions.create.mockResolvedValue({ - choices: [{ - message: { - content: JSON.stringify({ - intent: 'create_task', - confidence: 0.9, - actions: [{ - type: 'create_task', - parameters: { title: 'Create task with SQL injection' } - }] - }) - } - }] - }); - - const result = await openAIService.processVoiceCommand(maliciousInput, { - userId: 'user-123', - organizationId: 'org-456', - conversationHistory: [] - }); - - // Check that the API call sanitized the input - const apiCall = mockOpenAI.chat.completions.create.mock.calls[0][0]; - const userMessage = apiCall.messages.find((m: any) => m.role === 'user'); - - expect(userMessage.content).not.toContain('