feat: add UPSTREAM_URL validation and error handling in proxy request

This commit is contained in:
Praveen Thirumurugan
2025-10-18 01:57:44 +05:30
parent 1b3ce75155
commit 4c6610eecc
2 changed files with 32 additions and 14 deletions
+6 -4
View File
@@ -7,11 +7,11 @@ A lightweight, production-ready OpenAI-compatible proxy server that seamlessly f
| Environment Variable | Description | Default Value | | Environment Variable | Description | Default Value |
|----------------------|-------------|-----------------| |----------------------|-------------|-----------------|
| `PORT` | Server port | `3007` | | `PORT` | Server port | `3007` |
| `UPSTREAM_URL` | Your LLM endpoint URL | `""` | | `UPSTREAM_URL` | Your LLM endpoint URL | **Required** |
| `DATABASE_URL` | PostgreSQL connection string for logging | `""` | | `DATABASE_URL` | PostgreSQL connection string for logging | **Required** |
| `DATABASE_TABLE` | Name of the table to store the logs | `"llm_proxy"` | | `DATABASE_TABLE` | Name of the table to store the logs | `"llm_proxy"` |
### Cost Calculation ## 💰 Cost Calculation
The cost is calculated based on the model and token usage with configurable pricing per model. The cost is calculated based on the model and token usage with configurable pricing per model.
@@ -27,7 +27,9 @@ export const MODEL_COSTS: Record<string, CostConfig> = {
You can add more models to the `MODEL_COSTS` object to support your specific LLM providers. You can add more models to the `MODEL_COSTS` object to support your specific LLM providers.
## 📊 PostgreSQL Table Schema ## 📊 Database Table Schema
Before running the proxy, you need to create the table in the database.
```sql ```sql
CREATE TABLE IF NOT EXISTS <DATABASE_TABLE> ( CREATE TABLE IF NOT EXISTS <DATABASE_TABLE> (
+26 -10
View File
@@ -12,6 +12,12 @@ import { calculateCost } from "./cost";
const PORT = Number(process.env.PORT || 3007); const PORT = Number(process.env.PORT || 3007);
const UPSTREAM_URL = (process.env.UPSTREAM_URL || "").replace(/\/+$/, ""); const UPSTREAM_URL = (process.env.UPSTREAM_URL || "").replace(/\/+$/, "");
// Validate UPSTREAM_URL is configured
if (!UPSTREAM_URL) {
console.error("❌ UPSTREAM_URL environment variable is required");
process.exit(1);
}
// --- PostgreSQL connection --- // --- PostgreSQL connection ---
const pool = new Pool({ const pool = new Pool({
connectionString: process.env.DATABASE_URL, connectionString: process.env.DATABASE_URL,
@@ -80,16 +86,26 @@ const server = http.createServer(async (req, res) => {
const bodyBuf = method === "POST" ? await readBody(req) : Buffer.from(""); const bodyBuf = method === "POST" ? await readBody(req) : Buffer.from("");
const requestJson = bodyBuf.length ? JSON.parse(bodyBuf.toString()) : null; const requestJson = bodyBuf.length ? JSON.parse(bodyBuf.toString()) : null;
const upstreamRes = await fetch(UPSTREAM_URL + path + url.search, { const targetUrl = UPSTREAM_URL + path + url.search;
method,
headers: { let upstreamRes;
"Content-Type": (req.headers["content-type"] as string) || "application/json", try {
Authorization: auth.toString(), upstreamRes = await fetch(targetUrl, {
}, method,
// @ts-ignore headers: {
duplex: "half", "Content-Type": (req.headers["content-type"] as string) || "application/json",
body: method === "POST" ? bodyBuf.toString() : undefined, Authorization: auth.toString(),
}); },
// @ts-ignore
duplex: "half",
body: method === "POST" ? bodyBuf.toString() : undefined,
});
} catch (fetchError: any) {
console.error("Fetch error:", fetchError.message, "URL:", targetUrl);
res.statusCode = 502;
res.end(JSON.stringify({ error: "Failed to connect to upstream", message: fetchError.message }));
return;
}
const contentType = upstreamRes.headers.get("content-type") || "application/json"; const contentType = upstreamRes.headers.get("content-type") || "application/json";
res.statusCode = upstreamRes.status; res.statusCode = upstreamRes.status;