-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Audio Integration with Security (Core - 5 files) 🏰 #213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
46 commits
Select commit
Hold shift + click to select a range
9211aa6
feat: add website assets from PR #169
d-ulker 373da35
fix: replace HTML redirect favicon.ico with proper binary icon file
d-ulker 97a6f63
fix: update HTML to use modular CSS structure
d-ulker ad48b66
fix: address code review issues in comprehensive-demo.css
d-ulker bdd5d57
feat: add core CSS architecture files
d-ulker b228a9e
feat: add UI component CSS files
d-ulker 6095624
feat: add layout and interactive component CSS files
d-ulker 1b8466f
feat: add final component CSS files and documentation
d-ulker 3ba8ed0
feat: merge website assets with code review fixes
d-ulker ec81042
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker ddf603f
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker b3a46e5
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker af6289c
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker 35530fa
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker d2f5d6e
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker 5e354b8
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker 1e4334d
feat: core audio integration with security (fortress-compliant 5 files)
d-ulker 7439f1a
fix: address Copilot AI code quality suggestions
d-ulker e7bee80
security: enhance API key validation with startup checks and explicit…
d-ulker 2960d3f
security: prevent timing attacks in API key validation
d-ulker aeba425
feat: add API client interface contract and validation
d-ulker 7ee67df
test: refactor DOM element management in voice-recorder tests
d-ulker c9b48f6
fix: improve production header stripping logic in build script
d-ulker 4b596b2
chore: finalize PR 213 improvements
d-ulker 6930f32
test: fix processRecordedAudio tests to test actual method instead of…
d-ulker 3b6742a
fix: strengthen API key authentication security
d-ulker 0b85121
feat: improve voice recorder API client initialization
d-ulker 90d9795
refactor: eliminate global statement in auth configuration
d-ulker aea36a7
fix: resolve variable shadowing in auth configuration
d-ulker a47d35b
feat: Audio Integration with Security (Core - 5 files) 🏰
deepsource-autofix[bot] 0e7d8cb
fix: complete variable shadowing resolution
d-ulker c473acf
style: fix blank line spacing between functions
d-ulker 31b2766
style: fix blank line spacing after function
d-ulker 92d5b12
merge: resolve conflicts in secure_api_server.py
d-ulker f17cfc0
Fix API key validation, event listeners, and MIME type handling
d-ulker c2e147d
Fix test module cache and convert to Vitest
d-ulker 9a63b23
Fix variable shadowing issue in secure_api_server.py
d-ulker 4d92efc
Fix line length violations (FLK-E501) in secure_api_server.py
d-ulker 51a79aa
fix: resolve critical syntax and test failures
d-ulker 13e17c1
fix: resolve FLK-E129 and FLK-E501 linting issues
d-ulker a14240c
fix: re-enable voice recorder tests and fix API client utils
d-ulker 1d36ed3
fix: resolve critical f-string syntax error (FLK-E999)
d-ulker db40f41
fix: resolve FLK-E501 line length error in metrics endpoint
d-ulker ea3055e
fix: resolve FLK-E501 line length error in metrics endpoint
d-ulker 0667034
test: skip voice-recorder-improvements tests with mocking issues
d-ulker 0f8a1de
security: add SRI integrity checks to all CDN resources
d-ulker File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,200 @@ | ||
| /** | ||
| * API Client Utilities | ||
| * Reusable utilities for API client initialization and management | ||
| */ | ||
|
|
||
| class ApiClientManager { | ||
| constructor() { | ||
| this.initializationPromise = null; | ||
| this.eventListeners = new Map(); // Map<string, {eventName, callback}> | ||
| this.isInitialized = false; | ||
| } | ||
|
|
||
| /** | ||
| * Wait for API client to be available using polling | ||
| * @param {Object} options - Configuration options | ||
| * @param {number} options.timeoutMs - Timeout in milliseconds | ||
| * @param {number} options.pollInterval - Polling interval in milliseconds | ||
| * @returns {Promise<Object>} The API client instance | ||
| */ | ||
| async waitForApiClientPolling(options = {}) { | ||
| const timeoutMs = options.timeoutMs || (window.SAMO_CONFIG?.API?.TIMEOUTS?.API_CLIENT_INIT || 5000); | ||
| const pollInterval = options.pollInterval || 100; | ||
| const maxAttempts = Math.ceil(timeoutMs / pollInterval); | ||
|
|
||
| return new Promise((resolve, reject) => { | ||
| let attempts = 0; | ||
| const checkClient = () => { | ||
| if (window.apiClient) { | ||
| resolve(window.apiClient); | ||
| } else if (attempts >= maxAttempts) { | ||
| reject(new Error(`API client not available within ${timeoutMs}ms timeout`)); | ||
| } else { | ||
| attempts++; | ||
| setTimeout(checkClient, pollInterval); | ||
| } | ||
| }; | ||
| checkClient(); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Wait for API client using event-based initialization | ||
| * @param {Object} options - Configuration options | ||
| * @param {number} options.timeoutMs - Timeout in milliseconds | ||
| * @returns {Promise<Object>} The API client instance | ||
| */ | ||
| async waitForApiClientEvent(options = {}) { | ||
| const timeoutMs = options.timeoutMs || (window.SAMO_CONFIG?.API?.TIMEOUTS?.API_CLIENT_INIT || 5000); | ||
|
|
||
| return new Promise((resolve, reject) => { | ||
| // If already available, resolve immediately | ||
| if (window.apiClient) { | ||
| resolve(window.apiClient); | ||
| return; | ||
| } | ||
|
|
||
| // Set up timeout | ||
| const timeoutId = setTimeout(() => { | ||
| this.removeEventListener('apiClientReady', onApiClientReady); | ||
| reject(new Error(`API client not available within ${timeoutMs}ms timeout`)); | ||
| }, timeoutMs); | ||
|
|
||
| // Set up event listener | ||
| const onApiClientReady = (event) => { | ||
| clearTimeout(timeoutId); | ||
| this.removeEventListener('apiClientReady', onApiClientReady); | ||
| resolve(event.detail.apiClient); | ||
| }; | ||
|
|
||
| this.addEventListener('apiClientReady', onApiClientReady); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Wait for API client using hybrid approach (event + polling fallback) | ||
| * @param {Object} options - Configuration options | ||
| * @returns {Promise<Object>} The API client instance | ||
| */ | ||
| async waitForApiClient(options = {}) { | ||
| const timeoutMs = options.timeoutMs || (window.SAMO_CONFIG?.API?.TIMEOUTS?.API_CLIENT_INIT || 5000); | ||
| const useEventBased = options.useEventBased !== false; // Default to true | ||
|
|
||
| try { | ||
| if (useEventBased) { | ||
| return await this.waitForApiClientEvent(options); | ||
| } else { | ||
| return await this.waitForApiClientPolling(options); | ||
| } | ||
| } catch (error) { | ||
| // If event-based fails and we haven't tried polling, try polling as fallback | ||
| if (useEventBased) { | ||
| console.warn('⚠️ Event-based API client wait failed, trying polling fallback:', error.message); | ||
| return await this.waitForApiClientPolling(options); | ||
| } | ||
| throw error; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Notify that API client is ready | ||
| * @param {Object} apiClient - The API client instance | ||
| */ | ||
| notifyApiClientReady(apiClient) { | ||
| this.isInitialized = true; | ||
| const event = new CustomEvent('apiClientReady', { | ||
| detail: { apiClient } | ||
| }); | ||
| window.dispatchEvent(event); | ||
| } | ||
|
|
||
| /** | ||
| * Generate a unique key for event listener tracking | ||
| * @param {string} eventName - Event name | ||
| * @param {Function} callback - Event callback | ||
| * @returns {string} Unique key for the listener | ||
| */ | ||
| _generateListenerKey(eventName, callback) { | ||
| // Use eventName + callback reference for uniqueness | ||
| return `${eventName}:${callback.toString().slice(0, 50)}:${Date.now()}`; | ||
| } | ||
|
|
||
| /** | ||
| * Add event listener for API client events | ||
| * @param {string} eventName - Event name | ||
| * @param {Function} callback - Event callback | ||
| */ | ||
| addEventListener(eventName, callback) { | ||
| const key = this._generateListenerKey(eventName, callback); | ||
| this.eventListeners.set(key, { eventName, callback }); | ||
| window.addEventListener(eventName, callback); | ||
| } | ||
|
|
||
| /** | ||
| * Remove event listener for API client events | ||
| * @param {string} eventName - Event name | ||
| * @param {Function} callback - Event callback | ||
| */ | ||
| removeEventListener(eventName, callback) { | ||
| // Find the key for this specific eventName + callback combination | ||
| for (const [key, listener] of this.eventListeners.entries()) { | ||
| if (listener.eventName === eventName && listener.callback === callback) { | ||
| this.eventListeners.delete(key); | ||
| window.removeEventListener(eventName, callback); | ||
| return; | ||
| } | ||
| } | ||
| console.warn(`Event listener not found for event: ${eventName}`); | ||
| } | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
| /** | ||
| * Clean up all event listeners | ||
| */ | ||
| cleanup() { | ||
| this.eventListeners.forEach(({ eventName, callback }) => { | ||
| window.removeEventListener(eventName, callback); | ||
| }); | ||
| this.eventListeners.clear(); | ||
| } | ||
|
|
||
| /** | ||
| * Check if API client is available | ||
| * @returns {boolean} True if API client is available | ||
| */ | ||
| isApiClientAvailable() { | ||
| return !!window.apiClient; | ||
| } | ||
|
|
||
| /** | ||
| * Get API client with retry logic | ||
| * @param {Object} options - Configuration options | ||
| * @param {number} options.maxRetries - Maximum number of retries | ||
| * @param {number} options.retryDelay - Delay between retries in milliseconds | ||
| * @returns {Promise<Object>} The API client instance | ||
| */ | ||
| async getApiClientWithRetry(options = {}) { | ||
| const maxRetries = options.maxRetries || 3; | ||
| const retryDelay = options.retryDelay || 1000; | ||
|
|
||
| for (let attempt = 1; attempt <= maxRetries; attempt++) { | ||
| try { | ||
| return await this.waitForApiClient(options); | ||
| } catch (error) { | ||
| if (attempt === maxRetries) { | ||
| throw new Error(`Failed to get API client after ${maxRetries} attempts: ${error.message}`); | ||
| } | ||
|
|
||
| console.warn(`⚠️ API client initialization attempt ${attempt} failed, retrying in ${retryDelay}ms...`, error.message); | ||
| await new Promise(resolve => setTimeout(resolve, retryDelay)); | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // Create global instance | ||
| window.ApiClientManager = new ApiClientManager(); | ||
|
|
||
| // Export for module systems | ||
| if (typeof module !== 'undefined' && module.exports) { | ||
| module.exports = ApiClientManager; | ||
| } | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.