UNPKG

mya-cli

Version:

MYA - AI-Powered Stock & Options Analysis CLI Tool

383 lines 13.8 kB
/** * MYA API Backend - Multi-User Architecture with RabbitMQ + Cloudflare KV * Main entry point for the backend API * * This module provides the core functionality for the MYA trading intelligence platform, * including user authentication, session management, and analysis request processing. * * Last updated: July 7, 2025 */ import { HybridUserContextManager } from './utils/hybrid-user-context.js'; /** * Global services for multi-user support */ let hybridContextManager; /** * Initialize application services */ export async function initializeServices(config) { try { // Initialize hybrid context manager hybridContextManager = HybridUserContextManager.getInstance({ apiUrl: config.apiUrl, kvNamespace: config.kvNamespace, sessionTtl: 30 * 24 * 60 * 60, // 30 days for persistent login queueTtl: 300, // 5 minutes maxRetries: 3, env: config.env, // Pass environment variables to context manager }); await hybridContextManager.initialize(); console.log('✅ All services initialized successfully'); } catch (error) { console.error('❌ Failed to initialize services:', error); throw error; } } /** * Process authentication request * Initiates the authentication flow by sending an OTP to the user's email * * @param email - User's email address * @param _userAgent - User agent string (unused, kept for API compatibility) * @param _ipAddress - IP address (unused, kept for API compatibility) * @param env - Optional environment object for Worker contexts * @returns Authentication result with method ID for OTP verification */ export async function processAuth(email, _userAgent, _ipAddress, env) { try { // Import the Stytch provider components const { StytchAuthProvider, StytchConfigFactory } = await import('./auth/stytch-auth-provider.js'); let envProvider; if (env) { // Use Worker environment provider for Cloudflare Workers const { WorkerEnvironmentProvider } = await import('./storage/worker-env-provider.js'); envProvider = new WorkerEnvironmentProvider(env); } else { // Use file environment provider for CLI/local development const { FileEnvironmentProvider } = await import('./storage/env-provider.js'); envProvider = new FileEnvironmentProvider(); } // Create Stytch configuration const configFactory = new StytchConfigFactory(envProvider); const config = configFactory.createConfig(); // Create auth provider and send OTP const authProvider = new StytchAuthProvider(config); const otpResponse = await authProvider.sendOtpCode(email); return { success: true, method_id: otpResponse.method_id, methodId: otpResponse.method_id, user_id: otpResponse.user_id, }; } catch (error) { console.error('Auth error:', error); return { success: false, error: 'Authentication failed', details: error instanceof Error ? error.message : String(error), }; } } /** * Verify OTP and create session * Completes the authentication flow by verifying the OTP code and creating a user session * * @param methodId - Method ID from the authentication process * @param code - OTP code entered by the user * @param email - User's email address * @param _userAgent - User agent string (unused, kept for API compatibility) * @param _ipAddress - IP address (unused, kept for API compatibility) * @returns Session creation result with user and session details */ export async function verifyOtpAndCreateSession(methodId, code, email, _userAgent, _ipAddress, env) { try { console.log('DEBUG: Starting OTP verification with methodId:', methodId, 'code:', code); // Ensure hybrid context manager is initialized if (!hybridContextManager) { // Get the KV namespace from the environment const envWithKV = env; const globalWithKV = globalThis; const kvNamespace = envWithKV?.KV_NAMESPACE || globalWithKV.env?.KV_NAMESPACE || globalWithKV.KV_NAMESPACE; // Initialize with default config for verification hybridContextManager = HybridUserContextManager.getInstance({ apiUrl: 'http://localhost:8787', kvNamespace: kvNamespace || {}, // Fallback if KV not available sessionTtl: 30 * 24 * 60 * 60, // 30 days queueTtl: 300, // 5 minutes maxRetries: 3, }); await hybridContextManager.initialize(); } // Import the Stytch provider components const { StytchAuthProvider, StytchConfigFactory } = await import('./auth/stytch-auth-provider.js'); const { WorkerEnvironmentProvider } = await import('./storage/worker-env-provider.js'); // Create Stytch configuration using Worker environment const workerEnv = env; const envProvider = new WorkerEnvironmentProvider(workerEnv); const configFactory = new StytchConfigFactory(envProvider); const config = configFactory.createConfig(); // Create auth provider and verify OTP const authProvider = new StytchAuthProvider(config); const verifyResponse = await authProvider.verifyOtpCode(methodId, code); if (!verifyResponse.session_token) { return { success: false, error: 'Invalid OTP code', }; } // Generate machine fingerprint const machineId = await hybridContextManager.generateMachineFingerprint({ userAgent: _userAgent, platform: 'cli', architecture: process.arch, hostname: 'unknown', ipAddress: _ipAddress, }); // Create session const sessionData = { userId: verifyResponse.user_id, email, machineId, sessionToken: verifyResponse.session_token, expiresAt: Date.now() + 30 * 24 * 60 * 60 * 1000, // 30 days permissions: ['analyze', 'options', 'volatility', 'earnings', 'announcements', 'benchmark'], metadata: { userAgent: _userAgent, ipAddress: _ipAddress, deviceName: 'CLI', osInfo: process.platform, }, }; const session = await hybridContextManager.createSession(sessionData); // Store session in KV for CLI access const envWithKV = env; if (envWithKV?.KV_NAMESPACE) { const sessionKey = `cli_session:${verifyResponse.user_id}:${machineId}`; await envWithKV.KV_NAMESPACE.put(sessionKey, JSON.stringify({ ...sessionData, sessionJwt: verifyResponse.session_jwt, }), { expirationTtl: 30 * 24 * 60 * 60, // 30 days }); } return { success: true, userId: verifyResponse.user_id, machineId, sessionToken: verifyResponse.session_token, sessionJwt: verifyResponse.session_jwt, expiresAt: session.expiresAt, }; } catch (error) { console.error('Verify OTP error:', error); // More detailed error logging if (error instanceof Error) { console.error('Error message:', error.message); console.error('Error stack:', error.stack); } return { success: false, error: error instanceof Error ? error.message : 'Verification failed', }; } } /** * Validate session */ export async function validateSession(userId, machineId, sessionToken) { try { const isValid = await hybridContextManager.setUserContext(userId, machineId, sessionToken); if (!isValid) { return { valid: false, error: 'Session invalid or expired' }; } const session = hybridContextManager.getCurrentContext(); return { valid: true, session }; } catch (error) { console.error('Session validation error:', error); return { valid: false, error: 'Session validation failed' }; } } /** * Submit analysis request to queue * Queues an analysis request for processing based on the specified type * * @param userId - Unique user identifier * @param machineId - Unique machine identifier * @param analysisType - Type of analysis (double, analyze, earnings, announcements) * @param parameters - Additional parameters for the analysis * @returns Submission result with request ID for tracking */ export async function submitAnalysisRequest(userId, machineId, analysisType, parameters) { try { // Set user context await hybridContextManager.setUserContext(userId, machineId, ''); const result = await hybridContextManager.submitAnalysisRequest(analysisType, { ...parameters, priority: 1, waitForResult: false, }); return { success: true, requestId: result.requestId, status: result.status, }; } catch (error) { console.error('Submit analysis request error:', error); return { success: false, error: 'Failed to submit request', }; } } /** * Get analysis result * Retrieves the result of a previously submitted analysis request * * @param requestId - Unique request identifier * @param userId - Unique user identifier * @returns Analysis result or status information */ export async function getAnalysisResult(requestId, userId) { try { // For now, return a placeholder since we need to implement result retrieval return { requestId, userId, status: 'processing', message: 'Analysis in progress', }; } catch (error) { console.error('Get analysis result error:', error); return { success: false, error: 'Failed to get result', }; } } /** * Process analysis request (for worker/processor) * Internal function to process queued analysis requests * * @param request - Analysis request object from the queue * @returns Processing result */ export async function processAnalysisRequest(request) { try { return await hybridContextManager.processAnalysisRequest(request); } catch (error) { console.error('Process analysis request error:', error); throw error; } } /** * Start processing analysis requests (for worker processes) * Initiates the background processing of queued analysis requests * * @returns Promise that resolves when processing starts */ export async function startProcessingRequests() { try { await hybridContextManager.startProcessingRequests(); } catch (error) { console.error('Failed to start processing requests:', error); throw error; } } /** * Wait for analysis result * Polls for an analysis result until completion or timeout * * @param requestId - Unique request identifier * @param userId - Unique user identifier * @param timeoutMs - Timeout in milliseconds (default: 180 seconds for cache warming + analysis) * @returns Analysis result when ready */ export async function waitForAnalysisResult(requestId, userId, timeoutMs = 180000) { try { return await hybridContextManager.waitForResult(requestId, timeoutMs); } catch (error) { console.error('Wait for analysis result error:', error); return null; } } /** * Logout user */ export async function logoutUser(userId, machineId) { try { await hybridContextManager.invalidateSession(userId, machineId); return { success: true }; } catch (error) { console.error('Logout error:', error); return { success: false, error: 'Logout failed' }; } } /** * Get user status */ export async function getUserStatus(userId, machineId) { try { const isValid = await hybridContextManager.setUserContext(userId, machineId, ''); if (!isValid) { return { authenticated: false }; } const context = hybridContextManager.getCurrentContext(); if (!context) { return { authenticated: false }; } return { authenticated: true, userId: context.userId, email: context.email, machineId: context.machineId, expiresAt: context.expiresAt, permissions: context.permissions, }; } catch (error) { console.error('Get user status error:', error); return { authenticated: false, error: 'Failed to get status' }; } } /** * Get service statistics */ export async function getServiceStats() { try { const queueStats = await hybridContextManager.getQueueStats(); const sessionStats = await hybridContextManager.getSessionStats(); return { queue: queueStats, sessions: sessionStats, }; } catch (error) { console.error('Get service stats error:', error); return { error: 'Failed to get stats' }; } } /** * Cleanup resources */ export async function cleanup() { try { await hybridContextManager.cleanup(); } catch (error) { console.error('Cleanup error:', error); } } // Export the hybrid context manager for use in different environments export { hybridContextManager }; //# sourceMappingURL=index.js.map