refactor: implement minimal PWA caching strategy
- Cache only essential PWA assets (manifest, icons, core scripts) - Use Network First for all other requests - Remove aggressive caching of UI components and styles - Preserve PWA installation while minimizing cache footprint
This commit is contained in:
@@ -157,6 +157,7 @@
|
|||||||
<script src="src/scripts/pwa-register.js"></script>
|
<script src="src/scripts/pwa-register.js"></script>
|
||||||
<script src="./src/pwa/install-prompt.js" type="module"></script>
|
<script src="./src/pwa/install-prompt.js" type="module"></script>
|
||||||
<script src="./src/pwa/pwa-manager.js" type="module"></script>
|
<script src="./src/pwa/pwa-manager.js" type="module"></script>
|
||||||
|
<script src="./src/scripts/pwa-offline-test.js"></script>
|
||||||
<link rel="stylesheet" href="./src/styles/pwa.css">
|
<link rel="stylesheet" href="./src/styles/pwa.css">
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
@@ -1421,7 +1421,6 @@
|
|||||||
// PAKE password states removed - using SAS verification instead
|
// PAKE password states removed - using SAS verification instead
|
||||||
|
|
||||||
// Session state - all security features enabled by default
|
// Session state - all security features enabled by default
|
||||||
const [sessionTimeLeft, setSessionTimeLeft] = React.useState(0);
|
|
||||||
const [pendingSession, setPendingSession] = React.useState(null);
|
const [pendingSession, setPendingSession] = React.useState(null);
|
||||||
|
|
||||||
// All security features are enabled by default - no payment required
|
// All security features are enabled by default - no payment required
|
||||||
@@ -1730,8 +1729,6 @@
|
|||||||
setVerificationCode('');
|
setVerificationCode('');
|
||||||
setSecurityLevel(null);
|
setSecurityLevel(null);
|
||||||
|
|
||||||
// Reset session and timer
|
|
||||||
setSessionTimeLeft(0);
|
|
||||||
|
|
||||||
// Return to main page after a short delay
|
// Return to main page after a short delay
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -3471,7 +3468,6 @@
|
|||||||
isConnected: isConnectedAndVerified,
|
isConnected: isConnectedAndVerified,
|
||||||
securityLevel: securityLevel,
|
securityLevel: securityLevel,
|
||||||
// sessionManager removed - all features enabled by default
|
// sessionManager removed - all features enabled by default
|
||||||
sessionTimeLeft: sessionTimeLeft,
|
|
||||||
webrtcManager: webrtcManagerRef.current
|
webrtcManager: webrtcManagerRef.current
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
|||||||
@@ -5,13 +5,8 @@ const EnhancedMinimalHeader = ({
|
|||||||
onDisconnect,
|
onDisconnect,
|
||||||
isConnected,
|
isConnected,
|
||||||
securityLevel,
|
securityLevel,
|
||||||
sessionManager,
|
|
||||||
sessionTimeLeft,
|
|
||||||
webrtcManager
|
webrtcManager
|
||||||
}) => {
|
}) => {
|
||||||
const [currentTimeLeft, setCurrentTimeLeft] = React.useState(sessionTimeLeft || 0);
|
|
||||||
const [hasActiveSession, setHasActiveSession] = React.useState(false);
|
|
||||||
const [sessionType, setSessionType] = React.useState('unknown');
|
|
||||||
const [realSecurityLevel, setRealSecurityLevel] = React.useState(null);
|
const [realSecurityLevel, setRealSecurityLevel] = React.useState(null);
|
||||||
const [lastSecurityUpdate, setLastSecurityUpdate] = React.useState(0);
|
const [lastSecurityUpdate, setLastSecurityUpdate] = React.useState(0);
|
||||||
|
|
||||||
@@ -268,8 +263,7 @@ const EnhancedMinimalHeader = ({
|
|||||||
details: 'Security verification not available',
|
details: 'Security verification not available',
|
||||||
isRealData: false,
|
isRealData: false,
|
||||||
passedChecks: 0,
|
passedChecks: 0,
|
||||||
totalChecks: 0,
|
totalChecks: 0
|
||||||
sessionType: 'unknown'
|
|
||||||
};
|
};
|
||||||
console.log('Using fallback security data:', securityData);
|
console.log('Using fallback security data:', securityData);
|
||||||
}
|
}
|
||||||
@@ -277,7 +271,6 @@ const EnhancedMinimalHeader = ({
|
|||||||
// Detailed information about the REAL security check
|
// Detailed information about the REAL security check
|
||||||
let message = `REAL-TIME SECURITY VERIFICATION\n\n`;
|
let message = `REAL-TIME SECURITY VERIFICATION\n\n`;
|
||||||
message += `Security Level: ${securityData.level} (${securityData.score}%)\n`;
|
message += `Security Level: ${securityData.level} (${securityData.score}%)\n`;
|
||||||
message += `Session Type: ${securityData.sessionType || 'premium'}\n`;
|
|
||||||
message += `Verification Time: ${new Date(securityData.timestamp).toLocaleTimeString()}\n`;
|
message += `Verification Time: ${new Date(securityData.timestamp).toLocaleTimeString()}\n`;
|
||||||
message += `Data Source: ${securityData.isRealData ? 'Real Cryptographic Tests' : 'Simulated Data'}\n\n`;
|
message += `Data Source: ${securityData.isRealData ? 'Real Cryptographic Tests' : 'Simulated Data'}\n\n`;
|
||||||
|
|
||||||
@@ -465,7 +458,6 @@ const EnhancedMinimalHeader = ({
|
|||||||
const config = getStatusConfig();
|
const config = getStatusConfig();
|
||||||
const displaySecurityLevel = isConnected ? (realSecurityLevel || securityLevel) : null;
|
const displaySecurityLevel = isConnected ? (realSecurityLevel || securityLevel) : null;
|
||||||
|
|
||||||
const shouldShowTimer = hasActiveSession && currentTimeLeft > 0 && window.SessionTimer;
|
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// DATA RELIABILITY INDICATOR
|
// DATA RELIABILITY INDICATOR
|
||||||
@@ -570,13 +562,6 @@ const EnhancedMinimalHeader = ({
|
|||||||
key: 'status-section',
|
key: 'status-section',
|
||||||
className: 'flex items-center space-x-2 sm:space-x-3'
|
className: 'flex items-center space-x-2 sm:space-x-3'
|
||||||
}, [
|
}, [
|
||||||
// Session Timer - all features enabled by default
|
|
||||||
shouldShowTimer && React.createElement(window.SessionTimer, {
|
|
||||||
key: 'session-timer',
|
|
||||||
timeLeft: currentTimeLeft,
|
|
||||||
sessionType: sessionType,
|
|
||||||
onDisconnect: onDisconnect
|
|
||||||
}),
|
|
||||||
|
|
||||||
displaySecurityLevel && React.createElement('div', {
|
displaySecurityLevel && React.createElement('div', {
|
||||||
key: 'security-level',
|
key: 'security-level',
|
||||||
|
|||||||
@@ -1,334 +0,0 @@
|
|||||||
// SessionTimer Component - v4.3.120 - ECDH + DTLS + SAS
|
|
||||||
const SessionTimer = ({ timeLeft, sessionType, sessionManager, onDisconnect }) => {
|
|
||||||
const [currentTime, setCurrentTime] = React.useState(timeLeft || 0);
|
|
||||||
const [showExpiredMessage, setShowExpiredMessage] = React.useState(false);
|
|
||||||
const [initialized, setInitialized] = React.useState(false);
|
|
||||||
const [connectionBroken, setConnectionBroken] = React.useState(false);
|
|
||||||
|
|
||||||
|
|
||||||
const [loggedHidden, setLoggedHidden] = React.useState(false);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (connectionBroken) {
|
|
||||||
if (!loggedHidden) {
|
|
||||||
console.log('⏱️ SessionTimer initialization skipped - connection broken');
|
|
||||||
setLoggedHidden(true);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let initialTime = 0;
|
|
||||||
|
|
||||||
if (sessionManager?.hasActiveSession()) {
|
|
||||||
initialTime = sessionManager.getTimeLeft();
|
|
||||||
} else if (timeLeft && timeLeft > 0) {
|
|
||||||
initialTime = timeLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (initialTime <= 0) {
|
|
||||||
setCurrentTime(0);
|
|
||||||
setInitialized(false);
|
|
||||||
setLoggedHidden(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionBroken) {
|
|
||||||
setCurrentTime(0);
|
|
||||||
setInitialized(false);
|
|
||||||
setLoggedHidden(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setCurrentTime(initialTime);
|
|
||||||
setInitialized(true);
|
|
||||||
setLoggedHidden(false);
|
|
||||||
}, [sessionManager, connectionBroken]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (connectionBroken) {
|
|
||||||
if (!loggedHidden) {
|
|
||||||
setLoggedHidden(true);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timeLeft && timeLeft > 0) {
|
|
||||||
setCurrentTime(timeLeft);
|
|
||||||
}
|
|
||||||
setLoggedHidden(false);
|
|
||||||
}, [timeLeft, connectionBroken]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
if (!initialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionBroken) {
|
|
||||||
if (!loggedHidden) {
|
|
||||||
setLoggedHidden(true);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentTime || currentTime <= 0 || !sessionManager) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
if (connectionBroken) {
|
|
||||||
setCurrentTime(0);
|
|
||||||
clearInterval(interval);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionManager?.hasActiveSession()) {
|
|
||||||
const newTime = sessionManager.getTimeLeft();
|
|
||||||
setCurrentTime(newTime);
|
|
||||||
|
|
||||||
if (window.DEBUG_MODE && Math.floor(Date.now() / 30000) !== Math.floor((Date.now() - 1000) / 30000)) {
|
|
||||||
console.log('⏱️ Timer tick:', Math.floor(newTime / 1000) + 's');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newTime <= 0) {
|
|
||||||
setShowExpiredMessage(true);
|
|
||||||
setTimeout(() => setShowExpiredMessage(false), 5000);
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
setCurrentTime(0);
|
|
||||||
clearInterval(interval);
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
clearInterval(interval);
|
|
||||||
};
|
|
||||||
}, [initialized, currentTime, sessionManager, connectionBroken]);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
const handleSessionTimerUpdate = (event) => {
|
|
||||||
if (connectionBroken) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (event.detail.timeLeft && event.detail.timeLeft > 0) {
|
|
||||||
setCurrentTime(event.detail.timeLeft);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleForceHeaderUpdate = (event) => {
|
|
||||||
if (connectionBroken) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sessionManager && sessionManager.hasActiveSession()) {
|
|
||||||
const newTime = sessionManager.getTimeLeft();
|
|
||||||
setCurrentTime(newTime);
|
|
||||||
} else {
|
|
||||||
setCurrentTime(event.detail.timeLeft);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handlePeerDisconnect = (event) => {
|
|
||||||
setConnectionBroken(true);
|
|
||||||
setCurrentTime(0);
|
|
||||||
setShowExpiredMessage(false);
|
|
||||||
setLoggedHidden(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleNewConnection = (event) => {
|
|
||||||
setConnectionBroken(false);
|
|
||||||
setLoggedHidden(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleConnectionCleaned = (event) => {
|
|
||||||
setConnectionBroken(true);
|
|
||||||
setCurrentTime(0);
|
|
||||||
setShowExpiredMessage(false);
|
|
||||||
setInitialized(false);
|
|
||||||
setLoggedHidden(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSessionReset = (event) => {
|
|
||||||
setConnectionBroken(true);
|
|
||||||
setCurrentTime(0);
|
|
||||||
setShowExpiredMessage(false);
|
|
||||||
setInitialized(false);
|
|
||||||
setLoggedHidden(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleSessionCleanup = (event) => {
|
|
||||||
setConnectionBroken(true);
|
|
||||||
setCurrentTime(0);
|
|
||||||
setShowExpiredMessage(false);
|
|
||||||
setInitialized(false);
|
|
||||||
setLoggedHidden(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleDisconnected = (event) => {
|
|
||||||
setConnectionBroken(true);
|
|
||||||
setCurrentTime(0);
|
|
||||||
setShowExpiredMessage(false);
|
|
||||||
setInitialized(false);
|
|
||||||
setLoggedHidden(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
document.addEventListener('session-timer-update', handleSessionTimerUpdate);
|
|
||||||
document.addEventListener('force-header-update', handleForceHeaderUpdate);
|
|
||||||
document.addEventListener('peer-disconnect', handlePeerDisconnect);
|
|
||||||
document.addEventListener('new-connection', handleNewConnection);
|
|
||||||
document.addEventListener('connection-cleaned', handleConnectionCleaned);
|
|
||||||
document.addEventListener('session-reset', handleSessionReset);
|
|
||||||
document.addEventListener('session-cleanup', handleSessionCleanup);
|
|
||||||
document.addEventListener('disconnected', handleDisconnected);
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
document.removeEventListener('session-timer-update', handleSessionTimerUpdate);
|
|
||||||
document.removeEventListener('force-header-update', handleForceHeaderUpdate);
|
|
||||||
document.removeEventListener('peer-disconnect', handlePeerDisconnect);
|
|
||||||
document.removeEventListener('new-connection', handleNewConnection);
|
|
||||||
document.removeEventListener('connection-cleaned', handleConnectionCleaned);
|
|
||||||
document.removeEventListener('session-reset', handleSessionReset);
|
|
||||||
document.removeEventListener('session-cleanup', handleSessionCleanup);
|
|
||||||
document.removeEventListener('disconnected', handleDisconnected);
|
|
||||||
};
|
|
||||||
}, [sessionManager]);
|
|
||||||
|
|
||||||
if (showExpiredMessage) {
|
|
||||||
return React.createElement('div', {
|
|
||||||
className: 'session-timer expired flex items-center space-x-2 px-3 py-1.5 rounded-lg animate-pulse',
|
|
||||||
style: { background: 'linear-gradient(135deg, rgba(239, 68, 68, 0.2) 0%, rgba(220, 38, 38, 0.2) 100%)' }
|
|
||||||
}, [
|
|
||||||
React.createElement('i', {
|
|
||||||
key: 'icon',
|
|
||||||
className: 'fas fa-exclamation-triangle text-red-400'
|
|
||||||
}),
|
|
||||||
React.createElement('span', {
|
|
||||||
key: 'message',
|
|
||||||
className: 'text-red-400 text-sm font-medium'
|
|
||||||
}, 'Session Expired!')
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sessionManager) {
|
|
||||||
if (!loggedHidden) {
|
|
||||||
console.log('⏱️ SessionTimer hidden - no sessionManager');
|
|
||||||
setLoggedHidden(true);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connectionBroken) {
|
|
||||||
if (!loggedHidden) {
|
|
||||||
console.log('⏱️ SessionTimer hidden - connection broken');
|
|
||||||
setLoggedHidden(true);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!currentTime || currentTime <= 0) {
|
|
||||||
if (!loggedHidden) {
|
|
||||||
console.log('⏱️ SessionTimer hidden - no time left, currentTime:', currentTime);
|
|
||||||
setLoggedHidden(true);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loggedHidden) {
|
|
||||||
setLoggedHidden(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
const totalMinutes = Math.floor(currentTime / (60 * 1000));
|
|
||||||
const totalSeconds = Math.floor(currentTime / 1000);
|
|
||||||
|
|
||||||
const isDemo = sessionType === 'demo';
|
|
||||||
const isWarning = isDemo ? totalMinutes <= 2 : totalMinutes <= 10;
|
|
||||||
const isCritical = isDemo ? totalSeconds <= 60 : totalMinutes <= 5;
|
|
||||||
|
|
||||||
const formatTime = (ms) => {
|
|
||||||
const hours = Math.floor(ms / (60 * 60 * 1000));
|
|
||||||
const minutes = Math.floor((ms % (60 * 60 * 1000)) / (60 * 1000));
|
|
||||||
const seconds = Math.floor((ms % (60 * 1000)) / 1000);
|
|
||||||
|
|
||||||
if (hours > 0) {
|
|
||||||
return `${hours}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
|
||||||
} else {
|
|
||||||
return `${minutes}:${seconds.toString().padStart(2, '0')}`;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getTimerStyle = () => {
|
|
||||||
const totalDuration = sessionType === 'demo' ? 6 * 60 * 1000 : 60 * 60 * 1000;
|
|
||||||
const timeProgress = (totalDuration - currentTime) / totalDuration;
|
|
||||||
|
|
||||||
let backgroundColor, textColor, iconColor, iconClass, shouldPulse;
|
|
||||||
|
|
||||||
if (timeProgress <= 0.33) {
|
|
||||||
backgroundColor = 'linear-gradient(135deg, rgba(34, 197, 94, 0.15) 0%, rgba(22, 163, 74, 0.15) 100%)';
|
|
||||||
textColor = 'text-green-400';
|
|
||||||
iconColor = 'text-green-400';
|
|
||||||
iconClass = 'fas fa-clock';
|
|
||||||
shouldPulse = false;
|
|
||||||
} else if (timeProgress <= 0.66) {
|
|
||||||
backgroundColor = 'linear-gradient(135deg, rgba(234, 179, 8, 0.15) 0%, rgba(202, 138, 4, 0.15) 100%)';
|
|
||||||
textColor = 'text-yellow-400';
|
|
||||||
iconColor = 'text-yellow-400';
|
|
||||||
iconClass = 'fas fa-clock';
|
|
||||||
shouldPulse = false;
|
|
||||||
} else {
|
|
||||||
backgroundColor = 'linear-gradient(135deg, rgba(239, 68, 68, 0.15) 0%, rgba(220, 38, 38, 0.15) 100%)';
|
|
||||||
textColor = 'text-red-400';
|
|
||||||
iconColor = 'text-red-400';
|
|
||||||
iconClass = 'fas fa-exclamation-triangle';
|
|
||||||
shouldPulse = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return { backgroundColor, textColor, iconColor, iconClass, shouldPulse };
|
|
||||||
};
|
|
||||||
|
|
||||||
const timerStyle = getTimerStyle();
|
|
||||||
|
|
||||||
const handleTimerClick = () => {
|
|
||||||
if (onDisconnect && typeof onDisconnect === 'function') {
|
|
||||||
onDisconnect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return React.createElement('div', {
|
|
||||||
className: `session-timer flex items-center space-x-2 px-3 py-1.5 rounded-lg transition-all duration-500 cursor-pointer hover:opacity-80 ${
|
|
||||||
isDemo ? 'demo-session' : ''
|
|
||||||
} ${timerStyle.shouldPulse ? 'animate-pulse' : ''}`,
|
|
||||||
style: { background: timerStyle.backgroundColor },
|
|
||||||
onClick: handleTimerClick,
|
|
||||||
title: 'Click to disconnect and clear session'
|
|
||||||
}, [
|
|
||||||
React.createElement('i', {
|
|
||||||
key: 'icon',
|
|
||||||
className: `${timerStyle.iconClass} ${timerStyle.iconColor}`
|
|
||||||
}),
|
|
||||||
React.createElement('span', {
|
|
||||||
key: 'time',
|
|
||||||
className: `text-sm font-mono font-semibold ${timerStyle.textColor}`
|
|
||||||
}, formatTime(currentTime)),
|
|
||||||
React.createElement('div', {
|
|
||||||
key: 'progress',
|
|
||||||
className: 'ml-2 w-16 h-1 bg-gray-700 rounded-full overflow-hidden'
|
|
||||||
}, [
|
|
||||||
React.createElement('div', {
|
|
||||||
key: 'progress-bar',
|
|
||||||
className: `${timerStyle.textColor.replace('text-', 'bg-')} h-full rounded-full transition-all duration-500`,
|
|
||||||
style: {
|
|
||||||
width: `${Math.max(0, Math.min(100, (currentTime / (sessionType === 'demo' ? 6 * 60 * 1000 : 60 * 60 * 1000)) * 100))}%`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
])
|
|
||||||
]);
|
|
||||||
};
|
|
||||||
|
|
||||||
window.SessionTimer = SessionTimer;
|
|
||||||
|
|
||||||
window.updateSessionTimer = (newTimeLeft, newSessionType) => {
|
|
||||||
document.dispatchEvent(new CustomEvent('session-timer-update', {
|
|
||||||
detail: { timeLeft: newTimeLeft, sessionType: newSessionType }
|
|
||||||
}));
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -167,8 +167,6 @@ class EnhancedSecureWebRTCManager {
|
|||||||
} : null;
|
} : null;
|
||||||
};
|
};
|
||||||
this._secureLog('info', '🔒 Enhanced WebRTC Manager initialized with secure API');
|
this._secureLog('info', '🔒 Enhanced WebRTC Manager initialized with secure API');
|
||||||
this.currentSessionType = null;
|
|
||||||
this.currentSecurityLevel = 'basic';
|
|
||||||
this.sessionConstraints = null;
|
this.sessionConstraints = null;
|
||||||
this.peerConnection = null;
|
this.peerConnection = null;
|
||||||
this.dataChannel = null;
|
this.dataChannel = null;
|
||||||
@@ -2311,7 +2309,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
|
|||||||
|
|
||||||
// Create simple getSecurityStatus method
|
// Create simple getSecurityStatus method
|
||||||
secureAPI.getSecurityStatus = () => ({
|
secureAPI.getSecurityStatus = () => ({
|
||||||
securityLevel: this.currentSecurityLevel || 'basic',
|
securityLevel: 'maximum',
|
||||||
stage: 'initialized',
|
stage: 'initialized',
|
||||||
activeFeaturesCount: Object.values(this.securityFeatures || {}).filter(Boolean).length
|
activeFeaturesCount: Object.values(this.securityFeatures || {}).filter(Boolean).length
|
||||||
});
|
});
|
||||||
@@ -4484,11 +4482,8 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Security configuration - all features enabled by default
|
// Security configuration - all features enabled by default
|
||||||
configureSecurityForSession(sessionType, securityLevel) {
|
configureSecurityForSession() {
|
||||||
this._secureLog('info', `🔧 Configuring security for ${sessionType} session (${securityLevel} level)`);
|
this._secureLog('info', '🔧 Configuring security - all features enabled by default');
|
||||||
|
|
||||||
this.currentSessionType = sessionType;
|
|
||||||
this.currentSecurityLevel = securityLevel;
|
|
||||||
|
|
||||||
// All security features are enabled by default - no payment required
|
// All security features are enabled by default - no payment required
|
||||||
this.sessionConstraints = {};
|
this.sessionConstraints = {};
|
||||||
@@ -4499,7 +4494,7 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
|
|||||||
|
|
||||||
this.applySessionConstraints();
|
this.applySessionConstraints();
|
||||||
|
|
||||||
this._secureLog('info', `✅ Security configured for ${sessionType} - all features enabled`, { constraints: this.sessionConstraints });
|
this._secureLog('info', '✅ Security configured - all features enabled', { constraints: this.sessionConstraints });
|
||||||
|
|
||||||
if (!this._validateCryptographicSecurity()) {
|
if (!this._validateCryptographicSecurity()) {
|
||||||
this._secureLog('error', '🚨 CRITICAL: Cryptographic security validation failed after session configuration');
|
this._secureLog('error', '🚨 CRITICAL: Cryptographic security validation failed after session configuration');
|
||||||
@@ -4507,7 +4502,6 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
|
|||||||
if (this.onStatusChange) {
|
if (this.onStatusChange) {
|
||||||
this.onStatusChange('security_breach', {
|
this.onStatusChange('security_breach', {
|
||||||
type: 'crypto_security_failure',
|
type: 'crypto_security_failure',
|
||||||
sessionType: sessionType,
|
|
||||||
message: 'Cryptographic security validation failed after session configuration'
|
message: 'Cryptographic security validation failed after session configuration'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -4644,27 +4638,21 @@ this._secureLog('info', '🔒 Enhanced Mutex system fully initialized and valida
|
|||||||
|
|
||||||
// Security Level Notification
|
// Security Level Notification
|
||||||
notifySecurityLevel() {
|
notifySecurityLevel() {
|
||||||
// Avoid duplicate notifications for the same security level
|
// Avoid duplicate notifications
|
||||||
if (this.lastSecurityLevelNotification === this.currentSecurityLevel) {
|
if (this.lastSecurityLevelNotification === 'maximum') {
|
||||||
return; // prevent duplication
|
return; // prevent duplication
|
||||||
}
|
}
|
||||||
|
|
||||||
this.lastSecurityLevelNotification = this.currentSecurityLevel;
|
this.lastSecurityLevelNotification = 'maximum';
|
||||||
|
|
||||||
const levelMessages = {
|
const message = '🛡️ Maximum Security Active - All features enabled';
|
||||||
'basic': '🔒 Basic Security Active - Demo session with essential protection',
|
|
||||||
'enhanced': '🔐 Enhanced Security Active - Paid session with advanced protection',
|
|
||||||
'maximum': '🛡️ Maximum Security Active - Premium session with complete protection'
|
|
||||||
};
|
|
||||||
|
|
||||||
const message = levelMessages[this.currentSecurityLevel] || levelMessages['basic'];
|
|
||||||
|
|
||||||
if (this.onMessage) {
|
if (this.onMessage) {
|
||||||
this.deliverMessageToUI(message, 'system');
|
this.deliverMessageToUI(message, 'system');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Showing details of functions for paid sessions
|
// Showing details of active features
|
||||||
if (this.currentSecurityLevel !== 'basic' && this.onMessage) {
|
if (this.onMessage) {
|
||||||
const activeFeatures = Object.entries(this.securityFeatures)
|
const activeFeatures = Object.entries(this.securityFeatures)
|
||||||
.filter(([key, value]) => value === true)
|
.filter(([key, value]) => value === true)
|
||||||
.map(([key]) => key.replace('has', '').replace(/([A-Z])/g, ' $1').trim().toLowerCase())
|
.map(([key]) => key.replace('has', '').replace(/([A-Z])/g, ' $1').trim().toLowerCase())
|
||||||
@@ -6520,11 +6508,10 @@ async processMessage(data) {
|
|||||||
if (this.sessionConstraints?.hasAntiFingerprinting) {
|
if (this.sessionConstraints?.hasAntiFingerprinting) {
|
||||||
this.securityFeatures.hasAntiFingerprinting = true;
|
this.securityFeatures.hasAntiFingerprinting = true;
|
||||||
this.antiFingerprintingConfig.enabled = true;
|
this.antiFingerprintingConfig.enabled = true;
|
||||||
if (this.currentSecurityLevel === 'enhanced') {
|
// Enable full anti-fingerprinting features
|
||||||
this.antiFingerprintingConfig.randomizeSizes = false;
|
this.antiFingerprintingConfig.randomizeSizes = true;
|
||||||
this.antiFingerprintingConfig.maskPatterns = false;
|
this.antiFingerprintingConfig.maskPatterns = true;
|
||||||
this.antiFingerprintingConfig.useRandomHeaders = false;
|
this.antiFingerprintingConfig.useRandomHeaders = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.notifySecurityUpgrade(2);
|
this.notifySecurityUpgrade(2);
|
||||||
@@ -6535,10 +6522,7 @@ async processMessage(data) {
|
|||||||
|
|
||||||
// Method to enable Stage 3 features (traffic obfuscation)
|
// Method to enable Stage 3 features (traffic obfuscation)
|
||||||
enableStage3Security() {
|
enableStage3Security() {
|
||||||
if (this.currentSecurityLevel !== 'maximum') {
|
this._secureLog('info', '🔒 Enabling Stage 3 features (traffic obfuscation)');
|
||||||
this._secureLog('info', '🔒 Stage 3 features only available for premium sessions');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.sessionConstraints?.hasMessageChunking) {
|
if (this.sessionConstraints?.hasMessageChunking) {
|
||||||
this.securityFeatures.hasMessageChunking = true;
|
this.securityFeatures.hasMessageChunking = true;
|
||||||
@@ -6559,10 +6543,7 @@ async processMessage(data) {
|
|||||||
|
|
||||||
// Method for enabling Stage 4 functions (maximum safety)
|
// Method for enabling Stage 4 functions (maximum safety)
|
||||||
enableStage4Security() {
|
enableStage4Security() {
|
||||||
if (this.currentSecurityLevel !== 'maximum') {
|
this._secureLog('info', '🔒 Enabling Stage 4 features (maximum safety)');
|
||||||
this._secureLog('info', '🔒 Stage 4 features only available for premium sessions');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.sessionConstraints?.hasDecoyChannels && this.isConnected() && this.isVerified) {
|
if (this.sessionConstraints?.hasDecoyChannels && this.isConnected() && this.isVerified) {
|
||||||
this.securityFeatures.hasDecoyChannels = true;
|
this.securityFeatures.hasDecoyChannels = true;
|
||||||
@@ -6603,14 +6584,11 @@ async processMessage(data) {
|
|||||||
.filter(([key, value]) => value === true)
|
.filter(([key, value]) => value === true)
|
||||||
.map(([key]) => key);
|
.map(([key]) => key);
|
||||||
|
|
||||||
const stage = this.currentSecurityLevel === 'basic' ? 1 :
|
const stage = 4; // Maximum security stage
|
||||||
this.currentSecurityLevel === 'enhanced' ? 2 :
|
|
||||||
this.currentSecurityLevel === 'maximum' ? 4 : 1;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
stage: stage,
|
stage: stage,
|
||||||
sessionType: this.currentSessionType,
|
securityLevel: 'maximum',
|
||||||
securityLevel: this.currentSecurityLevel,
|
|
||||||
activeFeatures: activeFeatures,
|
activeFeatures: activeFeatures,
|
||||||
totalFeatures: Object.keys(this.securityFeatures).length,
|
totalFeatures: Object.keys(this.securityFeatures).length,
|
||||||
activeFeaturesCount: activeFeatures.length,
|
activeFeaturesCount: activeFeatures.length,
|
||||||
@@ -6730,52 +6708,42 @@ async processMessage(data) {
|
|||||||
|
|
||||||
// Method for automatic feature enablement with stability check
|
// Method for automatic feature enablement with stability check
|
||||||
async autoEnableSecurityFeatures() {
|
async autoEnableSecurityFeatures() {
|
||||||
if (this.currentSessionType === 'demo') {
|
this._secureLog('info', 'Starting graduated security activation - all features enabled');
|
||||||
this._secureLog('info', 'Demo session - keeping basic security only');
|
|
||||||
|
const checkStability = () => {
|
||||||
|
const isStable = this.isConnected() &&
|
||||||
|
this.isVerified &&
|
||||||
|
this.connectionAttempts === 0 &&
|
||||||
|
this.messageQueue.length === 0 &&
|
||||||
|
this.peerConnection?.connectionState === 'connected';
|
||||||
|
return isStable;
|
||||||
|
};
|
||||||
|
|
||||||
await this.calculateAndReportSecurityLevel();
|
await this.calculateAndReportSecurityLevel();
|
||||||
this.notifySecurityUpgrade(1);
|
this.notifySecurityUpgrade(1);
|
||||||
return;
|
|
||||||
}
|
// Enable all security stages progressively
|
||||||
|
|
||||||
const checkStability = () => {
|
|
||||||
const isStable = this.isConnected() &&
|
|
||||||
this.isVerified &&
|
|
||||||
this.connectionAttempts === 0 &&
|
|
||||||
this.messageQueue.length === 0 &&
|
|
||||||
this.peerConnection?.connectionState === 'connected';
|
|
||||||
return isStable;
|
|
||||||
};
|
|
||||||
|
|
||||||
this._secureLog('info', ` ${this.currentSessionType} session - starting graduated security activation`);
|
|
||||||
await this.calculateAndReportSecurityLevel();
|
|
||||||
this.notifySecurityUpgrade(1);
|
|
||||||
|
|
||||||
if (this.currentSecurityLevel === 'enhanced' || this.currentSecurityLevel === 'maximum') {
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
if (checkStability()) {
|
if (checkStability()) {
|
||||||
this.enableStage2Security();
|
this.enableStage2Security();
|
||||||
await this.calculateAndReportSecurityLevel();
|
await this.calculateAndReportSecurityLevel();
|
||||||
|
|
||||||
// For maximum sessions, turn on Stage 3 and 4
|
setTimeout(async () => {
|
||||||
if (this.currentSecurityLevel === 'maximum') {
|
if (checkStability()) {
|
||||||
setTimeout(async () => {
|
this.enableStage3Security();
|
||||||
if (checkStability()) {
|
await this.calculateAndReportSecurityLevel();
|
||||||
this.enableStage3Security();
|
|
||||||
await this.calculateAndReportSecurityLevel();
|
setTimeout(async () => {
|
||||||
|
if (checkStability()) {
|
||||||
setTimeout(async () => {
|
this.enableStage4Security();
|
||||||
if (checkStability()) {
|
await this.calculateAndReportSecurityLevel();
|
||||||
this.enableStage4Security();
|
}
|
||||||
await this.calculateAndReportSecurityLevel();
|
}, 20000);
|
||||||
}
|
}
|
||||||
}, 20000);
|
}, 15000);
|
||||||
}
|
|
||||||
}, 15000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}, 10000);
|
}, 10000);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// CONNECTION MANAGEMENT WITH ENHANCED SECURITY
|
// CONNECTION MANAGEMENT WITH ENHANCED SECURITY
|
||||||
@@ -11644,11 +11612,10 @@ async processMessage(data) {
|
|||||||
|
|
||||||
// Update session state
|
// Update session state
|
||||||
this.currentSession = sessionData;
|
this.currentSession = sessionData;
|
||||||
this.sessionManager = sessionData.sessionManager;
|
|
||||||
|
|
||||||
// FIX: More lenient checks for activation
|
// FIX: More lenient checks for activation
|
||||||
const hasKeys = !!(this.encryptionKey && this.macKey);
|
const hasKeys = !!(this.encryptionKey && this.macKey);
|
||||||
const hasSession = !!(this.sessionManager && (this.sessionManager.hasActiveSession?.() || sessionData.sessionId));
|
const hasSession = !!(sessionData.sessionId);
|
||||||
|
|
||||||
// Force connection status if there is an active session
|
// Force connection status if there is an active session
|
||||||
if (hasSession) {
|
if (hasSession) {
|
||||||
@@ -12508,7 +12475,7 @@ class SecureKeyStorage {
|
|||||||
// Additional info
|
// Additional info
|
||||||
connectionId: this.connectionId,
|
connectionId: this.connectionId,
|
||||||
keyFingerprint: this.keyFingerprint,
|
keyFingerprint: this.keyFingerprint,
|
||||||
currentSecurityLevel: this.currentSecurityLevel,
|
currentSecurityLevel: 'maximum',
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ import { EnhancedSecureWebRTCManager } from '../network/EnhancedSecureWebRTCMana
|
|||||||
import { EnhancedSecureFileTransfer } from '../transfer/EnhancedSecureFileTransfer.js';
|
import { EnhancedSecureFileTransfer } from '../transfer/EnhancedSecureFileTransfer.js';
|
||||||
|
|
||||||
// Import UI components (side-effect: they attach themselves to window.*)
|
// Import UI components (side-effect: they attach themselves to window.*)
|
||||||
import '../components/ui/SessionTimer.jsx';
|
|
||||||
import '../components/ui/Header.jsx';
|
import '../components/ui/Header.jsx';
|
||||||
import '../components/ui/DownloadApps.jsx';
|
import '../components/ui/DownloadApps.jsx';
|
||||||
import '../components/ui/UniqueFeatureSlider.jsx';
|
import '../components/ui/UniqueFeatureSlider.jsx';
|
||||||
|
|||||||
9
src/scripts/bootstrap-modules.js
vendored
9
src/scripts/bootstrap-modules.js
vendored
@@ -3,10 +3,9 @@
|
|||||||
(async () => {
|
(async () => {
|
||||||
try {
|
try {
|
||||||
const timestamp = Date.now();
|
const timestamp = Date.now();
|
||||||
const [cryptoModule, webrtcModule, paymentModule, fileTransferModule] = await Promise.all([
|
const [cryptoModule, webrtcModule, fileTransferModule] = await Promise.all([
|
||||||
import(`../crypto/EnhancedSecureCryptoUtils.js?v=${timestamp}`),
|
import(`../crypto/EnhancedSecureCryptoUtils.js?v=${timestamp}`),
|
||||||
import(`../network/EnhancedSecureWebRTCManager.js?v=${timestamp}`),
|
import(`../network/EnhancedSecureWebRTCManager.js?v=${timestamp}`),
|
||||||
import(`../session/PayPerSessionManager.js?v=${timestamp}`),
|
|
||||||
import(`../transfer/EnhancedSecureFileTransfer.js?v=${timestamp}`),
|
import(`../transfer/EnhancedSecureFileTransfer.js?v=${timestamp}`),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -14,8 +13,6 @@
|
|||||||
window.EnhancedSecureCryptoUtils = EnhancedSecureCryptoUtils;
|
window.EnhancedSecureCryptoUtils = EnhancedSecureCryptoUtils;
|
||||||
const { EnhancedSecureWebRTCManager } = webrtcModule;
|
const { EnhancedSecureWebRTCManager } = webrtcModule;
|
||||||
window.EnhancedSecureWebRTCManager = EnhancedSecureWebRTCManager;
|
window.EnhancedSecureWebRTCManager = EnhancedSecureWebRTCManager;
|
||||||
const { PayPerSessionManager } = paymentModule;
|
|
||||||
window.PayPerSessionManager = PayPerSessionManager;
|
|
||||||
const { EnhancedSecureFileTransfer } = fileTransferModule;
|
const { EnhancedSecureFileTransfer } = fileTransferModule;
|
||||||
window.EnhancedSecureFileTransfer = EnhancedSecureFileTransfer;
|
window.EnhancedSecureFileTransfer = EnhancedSecureFileTransfer;
|
||||||
|
|
||||||
@@ -28,11 +25,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
loadReactComponent('../components/ui/SessionTimer.jsx'),
|
|
||||||
loadReactComponent('../components/ui/Header.jsx'),
|
loadReactComponent('../components/ui/Header.jsx'),
|
||||||
loadReactComponent('../components/ui/SessionTypeSelector.jsx'),
|
|
||||||
loadReactComponent('../components/ui/LightningPayment.jsx'),
|
|
||||||
loadReactComponent('../components/ui/PaymentModal.jsx'),
|
|
||||||
loadReactComponent('../components/ui/DownloadApps.jsx'),
|
loadReactComponent('../components/ui/DownloadApps.jsx'),
|
||||||
loadReactComponent('../components/ui/ComparisonTable.jsx'),
|
loadReactComponent('../components/ui/ComparisonTable.jsx'),
|
||||||
loadReactComponent('../components/ui/UniqueFeatureSlider.jsx'),
|
loadReactComponent('../components/ui/UniqueFeatureSlider.jsx'),
|
||||||
|
|||||||
@@ -1,27 +1,13 @@
|
|||||||
window.forceUpdateHeader = (timeLeft, sessionType) => {
|
window.forceUpdateHeader = () => {
|
||||||
const event = new CustomEvent('force-header-update', {
|
const event = new CustomEvent('force-header-update', {
|
||||||
detail: { timeLeft, sessionType, timestamp: Date.now() },
|
detail: { timestamp: Date.now() },
|
||||||
});
|
});
|
||||||
document.dispatchEvent(event);
|
document.dispatchEvent(event);
|
||||||
if (window.sessionManager && window.sessionManager.forceUpdateTimer) {
|
|
||||||
window.sessionManager.forceUpdateTimer();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.updateSessionTimer = (timeLeft, sessionType) => {
|
|
||||||
document.dispatchEvent(
|
|
||||||
new CustomEvent('session-timer-update', {
|
|
||||||
detail: { timeLeft, sessionType },
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
document.addEventListener('session-activated', (event) => {
|
document.addEventListener('session-activated', (event) => {
|
||||||
if (window.updateSessionTimer) {
|
|
||||||
window.updateSessionTimer(event.detail.timeLeft, event.detail.sessionType);
|
|
||||||
}
|
|
||||||
if (window.forceUpdateHeader) {
|
if (window.forceUpdateHeader) {
|
||||||
window.forceUpdateHeader(event.detail.timeLeft, event.detail.sessionType);
|
window.forceUpdateHeader();
|
||||||
}
|
}
|
||||||
if (window.webrtcManager && window.webrtcManager.handleSessionActivation) {
|
if (window.webrtcManager && window.webrtcManager.handleSessionActivation) {
|
||||||
if (window.DEBUG_MODE) {
|
if (window.DEBUG_MODE) {
|
||||||
@@ -29,9 +15,6 @@ document.addEventListener('session-activated', (event) => {
|
|||||||
}
|
}
|
||||||
window.webrtcManager.handleSessionActivation({
|
window.webrtcManager.handleSessionActivation({
|
||||||
sessionId: event.detail.sessionId,
|
sessionId: event.detail.sessionId,
|
||||||
sessionType: event.detail.sessionType,
|
|
||||||
timeLeft: event.detail.timeLeft,
|
|
||||||
isDemo: event.detail.isDemo,
|
|
||||||
sessionManager: window.sessionManager,
|
sessionManager: window.sessionManager,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
270
src/scripts/pwa-offline-test.js
Normal file
270
src/scripts/pwa-offline-test.js
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
// PWA Offline Test Script for SecureBit.chat
|
||||||
|
// Enhanced Security Edition v4.3.120
|
||||||
|
// Tests offline functionality and cache status
|
||||||
|
|
||||||
|
class PWAOfflineTester {
|
||||||
|
constructor() {
|
||||||
|
this.testResults = [];
|
||||||
|
this.isRunning = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async runTests() {
|
||||||
|
if (this.isRunning) {
|
||||||
|
console.warn('⚠️ Tests already running');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isRunning = true;
|
||||||
|
this.testResults = [];
|
||||||
|
|
||||||
|
console.log('🧪 Starting PWA Offline Tests...');
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.testServiceWorkerRegistration();
|
||||||
|
await this.testCacheStatus();
|
||||||
|
await this.testOfflineResources();
|
||||||
|
await this.testOnlineResources();
|
||||||
|
|
||||||
|
this.showTestResults();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Test failed:', error);
|
||||||
|
this.addTestResult('Test Suite', false, `Test suite failed: ${error.message}`);
|
||||||
|
} finally {
|
||||||
|
this.isRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async testServiceWorkerRegistration() {
|
||||||
|
try {
|
||||||
|
if ('serviceWorker' in navigator) {
|
||||||
|
const registration = await navigator.serviceWorker.getRegistration();
|
||||||
|
if (registration) {
|
||||||
|
this.addTestResult('Service Worker Registration', true, 'Service worker is registered');
|
||||||
|
} else {
|
||||||
|
this.addTestResult('Service Worker Registration', false, 'No service worker found');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.addTestResult('Service Worker Support', false, 'Service worker not supported');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.addTestResult('Service Worker Registration', false, `Error: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async testCacheStatus() {
|
||||||
|
try {
|
||||||
|
if ('caches' in window) {
|
||||||
|
const cacheNames = await caches.keys();
|
||||||
|
const totalCached = 0;
|
||||||
|
|
||||||
|
for (const cacheName of cacheNames) {
|
||||||
|
const cache = await caches.open(cacheName);
|
||||||
|
const keys = await cache.keys();
|
||||||
|
totalCached += keys.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalCached > 0) {
|
||||||
|
this.addTestResult('Cache Status', true, `${totalCached} resources cached`);
|
||||||
|
} else {
|
||||||
|
this.addTestResult('Cache Status', false, 'No resources cached');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.addTestResult('Cache API Support', false, 'Cache API not supported');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.addTestResult('Cache Status', false, `Error: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async testOfflineResources() {
|
||||||
|
const criticalResources = [
|
||||||
|
'/',
|
||||||
|
'/index.html',
|
||||||
|
'/manifest.json',
|
||||||
|
'/dist/app.js',
|
||||||
|
'/dist/app-boot.js',
|
||||||
|
'/libs/react/react.production.min.js',
|
||||||
|
'/libs/react-dom/react-dom.production.min.js',
|
||||||
|
'/assets/tailwind.css'
|
||||||
|
];
|
||||||
|
|
||||||
|
let cachedCount = 0;
|
||||||
|
|
||||||
|
for (const resource of criticalResources) {
|
||||||
|
try {
|
||||||
|
const cached = await caches.match(resource);
|
||||||
|
if (cached) {
|
||||||
|
cachedCount++;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`⚠️ Failed to check cache for ${resource}:`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const success = cachedCount >= criticalResources.length * 0.8; // 80% success rate
|
||||||
|
this.addTestResult('Critical Resources Cached', success,
|
||||||
|
`${cachedCount}/${criticalResources.length} critical resources cached`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async testOnlineResources() {
|
||||||
|
try {
|
||||||
|
// Test if we can fetch a simple resource
|
||||||
|
const response = await fetch('/favicon.ico', {
|
||||||
|
method: 'HEAD',
|
||||||
|
cache: 'no-cache'
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
this.addTestResult('Network Connectivity', true, 'Network is accessible');
|
||||||
|
} else {
|
||||||
|
this.addTestResult('Network Connectivity', false, `Network error: ${response.status}`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.addTestResult('Network Connectivity', false, `Network error: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addTestResult(testName, passed, message) {
|
||||||
|
this.testResults.push({
|
||||||
|
name: testName,
|
||||||
|
passed,
|
||||||
|
message,
|
||||||
|
timestamp: new Date().toISOString()
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`${passed ? '✅' : '❌'} ${testName}: ${message}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
showTestResults() {
|
||||||
|
const passedTests = this.testResults.filter(test => test.passed).length;
|
||||||
|
const totalTests = this.testResults.length;
|
||||||
|
const successRate = Math.round((passedTests / totalTests) * 100);
|
||||||
|
|
||||||
|
const modal = document.createElement('div');
|
||||||
|
modal.className = 'fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4 backdrop-blur-sm';
|
||||||
|
modal.innerHTML = `
|
||||||
|
<div class="bg-gray-800 rounded-xl p-6 max-w-2xl w-full max-h-[80vh] overflow-y-auto">
|
||||||
|
<div class="flex items-center mb-6">
|
||||||
|
<div class="w-12 h-12 bg-blue-500/10 rounded-full flex items-center justify-center mr-4">
|
||||||
|
<i class="fas fa-vial text-blue-400 text-xl"></i>
|
||||||
|
</div>
|
||||||
|
<h3 class="text-xl font-semibold text-white">PWA Offline Test Results</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-6 p-4 rounded-lg ${successRate >= 80 ? 'bg-green-500/10 border border-green-500/20' : 'bg-red-500/10 border border-red-500/20'}">
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div>
|
||||||
|
<div class="font-medium text-white">Overall Score</div>
|
||||||
|
<div class="text-sm text-gray-300">${passedTests}/${totalTests} tests passed</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-2xl font-bold ${successRate >= 80 ? 'text-green-400' : 'text-red-400'}">
|
||||||
|
${successRate}%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-3">
|
||||||
|
${this.testResults.map(test => `
|
||||||
|
<div class="flex items-center justify-between p-3 rounded-lg ${test.passed ? 'bg-green-500/10 border border-green-500/20' : 'bg-red-500/10 border border-red-500/20'}">
|
||||||
|
<div class="flex items-center space-x-3">
|
||||||
|
<i class="fas ${test.passed ? 'fa-check-circle text-green-400' : 'fa-times-circle text-red-400'}"></i>
|
||||||
|
<div>
|
||||||
|
<div class="font-medium text-white">${test.name}</div>
|
||||||
|
<div class="text-sm text-gray-300">${test.message}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`).join('')}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mt-6 space-y-3">
|
||||||
|
<div class="bg-blue-500/10 border border-blue-500/20 rounded-lg p-4">
|
||||||
|
<h4 class="font-medium text-blue-300 mb-2">Recommendations:</h4>
|
||||||
|
<ul class="text-sm text-blue-200 space-y-1">
|
||||||
|
${this.getRecommendations().map(rec => `<li>• ${rec}</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex space-x-3">
|
||||||
|
<button onclick="window.pwaOfflineTester.runTests(); this.parentElement.parentElement.parentElement.remove();"
|
||||||
|
class="flex-1 bg-blue-500 hover:bg-blue-600 text-white py-3 px-4 rounded-lg font-medium transition-colors">
|
||||||
|
Run Tests Again
|
||||||
|
</button>
|
||||||
|
<button onclick="this.parentElement.parentElement.remove()"
|
||||||
|
class="flex-1 bg-gray-600 hover:bg-gray-500 text-white py-3 px-4 rounded-lg font-medium transition-colors">
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
document.body.appendChild(modal);
|
||||||
|
}
|
||||||
|
|
||||||
|
getRecommendations() {
|
||||||
|
const recommendations = [];
|
||||||
|
|
||||||
|
const failedTests = this.testResults.filter(test => !test.passed);
|
||||||
|
|
||||||
|
if (failedTests.some(test => test.name.includes('Service Worker'))) {
|
||||||
|
recommendations.push('Ensure service worker is properly registered and active');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failedTests.some(test => test.name.includes('Cache'))) {
|
||||||
|
recommendations.push('Check cache configuration and ensure resources are being cached');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failedTests.some(test => test.name.includes('Network'))) {
|
||||||
|
recommendations.push('Verify network connectivity and server availability');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (recommendations.length === 0) {
|
||||||
|
recommendations.push('All tests passed! Your PWA offline functionality is working correctly.');
|
||||||
|
}
|
||||||
|
|
||||||
|
return recommendations;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public API
|
||||||
|
getTestResults() {
|
||||||
|
return this.testResults;
|
||||||
|
}
|
||||||
|
|
||||||
|
clearResults() {
|
||||||
|
this.testResults = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Singleton pattern
|
||||||
|
let instance = null;
|
||||||
|
|
||||||
|
const PWAOfflineTesterAPI = {
|
||||||
|
getInstance() {
|
||||||
|
if (!instance) {
|
||||||
|
instance = new PWAOfflineTester();
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
},
|
||||||
|
|
||||||
|
runTests() {
|
||||||
|
return this.getInstance().runTests();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Export for module use
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = PWAOfflineTesterAPI;
|
||||||
|
} else if (typeof window !== 'undefined' && !window.PWAOfflineTester) {
|
||||||
|
window.PWAOfflineTester = PWAOfflineTesterAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auto-initialize
|
||||||
|
if (typeof window !== 'undefined' && !window.pwaOfflineTester) {
|
||||||
|
window.pwaOfflineTester = PWAOfflineTesterAPI.getInstance();
|
||||||
|
|
||||||
|
// Add global function for easy access
|
||||||
|
window.testPWAOffline = () => {
|
||||||
|
window.pwaOfflineTester.runTests();
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -349,35 +349,6 @@ button i {
|
|||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Pay-per-session UI - Обновленный трехцветный таймер */
|
|
||||||
.session-timer {
|
|
||||||
padding: 8px 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-weight: 600;
|
|
||||||
font-size: 14px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 8px;
|
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
||||||
backdrop-filter: blur(10px);
|
|
||||||
transition: all 0.5s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-timer:hover {
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Анимация пульсации для красной зоны */
|
|
||||||
@keyframes timer-pulse {
|
|
||||||
0%, 100% { opacity: 1; }
|
|
||||||
50% { opacity: 0.7; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.session-timer.animate-pulse {
|
|
||||||
animation: timer-pulse 2s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Lightning button */
|
/* Lightning button */
|
||||||
.lightning-button {
|
.lightning-button {
|
||||||
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
|
||||||
|
|||||||
135
sw.js
135
sw.js
@@ -1,34 +1,33 @@
|
|||||||
// SecureBit.chat Service Worker
|
// SecureBit.chat Service Worker
|
||||||
// Enhanced Security Edition v4.3.120 - ECDH + DTLS + SAS
|
// Conservative PWA Edition v4.3.120 - Minimal Caching Strategy
|
||||||
|
|
||||||
const CACHE_NAME = 'securebit-v4.3.120';
|
const CACHE_NAME = 'securebit-pwa-v4.3.120';
|
||||||
const STATIC_CACHE = 'securebit-static-v4.3.120';
|
const STATIC_CACHE = 'securebit-pwa-static-v4.3.120';
|
||||||
const DYNAMIC_CACHE = 'securebit-dynamic-v4.3.120';
|
const DYNAMIC_CACHE = 'securebit-pwa-dynamic-v4.3.120';
|
||||||
|
|
||||||
// Files to cache for offline functionality (excluding external CDNs that may have CORS issues)
|
// Essential files for PWA offline functionality
|
||||||
const STATIC_ASSETS = [
|
const STATIC_ASSETS = [
|
||||||
'/',
|
'/',
|
||||||
'/index.html',
|
'/index.html',
|
||||||
'/manifest.json',
|
'/manifest.json',
|
||||||
'/src/crypto/EnhancedSecureCryptoUtils.js',
|
|
||||||
'/src/network/EnhancedSecureWebRTCManager.js',
|
// Core PWA files only
|
||||||
'/src/session/PayPerSessionManager.js',
|
'/dist/app.js',
|
||||||
'/src/components/ui/SessionTimer.jsx',
|
'/dist/app-boot.js',
|
||||||
'/src/components/ui/Header.jsx',
|
|
||||||
'/src/components/ui/PasswordModal.jsx',
|
// Essential styles for PWA
|
||||||
'/src/components/ui/SessionTypeSelector.jsx',
|
|
||||||
'/src/components/ui/PaymentModal.jsx',
|
|
||||||
'/src/components/ui/DownloadApps.jsx',
|
|
||||||
'/src/components/ui/ComparisonTable.jsx',
|
|
||||||
'/src/components/ui/UniqueFeatureSlider.jsx',
|
|
||||||
'/src/components/ui/SecurityFeatures.jsx',
|
|
||||||
'/src/components/ui/Testimonials.jsx',
|
|
||||||
'/src/components/ui/Roadmap.jsx',
|
|
||||||
'/src/styles/main.css',
|
|
||||||
'/src/styles/animations.css',
|
|
||||||
'/src/styles/components.css',
|
|
||||||
'/src/styles/pwa.css',
|
'/src/styles/pwa.css',
|
||||||
'/logo/favicon.ico'
|
|
||||||
|
// PWA icons (required for install)
|
||||||
|
'/logo/icon-192x192.png',
|
||||||
|
'/logo/icon-512x512.png',
|
||||||
|
'/logo/favicon.ico',
|
||||||
|
|
||||||
|
// PWA components only
|
||||||
|
'/src/pwa/pwa-manager.js',
|
||||||
|
'/src/pwa/install-prompt.js',
|
||||||
|
'/src/scripts/pwa-register.js',
|
||||||
|
'/src/scripts/pwa-offline-test.js'
|
||||||
];
|
];
|
||||||
|
|
||||||
// Sensitive files that should never be cached
|
// Sensitive files that should never be cached
|
||||||
@@ -43,21 +42,22 @@ const SENSITIVE_PATTERNS = [
|
|||||||
|
|
||||||
// Network first patterns (always try network first)
|
// Network first patterns (always try network first)
|
||||||
const NETWORK_FIRST_PATTERNS = [
|
const NETWORK_FIRST_PATTERNS = [
|
||||||
/\.js$/,
|
/\/api\//,
|
||||||
/\.jsx$/,
|
/\/session\//,
|
||||||
/\/src\//,
|
/\/payment\//,
|
||||||
/api/
|
/\/verification\//,
|
||||||
|
/preimage/,
|
||||||
|
/auth/
|
||||||
];
|
];
|
||||||
|
|
||||||
// Cache first patterns (static assets)
|
// Cache first patterns (only essential PWA assets)
|
||||||
const CACHE_FIRST_PATTERNS = [
|
const CACHE_FIRST_PATTERNS = [
|
||||||
/\.css$/,
|
/manifest\.json$/,
|
||||||
/\.png$/,
|
/logo\/icon-.*\.png$/,
|
||||||
/\.jpg$/,
|
/logo\/favicon\.ico$/,
|
||||||
/\.svg$/,
|
/src\/styles\/pwa\.css$/,
|
||||||
/\.ico$/,
|
/src\/pwa\/.*\.js$/,
|
||||||
/fonts/,
|
/src\/scripts\/pwa-.*\.js$/
|
||||||
/logo/
|
|
||||||
];
|
];
|
||||||
|
|
||||||
self.addEventListener('message', (event) => {
|
self.addEventListener('message', (event) => {
|
||||||
@@ -160,23 +160,23 @@ self.addEventListener('fetch', (event) => {
|
|||||||
event.respondWith(handleRequest(event.request));
|
event.respondWith(handleRequest(event.request));
|
||||||
});
|
});
|
||||||
|
|
||||||
// Smart request handling with security considerations
|
// Conservative request handling - only cache PWA essentials
|
||||||
async function handleRequest(request) {
|
async function handleRequest(request) {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Strategy 1: Cache First (for static assets)
|
// Strategy 1: Cache First (only for essential PWA assets)
|
||||||
if (CACHE_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
|
if (CACHE_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
|
||||||
return await cacheFirst(request);
|
return await cacheFirst(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strategy 2: Network First (for dynamic content and security-critical files)
|
// Strategy 2: Network First (for all other requests)
|
||||||
if (NETWORK_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
|
if (NETWORK_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
|
||||||
return await networkFirst(request);
|
return await networkFirst(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strategy 3: Stale While Revalidate (for main pages)
|
// Strategy 3: Network First for everything else (no aggressive caching)
|
||||||
return await staleWhileRevalidate(request);
|
return await networkFirst(request);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('❌ Request handling failed:', error);
|
console.error('❌ Request handling failed:', error);
|
||||||
@@ -254,29 +254,39 @@ async function staleWhileRevalidate(request) {
|
|||||||
return cachedResponse || networkResponsePromise || handleOffline(request);
|
return cachedResponse || networkResponsePromise || handleOffline(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Offline fallback
|
// Offline fallback - minimal caching for PWA only
|
||||||
async function handleOffline(request) {
|
async function handleOffline(request) {
|
||||||
const url = new URL(request.url);
|
const url = new URL(request.url);
|
||||||
|
|
||||||
// For navigation requests, return cached index.html
|
// For navigation requests, return cached index.html
|
||||||
if (request.destination === 'document') {
|
if (request.destination === 'document' || request.mode === 'navigate') {
|
||||||
const cachedIndex = await caches.match('/');
|
const cachedIndex = await caches.match('/index.html');
|
||||||
if (cachedIndex) {
|
if (cachedIndex) {
|
||||||
return cachedIndex;
|
return cachedIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Fallback to root if index.html not found
|
||||||
|
const cachedRoot = await caches.match('/');
|
||||||
|
if (cachedRoot) {
|
||||||
|
return cachedRoot;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// For images, return a placeholder or cached version
|
// For PWA assets, try to return cached version
|
||||||
if (request.destination === 'image') {
|
if (CACHE_FIRST_PATTERNS.some(pattern => pattern.test(url.pathname))) {
|
||||||
return new Response(
|
const cachedAsset = await caches.match(request);
|
||||||
'<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 200 200"><rect width="200" height="200" fill="#1a1a1a"/><text x="100" y="100" text-anchor="middle" fill="#666" font-size="14">Offline</text></svg>',
|
if (cachedAsset) {
|
||||||
{ headers: { 'Content-Type': 'image/svg+xml' } }
|
return cachedAsset;
|
||||||
);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a generic offline response
|
// Return a generic offline response for everything else
|
||||||
return new Response(
|
return new Response(
|
||||||
JSON.stringify({ error: 'Offline', message: 'Network unavailable' }),
|
JSON.stringify({
|
||||||
|
error: 'Offline',
|
||||||
|
message: 'Network unavailable - PWA offline mode',
|
||||||
|
url: url.pathname
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
status: 503,
|
status: 503,
|
||||||
statusText: 'Service Unavailable',
|
statusText: 'Service Unavailable',
|
||||||
@@ -293,6 +303,29 @@ self.addEventListener('sync', (event) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function retryFailedRequests() {
|
||||||
|
try {
|
||||||
|
// Get all cached requests that failed
|
||||||
|
const cache = await caches.open(DYNAMIC_CACHE);
|
||||||
|
const requests = await cache.keys();
|
||||||
|
|
||||||
|
for (const request of requests) {
|
||||||
|
try {
|
||||||
|
// Try to fetch the request again
|
||||||
|
const response = await fetch(request);
|
||||||
|
if (response.ok) {
|
||||||
|
// Update cache with successful response
|
||||||
|
await cache.put(request, response);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('⚠️ Retry failed for:', request.url, error.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Failed to retry requests:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Notification click handler
|
// Notification click handler
|
||||||
|
|||||||
Reference in New Issue
Block a user