mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-02 13:22:11 +03:00
OmniRoute is an intelligent API gateway that unifies 20+ AI providers behind a single OpenAI-compatible endpoint. Features include intelligent routing with 6 strategies, multi-format translation (OpenAI/Claude/Gemini/Responses API), circuit breakers, semantic caching, combo fallback chains, real-time health monitoring, and a full dashboard with provider management, analytics, and CLI tool integration. Key highlights: - 20+ providers (Claude Code, Codex, Gemini CLI, GitHub Copilot, iFlow, Qwen, Kiro, etc.) - 6 routing strategies (Fill First, Round Robin, P2C, Random, Least Used, Cost Optimized) - Export/Import database backup with full archive support - Translator Playground with 4 modes (Playground, Chat Tester, Test Bench, Live Monitor) - 100% TypeScript across src/ and open-sse/ - Docker support with multi-stage builds - Comprehensive documentation and 9 dashboard screenshots
151 lines
4.1 KiB
TypeScript
151 lines
4.1 KiB
TypeScript
/**
|
|
* Shared formatting utilities — DRY extraction from duplicated functions
|
|
* across RequestLoggerV2.js, UsageAnalytics.js, ProxyLogger.js
|
|
*
|
|
* Prevents copy-paste duplication and provides a single source of truth.
|
|
*/
|
|
|
|
/**
|
|
* Format an ISO date string to a localized time string (HH:MM:SS).
|
|
* @param {string} isoString - ISO 8601 date string
|
|
* @returns {string}
|
|
*/
|
|
export function formatTime(isoString) {
|
|
try {
|
|
const d = new Date(isoString);
|
|
return d.toLocaleTimeString("en-US", {
|
|
hour12: false,
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
second: "2-digit",
|
|
});
|
|
} catch {
|
|
return "-";
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Format a duration in milliseconds to a human-readable string.
|
|
* @param {number} ms - Duration in milliseconds
|
|
* @returns {string} e.g., "42ms", "1.2s", "-"
|
|
*/
|
|
export function formatDuration(ms) {
|
|
if (!ms) return "-";
|
|
if (ms < 1000) return `${ms}ms`;
|
|
return `${(ms / 1000).toFixed(1)}s`;
|
|
}
|
|
|
|
/**
|
|
* Format an ISO date to a full date+time string (pt-BR locale).
|
|
* @param {string} iso - ISO 8601 date string
|
|
* @returns {string}
|
|
*/
|
|
export function formatDateTime(iso) {
|
|
try {
|
|
const d = new Date(iso);
|
|
return d.toLocaleDateString("pt-BR") + ", " + d.toLocaleTimeString("en-US", { hour12: false });
|
|
} catch {
|
|
return iso;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Mask a string by showing only start and end characters.
|
|
* @param {string} value - Value to mask
|
|
* @param {number} start - Number of characters to show at start (default: 2)
|
|
* @param {number} end - Number of characters to show at end (default: 2)
|
|
* @returns {string}
|
|
*/
|
|
export function maskSegment(value, start = 2, end = 2) {
|
|
if (!value) return "";
|
|
if (value.length <= start + end) return `${value.slice(0, 1)}***`;
|
|
return `${value.slice(0, start)}***${value.slice(-end)}`;
|
|
}
|
|
|
|
/**
|
|
* Mask an email or account string for display.
|
|
* @param {string} account - Account identifier (email or username)
|
|
* @returns {string}
|
|
*/
|
|
export function maskAccount(account) {
|
|
if (!account || account === "-") return "-";
|
|
const atIdx = account.indexOf("@");
|
|
if (atIdx > 3) {
|
|
return account.slice(0, 3) + "***" + account.slice(atIdx);
|
|
}
|
|
if (account.length > 8) {
|
|
return account.slice(0, 5) + "***";
|
|
}
|
|
return account;
|
|
}
|
|
|
|
/**
|
|
* Format an API key label, masking both name and ID.
|
|
* @param {string} apiKeyName - Human-readable name of the key
|
|
* @param {string} apiKeyId - Unique ID of the key
|
|
* @returns {string}
|
|
*/
|
|
export function formatApiKeyLabel(apiKeyName, apiKeyId) {
|
|
if (!apiKeyName && !apiKeyId) return "—";
|
|
const maskedName = apiKeyName ? maskSegment(apiKeyName, 2, 1) : "key";
|
|
if (!apiKeyId) return maskedName;
|
|
return `${maskedName} (${maskSegment(apiKeyId, 4, 4)})`;
|
|
}
|
|
|
|
/**
|
|
* Mask a sensitive key for log output.
|
|
* @param {string} key - API key or token to mask
|
|
* @returns {string}
|
|
*/
|
|
export function maskKey(key) {
|
|
if (!key || key.length < 8) return "***";
|
|
return `${key.slice(0, 4)}...${key.slice(-4)}`;
|
|
}
|
|
|
|
/**
|
|
* Format large numbers with K/M/B suffixes.
|
|
* @param {number} n - Number to format
|
|
* @returns {string}
|
|
*/
|
|
export function fmtCompact(n) {
|
|
if (n >= 1_000_000_000) return `${(n / 1_000_000_000).toFixed(1)}B`;
|
|
if (n >= 1_000_000) return `${(n / 1_000_000).toFixed(1)}M`;
|
|
if (n >= 1_000) return `${(n / 1_000).toFixed(1)}K`;
|
|
return new Intl.NumberFormat().format(n || 0);
|
|
}
|
|
|
|
/**
|
|
* Format a number with full locale formatting.
|
|
* @param {number} n - Number to format
|
|
* @returns {string}
|
|
*/
|
|
export function fmtFull(n) {
|
|
return new Intl.NumberFormat().format(n || 0);
|
|
}
|
|
|
|
/**
|
|
* Format a cost value with dollar sign.
|
|
* @param {number} n - Cost value
|
|
* @returns {string}
|
|
*/
|
|
export function fmtCost(n) {
|
|
return `$${(n || 0).toFixed(2)}`;
|
|
}
|
|
|
|
/**
|
|
* Truncate a URL for compact display.
|
|
* @param {string} url - Full URL
|
|
* @param {number} max - Maximum characters (default: 50)
|
|
* @returns {string}
|
|
*/
|
|
export function truncateUrl(url, max = 50) {
|
|
if (!url) return "-";
|
|
try {
|
|
const parsed = new URL(url);
|
|
const display = parsed.hostname + parsed.pathname;
|
|
return display.length > max ? display.slice(0, max) + "…" : display;
|
|
} catch {
|
|
return url.length > max ? url.slice(0, max) + "…" : url;
|
|
}
|
|
}
|