stock-bot/apps/stock/data-ingestion/src/handlers/qm/actions/session.action.ts

169 lines
No EOL
5.4 KiB
TypeScript

/**
* 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,
};
}
}