mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
Merge pull request #391 from diegosouzapw/fix/multi-issues-390-340-378
fix(media,auth,oauth): hide unconfigured local providers, round-robin improvement, OAuth popup fix
This commit is contained in:
@@ -419,7 +419,46 @@ export default function MediaPageClient() {
|
||||
// Transcription-specific
|
||||
const [audioFile, setAudioFile] = useState<File | null>(null);
|
||||
|
||||
const currentProviders = PROVIDER_MODELS[activeTab] ?? [];
|
||||
// Fix #390: Track which local providers (sdwebui, comfyui) are actually configured
|
||||
// so we can hide them when they haven't been set up in the providers page
|
||||
const LOCAL_PROVIDERS = ["sdwebui", "comfyui"];
|
||||
const [configuredLocalProviders, setConfiguredLocalProviders] = useState<Set<string>>(
|
||||
new Set(LOCAL_PROVIDERS) // Optimistic: show all until we know otherwise
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
// Fetch configured provider connections to determine which local providers are set up
|
||||
fetch("/api/providers")
|
||||
.then((r) => r.json())
|
||||
.then((data) => {
|
||||
const connections: { provider?: string; testStatus?: string }[] = Array.isArray(data)
|
||||
? data
|
||||
: (data?.connections ?? data?.providers ?? []);
|
||||
const configured = new Set<string>();
|
||||
for (const conn of connections) {
|
||||
const pId = conn?.provider;
|
||||
if (pId && LOCAL_PROVIDERS.includes(pId)) {
|
||||
configured.add(pId);
|
||||
}
|
||||
}
|
||||
// Only update if at least one local provider was found, otherwise keep optimistic
|
||||
if (configured.size > 0) {
|
||||
setConfiguredLocalProviders(configured);
|
||||
} else {
|
||||
// No local providers configured — hide sdwebui/comfyui
|
||||
setConfiguredLocalProviders(new Set());
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// On error, keep showing all (fail-open)
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
// Filter out unconfigured local providers from the provider list
|
||||
const currentProviders = (PROVIDER_MODELS[activeTab] ?? []).filter(
|
||||
(p) => !LOCAL_PROVIDERS.includes(p.id) || configuredLocalProviders.has(p.id)
|
||||
);
|
||||
const currentModels = currentProviders.find((p) => p.id === selectedProvider)?.models ?? [];
|
||||
|
||||
const switchTab = (tab: Modality) => {
|
||||
|
||||
@@ -433,6 +433,51 @@ export default function OAuthModal({
|
||||
};
|
||||
}, [authData, exchangeTokens]);
|
||||
|
||||
// Fix #344: Detect when OAuth popup is closed without completing authorization
|
||||
// Some providers (like iFlow) redirect to their own chat UI instead of sending a callback,
|
||||
// leaving the modal stuck at "Waiting for Authorization" forever.
|
||||
useEffect(() => {
|
||||
if (step !== "waiting" || isDeviceCode || !popupRef.current) return;
|
||||
|
||||
let closed = false;
|
||||
const popupClosedInterval = setInterval(() => {
|
||||
if (callbackProcessedRef.current) {
|
||||
clearInterval(popupClosedInterval);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (popupRef.current?.closed) {
|
||||
closed = true;
|
||||
clearInterval(popupClosedInterval);
|
||||
// Popup was closed without completing OAuth — switch to manual input mode
|
||||
// so user can paste the callback URL from their browser address bar
|
||||
if (step === "waiting") {
|
||||
setStep("input");
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Cross-origin access may throw — ignore
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
// Safety timeout: 5 minutes
|
||||
const safetyTimeout = setTimeout(
|
||||
() => {
|
||||
if (!callbackProcessedRef.current && step === "waiting") {
|
||||
clearInterval(popupClosedInterval);
|
||||
setStep("input");
|
||||
}
|
||||
},
|
||||
5 * 60 * 1000
|
||||
);
|
||||
|
||||
return () => {
|
||||
clearInterval(popupClosedInterval);
|
||||
clearTimeout(safetyTimeout);
|
||||
};
|
||||
|
||||
}, [step, isDeviceCode]);
|
||||
|
||||
// Handle manual URL input
|
||||
const handleManualSubmit = async () => {
|
||||
try {
|
||||
@@ -471,9 +516,13 @@ export default function OAuthModal({
|
||||
</span>
|
||||
</div>
|
||||
<h3 className="text-lg font-semibold mb-2">Waiting for Authorization</h3>
|
||||
<p className="text-sm text-text-muted mb-4">
|
||||
<p className="text-sm text-text-muted mb-2">
|
||||
Complete the authorization in the popup window.
|
||||
</p>
|
||||
<p className="text-xs text-text-muted mb-4 opacity-70">
|
||||
If the popup closes without redirecting back (e.g. iFlow), this dialog will
|
||||
automatically switch to manual URL input mode.
|
||||
</p>
|
||||
<Button variant="ghost" onClick={() => setStep("input")}>
|
||||
Popup blocked? Enter URL manually
|
||||
</Button>
|
||||
|
||||
@@ -382,7 +382,13 @@ export async function getProviderCredentials(
|
||||
});
|
||||
} else {
|
||||
// Pick the least recently used (excluding current if possible)
|
||||
// Also penalize accounts with high backoffLevel (previously rate-limited)
|
||||
// so they don't get immediately re-selected after cooldown (#340)
|
||||
const sortedByOldest = [...orderedConnections].sort((a: any, b: any) => {
|
||||
// Penalize previously rate-limited accounts (backoffLevel > 0)
|
||||
const aBackoff = a.backoffLevel || 0;
|
||||
const bBackoff = b.backoffLevel || 0;
|
||||
if (aBackoff !== bBackoff) return aBackoff - bBackoff; // lower backoff first
|
||||
if (!a.lastUsedAt && !b.lastUsedAt) return (a.priority || 999) - (b.priority || 999);
|
||||
if (!a.lastUsedAt) return -1;
|
||||
if (!b.lastUsedAt) return 1;
|
||||
@@ -404,7 +410,11 @@ export async function getProviderCredentials(
|
||||
} else {
|
||||
// Fallback scenario: excluded an account due to failure
|
||||
// Always pick the least recently used to ensure proper cycling
|
||||
// Also penalize accounts with high backoffLevel (#340)
|
||||
const sortedByOldest = [...orderedConnections].sort((a: any, b: any) => {
|
||||
const aBackoff = a.backoffLevel || 0;
|
||||
const bBackoff = b.backoffLevel || 0;
|
||||
if (aBackoff !== bBackoff) return aBackoff - bBackoff;
|
||||
if (!a.lastUsedAt && !b.lastUsedAt) return (a.priority || 999) - (b.priority || 999);
|
||||
if (!a.lastUsedAt) return -1;
|
||||
if (!b.lastUsedAt) return 1;
|
||||
|
||||
Reference in New Issue
Block a user