mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix: clean up proxy page redundancy and fix 1proxy sync empty body error (#2052)
Integrated into release/v3.8.0
This commit is contained in:
@@ -1,11 +1,9 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { Card, Button, ProxyConfigModal, Toggle } from "@/shared/components";
|
||||
import { Card, Button, ProxyConfigModal } from "@/shared/components";
|
||||
import { useTranslations } from "next-intl";
|
||||
import ProxyRegistryManager from "./ProxyRegistryManager";
|
||||
import OneproxyTab from "./OneproxyTab";
|
||||
import RequestLimitsTab from "./RequestLimitsTab";
|
||||
|
||||
export default function ProxyTab() {
|
||||
const [proxyModalOpen, setProxyModalOpen] = useState(false);
|
||||
@@ -13,11 +11,6 @@ export default function ProxyTab() {
|
||||
const mountedRef = useRef(true);
|
||||
const t = useTranslations("settings");
|
||||
const tc = useTranslations("common");
|
||||
const [debugMode, setDebugMode] = useState(false);
|
||||
const [usageTokenBuffer, setUsageTokenBuffer] = useState<number | null>(null);
|
||||
const [bufferInput, setBufferInput] = useState("");
|
||||
const [bufferSaving, setBufferSaving] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const loadGlobalProxy = async () => {
|
||||
try {
|
||||
@@ -29,44 +22,6 @@ export default function ProxyTab() {
|
||||
} catch {}
|
||||
};
|
||||
|
||||
const updateDebugMode = async (value: boolean) => {
|
||||
const previousValue = debugMode;
|
||||
setDebugMode(value);
|
||||
try {
|
||||
const res = await fetch("/api/settings", {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ debugMode: value }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
setDebugMode(previousValue);
|
||||
}
|
||||
} catch (err) {
|
||||
setDebugMode(previousValue);
|
||||
console.error("Failed to update debugMode:", err);
|
||||
}
|
||||
};
|
||||
|
||||
const updateUsageTokenBuffer = async () => {
|
||||
const val = parseInt(bufferInput, 10);
|
||||
if (isNaN(val) || val < 0 || val > 50000) return;
|
||||
setBufferSaving(true);
|
||||
try {
|
||||
const res = await fetch("/api/settings", {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ usageTokenBuffer: val }),
|
||||
});
|
||||
if (res.ok) {
|
||||
setUsageTokenBuffer(val);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to update usageTokenBuffer:", err);
|
||||
} finally {
|
||||
setBufferSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
mountedRef.current = true;
|
||||
async function init() {
|
||||
@@ -85,22 +40,6 @@ export default function ProxyTab() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
fetch("/api/settings", { cache: "no-store" })
|
||||
.then((res) => {
|
||||
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
|
||||
return res.json();
|
||||
})
|
||||
.then((data) => {
|
||||
setDebugMode(data.debugMode === true);
|
||||
const buf = typeof data.usageTokenBuffer === "number" ? data.usageTokenBuffer : 2000;
|
||||
setUsageTokenBuffer(buf);
|
||||
setBufferInput(String(buf));
|
||||
setLoading(false);
|
||||
})
|
||||
.catch(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col gap-6">
|
||||
@@ -139,53 +78,6 @@ export default function ProxyTab() {
|
||||
</Card>
|
||||
|
||||
<ProxyRegistryManager />
|
||||
<OneproxyTab />
|
||||
<Card className="p-6 mt-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="font-medium">{t("debugToggle")}</p>
|
||||
</div>
|
||||
<Toggle checked={debugMode} onChange={updateDebugMode} disabled={loading} />
|
||||
</div>
|
||||
</Card>
|
||||
<Card className="p-6 mt-4">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div>
|
||||
<p className="font-medium">Usage Token Buffer</p>
|
||||
<p className="text-sm text-text-muted mt-1">
|
||||
Extra tokens added to reported usage to account for system prompt overhead. Set to 0
|
||||
to report raw provider token counts. Default: 2000. Changes take effect within 30
|
||||
seconds.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
max={50000}
|
||||
value={bufferInput}
|
||||
onChange={(e) => setBufferInput(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") updateUsageTokenBuffer();
|
||||
}}
|
||||
className="w-32 px-3 py-1.5 rounded bg-surface-2 border border-border text-sm text-text-primary"
|
||||
disabled={loading}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="primary"
|
||||
onClick={updateUsageTokenBuffer}
|
||||
disabled={bufferSaving || loading || parseInt(bufferInput, 10) === usageTokenBuffer}
|
||||
>
|
||||
{bufferSaving ? tc("saving") : tc("save")}
|
||||
</Button>
|
||||
{usageTokenBuffer !== null && parseInt(bufferInput, 10) !== usageTokenBuffer && (
|
||||
<span className="text-xs text-text-muted">Current: {usageTokenBuffer}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
<RequestLimitsTab />
|
||||
</div>
|
||||
|
||||
<ProxyConfigModal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState, useEffect, useRef } from "react";
|
||||
import { Card, Button, Badge } from "@/shared/components";
|
||||
import { Card, Button, Badge, Toggle } from "@/shared/components";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
|
||||
const rowCountFormatter = new Intl.NumberFormat("en-US");
|
||||
@@ -73,6 +73,11 @@ export default function SystemStorageTab() {
|
||||
const [dbSettingsLoading, setDbSettingsLoading] = useState(true);
|
||||
const [dbSettingsSaving, setDbSettingsSaving] = useState(false);
|
||||
const [dbStatsRefreshing, setDbStatsRefreshing] = useState(false);
|
||||
const [debugMode, setDebugMode] = useState(false);
|
||||
const [usageTokenBuffer, setUsageTokenBuffer] = useState<number | null>(null);
|
||||
const [bufferInput, setBufferInput] = useState("");
|
||||
const [bufferSaving, setBufferSaving] = useState(false);
|
||||
const [generalLoading, setGeneralLoading] = useState(true);
|
||||
|
||||
const loadBackups = async () => {
|
||||
setBackupsLoading(true);
|
||||
@@ -245,8 +250,65 @@ export default function SystemStorageTab() {
|
||||
useEffect(() => {
|
||||
loadStorageHealth();
|
||||
loadDatabaseSettings();
|
||||
loadGeneralSettings();
|
||||
}, []);
|
||||
|
||||
const loadGeneralSettings = async () => {
|
||||
setGeneralLoading(true);
|
||||
try {
|
||||
const res = await fetch("/api/settings", { cache: "no-store" });
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
setDebugMode(data.debugMode === true);
|
||||
const buf = typeof data.usageTokenBuffer === "number" ? data.usageTokenBuffer : 2000;
|
||||
setUsageTokenBuffer(buf);
|
||||
setBufferInput(String(buf));
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
} finally {
|
||||
setGeneralLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const updateDebugMode = async (value: boolean) => {
|
||||
const previousValue = debugMode;
|
||||
setDebugMode(value);
|
||||
try {
|
||||
const res = await fetch("/api/settings", {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ debugMode: value }),
|
||||
});
|
||||
if (!res.ok) {
|
||||
setDebugMode(previousValue);
|
||||
}
|
||||
} catch (err) {
|
||||
setDebugMode(previousValue);
|
||||
console.error("Failed to update debugMode:", err);
|
||||
}
|
||||
};
|
||||
|
||||
const updateUsageTokenBuffer = async () => {
|
||||
const val = parseInt(bufferInput, 10);
|
||||
if (isNaN(val) || val < 0 || val > 50000) return;
|
||||
setBufferSaving(true);
|
||||
try {
|
||||
const res = await fetch("/api/settings", {
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ usageTokenBuffer: val }),
|
||||
});
|
||||
if (res.ok) {
|
||||
setUsageTokenBuffer(val);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Failed to update usageTokenBuffer:", err);
|
||||
} finally {
|
||||
setBufferSaving(false);
|
||||
}
|
||||
};
|
||||
|
||||
/** Triggers a browser file download from an existing Blob. */
|
||||
const triggerDownload = (blob: Blob, filename: string) => {
|
||||
const url = URL.createObjectURL(blob);
|
||||
@@ -1774,6 +1836,72 @@ export default function SystemStorageTab() {
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Debug Mode */}
|
||||
<div className="mt-6 pt-3 border-t border-border/50">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className="material-symbols-outlined text-[18px] text-text-muted"
|
||||
aria-hidden="true"
|
||||
>
|
||||
bug_report
|
||||
</span>
|
||||
<div>
|
||||
<p className="font-medium">{t("debugToggle")}</p>
|
||||
</div>
|
||||
</div>
|
||||
<Toggle checked={debugMode} onChange={updateDebugMode} disabled={generalLoading} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Usage Token Buffer */}
|
||||
<div className="mt-4 pt-3 border-t border-border/50">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span
|
||||
className="material-symbols-outlined text-[18px] text-text-muted"
|
||||
aria-hidden="true"
|
||||
>
|
||||
pin
|
||||
</span>
|
||||
<div>
|
||||
<p className="font-medium">Usage Token Buffer</p>
|
||||
<p className="text-sm text-text-muted mt-1">
|
||||
Extra tokens added to reported usage to account for system prompt overhead. Set to 0
|
||||
to report raw provider token counts. Default: 2000.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="number"
|
||||
min={0}
|
||||
max={50000}
|
||||
value={bufferInput}
|
||||
onChange={(e) => setBufferInput(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") updateUsageTokenBuffer();
|
||||
}}
|
||||
className="w-32 px-3 py-1.5 rounded bg-surface-2 border border-border text-sm text-text-primary"
|
||||
disabled={generalLoading}
|
||||
/>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="primary"
|
||||
onClick={updateUsageTokenBuffer}
|
||||
disabled={
|
||||
bufferSaving || generalLoading || parseInt(bufferInput, 10) === usageTokenBuffer
|
||||
}
|
||||
>
|
||||
{bufferSaving ? tc("saving") : tc("save")}
|
||||
</Button>
|
||||
{usageTokenBuffer !== null && parseInt(bufferInput, 10) !== usageTokenBuffer && (
|
||||
<span className="text-xs text-text-muted">Current: {usageTokenBuffer}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import ResilienceTab from "./components/ResilienceTab";
|
||||
import CliproxyapiSettingsTab from "./components/CliproxyapiSettingsTab";
|
||||
import PayloadRulesTab from "./components/PayloadRulesTab";
|
||||
import VisionBridgeSettingsTab from "./components/VisionBridgeSettingsTab";
|
||||
import RequestLimitsTab from "./components/RequestLimitsTab";
|
||||
import ModelRoutingSection from "@/shared/components/ModelRoutingSection";
|
||||
|
||||
const tabs = [
|
||||
@@ -136,6 +137,7 @@ export default function SettingsPage() {
|
||||
{activeTab === "advanced" && (
|
||||
<div className="flex flex-col gap-6">
|
||||
<PayloadRulesTab />
|
||||
<RequestLimitsTab />
|
||||
<CliproxyapiSettingsTab />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -65,14 +65,19 @@ export async function POST(request: Request) {
|
||||
if (authError) return authError;
|
||||
|
||||
let rawBody: unknown;
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return createErrorResponse({
|
||||
status: 400,
|
||||
message: "Invalid JSON body",
|
||||
type: "invalid_request",
|
||||
});
|
||||
const contentType = request.headers.get("content-type") || "";
|
||||
if (contentType.includes("application/json")) {
|
||||
try {
|
||||
rawBody = await request.json();
|
||||
} catch {
|
||||
return createErrorResponse({
|
||||
status: 400,
|
||||
message: "Invalid JSON body",
|
||||
type: "invalid_request",
|
||||
});
|
||||
}
|
||||
} else {
|
||||
rawBody = {};
|
||||
}
|
||||
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user