Files
giglez/static/js/error_reporter.js
T
leetcrypt 9f73595b20 feat: RTL_433 protocol database import - iteration 1/5
- 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.
2026-02-14 18:55:55 -08:00

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;
}