1. Completely Rebuilt Dashboard

index.html - New Modern Interface:

Header with animated status badge (Running/Stopped)
Grid of 6 statistics (Accounts, Points, Completed, Errors, Uptime, Memory)
Control Panel with 4 buttons (Start/Stop/Restart/Reset State)
Account List with avatars and statuses
Quick Actions (Run Single, Export Logs, Config, History)
Points History Graph (Chart.js) with 7D/30D selection
Activity Breakdown Graph (donut chart)
Real-time log viewer with level filtering
Toast system for notifications
Modal system for confirmations
WebSocket connection indicator
Light/dark theme with toggle
Responsive mobile design
style.css - Modern CSS with:

CSS variables for theming (dark + light)
Smooth animations (pulse, fade-in, slideIn)
GitHub-inspired design
Custom scrollbar
Responsive breakpoints
app.js - Full JavaScript:

WebSocket handling with automatic reconnection
Chart.js initialized with 2 charts
Centralized state management
Real-time uptime timer
Log export to text file
Email hiding
Persistent theme in localStorage
2. Improved backend
routes.ts:233-302 - New API routes:

POST /api/reset-state - Resets the state of today's jobs
GET /api/memory - Returns current memory usage
3. Enhanced anti-detection (previous session)
Browser.ts - 8 new layers of protection:

WebRTC Leak Prevention
Battery API Spoofing
Hardware Concurrency Normalization
Device Memory Consistency
Audio Fingerprint Protection
Timezone Consistency
Connection Info Spoofing
This commit is contained in:
2025-12-16 21:32:36 +01:00
parent 3972767c81
commit 744e3c9c4a
7 changed files with 1982 additions and 874 deletions

View File

@@ -257,11 +257,162 @@ class Browser {
description: 'Portable Document Format',
filename: 'internal-pdf-viewer',
length: 2
},
{
name: 'Chromium PDF Viewer',
description: 'Portable Document Format',
filename: 'internal-pdf-viewer',
length: 2
}
]
})
} catch { /* Plugins may be frozen */ }
// ═══════════════════════════════════════════════════════════════
// ANTI-DETECTION LAYER 5: WebRTC Leak Prevention
// ═══════════════════════════════════════════════════════════════
// CRITICAL: Prevent WebRTC from leaking real IP address
try {
// Override RTCPeerConnection to prevent IP leaks
const originalRTCPeerConnection = window.RTCPeerConnection
// @ts-ignore
window.RTCPeerConnection = function (config?: RTCConfiguration) {
// Force STUN servers through proxy or disable
const modifiedConfig: RTCConfiguration = {
...config,
iceServers: [] // Disable ICE to prevent IP leak
}
return new originalRTCPeerConnection(modifiedConfig)
}
// @ts-ignore
window.RTCPeerConnection.prototype = originalRTCPeerConnection.prototype
} catch { /* WebRTC override may fail */ }
// ═══════════════════════════════════════════════════════════════
// ANTI-DETECTION LAYER 6: Battery API Spoofing
// ═══════════════════════════════════════════════════════════════
// Headless browsers may have unusual battery states
try {
// @ts-ignore
if (navigator.getBattery) {
// @ts-ignore
navigator.getBattery = () => Promise.resolve({
charging: true,
chargingTime: 0,
dischargingTime: Infinity,
level: 1,
addEventListener: () => { },
removeEventListener: () => { },
dispatchEvent: () => true
})
}
} catch { /* Battery API override may fail */ }
// ═══════════════════════════════════════════════════════════════
// ANTI-DETECTION LAYER 7: Hardware Concurrency Consistency
// ═══════════════════════════════════════════════════════════════
// Ensure hardware concurrency looks realistic
try {
const realCores = navigator.hardwareConcurrency || 4
// Round to common values: 2, 4, 6, 8, 12, 16
const commonCores = [2, 4, 6, 8, 12, 16]
const normalizedCores = commonCores.reduce((prev, curr) =>
Math.abs(curr - realCores) < Math.abs(prev - realCores) ? curr : prev
)
Object.defineProperty(navigator, 'hardwareConcurrency', {
get: () => normalizedCores,
configurable: true
})
} catch { /* Hardware concurrency override may fail */ }
// ═══════════════════════════════════════════════════════════════
// ANTI-DETECTION LAYER 8: Device Memory Consistency
// ═══════════════════════════════════════════════════════════════
try {
// @ts-ignore
const realMemory = navigator.deviceMemory || 8
// Round to common values: 2, 4, 8, 16
const commonMemory = [2, 4, 8, 16]
const normalizedMemory = commonMemory.reduce((prev, curr) =>
Math.abs(curr - realMemory) < Math.abs(prev - realMemory) ? curr : prev
)
Object.defineProperty(navigator, 'deviceMemory', {
get: () => normalizedMemory,
configurable: true
})
} catch { /* Device memory override may fail */ }
// ═══════════════════════════════════════════════════════════════
// ANTI-DETECTION LAYER 9: Audio Fingerprint Protection
// ═══════════════════════════════════════════════════════════════
try {
const originalCreateOscillator = AudioContext.prototype.createOscillator
const originalCreateDynamicsCompressor = AudioContext.prototype.createDynamicsCompressor
// Add slight randomization to audio context to prevent fingerprinting
AudioContext.prototype.createOscillator = function () {
const oscillator = originalCreateOscillator.apply(this)
const originalGetFloatFrequencyData = AnalyserNode.prototype.getFloatFrequencyData
AnalyserNode.prototype.getFloatFrequencyData = function (array) {
originalGetFloatFrequencyData.apply(this, [array])
// Add imperceptible noise
for (let i = 0; i < array.length; i++) {
array[i] = array[i]! + (Math.random() * 0.0001)
}
}
return oscillator
}
AudioContext.prototype.createDynamicsCompressor = function () {
const compressor = originalCreateDynamicsCompressor.apply(this)
// Slightly randomize default values
try {
compressor.threshold.value = -24 + (Math.random() * 0.001)
compressor.knee.value = 30 + (Math.random() * 0.001)
} catch { /* May be read-only */ }
return compressor
}
} catch { /* Audio API override may fail */ }
// ═══════════════════════════════════════════════════════════════
// ANTI-DETECTION LAYER 10: Timezone & Locale Consistency
// ═══════════════════════════════════════════════════════════════
try {
// Ensure Date.prototype.getTimezoneOffset is consistent
const originalGetTimezoneOffset = Date.prototype.getTimezoneOffset
const consistentOffset = originalGetTimezoneOffset.call(new Date())
Date.prototype.getTimezoneOffset = function () {
return consistentOffset
}
} catch { /* Timezone override may fail */ }
// ═══════════════════════════════════════════════════════════════
// ANTI-DETECTION LAYER 11: Connection Info Spoofing
// ═══════════════════════════════════════════════════════════════
try {
// @ts-ignore
if (navigator.connection) {
Object.defineProperty(navigator, 'connection', {
get: () => ({
effectiveType: '4g',
rtt: 50,
downlink: 10,
saveData: false,
addEventListener: () => { },
removeEventListener: () => { }
}),
configurable: true
})
}
} catch { /* Connection API override may fail */ }
// ═══════════════════════════════════════════════════════════════
// Standard styling (non-detection related)
// ═══════════════════════════════════════════════════════════════

View File

@@ -1,305 +0,0 @@
# Dashboard - Modern Real-Time Interface
## 🎨 New Features (2025 Update - November)
### ✨ Modern UI Enhancements v2.0
- **Professional Dark Theme**: Default dark mode with improved color palette and contrast
- **Refined Design System**: Consistent spacing, typography, and component styling
- **Improved Animations**: Smoother transitions with optimized performance
- **Enhanced Glassmorphism**: Better backdrop blur and shadow layering
- **Staggered Card Entrance**: Beautiful loading animations for stats cards
- **Better Visual Hierarchy**: Improved text sizing and weight differentiation
- **Refined Components**: Polished buttons, badges, and interactive elements
- **Optimized Icons**: Gradient overlays with better sizing
### Previous v1.0 Features
- **Dark Mode Support**: Toggle between light and dark themes with persistent preference
- **Real-Time Updates**: WebSocket-powered live log streaming and status updates
- **Glassmorphism Design**: Modern blur effects and smooth animations
- **Responsive Layout**: Optimized for desktop, tablet, and mobile devices
- **Enhanced Stats Cards**: Animated counters with gradient icons
- **Log Statistics**: Real-time error and warning counters
### 🚀 Performance Improvements
- **Optimized Log Management**: Maximum 200 logs in memory with automatic cleanup
- **Smart WebSocket Reconnection**: Automatic reconnection on network failures
- **Reduced Bundle Size**: Removed unused console.log calls (-15% size)
- **Better Error Handling**: Comprehensive validation and user-friendly error messages
### 🔧 Technical Enhancements
- **Proper Log Interception**: Fixed "No logs yet..." issue by intercepting at module level
- **Type-Safe API**: Full TypeScript support with proper error handling
- **Consistent Logging**: All console.log calls replaced with structured logging
- **Memory Management**: Automatic cleanup of old WebSocket buffers
---
## 📊 Dashboard UI
### Control Panel
- **Start/Stop/Restart Bot**: Full bot lifecycle management
- **Refresh Data**: Manual data synchronization
- **Clear Logs**: Reset log history
### Real-Time Monitoring
- **Live Logs**: Color-coded logs with timestamps, platform tags, and titles
- **Account Status**: Per-account progress with points tracking
- **Statistics Dashboard**: Total accounts, points, completed runs, errors
### Theme Support
- **Light Mode**: Clean white interface with subtle shadows
- **Dark Mode**: Eye-friendly dark interface for night work
---
## API Endpoints
### Status & Control
#### `GET /api/status`
Get current bot status.
**Response:**
```json
{
"running": false,
"lastRun": "2025-11-03T10:30:00.000Z",
"currentAccount": "user@example.com",
"totalAccounts": 5
}
```
#### `POST /api/start`
Start bot execution in background.
**Response:**
```json
{
"success": true,
"pid": 12345
}
```
#### `POST /api/stop`
Stop bot execution.
**Response:**
```json
{
"success": true
}
```
---
### Accounts
#### `GET /api/accounts`
List all accounts with masked emails and status.
**Response:**
```json
[
{
"email": "user@example.com",
"maskedEmail": "u***@e***.com",
"points": 5420,
"lastSync": "2025-11-03T10:30:00.000Z",
"status": "completed",
"errors": []
}
]
```
#### `POST /api/sync/:email`
Force synchronization for a single account.
**Parameters:**
- `email` (path): Account email
**Response:**
```json
{
"success": true,
"pid": 12346
}
```
---
### Logs & History
#### `GET /api/logs?limit=100`
Get recent logs.
**Query Parameters:**
- `limit` (optional): Max number of logs (default: 100, max: 500)
**Response:**
```json
[
{
"timestamp": "2025-11-03T10:30:00.000Z",
"level": "log",
"platform": "DESKTOP",
"title": "SEARCH",
"message": "Completed 30 searches"
}
]
```
#### `DELETE /api/logs`
Clear all logs from memory.
**Response:**
```json
{
"success": true
}
```
#### `GET /api/history`
Get recent run summaries (last 7 days).
**Response:**
```json
[
{
"runId": "abc123",
"timestamp": "2025-11-03T10:00:00.000Z",
"totals": {
"totalCollected": 450,
"totalAccounts": 5,
"accountsWithErrors": 0
},
"perAccount": [...]
}
]
```
---
### Configuration
#### `GET /api/config`
Get current configuration (sensitive data masked).
**Response:**
```json
{
"baseURL": "https://rewards.bing.com",
"headless": true,
"clusters": 2,
"webhook": {
"enabled": true,
"url": "htt***://dis***"
}
}
```
#### `POST /api/config`
Update configuration (creates automatic backup).
**Request Body:** Full config object
**Response:**
```json
{
"success": true,
"backup": "/path/to/config.jsonc.backup.1730634000000"
}
```
---
### Metrics
#### `GET /api/metrics`
Get aggregated metrics.
**Response:**
```json
{
"totalAccounts": 5,
"totalPoints": 27100,
"accountsWithErrors": 0,
"accountsRunning": 0,
"accountsCompleted": 5
}
```
---
## WebSocket
Connect to `ws://localhost:3000/ws` for real-time log streaming.
**Message Format:**
```json
{
"type": "log",
"log": {
"timestamp": "2025-11-03T10:30:00.000Z",
"level": "log",
"platform": "DESKTOP",
"title": "SEARCH",
"message": "Completed search"
}
}
```
**On Connect:**
Receives history of last 50 logs:
```json
{
"type": "history",
"logs": [...]
}
```
---
## Usage
### Start Dashboard
```bash
npm run dashboard
# or in dev mode
npm run dashboard-dev
```
Default: `http://127.0.0.1:3000`
### Environment Variables
- `DASHBOARD_PORT`: Port number (default: 3000)
- `DASHBOARD_HOST`: Bind address (default: 127.0.0.1)
### Security
- **Localhost only**: Dashboard binds to `127.0.0.1` by default
- **Email masking**: Emails are partially masked in API responses
- **Token masking**: Webhook URLs and auth tokens are masked
- **Config backup**: Automatic backup before any config modification
---
## Example Usage
### Check Status
```bash
curl http://localhost:3000/api/status
```
### Start Bot
```bash
curl -X POST http://localhost:3000/api/start
```
### Get Logs
```bash
curl http://localhost:3000/api/logs?limit=50
```
### Sync Single Account
```bash
curl -X POST http://localhost:3000/api/sync/user@example.com
```

View File

@@ -259,6 +259,78 @@ apiRouter.post('/account/:email/reset', (req: Request, res: Response): void => {
}
})
// POST /api/reset-state - Reset all job states for today
apiRouter.post('/reset-state', (_req: Request, res: Response): void => {
try {
const jobStatePath = path.join(process.cwd(), 'sessions', 'job-state')
if (!fs.existsSync(jobStatePath)) {
res.json({ success: true, message: 'No job state to reset' })
return
}
const today = new Date().toISOString().slice(0, 10)
let resetCount = 0
// Read all job state files and reset today's entries
const files = fs.readdirSync(jobStatePath).filter(f => f.endsWith('.json'))
for (const file of files) {
try {
const filePath = path.join(jobStatePath, file)
const content = JSON.parse(fs.readFileSync(filePath, 'utf-8'))
// Reset today's completed activities
if (content[today]) {
delete content[today]
fs.writeFileSync(filePath, JSON.stringify(content, null, 2), 'utf-8')
resetCount++
}
} catch {
// Continue processing other files if one fails
continue
}
}
// Reset account statuses in dashboard state
const accounts = dashboardState.getAccounts()
for (const account of accounts) {
dashboardState.updateAccount(account.email, {
status: 'idle',
errors: []
})
}
res.json({
success: true,
message: `Reset job state for ${resetCount} account(s)`,
resetCount
})
} catch (error) {
res.status(500).json({ error: getErr(error) })
}
})
// GET /api/memory - Get current memory usage
apiRouter.get('/memory', (_req: Request, res: Response) => {
try {
const memUsage = process.memoryUsage()
res.json({
heapUsed: memUsage.heapUsed,
heapTotal: memUsage.heapTotal,
rss: memUsage.rss,
external: memUsage.external,
formatted: {
heapUsed: `${(memUsage.heapUsed / 1024 / 1024).toFixed(1)} MB`,
heapTotal: `${(memUsage.heapTotal / 1024 / 1024).toFixed(1)} MB`,
rss: `${(memUsage.rss / 1024 / 1024).toFixed(1)} MB`
}
})
} catch (error) {
res.status(500).json({ error: getErr(error) })
}
})
// Helper to mask sensitive URLs
function maskUrl(url: string): string {
try {