mirror of
https://github.com/LightZirconite/Microsoft-Rewards-Bot.git
synced 2026-01-11 01:36:16 +00:00
feat: Implement main application logic and UI styling
- Added app.js to manage global state, WebSocket connections, and API interactions. - Implemented functions for theme toggling, toast notifications, and updating UI elements. - Created functions to fetch and display account metrics, logs, and status updates. - Added event handlers for starting, stopping, and restarting the bot. - Introduced WebSocket handling for real-time updates. - Added style.css for consistent theming and responsive design across the application. - Included styles for various UI components such as buttons, cards, logs, and empty states. - Implemented light theme support with appropriate styles.
This commit is contained in:
@@ -1,6 +1,58 @@
|
||||
# Dashboard API Reference
|
||||
# Dashboard - Modern Real-Time Interface
|
||||
|
||||
## Endpoints
|
||||
## 🎨 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
|
||||
|
||||
|
||||
@@ -41,8 +41,23 @@ export class DashboardServer {
|
||||
|
||||
private setupMiddleware(): void {
|
||||
this.app.use(express.json())
|
||||
this.app.use('/assets', express.static(path.join(__dirname, '../../assets')))
|
||||
this.app.use(express.static(path.join(__dirname, '../../public')))
|
||||
|
||||
// Disable caching for all static files
|
||||
this.app.use((req, res, next) => {
|
||||
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private')
|
||||
res.set('Pragma', 'no-cache')
|
||||
res.set('Expires', '0')
|
||||
next()
|
||||
})
|
||||
|
||||
this.app.use('/assets', express.static(path.join(__dirname, '../../assets'), {
|
||||
etag: false,
|
||||
maxAge: 0
|
||||
}))
|
||||
this.app.use(express.static(path.join(__dirname, '../../public'), {
|
||||
etag: false,
|
||||
maxAge: 0
|
||||
}))
|
||||
}
|
||||
|
||||
private setupRoutes(): void {
|
||||
@@ -53,14 +68,16 @@ export class DashboardServer {
|
||||
res.json({ status: 'ok', uptime: process.uptime() })
|
||||
})
|
||||
|
||||
// Serve dashboard UI (with fallback if file doesn't exist)
|
||||
// Serve dashboard UI
|
||||
this.app.get('/', (_req, res) => {
|
||||
const dashboardPath = path.join(__dirname, '../../public/dashboard.html')
|
||||
const indexPath = path.join(__dirname, '../../public/index.html')
|
||||
|
||||
if (fs.existsSync(dashboardPath)) {
|
||||
res.sendFile(dashboardPath)
|
||||
} else if (fs.existsSync(indexPath)) {
|
||||
// Force no cache on HTML files
|
||||
res.set('Cache-Control', 'no-store, no-cache, must-revalidate, private')
|
||||
res.set('Pragma', 'no-cache')
|
||||
res.set('Expires', '0')
|
||||
|
||||
if (fs.existsSync(indexPath)) {
|
||||
res.sendFile(indexPath)
|
||||
} else {
|
||||
res.status(200).send(`
|
||||
@@ -113,16 +130,23 @@ export class DashboardServer {
|
||||
}
|
||||
|
||||
private interceptBotLogs(): void {
|
||||
const originalLog = botLog
|
||||
// Intercept Logger.log calls by wrapping at module level
|
||||
// This ensures all log calls go through dashboard state
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const loggerModule = require('../util/Logger') as { log: typeof botLog }
|
||||
const originalLog = loggerModule.log
|
||||
|
||||
;(global as Record<string, unknown>).botLog = (
|
||||
loggerModule.log = (
|
||||
isMobile: boolean | 'main',
|
||||
title: string,
|
||||
message: string,
|
||||
type: 'log' | 'warn' | 'error' = 'log'
|
||||
type: 'log' | 'warn' | 'error' = 'log',
|
||||
color?: keyof typeof import('chalk')
|
||||
) => {
|
||||
const result = originalLog(isMobile, title, message, type)
|
||||
// Call original log function
|
||||
const result = originalLog(isMobile, title, message, type, color as keyof typeof import('chalk'))
|
||||
|
||||
// Create log entry for dashboard
|
||||
const logEntry: DashboardLog = {
|
||||
timestamp: new Date().toISOString(),
|
||||
level: type,
|
||||
@@ -131,11 +155,14 @@ export class DashboardServer {
|
||||
message
|
||||
}
|
||||
|
||||
// Add to dashboard state and broadcast
|
||||
dashboardState.addLog(logEntry)
|
||||
this.broadcastUpdate('log', { log: logEntry })
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
dashLog('Bot log interception active')
|
||||
}
|
||||
|
||||
public broadcastUpdate(type: string, data: unknown): void {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
import type { Config } from '../interface/Config'
|
||||
import { ConclusionWebhook } from '../util/ConclusionWebhook'
|
||||
import { JobState } from '../util/JobState'
|
||||
import { log } from '../util/Logger'
|
||||
import { Ntfy } from '../util/Ntfy'
|
||||
|
||||
export interface AccountResult {
|
||||
@@ -80,7 +81,7 @@ export class SummaryReporter {
|
||||
summary.failureCount > 0 ? 0xFF5555 : 0x00FF00
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('[SUMMARY] Failed to send webhook:', error)
|
||||
log('main', 'SUMMARY', `Failed to send webhook: ${error instanceof Error ? error.message : String(error)}`, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,7 +98,7 @@ export class SummaryReporter {
|
||||
|
||||
await Ntfy(message, summary.failureCount > 0 ? 'warn' : 'log')
|
||||
} catch (error) {
|
||||
console.error('[SUMMARY] Failed to send Ntfy notification:', error)
|
||||
log('main', 'SUMMARY', `Failed to send Ntfy notification: ${error instanceof Error ? error.message : String(error)}`, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +122,7 @@ export class SummaryReporter {
|
||||
)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('[SUMMARY] Failed to update job state:', error)
|
||||
log('main', 'SUMMARY', `Failed to update job state: ${error instanceof Error ? error.message : String(error)}`, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,36 +130,36 @@ export class SummaryReporter {
|
||||
* Generate and send comprehensive summary
|
||||
*/
|
||||
async generateReport(summary: SummaryData): Promise<void> {
|
||||
console.log('\n' + '═'.repeat(80))
|
||||
console.log('📊 EXECUTION SUMMARY')
|
||||
console.log('═'.repeat(80))
|
||||
log('main', 'SUMMARY', '═'.repeat(80))
|
||||
log('main', 'SUMMARY', '📊 EXECUTION SUMMARY')
|
||||
log('main', 'SUMMARY', '═'.repeat(80))
|
||||
|
||||
const duration = Math.round((summary.endTime.getTime() - summary.startTime.getTime()) / 1000)
|
||||
console.log(`\n⏱️ Duration: ${Math.floor(duration / 60)}m ${duration % 60}s`)
|
||||
console.log(`📈 Total Points Collected: ${summary.totalPoints}`)
|
||||
console.log(`✅ Successful Accounts: ${summary.successCount}/${summary.accounts.length}`)
|
||||
log('main', 'SUMMARY', `⏱️ Duration: ${Math.floor(duration / 60)}m ${duration % 60}s`)
|
||||
log('main', 'SUMMARY', `📈 Total Points Collected: ${summary.totalPoints}`)
|
||||
log('main', 'SUMMARY', `✅ Successful Accounts: ${summary.successCount}/${summary.accounts.length}`)
|
||||
|
||||
if (summary.failureCount > 0) {
|
||||
console.log(`❌ Failed Accounts: ${summary.failureCount}`)
|
||||
log('main', 'SUMMARY', `❌ Failed Accounts: ${summary.failureCount}`, 'warn')
|
||||
}
|
||||
|
||||
console.log('\n' + '─'.repeat(80))
|
||||
console.log('Account Breakdown:')
|
||||
console.log('─'.repeat(80))
|
||||
log('main', 'SUMMARY', '─'.repeat(80))
|
||||
log('main', 'SUMMARY', 'Account Breakdown:')
|
||||
log('main', 'SUMMARY', '─'.repeat(80))
|
||||
|
||||
for (const account of summary.accounts) {
|
||||
const status = account.errors?.length ? '❌ FAILED' : '✅ SUCCESS'
|
||||
const duration = Math.round(account.runDuration / 1000)
|
||||
|
||||
console.log(`\n${status} | ${account.email}`)
|
||||
console.log(` Points: ${account.pointsEarned} | Duration: ${duration}s`)
|
||||
log('main', 'SUMMARY', `${status} | ${account.email}`)
|
||||
log('main', 'SUMMARY', ` Points: ${account.pointsEarned} | Duration: ${duration}s`)
|
||||
|
||||
if (account.errors?.length) {
|
||||
console.log(` Error: ${account.errors[0]}`)
|
||||
log('main', 'SUMMARY', ` Error: ${account.errors[0]}`, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + '═'.repeat(80) + '\n')
|
||||
log('main', 'SUMMARY', '═'.repeat(80))
|
||||
|
||||
// Send notifications
|
||||
await Promise.all([
|
||||
|
||||
Reference in New Issue
Block a user