|
|
import { NextResponse } from "next/server"; |
|
|
import { NextRequest } from "next/server"; |
|
|
import { getCustomModelList, multiApiKeyPolling } from "@/utils/model"; |
|
|
import { verifySignature } from "@/utils/signature"; |
|
|
|
|
|
const NODE_ENV = process.env.NODE_ENV; |
|
|
const accessPassword = process.env.ACCESS_PASSWORD || ""; |
|
|
|
|
|
const GOOGLE_GENERATIVE_AI_API_KEY = |
|
|
process.env.GOOGLE_GENERATIVE_AI_API_KEY || ""; |
|
|
const OPENROUTER_API_KEY = process.env.OPENROUTER_API_KEY || ""; |
|
|
const OPENAI_API_KEY = process.env.OPENAI_API_KEY || ""; |
|
|
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY || ""; |
|
|
const DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY || ""; |
|
|
const XAI_API_KEY = process.env.XAI_API_KEY || ""; |
|
|
const MISTRAL_API_KEY = process.env.MISTRAL_API_KEY || ""; |
|
|
const AZURE_API_KEY = process.env.AZURE_API_KEY || ""; |
|
|
const OPENAI_COMPATIBLE_API_KEY = process.env.OPENAI_COMPATIBLE_API_KEY || ""; |
|
|
|
|
|
const TAVILY_API_KEY = process.env.TAVILY_API_KEY || ""; |
|
|
const FIRECRAWL_API_KEY = process.env.FIRECRAWL_API_KEY || ""; |
|
|
const EXA_API_KEY = process.env.EXA_API_KEY || ""; |
|
|
const BOCHA_API_KEY = process.env.BOCHA_API_KEY || ""; |
|
|
|
|
|
const DISABLED_AI_PROVIDER = process.env.NEXT_PUBLIC_DISABLED_AI_PROVIDER || ""; |
|
|
const DISABLED_SEARCH_PROVIDER = |
|
|
process.env.NEXT_PUBLIC_DISABLED_SEARCH_PROVIDER || ""; |
|
|
const MODEL_LIST = process.env.NEXT_PUBLIC_MODEL_LIST || ""; |
|
|
|
|
|
|
|
|
export const config = { |
|
|
matcher: "/api/:path*", |
|
|
}; |
|
|
|
|
|
const ERRORS = { |
|
|
NO_PERMISSIONS: { |
|
|
code: 403, |
|
|
message: "No permissions", |
|
|
status: "FORBIDDEN", |
|
|
}, |
|
|
NO_API_KEY: { |
|
|
code: 500, |
|
|
message: "The server does not have an API key.", |
|
|
status: "Internal Server Error", |
|
|
}, |
|
|
}; |
|
|
|
|
|
export async function middleware(request: NextRequest) { |
|
|
if (NODE_ENV === "production") console.debug(request); |
|
|
|
|
|
const disabledAIProviders = |
|
|
DISABLED_AI_PROVIDER.length > 0 ? DISABLED_AI_PROVIDER.split(",") : []; |
|
|
const disabledSearchProviders = |
|
|
DISABLED_SEARCH_PROVIDER.length > 0 |
|
|
? DISABLED_SEARCH_PROVIDER.split(",") |
|
|
: []; |
|
|
|
|
|
const hasDisabledGeminiModel = () => { |
|
|
if (request.method.toUpperCase() === "GET") return false; |
|
|
const { availableModelList, disabledModelList } = getCustomModelList( |
|
|
MODEL_LIST.length > 0 ? MODEL_LIST.split(",") : [] |
|
|
); |
|
|
const isAvailableModel = availableModelList.some((availableModel) => |
|
|
request.nextUrl.pathname.includes(`models/${availableModel}:`) |
|
|
); |
|
|
if (isAvailableModel) return false; |
|
|
if (disabledModelList.includes("all")) return true; |
|
|
return disabledModelList.some((disabledModel) => |
|
|
request.nextUrl.pathname.includes(`models/${disabledModel}:`) |
|
|
); |
|
|
}; |
|
|
const hasDisabledAIModel = async () => { |
|
|
if (request.method.toUpperCase() === "GET") return false; |
|
|
const { model = "" } = await request.json(); |
|
|
const { availableModelList, disabledModelList } = getCustomModelList( |
|
|
MODEL_LIST.length > 0 ? MODEL_LIST.split(",") : [] |
|
|
); |
|
|
const isAvailableModel = availableModelList.some( |
|
|
(availableModel) => availableModel === model |
|
|
); |
|
|
if (isAvailableModel) return false; |
|
|
if (disabledModelList.includes("all")) return true; |
|
|
return disabledModelList.some((disabledModel) => disabledModel === model); |
|
|
}; |
|
|
|
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/google")) { |
|
|
const authorization = request.headers.get("x-goog-api-key") || ""; |
|
|
const isDisabledGeminiModel = hasDisabledGeminiModel(); |
|
|
if ( |
|
|
!verifySignature(authorization, accessPassword, Date.now()) || |
|
|
disabledAIProviders.includes("google") || |
|
|
isDisabledGeminiModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(GOOGLE_GENERATIVE_AI_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set( |
|
|
"x-goog-api-client", |
|
|
request.headers.get("x-goog-api-client") || "genai-js/0.24.0" |
|
|
); |
|
|
requestHeaders.set("x-goog-api-key", apiKey); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/openrouter")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
const isDisabledModel = await hasDisabledAIModel(); |
|
|
if ( |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledAIProviders.includes("openrouter") || |
|
|
isDisabledModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(OPENROUTER_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/openaicompatible")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
const isDisabledModel = await hasDisabledAIModel(); |
|
|
if ( |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledAIProviders.includes("openaicompatible") || |
|
|
isDisabledModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(OPENAI_COMPATIBLE_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/openai")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
const isDisabledModel = await hasDisabledAIModel(); |
|
|
if ( |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledAIProviders.includes("openai") || |
|
|
isDisabledModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(OPENAI_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/anthropic")) { |
|
|
const authorization = request.headers.get("x-api-key") || ""; |
|
|
const isDisabledModel = await hasDisabledAIModel(); |
|
|
if ( |
|
|
!verifySignature(authorization, accessPassword, Date.now()) || |
|
|
disabledAIProviders.includes("anthropic") || |
|
|
isDisabledModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(ANTHROPIC_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("x-api-key", apiKey); |
|
|
requestHeaders.set( |
|
|
"anthropic-version", |
|
|
request.headers.get("anthropic-version") || "2023-06-01" |
|
|
); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/deepseek")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
const isDisabledModel = await hasDisabledAIModel(); |
|
|
if ( |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledAIProviders.includes("deepseek") || |
|
|
isDisabledModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(DEEPSEEK_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/xai")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
const isDisabledModel = await hasDisabledAIModel(); |
|
|
if ( |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledAIProviders.includes("xai") || |
|
|
isDisabledModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(XAI_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/mistral")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
const isDisabledModel = await hasDisabledAIModel(); |
|
|
if ( |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledAIProviders.includes("mistral") || |
|
|
isDisabledModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(MISTRAL_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/azure")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
const isDisabledModel = await hasDisabledAIModel(); |
|
|
if ( |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledAIProviders.includes("azure") || |
|
|
isDisabledModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(AZURE_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/pollinations")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
const isDisabledModel = await hasDisabledAIModel(); |
|
|
if ( |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledAIProviders.includes("pollinations") || |
|
|
isDisabledModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} |
|
|
} |
|
|
|
|
|
if (request.nextUrl.pathname.startsWith("/api/ai/ollama")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
const isDisabledModel = await hasDisabledAIModel(); |
|
|
if ( |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledAIProviders.includes("ollama") || |
|
|
isDisabledModel |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/search/tavily")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
if ( |
|
|
request.method.toUpperCase() !== "POST" || |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledSearchProviders.includes("tavily") |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(TAVILY_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/search/firecrawl")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
if ( |
|
|
request.method.toUpperCase() !== "POST" || |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledSearchProviders.includes("firecrawl") |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(FIRECRAWL_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/search/exa")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
if ( |
|
|
request.method.toUpperCase() !== "POST" || |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledSearchProviders.includes("exa") |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(EXA_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/search/bocha")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
if ( |
|
|
request.method.toUpperCase() !== "POST" || |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledSearchProviders.includes("bocha") |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const apiKey = multiApiKeyPolling(BOCHA_API_KEY); |
|
|
if (apiKey) { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.set("Authorization", `Bearer ${apiKey}`); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} else { |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: ERRORS.NO_API_KEY, |
|
|
}, |
|
|
{ status: 500 } |
|
|
); |
|
|
} |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/search/searxng")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
if ( |
|
|
request.method.toUpperCase() !== "POST" || |
|
|
!verifySignature( |
|
|
authorization.substring(7), |
|
|
accessPassword, |
|
|
Date.now() |
|
|
) || |
|
|
disabledSearchProviders.includes("searxng") |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.delete("Authorization"); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/crawler")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
if ( |
|
|
request.method.toUpperCase() !== "POST" || |
|
|
!verifySignature(authorization.substring(7), accessPassword, Date.now()) |
|
|
) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.delete("Authorization"); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/sse")) { |
|
|
let authorization = request.headers.get("authorization") || ""; |
|
|
if (authorization !== "") { |
|
|
authorization = authorization.substring(7); |
|
|
} else if (request.method.toUpperCase() === "GET") { |
|
|
authorization = request.nextUrl.searchParams.get("password") || ""; |
|
|
} |
|
|
if (authorization !== accessPassword) { |
|
|
return NextResponse.json( |
|
|
{ error: ERRORS.NO_PERMISSIONS }, |
|
|
{ status: 403 } |
|
|
); |
|
|
} else { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.delete("Authorization"); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} |
|
|
} |
|
|
if (request.nextUrl.pathname.startsWith("/api/mcp")) { |
|
|
const authorization = request.headers.get("authorization") || ""; |
|
|
if (authorization.substring(7) !== accessPassword) { |
|
|
const responseHeaders = new Headers(); |
|
|
responseHeaders.set("WWW-Authenticate", ERRORS.NO_PERMISSIONS.message); |
|
|
return NextResponse.json( |
|
|
{ |
|
|
error: 401, |
|
|
error_description: ERRORS.NO_PERMISSIONS.message, |
|
|
error_uri: request.nextUrl, |
|
|
}, |
|
|
{ headers: responseHeaders, status: 401 } |
|
|
); |
|
|
} else { |
|
|
const requestHeaders = new Headers(); |
|
|
requestHeaders.set( |
|
|
"Content-Type", |
|
|
request.headers.get("Content-Type") || "application/json" |
|
|
); |
|
|
requestHeaders.delete("Authorization"); |
|
|
return NextResponse.next({ |
|
|
request: { |
|
|
headers: requestHeaders, |
|
|
}, |
|
|
}); |
|
|
} |
|
|
} |
|
|
return NextResponse.next(); |
|
|
} |
|
|
|