9f73595b20
- Expanded protocol database from 18 → 299 signatures (16.6x increase) - Imported 281 protocols from RTL_433 open-source database (286 total devices) - Created automated import script: scripts/import_rtl433_protocols.py - Generated rtl433_protocols_imported.py with timing/frequency/modulation data - Updated protocol_database.py to include RTL433_PROTOCOLS - All 26 tests passing Breakdown by category: - Weather: 116 protocols - Sensors: 36 protocols - TPMS: 25 protocols - Security: 23 protocols - Home Automation: 18 protocols - Other: 50+ protocols Frequency coverage: - 433.92 MHz: 248 protocols - 315.00 MHz: 32 protocols - 915.00 MHz: 1 protocol This provides comprehensive coverage of Sub-GHz IoT devices for accurate identification from raw RF captures.
214 lines
6.0 KiB
JavaScript
214 lines
6.0 KiB
JavaScript
/**
|
|
* Client-Side Error Reporter
|
|
*
|
|
* Automatically captures and reports JavaScript errors during file uploads
|
|
* to help diagnose issues during beta testing.
|
|
*/
|
|
|
|
class ErrorReporter {
|
|
constructor() {
|
|
this.uploadId = null;
|
|
this.errorQueue = [];
|
|
this.reportEndpoint = '/api/v1/captures/report_client_error';
|
|
|
|
// Setup global error handler
|
|
this.setupGlobalErrorHandler();
|
|
}
|
|
|
|
/**
|
|
* Set current upload ID for error tracking
|
|
*/
|
|
setUploadId(uploadId) {
|
|
this.uploadId = uploadId;
|
|
console.log(`[ErrorReporter] Tracking upload: ${uploadId}`);
|
|
}
|
|
|
|
/**
|
|
* Setup global error handlers
|
|
*/
|
|
setupGlobalErrorHandler() {
|
|
// Catch unhandled JavaScript errors
|
|
window.addEventListener('error', (event) => {
|
|
this.reportError({
|
|
error_type: event.error?.name || 'Error',
|
|
error_message: event.message,
|
|
stack_trace: event.error?.stack,
|
|
filename: event.filename,
|
|
lineno: event.lineno,
|
|
colno: event.colno,
|
|
source: 'window.error'
|
|
});
|
|
});
|
|
|
|
// Catch unhandled promise rejections
|
|
window.addEventListener('unhandledrejection', (event) => {
|
|
this.reportError({
|
|
error_type: 'UnhandledPromiseRejection',
|
|
error_message: event.reason?.message || String(event.reason),
|
|
stack_trace: event.reason?.stack,
|
|
source: 'unhandledrejection'
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Report error to server
|
|
*/
|
|
async reportError(errorData) {
|
|
const errorReport = {
|
|
upload_id: this.uploadId || 'no_upload_id',
|
|
timestamp: new Date().toISOString(),
|
|
...errorData,
|
|
browser_info: this.getBrowserInfo(),
|
|
page_url: window.location.href
|
|
};
|
|
|
|
// Log to console
|
|
console.error('[ErrorReporter] Reporting error:', errorReport);
|
|
|
|
// Queue error (in case of network issues)
|
|
this.errorQueue.push(errorReport);
|
|
|
|
// Try to send immediately
|
|
try {
|
|
await this.sendErrorReport(errorReport);
|
|
// Remove from queue if successful
|
|
this.errorQueue = this.errorQueue.filter(e => e !== errorReport);
|
|
} catch (e) {
|
|
console.warn('[ErrorReporter] Failed to send error report:', e);
|
|
// Will retry later
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send error report to server
|
|
*/
|
|
async sendErrorReport(errorReport) {
|
|
const response = await fetch(this.reportEndpoint, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
body: JSON.stringify(errorReport)
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error(`HTTP ${response.status}`);
|
|
}
|
|
|
|
return response.json();
|
|
}
|
|
|
|
/**
|
|
* Report parse error specifically
|
|
*/
|
|
reportParseError(filename, error, fileContentSample = '') {
|
|
this.reportError({
|
|
error_type: 'SubFileParseError',
|
|
error_message: error.message || String(error),
|
|
stack_trace: error.stack,
|
|
filename: filename,
|
|
file_content_sample: fileContentSample.substring(0, 500),
|
|
source: 'sub_parser'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Report upload error
|
|
*/
|
|
reportUploadError(filename, error, stage = 'unknown') {
|
|
this.reportError({
|
|
error_type: 'UploadError',
|
|
error_message: error.message || String(error),
|
|
stack_trace: error.stack,
|
|
filename: filename,
|
|
upload_stage: stage,
|
|
source: 'upload_handler'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Report validation error
|
|
*/
|
|
reportValidationError(field, value, reason) {
|
|
this.reportError({
|
|
error_type: 'ValidationError',
|
|
error_message: `${field} validation failed: ${reason}`,
|
|
validation_field: field,
|
|
validation_value: String(value),
|
|
source: 'validation'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Report network error
|
|
*/
|
|
reportNetworkError(url, status, statusText) {
|
|
this.reportError({
|
|
error_type: 'NetworkError',
|
|
error_message: `${status} ${statusText}`,
|
|
request_url: url,
|
|
http_status: status,
|
|
source: 'network'
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get browser information
|
|
*/
|
|
getBrowserInfo() {
|
|
return {
|
|
user_agent: navigator.userAgent,
|
|
platform: navigator.platform,
|
|
language: navigator.language,
|
|
screen_width: screen.width,
|
|
screen_height: screen.height,
|
|
viewport_width: window.innerWidth,
|
|
viewport_height: window.innerHeight,
|
|
connection_type: navigator.connection?.effectiveType,
|
|
device_memory: navigator.deviceMemory,
|
|
hardware_concurrency: navigator.hardwareConcurrency,
|
|
online: navigator.onLine
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Flush error queue (send all pending errors)
|
|
*/
|
|
async flushErrorQueue() {
|
|
const pending = [...this.errorQueue];
|
|
this.errorQueue = [];
|
|
|
|
for (const error of pending) {
|
|
try {
|
|
await this.sendErrorReport(error);
|
|
} catch (e) {
|
|
// If still fails, put back in queue
|
|
this.errorQueue.push(error);
|
|
}
|
|
}
|
|
|
|
return {
|
|
sent: pending.length - this.errorQueue.length,
|
|
failed: this.errorQueue.length
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Generate upload ID
|
|
*/
|
|
generateUploadId() {
|
|
const timestamp = new Date().toISOString().replace(/[-:]/g, '').replace('T', '_').substring(0, 15);
|
|
const random = Math.random().toString(36).substring(2, 10);
|
|
return `upload_${timestamp}_${random}`;
|
|
}
|
|
}
|
|
|
|
// Create global instance
|
|
window.errorReporter = new ErrorReporter();
|
|
|
|
// Export for module use
|
|
if (typeof module !== 'undefined' && module.exports) {
|
|
module.exports = ErrorReporter;
|
|
}
|