diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.js b/src/app/(dashboard)/dashboard/providers/[id]/page.js index 73c94867d1..ce09e28a46 100644 --- a/src/app/(dashboard)/dashboard/providers/[id]/page.js +++ b/src/app/(dashboard)/dashboard/providers/[id]/page.js @@ -276,7 +276,7 @@ export default function ProviderDetailPage() { const handleToggleRateLimit = async (connectionId, enabled) => { try { - const res = await fetch("/api/rate-limit", { + const res = await fetch("/api/rate-limits", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ connectionId, enabled }), diff --git a/src/app/api/rate-limit/route.js b/src/app/api/rate-limit/route.js index e4f247b8f1..4be337aaa9 100644 --- a/src/app/api/rate-limit/route.js +++ b/src/app/api/rate-limit/route.js @@ -1,63 +1,18 @@ import { NextResponse } from "next/server"; -import { getProviderConnections, updateProviderConnection } from "@/lib/localDb"; -import { - enableRateLimitProtection, - disableRateLimitProtection, - getRateLimitStatus, - getAllRateLimitStatus, -} from "@omniroute/open-sse/services/rateLimitManager.js"; /** - * GET /api/rate-limit — Get rate limit status for all connections + * @deprecated Use /api/rate-limits instead. + * This route redirects to the consolidated rate-limits endpoint. */ -export async function GET() { - try { - const connections = await getProviderConnections(); - const statuses = connections.map((conn) => ({ - connectionId: conn.id, - provider: conn.provider, - name: conn.name || conn.email || conn.id.slice(0, 8), - rateLimitProtection: !!conn.rateLimitProtection, - ...getRateLimitStatus(conn.provider, conn.id), - })); - return NextResponse.json({ - connections: statuses, - overview: getAllRateLimitStatus(), - }); - } catch (error) { - console.error("[API ERROR] /api/rate-limit GET:", error); - return NextResponse.json({ error: "Failed to get rate limit status" }, { status: 500 }); - } +export async function GET(request) { + const url = new URL(request.url); + url.pathname = "/api/rate-limits"; + return NextResponse.redirect(url, 308); } -/** - * POST /api/rate-limit — Toggle rate limit protection for a connection - * Body: { connectionId: string, enabled: boolean } - */ export async function POST(request) { - try { - const { connectionId, enabled } = await request.json(); - - if (!connectionId) { - return NextResponse.json({ error: "Missing connectionId" }, { status: 400 }); - } - - // Update in-memory state - if (enabled) { - enableRateLimitProtection(connectionId); - } else { - disableRateLimitProtection(connectionId); - } - - // Persist to database - await updateProviderConnection(connectionId, { - rateLimitProtection: !!enabled, - }); - - return NextResponse.json({ success: true, connectionId, enabled: !!enabled }); - } catch (error) { - console.error("[API ERROR] /api/rate-limit POST:", error); - return NextResponse.json({ error: "Failed to toggle rate limit" }, { status: 500 }); - } + const url = new URL(request.url); + url.pathname = "/api/rate-limits"; + return NextResponse.redirect(url, 308); } diff --git a/src/app/api/rate-limits/route.js b/src/app/api/rate-limits/route.js index 51c97e2308..887c8e61db 100644 --- a/src/app/api/rate-limits/route.js +++ b/src/app/api/rate-limits/route.js @@ -1,15 +1,76 @@ import { NextResponse } from "next/server"; -import { - getAllModelLockouts, -} from "@omniroute/open-sse/services/accountFallback.js"; +import { getAllModelLockouts } from "@omniroute/open-sse/services/accountFallback.js"; import { getCacheStats } from "@omniroute/open-sse/services/signatureCache.js"; +import { getProviderConnections, updateProviderConnection } from "@/lib/localDb"; +import { + enableRateLimitProtection, + disableRateLimitProtection, + getRateLimitStatus, + getAllRateLimitStatus, +} from "@omniroute/open-sse/services/rateLimitManager.js"; +/** + * GET /api/rate-limits — Consolidated rate-limit status + * + * Returns: + * - Per-connection rate-limit status (protection toggle, current state) + * - Global overview (all providers) + * - Model lockouts + * - Signature cache stats + */ export async function GET() { try { + const connections = await getProviderConnections(); + const statuses = connections.map((conn) => ({ + connectionId: conn.id, + provider: conn.provider, + name: conn.name || conn.email || conn.id.slice(0, 8), + rateLimitProtection: !!conn.rateLimitProtection, + ...getRateLimitStatus(conn.provider, conn.id), + })); + const lockouts = getAllModelLockouts(); const cacheStats = getCacheStats(); - return NextResponse.json({ lockouts, cacheStats }); + + return NextResponse.json({ + connections: statuses, + overview: getAllRateLimitStatus(), + lockouts, + cacheStats, + }); } catch (error) { - return NextResponse.json({ error: error.message }, { status: 500 }); + console.error("[API ERROR] /api/rate-limits GET:", error); + return NextResponse.json({ error: "Failed to get rate limit status" }, { status: 500 }); + } +} + +/** + * POST /api/rate-limits — Toggle rate limit protection for a connection + * Body: { connectionId: string, enabled: boolean } + */ +export async function POST(request) { + try { + const { connectionId, enabled } = await request.json(); + + if (!connectionId) { + return NextResponse.json({ error: "Missing connectionId" }, { status: 400 }); + } + + // Update in-memory state + if (enabled) { + enableRateLimitProtection(connectionId); + } else { + disableRateLimitProtection(connectionId); + } + + // Persist to database + await updateProviderConnection(connectionId, { + rateLimitProtection: !!enabled, + }); + + return NextResponse.json({ success: true, connectionId, enabled: !!enabled }); + } catch (error) { + console.error("[API ERROR] /api/rate-limits POST:", error); + return NextResponse.json({ error: "Failed to toggle rate limit" }, { status: 500 }); } } diff --git a/src/app/global-error.js b/src/app/global-error.js index 4037f86a66..db7051860d 100644 --- a/src/app/global-error.js +++ b/src/app/global-error.js @@ -6,88 +6,29 @@ * Root-level error boundary for unrecoverable errors. * This is the last resort — catches errors that the per-page * error.js boundaries don't handle. + * Styled with TailwindCSS 4 (Phase 7.3). */ export default function GlobalError({ error, reset }) { return ( -
-+
+An unexpected error occurred. This has been logged and our team will investigate.
{process.env.NODE_ENV === "development" && error?.message && ( -+{error.message})} diff --git a/src/app/not-found.js b/src/app/not-found.js index 6328f42ca7..5707611146 100644 --- a/src/app/not-found.js +++ b/src/app/not-found.js @@ -4,70 +4,33 @@ * Custom Not Found Page — FASE-04 Error Handling * * Displayed when a user navigates to a non-existent route. + * Styled with TailwindCSS 4 (Phase 7.3). */ import Link from "next/link"; export default function NotFound() { return ( -+404-- Page not found -
-+
Page not found
+The page you're looking for doesn't exist or has been moved.
Go to Dashboard diff --git a/src/app/privacy/page.js b/src/app/privacy/page.js index 5beb3e3779..6972a482f2 100644 --- a/src/app/privacy/page.js +++ b/src/app/privacy/page.js @@ -145,7 +145,7 @@ export default function PrivacyPage() {