169 lines
No EOL
5.4 KiB
TypeScript
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,
|
|
};
|
|
}
|
|
} |