diff --git a/src/app/(dashboard)/dashboard/profile/page.js b/src/app/(dashboard)/dashboard/profile/page.js
index a68d0ee..5ea740d 100644
--- a/src/app/(dashboard)/dashboard/profile/page.js
+++ b/src/app/(dashboard)/dashboard/profile/page.js
@@ -1,11 +1,42 @@
"use client";
+import { useState, useEffect } from "react";
import { Card, Button, Badge, Toggle } from "@/shared/components";
import { useTheme } from "@/shared/hooks/useTheme";
import { APP_CONFIG } from "@/shared/constants/config";
export default function ProfilePage() {
const { theme, setTheme, isDark } = useTheme();
+ const [settings, setSettings] = useState({ fallbackStrategy: "fill-first" });
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ fetch("/api/settings")
+ .then((res) => res.json())
+ .then((data) => {
+ setSettings(data);
+ setLoading(false);
+ })
+ .catch((err) => {
+ console.error("Failed to fetch settings:", err);
+ setLoading(false);
+ });
+ }, []);
+
+ const updateFallbackStrategy = async (strategy) => {
+ try {
+ const res = await fetch("/api/settings", {
+ method: "PATCH",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ fallbackStrategy: strategy }),
+ });
+ if (res.ok) {
+ setSettings(prev => ({ ...prev, fallbackStrategy: strategy }));
+ }
+ } catch (err) {
+ console.error("Failed to update settings:", err);
+ }
+ };
return (
@@ -28,6 +59,31 @@ export default function ProfilePage() {
+ {/* Routing Preferences */}
+
+ Routing Strategy
+
+
+
+
Round Robin
+
+ Cycle through accounts to distribute load
+
+
+
updateFallbackStrategy(settings.fallbackStrategy === "round-robin" ? "fill-first" : "round-robin")}
+ disabled={loading}
+ />
+
+
+ {settings.fallbackStrategy === "round-robin"
+ ? "Currently distributing requests across all available accounts."
+ : "Currently using accounts in priority order (Fill First)."}
+
+
+
+
{/* Theme Preferences */}
Appearance
diff --git a/src/app/api/settings/route.js b/src/app/api/settings/route.js
index 423f182..5f10e09 100644
--- a/src/app/api/settings/route.js
+++ b/src/app/api/settings/route.js
@@ -1,5 +1,5 @@
import { NextResponse } from "next/server";
-import { getSettings } from "@/lib/localDb";
+import { getSettings, updateSettings } from "@/lib/localDb";
export async function GET() {
try {
@@ -10,3 +10,14 @@ export async function GET() {
return NextResponse.json({ error: error.message }, { status: 500 });
}
}
+
+export async function PATCH(request) {
+ try {
+ const body = await request.json();
+ const settings = await updateSettings(body);
+ return NextResponse.json(settings);
+ } catch (error) {
+ console.log("Error updating settings:", error);
+ return NextResponse.json({ error: error.message }, { status: 500 });
+ }
+}
diff --git a/src/sse/services/auth.js b/src/sse/services/auth.js
index 1520042..90c053b 100644
--- a/src/sse/services/auth.js
+++ b/src/sse/services/auth.js
@@ -1,16 +1,16 @@
-import { getProviderConnections, validateApiKey, updateProviderConnection } from "@/lib/localDb";
+import { getProviderConnections, validateApiKey, updateProviderConnection, getSettings } from "@/lib/localDb";
import { isAccountUnavailable, getUnavailableUntil } from "open-sse/services/accountFallback.js";
import * as log from "../utils/logger.js";
/**
* Get provider credentials from localDb
- * Filters out unavailable accounts and returns the highest priority available account
+ * Filters out unavailable accounts and returns the selected account based on strategy
* @param {string} provider - Provider name
* @param {string|null} excludeConnectionId - Connection ID to exclude (for retry with next account)
*/
export async function getProviderCredentials(provider, excludeConnectionId = null) {
const connections = await getProviderConnections({ provider, isActive: true });
-
+
if (connections.length === 0) {
log.warn("AUTH", `No credentials for ${provider}`);
return null;
@@ -28,7 +28,26 @@ export async function getProviderCredentials(provider, excludeConnectionId = nul
return null;
}
- const connection = availableConnections[0];
+ const settings = await getSettings();
+ const strategy = settings.fallbackStrategy || "fill-first";
+
+ let connection;
+ if (strategy === "round-robin") {
+ // Sort by lastUsed (nulls first) to pick the least recently used
+ const sorted = [...availableConnections].sort((a, b) => {
+ if (!a.lastUsedAt && !b.lastUsedAt) return (a.priority || 999) - (b.priority || 999);
+ if (!a.lastUsedAt) return -1;
+ if (!b.lastUsedAt) return 1;
+ return new Date(a.lastUsedAt) - new Date(b.lastUsedAt);
+ });
+ connection = sorted[0];
+
+ // Update lastUsedAt asynchronously
+ updateProviderConnection(connection.id, { lastUsedAt: new Date().toISOString() }).catch(() => {});
+ } else {
+ // Default: fill-first (already sorted by priority in getProviderConnections)
+ connection = availableConnections[0];
+ }
return {
apiKey: connection.apiKey,