peerpigeon
Version:
WebRTC-based peer-to-peer mesh networking library with intelligent routing and signaling server
147 lines (129 loc) • 5.13 kB
JavaScript
import { EventEmitter } from './EventEmitter.js';
import { environmentDetector } from './EnvironmentDetector.js';
import DebugLogger from './DebugLogger.js';
/**
* Manages storage operations, URL handling, and configuration persistence
*/
export class StorageManager extends EventEmitter {
constructor(mesh) {
super();
this.debug = DebugLogger.create('StorageManager');
this.mesh = mesh;
}
loadSignalingUrlFromStorage() {
// Use environment-aware storage detection
if (environmentDetector.hasLocalStorage) {
const savedUrl = localStorage.getItem('pigon-signaling-url');
if (savedUrl) {
this.mesh.signalingUrl = savedUrl;
this.mesh.emit('statusChanged', { type: 'urlLoaded', signalingUrl: savedUrl });
return savedUrl;
}
} else if (environmentDetector.isNodeJS) {
// In Node.js, we could potentially use file-based storage
this.debug.log('Local storage not available in Node.js environment');
}
return null;
}
saveSignalingUrlToStorage(url) {
if (environmentDetector.hasLocalStorage && url) {
localStorage.setItem('pigon-signaling-url', url);
} else if (environmentDetector.isNodeJS) {
// In Node.js, we could potentially save to a config file
this.debug.log('Storage not implemented for Node.js environment');
}
}
loadSignalingUrlFromQuery() {
// Only works in browser and NativeScript environments with location/URL support
if (!environmentDetector.isBrowser && !environmentDetector.isNativeScript) return this.loadSignalingUrlFromStorage();
// Check if we have URL search capabilities
if (typeof URLSearchParams === 'undefined' ||
(environmentDetector.isBrowser && typeof window === 'undefined') ||
(environmentDetector.isBrowser && typeof window.location === 'undefined')) {
return this.loadSignalingUrlFromStorage();
}
let searchParams;
if (environmentDetector.isBrowser) {
searchParams = new URLSearchParams(window.location.search);
} else if (environmentDetector.isNativeScript) {
// NativeScript might not have window.location, fallback to storage
return this.loadSignalingUrlFromStorage();
}
const signalingUrl = searchParams?.get('api') || searchParams?.get('url') || searchParams?.get('signaling');
if (signalingUrl) {
// Only emit event if URL is different from current one
const currentUrl = this.mesh.signalingUrl;
this.mesh.signalingUrl = signalingUrl;
this.saveSignalingUrlToStorage(signalingUrl);
if (currentUrl !== signalingUrl) {
this.mesh.emit('statusChanged', { type: 'urlLoaded', signalingUrl });
}
return signalingUrl;
}
// Fallback to localStorage if no URL in query params
return this.loadSignalingUrlFromStorage();
}
async generatePeerId() {
const array = new Uint8Array(20);
// Environment-aware random value generation
if (environmentDetector.hasRandomValues) {
if (environmentDetector.isBrowser || environmentDetector.isWorker || environmentDetector.isNativeScript) {
crypto.getRandomValues(array);
} else if (environmentDetector.isNodeJS) {
// In Node.js, use crypto module (handle both CommonJS and ES modules)
try {
if (typeof require !== 'undefined') {
const crypto = require('crypto');
const randomBytes = crypto.randomBytes(20);
array.set(randomBytes);
} else {
// ES module approach - import crypto dynamically
const crypto = await import('crypto');
const randomBytes = crypto.randomBytes(20);
array.set(randomBytes);
}
} catch (e) {
this.debug.warn('Could not use Node.js crypto, falling back to Math.random');
// Fallback to Math.random
for (let i = 0; i < array.length; i++) {
array[i] = Math.floor(Math.random() * 256);
}
}
}
} else {
// Fallback to less secure random generation
this.debug.warn('Secure random values not available, using fallback method');
for (let i = 0; i < array.length; i++) {
array[i] = Math.floor(Math.random() * 256);
}
}
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
}
validatePeerId(peerId) {
return typeof peerId === 'string' && /^[a-fA-F0-9]{40}$/.test(peerId);
}
saveSettings(settings) {
if (environmentDetector.hasLocalStorage) {
localStorage.setItem('pigon-settings', JSON.stringify(settings));
}
}
loadSettings() {
if (environmentDetector.hasLocalStorage) {
const saved = localStorage.getItem('pigon-settings');
if (saved) {
try {
return JSON.parse(saved);
} catch (error) {
this.debug.error('Failed to parse saved settings:', error);
}
}
}
return {};
}
clearStorage() {
if (typeof localStorage !== 'undefined') {
localStorage.removeItem('pigon-signaling-url');
localStorage.removeItem('pigon-settings');
}
}
}