File size: 4,291 Bytes
c16e487
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
import { NextResponse } from "next/server";
import { NextRequest } from "next/server";
import { verifySignature } from "@/utils/signature";

const accessPassword = process.env.ACCESS_PASSWORD || "";
const DISABLED_AI_PROVIDER = process.env.NEXT_PUBLIC_DISABLED_AI_PROVIDER || "";
const DISABLED_SEARCH_PROVIDER = process.env.NEXT_PUBLIC_DISABLED_SEARCH_PROVIDER || "";

// Limit the middleware to paths starting with `/api/`
export const config = {
  matcher: "/api/:path*",
};

const ERRORS = {
  NO_PERMISSIONS: {
    code: 403,
    message: "No permissions",
    status: "FORBIDDEN",
  },
};

export async function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  const requestId = Math.random().toString(36).substring(7);

  try {
    // Skip authorization for non-sensitive API routes
    if (!pathname.startsWith("/api/ai") && !pathname.startsWith("/api/search") && !pathname.startsWith("/api/sse") && !pathname.startsWith("/api/mcp") && !pathname.startsWith("/api/crawler")) {
      return NextResponse.next();
    }

    console.log(`[${requestId}] [Middleware] Incoming: ${request.method} ${pathname}`);

    const isAuthorized = (authStr: string) => {
      if (!accessPassword) return { ok: true, msg: "No password set" };
      if (!authStr) {
        return { ok: false, msg: "Missing authorization header" };
      }
      const token = authStr.startsWith("Bearer ") ? authStr.substring(7) : authStr;
      const verified = verifySignature(token, accessPassword, Date.now());
      return { ok: verified, msg: verified ? "Verified" : "Invalid signature" };
    };

    // 1. Check AI Provider restrictions
    if (pathname.startsWith("/api/ai")) {
      const provider = pathname.split("/")[3];
      const disabledAIProviders = DISABLED_AI_PROVIDER ? DISABLED_AI_PROVIDER.split(",") : [];
      const authHeader = request.headers.get("Authorization") ||
                         request.headers.get("authorization") ||
                         request.headers.get("x-goog-api-key") ||
                         request.headers.get("x-api-key") ||
                         request.headers.get("api-key") || "";

      console.log(`[${requestId}] [Middleware] [AI] Auth header present: ${!!authHeader}, length: ${authHeader?.length || 0}, start: ${authHeader ? authHeader.substring(0, 10) + "..." : "none"}`);

      const auth = isAuthorized(authHeader);
      if (!auth.ok || disabledAIProviders.includes(provider)) {
        console.warn(`[${requestId}] [Middleware] [AI] BLOCKED: provider=${provider}, auth=${auth.msg}`);
        return NextResponse.json({ error: ERRORS.NO_PERMISSIONS }, { status: 403 });
      }
    }

    // 2. Check Search Provider restrictions
    if (pathname.startsWith("/api/search")) {
      const provider = pathname.split("/")[3];
      const disabledSearchProviders = DISABLED_SEARCH_PROVIDER ? DISABLED_SEARCH_PROVIDER.split(",") : [];
      const authHeader = request.headers.get("Authorization") || request.headers.get("authorization") || "";
      const auth = isAuthorized(authHeader);
      if (!auth.ok || disabledSearchProviders.includes(provider)) {
        console.warn(`[${requestId}] [Middleware] [Search] BLOCKED: provider=${provider}, auth=${auth.msg}`);
        return NextResponse.json({ error: ERRORS.NO_PERMISSIONS }, { status: 403 });
      }
    }

    // 3. Check SSE/MCP/Crawler restrictions
    if (pathname.startsWith("/api/sse") || pathname.startsWith("/api/mcp") || pathname.startsWith("/api/crawler")) {
      let authHeader = request.headers.get("Authorization") || request.headers.get("authorization") || "";
      if (authHeader.startsWith("Bearer ")) authHeader = authHeader.substring(7);
      else if (request.method === "GET") authHeader = request.nextUrl.searchParams.get("password") || "";

      if (accessPassword && authHeader !== accessPassword) {
        console.warn(`[${requestId}] [Middleware] [Special] BLOCKED: path=${pathname}, auth=Password mismatch`);
        return NextResponse.json({ error: ERRORS.NO_PERMISSIONS }, { status: 403 });
      }
    }

    console.log(`[${requestId}] [Middleware] PASS: ${pathname}`);
    return NextResponse.next();
  } catch (err) {
    console.error(`[${requestId}] [Middleware] CRASH:`, err);
    return NextResponse.next();
  }
}