/** * QM Session Actions - Session management and creation */ import type { BaseHandler, ExecutionContext } from '@stock-bot/handlers'; import { BunRequestInit } from '@stock-bot/utils'; import { getQmHeaders, QM_CONFIG, QM_SESSION_IDS, SESSION_CONFIG } from '../shared/config'; import { QMSessionManager } from '../shared/session-manager'; import { QMSession } from '../shared/types'; /** * Check existing sessions and queue creation jobs for needed sessions * This is the main session management function that handles cleanup, maintenance, and initialization */ export async function checkSessions( this: BaseHandler, _input: unknown, _context: ExecutionContext ): Promise<{ cleaned: number; queued: number; message: string; }> { this.logger.info('Checking QM sessions'); const sessionManager = QMSessionManager.getInstance(); // Initialize with cache provider and logger sessionManager.initialize(this.cache, this.logger); // Always load fresh data from cache (don't rely on initialization flag) await sessionManager.loadFromCache(); // Cleanup failed sessions (this now handles its own cache sync) const cleanedCount = await sessionManager.cleanupFailedSessions(); // Check which session IDs need more sessions and queue creation jobs let queuedCount = 0; for (const [sessionType, sessionId] of Object.entries(QM_SESSION_IDS)) { this.logger.debug(`Checking session ID: ${sessionId}`); if (await sessionManager.needsMoreSessions(sessionId)) { const currentCount = sessionManager.getSessions(sessionId).length; const neededSessions = SESSION_CONFIG.MAX_SESSIONS - currentCount; // Queue up to 10 at a time to avoid overwhelming the system const toQueue = Math.min(neededSessions, 20); for (let i = 0; i < toQueue; i++) { await this.scheduleOperation('create-session', { sessionId, sessionType }, { delay: i * 2000, // Stagger creation by 2 seconds }); queuedCount++; } this.logger.info(`Queued ${toQueue} jobs to create sessions for ${sessionType}`, { currentCount, targetCount: SESSION_CONFIG.MAX_SESSIONS, }); } } this.logger.info('QM session check completed', { cleaned: cleanedCount, queued: queuedCount, }); return { cleaned: cleanedCount, queued: queuedCount, message: `Session check completed: cleaned ${cleanedCount}, queued ${queuedCount}`, }; } /** * Create a single session for a specific session ID */ interface CreateSessionInput { sessionId?: string; sessionType?: string; } export async function createSession( this: BaseHandler, input: CreateSessionInput ): Promise<{ sessionId: string; status: string; sessionType: string, session?: QMSession }> { const { sessionId, sessionType = 'LOOKUP' } = input || {}; const sessionManager = QMSessionManager.getInstance(); // Get the actual session ID from config const actualSessionId = sessionId || QM_SESSION_IDS[sessionType as keyof typeof QM_SESSION_IDS]; if (!actualSessionId) { throw new Error(`Invalid session type: ${sessionType}`); } // Initialize with cache provider and logger sessionManager.initialize(this.cache, this.logger); try { // Get proxy from proxy service const proxyUrl: string | null = this.proxy ? this.proxy.getProxy() : null; if (!proxyUrl) { this.logger.warn(`No proxy available for session type ${sessionType}`); throw new Error(`No proxy available for session type ${sessionType}`); } // Authenticate with QM API inline const sessionUrl = `${QM_CONFIG.BASE_URL}${QM_CONFIG.SESSION_PATH}/${sessionId}`; // Build request options const sessionRequest: BunRequestInit = { proxy: proxyUrl || undefined, headers: getQmHeaders(), }; this.logger.debug('Authenticating with QM API', { sessionUrl, sessionRequest }); const sessionResponse = await fetch(sessionUrl, sessionRequest); // Check if authentication was successful if (sessionResponse.status === 200 || sessionResponse.status === 302) { this.logger.info('QM authentication successful', { status: sessionResponse.status, }); }else{ this.logger.warn('QM authentication failed', { status: sessionResponse.status, statusText: sessionResponse.statusText, }); throw new Error(`QM authentication failed with status ${sessionResponse.status}`); } const sessionData = await sessionResponse.json(); // Add token to headers sessionRequest.headers['Datatool-Token'] = sessionData.token; // Create session object with unique ID const session: QMSession = { uuid: `${sessionType.toLowerCase()}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`, proxy: proxyUrl, headers: sessionRequest.headers, successfulCalls: 0, failedCalls: 0, lastUsed: new Date(), createdAt: new Date(), }; // Add session to manager (this now handles cache sync) await sessionManager.addSession(actualSessionId, session); this.logger.info(`Successfully created session for ${sessionType}`, { session }); return { sessionId: actualSessionId, status: 'created', sessionType, session, }; } catch (error) { this.logger.error(`Failed to create session for ${sessionType}`, { error }); return { sessionId: actualSessionId, status: 'error', sessionType, }; } }