diff --git a/package.json b/package.json
index 6849f7a..7955492 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "9router-app",
- "version": "0.3.62",
+ "version": "0.3.64",
"description": "9Router web dashboard",
"private": true,
"scripts": {
diff --git a/src/app/(dashboard)/dashboard/providers/[id]/page.js b/src/app/(dashboard)/dashboard/providers/[id]/page.js
index 00f45ed..7cb9eff 100644
--- a/src/app/(dashboard)/dashboard/providers/[id]/page.js
+++ b/src/app/(dashboard)/dashboard/providers/[id]/page.js
@@ -682,6 +682,13 @@ export default function ProviderDetailPage() {
+ {providerInfo.deprecated && (
+
+
info
+
{providerInfo.deprecationNotice}
+
+ )}
+
{isCompatible && providerNode && (
diff --git a/src/app/(dashboard)/dashboard/providers/page.js b/src/app/(dashboard)/dashboard/providers/page.js
index 219ccdd..f23edea 100644
--- a/src/app/(dashboard)/dashboard/providers/page.js
+++ b/src/app/(dashboard)/dashboard/providers/page.js
@@ -489,7 +489,6 @@ export default function ProvidersPage() {
function ProviderCard({ providerId, provider, stats, authType, onToggle }) {
const { connected, error, errorCode, errorTime, allDisabled } = stats;
- const isDeprecated = !!provider.deprecated;
const dotColors = {
free: "bg-green-500",
@@ -572,12 +571,6 @@ function ProviderCard({ providerId, provider, stats, authType, onToggle }) {
)}
- {isDeprecated && (
-
-
warning
-
{provider.deprecationNotice}
-
- )}
);
diff --git a/src/dashboardGuard.js b/src/dashboardGuard.js
index a683a6c..bff3b23 100644
--- a/src/dashboardGuard.js
+++ b/src/dashboardGuard.js
@@ -5,9 +5,70 @@ const SECRET = new TextEncoder().encode(
process.env.JWT_SECRET || "9router-default-secret-change-me"
);
+// Always require JWT token regardless of requireLogin setting
+const ALWAYS_PROTECTED = [
+ "/api/shutdown",
+ "/api/settings/database",
+];
+
+// Require auth, but allow through if requireLogin is disabled
+const PROTECTED_API_PATHS = [
+ "/api/settings",
+ "/api/keys",
+ "/api/providers/client",
+ "/api/provider-nodes/validate",
+];
+
+function isLocalRequest(request) {
+ const host = request.headers.get("host") || "";
+ const hostname = host.split(":")[0];
+ return hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1";
+}
+
+async function hasValidToken(request) {
+ const token = request.cookies.get("auth_token")?.value;
+ if (!token) return false;
+ try {
+ await jwtVerify(token, SECRET);
+ return true;
+ } catch {
+ return false;
+ }
+}
+
+async function isAuthenticated(request) {
+ if (await hasValidToken(request)) return true;
+ // Allow if requireLogin is disabled
+ const origin = request.nextUrl.origin;
+ try {
+ const res = await fetch(`${origin}/api/settings/require-login`);
+ const data = await res.json();
+ if (data.requireLogin === false) return true;
+ } catch {
+ // On error, require login
+ }
+ return false;
+}
+
export async function proxy(request) {
const { pathname } = request.nextUrl;
+ // Always protected - allow localhost or valid JWT only
+ if (ALWAYS_PROTECTED.some((p) => pathname.startsWith(p))) {
+ if (isLocalRequest(request) || await hasValidToken(request))
+ return NextResponse.next();
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
+ }
+
+ // Protect sensitive API endpoints (bypass if localhost or requireLogin = false)
+ if (PROTECTED_API_PATHS.some((p) => pathname.startsWith(p))) {
+ if (pathname === "/api/settings/require-login") return NextResponse.next();
+ if (isLocalRequest(request) || await isAuthenticated(request))
+ return NextResponse.next();
+ return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
+ }
+
+
// Protect all dashboard routes
if (pathname.startsWith("/dashboard")) {
const token = request.cookies.get("auth_token")?.value;
diff --git a/src/proxy.js b/src/proxy.js
index 0ea6605..e91fd54 100644
--- a/src/proxy.js
+++ b/src/proxy.js
@@ -1,5 +1,14 @@
export { proxy } from "./dashboardGuard";
export const config = {
- matcher: ["/", "/dashboard/:path*"],
+ matcher: [
+ "/",
+ "/dashboard/:path*",
+ "/api/shutdown",
+ "/api/settings/:path*",
+ "/api/keys",
+ "/api/keys/:path*",
+ "/api/providers/client",
+ "/api/provider-nodes/validate",
+ ],
};