mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-06 15:22:12 +03:00
feat: OmniRoute v1.0.0 — Intelligent AI Gateway & Universal LLM Proxy
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
This commit is contained in:
101
src/shared/utils/apiKey.ts
Normal file
101
src/shared/utils/apiKey.ts
Normal file
@@ -0,0 +1,101 @@
|
||||
import crypto from "crypto";
|
||||
|
||||
// FASE-01: No hardcoded fallback — enforced by secretsValidator at startup
|
||||
if (!process.env.API_KEY_SECRET) {
|
||||
console.error("[SECURITY] API_KEY_SECRET is not set. API key CRC will be insecure.");
|
||||
}
|
||||
const API_KEY_SECRET = process.env.API_KEY_SECRET;
|
||||
|
||||
/**
|
||||
* Generate 6-char random keyId
|
||||
*/
|
||||
function generateKeyId() {
|
||||
const chars = "abcdefghijklmnopqrstuvwxyz0123456789";
|
||||
let result = "";
|
||||
for (let i = 0; i < 6; i++) {
|
||||
result += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate CRC (8-char HMAC)
|
||||
*/
|
||||
function generateCrc(machineId, keyId) {
|
||||
return crypto
|
||||
.createHmac("sha256", API_KEY_SECRET)
|
||||
.update(machineId + keyId)
|
||||
.digest("hex")
|
||||
.slice(0, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate API key with machineId embedded
|
||||
* Format: sk-{machineId}-{keyId}-{crc8}
|
||||
* @param {string} machineId - 16-char machine ID
|
||||
* @returns {{ key: string, keyId: string }}
|
||||
*/
|
||||
export function generateApiKeyWithMachine(machineId) {
|
||||
const keyId = generateKeyId();
|
||||
const crc = generateCrc(machineId, keyId);
|
||||
const key = `sk-${machineId}-${keyId}-${crc}`;
|
||||
return { key, keyId };
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse API key and extract machineId + keyId
|
||||
* Supports both formats:
|
||||
* - New: sk-{machineId}-{keyId}-{crc8}
|
||||
* - Old: sk-{random8}
|
||||
* @param {string} apiKey
|
||||
* @returns {{ machineId: string, keyId: string, isNewFormat: boolean } | null}
|
||||
*/
|
||||
export function parseApiKey(apiKey) {
|
||||
if (!apiKey || !apiKey.startsWith("sk-")) return null;
|
||||
|
||||
const parts = apiKey.split("-");
|
||||
|
||||
// New format: sk-{machineId}-{keyId}-{crc8} = 4 parts
|
||||
if (parts.length === 4) {
|
||||
const [, machineId, keyId, crc] = parts;
|
||||
|
||||
// Validate CRC
|
||||
const expectedCrc = generateCrc(machineId, keyId);
|
||||
if (crc !== expectedCrc) return null;
|
||||
|
||||
return { machineId, keyId, isNewFormat: true };
|
||||
}
|
||||
|
||||
// Old format: sk-{random8} = 2 parts
|
||||
if (parts.length === 2) {
|
||||
return { machineId: null, keyId: parts[1], isNewFormat: false };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify API key CRC (only for new format)
|
||||
* @param {string} apiKey
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function verifyApiKeyCrc(apiKey) {
|
||||
const parsed = parseApiKey(apiKey);
|
||||
if (!parsed) return false;
|
||||
|
||||
// Old format doesn't have CRC, always valid if parsed
|
||||
if (!parsed.isNewFormat) return true;
|
||||
|
||||
// New format already verified in parseApiKey
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if API key is new format (contains machineId)
|
||||
* @param {string} apiKey
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isNewFormatKey(apiKey) {
|
||||
const parsed = parseApiKey(apiKey);
|
||||
return parsed?.isNewFormat === true;
|
||||
}
|
||||
Reference in New Issue
Block a user