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
128 lines
3.7 KiB
TypeScript
128 lines
3.7 KiB
TypeScript
import {
|
|
getProviderConnections,
|
|
getModelAliases,
|
|
getCombos,
|
|
getApiKeys,
|
|
updateProviderConnection,
|
|
} from "@/lib/localDb";
|
|
|
|
const CLOUD_URL = process.env.CLOUD_URL || process.env.NEXT_PUBLIC_CLOUD_URL;
|
|
const CLOUD_SYNC_TIMEOUT_MS = Number(process.env.CLOUD_SYNC_TIMEOUT_MS || 12000);
|
|
|
|
export async function fetchWithTimeout(url, options = {}, timeoutMs = CLOUD_SYNC_TIMEOUT_MS) {
|
|
const controller = new AbortController();
|
|
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
try {
|
|
return await fetch(url, { ...options, signal: controller.signal });
|
|
} finally {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sync data to Cloud (shared utility)
|
|
* @param {string} machineId
|
|
* @param {string|null} createdKey - Key created during enable
|
|
*/
|
|
export async function syncToCloud(machineId, createdKey = null) {
|
|
if (!CLOUD_URL) {
|
|
return { error: "NEXT_PUBLIC_CLOUD_URL is not configured" };
|
|
}
|
|
|
|
// Get current data from db
|
|
const providers = await getProviderConnections();
|
|
const modelAliases = await getModelAliases();
|
|
const combos = await getCombos();
|
|
const apiKeys = await getApiKeys();
|
|
|
|
let response;
|
|
try {
|
|
// Send to Cloud
|
|
response = await fetchWithTimeout(`${CLOUD_URL}/sync/${machineId}`, {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({
|
|
providers,
|
|
modelAliases,
|
|
combos,
|
|
apiKeys,
|
|
}),
|
|
});
|
|
} catch (error) {
|
|
const isTimeout = error?.name === "AbortError";
|
|
return { error: isTimeout ? "Cloud sync timeout" : "Cloud sync request failed" };
|
|
}
|
|
|
|
if (!response.ok) {
|
|
const errorText = await response.text();
|
|
const truncated = errorText.length > 200 ? errorText.slice(0, 200) + "…" : errorText;
|
|
console.log(`Cloud sync failed (${response.status}):`, truncated);
|
|
return { error: "Cloud sync failed" };
|
|
}
|
|
|
|
const result = await response.json();
|
|
|
|
// Update local db with tokens from Cloud (providers stored by ID)
|
|
if (result.data && result.data.providers) {
|
|
await updateLocalTokens(result.data.providers);
|
|
}
|
|
|
|
const responseData: any = {
|
|
success: true,
|
|
message: "Synced successfully",
|
|
changes: result.changes,
|
|
};
|
|
|
|
if (createdKey) {
|
|
responseData.createdKey = createdKey;
|
|
}
|
|
|
|
return responseData;
|
|
}
|
|
|
|
/**
|
|
* Update local db with data from Cloud
|
|
* Simple logic: if Cloud is newer, sync entire provider
|
|
* cloudProviders is object keyed by provider ID
|
|
*/
|
|
async function updateLocalTokens(cloudProviders) {
|
|
const localProviders = await getProviderConnections();
|
|
|
|
for (const localProvider of localProviders) {
|
|
const cloudProvider = cloudProviders[localProvider.id];
|
|
if (!cloudProvider) continue;
|
|
|
|
const cloudUpdatedAt = new Date(cloudProvider.updatedAt || 0).getTime();
|
|
const localUpdatedAt = new Date(localProvider.updatedAt || 0).getTime();
|
|
|
|
// Simple logic: if Cloud is newer, sync entire provider
|
|
if (cloudUpdatedAt > localUpdatedAt) {
|
|
const updates = {
|
|
// Tokens
|
|
accessToken: cloudProvider.accessToken,
|
|
refreshToken: cloudProvider.refreshToken,
|
|
expiresAt: cloudProvider.expiresAt,
|
|
expiresIn: cloudProvider.expiresIn,
|
|
|
|
// Provider specific data
|
|
providerSpecificData:
|
|
cloudProvider.providerSpecificData || localProvider.providerSpecificData,
|
|
|
|
// Status fields
|
|
testStatus: cloudProvider.status || "active",
|
|
lastError: cloudProvider.lastError,
|
|
lastErrorAt: cloudProvider.lastErrorAt,
|
|
errorCode: cloudProvider.errorCode,
|
|
rateLimitedUntil: cloudProvider.rateLimitedUntil,
|
|
|
|
// Metadata
|
|
updatedAt: cloudProvider.updatedAt,
|
|
};
|
|
|
|
await updateProviderConnection(localProvider.id, updates);
|
|
}
|
|
}
|
|
}
|
|
|
|
export { CLOUD_URL, CLOUD_SYNC_TIMEOUT_MS };
|