mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-01 04:42:10 +03:00
T01 (P1): requested_model column in call_logs - Migration 009_requested_model.sql: ALTER TABLE call_logs ADD COLUMN requested_model - callLogs.ts: INSERT + SELECT updated to include requestedModel field T02 (P1): Strip empty text blocks from nested tool_result.content - New stripEmptyTextBlocks() recursive helper in openai-to-claude.ts - Applied on tool_result content before forwarding to Anthropic - Prevents 400 'text content blocks must be non-empty' errors T03 (P1): Parse x-codex-5h-*/x-codex-7d-* headers for precise quota reset - parseCodexQuotaHeaders() in codex.ts extracts usage/limit/resetAt - getCodexResetTime() returns furthest-out reset timestamp for safe unblocking T04 (P1): X-Session-Id header for external sticky routing - extractExternalSessionId() in sessionManager.ts reads x-session-id, x-omniroute-session, session-id headers with 'ext:' prefix to avoid collisions T06 (P2): account_deactivated permanent expired status on 401 - ACCOUNT_DEACTIVATED_SIGNALS constant + isAccountDeactivated() in accountFallback.ts - Returns 1-year cooldown (effectively permanent) to prevent retrying dead accounts T07 (P2): X-Forwarded-For IP validation - New src/lib/ipUtils.ts with extractClientIp() and getClientIpFromRequest() - Skips 'unknown'/non-IP entries in X-Forwarded-For chain T10 (P2): credits_exhausted distinct account status - CREDITS_EXHAUSTED_SIGNALS + isCreditsExhausted() in accountFallback.ts - Returns 1h cooldown with creditsExhausted flag, distinct from rate_limit 429 T11 (P1): max reasoning_effort -> budget_tokens: 131072 - EFFORT_BUDGETS and THINKING_LEVEL_MAP updated with max: 131072, xhigh: 131072 - Reverse mapping now returns 'max' for full-budget responses - Unit test updated to expect 'max' (was 'high') T12 (P3): Model pricing updates - MiniMax M2.7 / MiniMax-M2.7 / minimax-m2.7-highspeed pricing added T15 (P1): Array content normalization for system/tool messages - normalizeContentToString() helper exported from openai-to-claude.ts - System messages with array content now correctly collapsed to string
57 lines
1.8 KiB
TypeScript
57 lines
1.8 KiB
TypeScript
import { isIP } from "node:net";
|
|
|
|
/**
|
|
* T07: Extract the real client IP from X-Forwarded-For header.
|
|
* Skips invalid entries like "unknown" or empty strings.
|
|
* Falls back to remoteAddress if no valid IP found.
|
|
* Ref: sub2api PR #1135
|
|
*
|
|
* @param xForwardedFor - Value of the X-Forwarded-For header (may be CSV)
|
|
* @param remoteAddress - Fallback from the raw socket (req.socket.remoteAddress)
|
|
* @returns The first valid IP address found, or "unknown"
|
|
*/
|
|
export function extractClientIp(
|
|
xForwardedFor: string | null | undefined,
|
|
remoteAddress: string | undefined
|
|
): string {
|
|
if (xForwardedFor) {
|
|
const entries = xForwardedFor.split(",");
|
|
for (const entry of entries) {
|
|
const trimmed = entry.trim();
|
|
if (trimmed && isIP(trimmed) !== 0) {
|
|
return trimmed; // First valid IP wins
|
|
}
|
|
}
|
|
}
|
|
return remoteAddress?.trim() ?? "unknown";
|
|
}
|
|
|
|
/**
|
|
* Extract client IP from a Request or NextRequest object.
|
|
* Checks X-Forwarded-For, X-Real-IP, CF-Connecting-IP, then socket.
|
|
*/
|
|
export function getClientIpFromRequest(req: {
|
|
headers?: Headers | { get?: (n: string) => string | null };
|
|
socket?: { remoteAddress?: string };
|
|
ip?: string;
|
|
}): string {
|
|
// Helper to get header value from either Headers object or plain object
|
|
const getHeader = (name: string): string | null => {
|
|
if (!req.headers) return null;
|
|
if (typeof (req.headers as Headers).get === "function") {
|
|
return (req.headers as Headers).get(name);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
// Priority: CF-Connecting-IP (Cloudflare) > X-Forwarded-For > X-Real-IP > socket
|
|
const cfIp = getHeader("cf-connecting-ip");
|
|
if (cfIp && isIP(cfIp.trim()) !== 0) return cfIp.trim();
|
|
|
|
const xff = getHeader("x-forwarded-for");
|
|
const realIp = getHeader("x-real-ip");
|
|
const remoteAddress = req.ip ?? req.socket?.remoteAddress;
|
|
|
|
return extractClientIp(xff ?? realIp, remoteAddress);
|
|
}
|