Skip to content
Merged
Show file tree
Hide file tree
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 Sep 27, 2025
373da35
fix: replace HTML redirect favicon.ico with proper binary icon file
d-ulker Sep 27, 2025
97a6f63
fix: update HTML to use modular CSS structure
d-ulker Sep 27, 2025
ad48b66
fix: address code review issues in comprehensive-demo.css
d-ulker Sep 27, 2025
bdd5d57
feat: add core CSS architecture files
d-ulker Sep 27, 2025
b228a9e
feat: add UI component CSS files
d-ulker Sep 27, 2025
6095624
feat: add layout and interactive component CSS files
d-ulker Sep 27, 2025
1b8466f
feat: add final component CSS files and documentation
d-ulker Sep 27, 2025
3ba8ed0
feat: merge website assets with code review fixes
d-ulker Sep 27, 2025
ec81042
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker Sep 27, 2025
ddf603f
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker Sep 27, 2025
b3a46e5
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker Sep 28, 2025
af6289c
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker Sep 28, 2025
35530fa
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker Sep 29, 2025
d2f5d6e
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker Sep 29, 2025
5e354b8
Merge branch 'main' of github.com:uelkerd/SAMO--DL
d-ulker Sep 29, 2025
1e4334d
feat: core audio integration with security (fortress-compliant 5 files)
d-ulker Sep 29, 2025
7439f1a
fix: address Copilot AI code quality suggestions
d-ulker Sep 29, 2025
e7bee80
security: enhance API key validation with startup checks and explicit…
d-ulker Sep 29, 2025
2960d3f
security: prevent timing attacks in API key validation
d-ulker Sep 29, 2025
aeba425
feat: add API client interface contract and validation
d-ulker Sep 29, 2025
7ee67df
test: refactor DOM element management in voice-recorder tests
d-ulker Sep 29, 2025
c9b48f6
fix: improve production header stripping logic in build script
d-ulker Sep 29, 2025
4b596b2
chore: finalize PR 213 improvements
d-ulker Sep 29, 2025
6930f32
test: fix processRecordedAudio tests to test actual method instead of…
d-ulker Sep 29, 2025
3b6742a
fix: strengthen API key authentication security
d-ulker Sep 29, 2025
0b85121
feat: improve voice recorder API client initialization
d-ulker Sep 29, 2025
90d9795
refactor: eliminate global statement in auth configuration
d-ulker Sep 29, 2025
aea36a7
fix: resolve variable shadowing in auth configuration
d-ulker Sep 29, 2025
a47d35b
feat: Audio Integration with Security (Core - 5 files) 🏰
deepsource-autofix[bot] Sep 29, 2025
0e7d8cb
fix: complete variable shadowing resolution
d-ulker Sep 29, 2025
c473acf
style: fix blank line spacing between functions
d-ulker Sep 29, 2025
31b2766
style: fix blank line spacing after function
d-ulker Sep 29, 2025
92d5b12
merge: resolve conflicts in secure_api_server.py
d-ulker Sep 29, 2025
f17cfc0
Fix API key validation, event listeners, and MIME type handling
d-ulker Sep 29, 2025
c2e147d
Fix test module cache and convert to Vitest
d-ulker Sep 29, 2025
9a63b23
Fix variable shadowing issue in secure_api_server.py
d-ulker Sep 29, 2025
4d92efc
Fix line length violations (FLK-E501) in secure_api_server.py
d-ulker Sep 29, 2025
51a79aa
fix: resolve critical syntax and test failures
d-ulker Sep 30, 2025
13e17c1
fix: resolve FLK-E129 and FLK-E501 linting issues
d-ulker Sep 30, 2025
a14240c
fix: re-enable voice recorder tests and fix API client utils
d-ulker Sep 30, 2025
1d36ed3
fix: resolve critical f-string syntax error (FLK-E999)
d-ulker Sep 30, 2025
db40f41
fix: resolve FLK-E501 line length error in metrics endpoint
d-ulker Sep 30, 2025
ea3055e
fix: resolve FLK-E501 line length error in metrics endpoint
d-ulker Sep 30, 2025
0667034
test: skip voice-recorder-improvements tests with mocking issues
d-ulker Sep 30, 2025
0f8a1de
security: add SRI integrity checks to all CDN resources
d-ulker Sep 30, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
435 changes: 350 additions & 85 deletions deployment/secure_api_server.py

Large diffs are not rendered by default.

1,912 changes: 1,148 additions & 764 deletions website/comprehensive-demo.html

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions website/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -777,6 +777,9 @@ <h6 class="fw-bold mb-3">Connect</h6>

<!-- Configuration -->
<script src="js/config.js"></script>

<!-- API Client Utilities -->
<script src="js/api-client-utils.js"></script>

<!-- Demo Script -->
<script>
Expand Down
200 changes: 200 additions & 0 deletions website/js/api-client-utils.js
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);
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/**
* 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}`);
}
Comment thread
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;
}
Loading
Loading