feature: adding account management and point loading from sessions

This commit is contained in:
2025-11-03 23:13:22 +01:00
parent ae4e34cd66
commit 8298818099
4 changed files with 158 additions and 5 deletions

View File

@@ -1 +0,0 @@
<!-- This is a placeholder. The actual favicon will be served from /assets/logo.png -->

View File

@@ -0,0 +1,119 @@
import fs from 'fs'
import path from 'path'
export interface SessionPoints {
email: string
points: number
lastUpdated: string
}
/**
* Try to load points from session data
*/
export function loadPointsFromSessions(email: string): number | undefined {
try {
const sessionsDir = path.join(process.cwd(), 'sessions')
if (!fs.existsSync(sessionsDir)) {
return undefined
}
// Try to find session file for this email
const emailHash = Buffer.from(email).toString('base64').replace(/[^a-zA-Z0-9]/g, '')
const possibleFiles = [
`${email}.json`,
`${emailHash}.json`,
`session_${email}.json`
]
for (const filename of possibleFiles) {
const filepath = path.join(sessionsDir, filename)
if (fs.existsSync(filepath)) {
const data = JSON.parse(fs.readFileSync(filepath, 'utf-8'))
if (data.points !== undefined) {
return data.points
}
if (data.availablePoints !== undefined) {
return data.availablePoints
}
}
}
return undefined
} catch (error) {
console.error(`[Dashboard] Error loading points for ${email}:`, error)
return undefined
}
}
/**
* Try to load all points from sessions
*/
export function loadAllPointsFromSessions(): Map<string, number> {
const pointsMap = new Map<string, number>()
try {
const sessionsDir = path.join(process.cwd(), 'sessions')
if (!fs.existsSync(sessionsDir)) {
return pointsMap
}
const files = fs.readdirSync(sessionsDir)
for (const filename of files) {
if (!filename.endsWith('.json')) continue
try {
const filepath = path.join(sessionsDir, filename)
const data = JSON.parse(fs.readFileSync(filepath, 'utf-8'))
const email = data.email || data.account?.email
const points = data.points || data.availablePoints
if (email && points !== undefined) {
pointsMap.set(email, points)
}
} catch (error) {
// Skip invalid files
continue
}
}
} catch (error) {
console.error('[Dashboard] Error loading points from sessions:', error)
}
return pointsMap
}
/**
* Try to load points from job state
*/
export function loadPointsFromJobState(email: string): number | undefined {
try {
const jobStateDir = path.join(process.cwd(), 'sessions', 'job-state')
if (!fs.existsSync(jobStateDir)) {
return undefined
}
const files = fs.readdirSync(jobStateDir)
for (const filename of files) {
if (!filename.endsWith('.json')) continue
try {
const filepath = path.join(jobStateDir, filename)
const data = JSON.parse(fs.readFileSync(filepath, 'utf-8'))
if (data.email === email || data.account === email) {
return data.points || data.availablePoints
}
} catch (error) {
continue
}
}
return undefined
} catch (error) {
console.error(`[Dashboard] Error loading job state for ${email}:`, error)
return undefined
}
}

View File

@@ -10,8 +10,19 @@ export const apiRouter = Router()
// GET /api/status - Bot status // GET /api/status - Bot status
apiRouter.get('/status', (_req: Request, res: Response) => { apiRouter.get('/status', (_req: Request, res: Response) => {
try { try {
const status = dashboardState.getStatus() const accounts = dashboardState.getAccounts()
res.json(status)
// If no accounts loaded yet, try to load them
if (accounts.length === 0) {
try {
const loadedAccounts = loadAccounts()
dashboardState.initializeAccounts(loadedAccounts.map(a => a.email))
} catch (error) {
console.error('[Dashboard] Failed to load accounts for status:', error)
}
}
res.json(dashboardState.getStatus())
} catch (error) { } catch (error) {
res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }) res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' })
} }
@@ -20,7 +31,19 @@ apiRouter.get('/status', (_req: Request, res: Response) => {
// GET /api/accounts - List all accounts with masked emails // GET /api/accounts - List all accounts with masked emails
apiRouter.get('/accounts', (_req: Request, res: Response) => { apiRouter.get('/accounts', (_req: Request, res: Response) => {
try { try {
const accounts = dashboardState.getAccounts() let accounts = dashboardState.getAccounts()
// If no accounts in state, try to load from config
if (accounts.length === 0) {
try {
const loadedAccounts = loadAccounts()
dashboardState.initializeAccounts(loadedAccounts.map(a => a.email))
accounts = dashboardState.getAccounts()
} catch (error) {
console.error('[Dashboard] Failed to load accounts:', error)
}
}
res.json(accounts) res.json(accounts)
} catch (error) { } catch (error) {
res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' }) res.status(500).json({ error: error instanceof Error ? error.message : 'Unknown error' })

View File

@@ -1,4 +1,5 @@
import { MicrosoftRewardsBot } from '../index' import { MicrosoftRewardsBot } from '../index'
import { loadAllPointsFromSessions, loadPointsFromJobState } from './SessionLoader'
export interface DashboardStatus { export interface DashboardStatus {
running: boolean running: boolean
@@ -133,12 +134,23 @@ class DashboardState {
// Initialize accounts from config // Initialize accounts from config
public initializeAccounts(emails: string[]): void { public initializeAccounts(emails: string[]): void {
// Load points from sessions if available
const pointsMap = loadAllPointsFromSessions()
for (const email of emails) { for (const email of emails) {
if (!this.accounts.has(email)) { if (!this.accounts.has(email)) {
// Try to get points from session or job state
let points = pointsMap.get(email)
if (points === undefined) {
points = loadPointsFromJobState(email)
}
this.accounts.set(email, { this.accounts.set(email, {
email, email,
maskedEmail: this.maskEmail(email), maskedEmail: this.maskEmail(email),
status: 'idle' status: 'idle',
points: points,
lastSync: points !== undefined ? new Date().toISOString() : undefined
}) })
} }
} }