mya-cli
Version:
MYA - AI-Powered Stock & Options Analysis CLI Tool
200 lines • 8.34 kB
JavaScript
/**
* Module: Worker Proxy
* Purpose: Handle request forwarding to mya-llm backend with optional queue support
* Dependencies: Cloudflare Workers API, WorkerEnv types, RequestQueue from queue.ts
* Used by: worker.ts
*
* Provides two modes of request handling:
* 1. Direct proxying: Immediate forwarding to backend (fast requests)
* 2. Queued proxying: FIFO queue-based processing (heavy workloads)
*
* Use proxyToBackend for direct requests (auth, health checks)
* Use proxyToBackendQueued for expensive operations (analyze, forecast)
*
* Error Handling:
* - Previous issue: When backend returns HTML error pages (5xx), JSON parsing failed
* - Current fix: Gracefully handle non-JSON responses with detailed error messages
* - Result: Users get meaningful error info about backend configuration issues
* - Root cause errors usually indicate: MYA_LLM_URL not set in wrangler secrets
*/
import { RequestQueue } from './queue.js';
export async function proxyToBackend(c, env, method, path) {
const myaLlmUrl = env.MYA_LLM_URL;
const llmApiToken = env.LLM_API_TOKEN;
if (!myaLlmUrl) {
return c.json({
error: 'Backend service not configured',
details: 'MYA_LLM_URL environment variable must be set to the backend service URL (e.g., https://[username]-mya-llm.hf.space for Hugging Face Spaces deployment)'
}, 503);
}
if (!llmApiToken) {
return c.json({
error: 'Backend authentication not configured',
details: 'LLM_API_TOKEN environment variable must be set with the worker API token for backend authentication'
}, 503);
}
try {
const url = new URL(myaLlmUrl);
url.pathname = path;
const requestInit = {
method,
headers: {
'Content-Type': 'application/json',
'X-Forwarded-For': c.req.header('X-Forwarded-For') || 'unknown',
'X-User-Id': c.get('userId') || 'anonymous',
'X-API-Token': llmApiToken,
},
};
if (method !== 'GET' && method !== 'HEAD') {
const body = await c.req.text();
if (body) {
requestInit.body = body;
}
}
console.log(`[PROXY] ${method} ${path} -> ${url.toString()}`);
const response = await fetch(url.toString(), requestInit);
const responseBody = await response.text();
let responseData;
try {
responseData = JSON.parse(responseBody || '{}');
}
catch {
// Backend returned non-JSON response (likely HTML error)
const isHtmlError = responseBody && responseBody.includes(' proxyToBackend(c, env, 'POST', '/api/v1/analyze'),
analyzeGet: async (c) => {
const jobId = c.req.param('jobId');
return proxyToBackend(c, env, 'GET', `/api/v1/analyze/${jobId}`);
},
forecastPost: async (c) => proxyToBackend(c, env, 'POST', '/api/v1/forecast'),
dailyReportGet: async (c) => proxyToBackend(c, env, 'GET', '/api/v1/daily-report'),
metricsGet: async (c) => proxyToBackend(c, env, 'GET', '/api/v1/learning-metrics'),
authPost: async (c) => proxyToBackend(c, env, 'POST', '/auth'),
authVerifyPost: async (c) => proxyToBackend(c, env, 'POST', '/auth/verify'),
otpVerifyPost: async (c) => proxyToBackend(c, env, 'POST', '/verify-otp'),
recommendationsGet: async (c) => proxyToBackend(c, env, 'GET', '/recommendations/open'),
};
}
//# sourceMappingURL=proxy.js.map