feat: 允许自定义URL并且解决了一部分pubmed跨域问题
This commit is contained in:
parent
2906e6f0d1
commit
cab4f0bf01
115
app/api/[...slug]/route.ts
Normal file
115
app/api/[...slug]/route.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
export async function POST(req: Request) {
|
||||
// 从请求头中读取上游 URL
|
||||
const headers = new Headers(req.headers);
|
||||
const upstreamUrl = headers.get("Upstream-Url");
|
||||
console.log("headers:", headers);
|
||||
if (!upstreamUrl) {
|
||||
throw new Error("Upstream URL not specified in headers");
|
||||
}
|
||||
try {
|
||||
// 创建新 URL
|
||||
const url = new URL(req.url);
|
||||
const apiPath = url.pathname.replace("/api", "");
|
||||
const upstreamEndpoint = upstreamUrl + apiPath;
|
||||
|
||||
// 创建新请求的headers对象
|
||||
const headers = new Headers(req.headers);
|
||||
// 移除或替换可能引起问题的头部
|
||||
// headers.delete("Host");
|
||||
headers.delete("Content-Length");
|
||||
headers.delete("Upstream-Url"); // 也删除上游 URL 头部,以免发送到上游服务器
|
||||
|
||||
// 读取并解析 JSON 请求体
|
||||
const reader = req.body.getReader();
|
||||
let requestBody = "";
|
||||
let done, value;
|
||||
while (!done) {
|
||||
({ done, value } = await reader.read());
|
||||
if (value) {
|
||||
requestBody += new TextDecoder().decode(value);
|
||||
}
|
||||
}
|
||||
|
||||
// 尝试解析为 JSON
|
||||
let jsonBody;
|
||||
try {
|
||||
jsonBody = JSON.parse(requestBody);
|
||||
} catch (error) {
|
||||
throw new Error("Failed to parse request body as JSON");
|
||||
}
|
||||
|
||||
// 使用fetch方法转发请求到上游服务器
|
||||
const response = await fetch(upstreamEndpoint, {
|
||||
method: "POST",
|
||||
headers: headers,
|
||||
body: JSON.stringify(jsonBody), // 确保将请求体转换为字符串
|
||||
});
|
||||
console.log("headers:", headers);
|
||||
console.log("req.body:", jsonBody);
|
||||
// 将响应数据发送回客户端
|
||||
return new Response(response.body, {
|
||||
status: response.status,
|
||||
headers: response.headers,
|
||||
});
|
||||
} catch (error) {
|
||||
// 错误处理
|
||||
console.error(error);
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Internal Server Error in NEXT" }),
|
||||
{
|
||||
status: 500,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET(req: Request) {
|
||||
// 从请求头中读取上游 URL
|
||||
const headers = new Headers(req.headers);
|
||||
const upstreamUrl = headers.get("Upstream-Url");
|
||||
if (!upstreamUrl) {
|
||||
throw new Error("Upstream URL not specified in headers");
|
||||
}
|
||||
try {
|
||||
// 创建新 URL
|
||||
const url = new URL(req.url);
|
||||
const apiPath = url.pathname.replace(/\/api\/paper|\/api/g, "");
|
||||
|
||||
const upstreamEndpoint = upstreamUrl + apiPath + url.search;
|
||||
|
||||
// 创建新请求的headers对象
|
||||
const headers = new Headers(req.headers);
|
||||
// 移除或替换可能引起问题的头部
|
||||
headers.delete("Host");
|
||||
headers.delete("Upstream-Url"); // 也删除上游 URL 头部,以免发送到上游服务器
|
||||
|
||||
// 使用fetch方法转发请求到上游服务器
|
||||
const response = await fetch(upstreamEndpoint, {
|
||||
method: "GET",
|
||||
headers: headers,
|
||||
});
|
||||
console.log("response:", response);
|
||||
// 将响应数据发送回客户端
|
||||
let text = await response.text();
|
||||
console.log("text", text);
|
||||
return new Response(text, {
|
||||
headers: headers,
|
||||
status: response.status,
|
||||
});
|
||||
} catch (error) {
|
||||
// 错误处理
|
||||
console.error(error);
|
||||
return new Response(
|
||||
JSON.stringify({ error: "Internal Server Error in NEXT" }),
|
||||
{
|
||||
status: 500,
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
|
@ -3,12 +3,12 @@
|
|||
import axios from "axios";
|
||||
import https from "https";
|
||||
|
||||
export default async function handler(req, res) {
|
||||
export async function POST(req: Request) {
|
||||
const upstreamUrl = "https://api.liuweiqing.top";
|
||||
|
||||
try {
|
||||
// 创建新 URL
|
||||
const url = upstreamUrl + req.url.replace("/api/proxy", "");
|
||||
const url = upstreamUrl + new URL(req.url).pathname.replace("/api", "");
|
||||
|
||||
// 创建新请求
|
||||
const newRequest = {
|
||||
|
@ -23,19 +23,25 @@ export default async function handler(req, res) {
|
|||
// 使用axios.post方法转发请求到上游服务器
|
||||
const response = await axios.post(url, newRequest.data, {
|
||||
headers: newRequest.headers,
|
||||
httpsAgent: agent, // 使用新的https.Agent
|
||||
// httpsAgent: agent, // 使用新的https.Agent
|
||||
});
|
||||
|
||||
// 将响应数据发送回客户端
|
||||
res.status(response.status).send(response.data);
|
||||
return new Response(response.data, {
|
||||
status: response.status,
|
||||
});
|
||||
} catch (error) {
|
||||
// 错误处理
|
||||
console.error(error);
|
||||
res.status(500).json({ error: "Internal Server Error" });
|
||||
return new Response(
|
||||
{ error: "Internal Server Error in NEXT" },
|
||||
{
|
||||
status: 500,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// pages/api/proxy.js
|
||||
// import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
// export default async function handler(
|
|
@ -4,12 +4,14 @@ export interface APIState {
|
|||
apiKey: string;
|
||||
referencesRedux: Reference[];
|
||||
editorContent: string;
|
||||
upsreamUrl: string;
|
||||
}
|
||||
|
||||
const initialState: APIState = {
|
||||
apiKey: "",
|
||||
referencesRedux: [],
|
||||
editorContent: "",
|
||||
upsreamUrl: "https://api.liuweiqing.top", //https://api.openai.com
|
||||
};
|
||||
|
||||
export const authSlice = createSlice({
|
||||
|
@ -19,6 +21,9 @@ export const authSlice = createSlice({
|
|||
setApiKey: (state, action: PayloadAction<string>) => {
|
||||
state.apiKey = action.payload;
|
||||
},
|
||||
setUpsreamUrl: (state, action: PayloadAction<string>) => {
|
||||
state.upsreamUrl = action.payload;
|
||||
},
|
||||
addReferenceRedux: (state, action: PayloadAction<Reference>) => {
|
||||
state.referencesRedux.push(action.payload);
|
||||
},
|
||||
|
@ -42,6 +47,7 @@ export const authSlice = createSlice({
|
|||
// Action creators are generated for each case reducer function
|
||||
export const {
|
||||
setApiKey,
|
||||
setUpsreamUrl,
|
||||
addReferenceRedux,
|
||||
addReferencesRedux,
|
||||
removeReferenceRedux,
|
||||
|
|
|
@ -41,11 +41,23 @@ async function getPubMedPapers(query: string, year: number, limit = 2) {
|
|||
async function getPubMedPaperDetails(idList: IDList) {
|
||||
try {
|
||||
const ids = idList.join(","); // 将ID列表转换为逗号分隔的字符串
|
||||
const baseURL = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi";
|
||||
// const baseURL = "https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi";
|
||||
const baseURL = process.env.NEXT_PUBLIC_PAPER_URL; //通过API接口进行转发
|
||||
const url = `${baseURL}?db=pubmed&id=${ids}&rettype=abstract&retmode=xml`;
|
||||
console.log(url);
|
||||
const response = await fetch(url, {
|
||||
headers: {
|
||||
"Upstream-Url":
|
||||
"https://eutils.ncbi.nlm.nih.gov/entrez/eutils/efetch.fcgi",
|
||||
"Accept-Encoding": "identity",
|
||||
},
|
||||
});
|
||||
|
||||
const response = await axios.get(url);
|
||||
const data = response.data; // 这里获取的数据是XML格式,需要解析
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.text(); // 获取响应文本
|
||||
// 解析XML数据
|
||||
const parser = new xml2js.Parser({
|
||||
explicitArray: false,
|
||||
|
|
|
@ -50,6 +50,7 @@ const toolbarOptions = [
|
|||
const QEditor = () => {
|
||||
//读取redux中的API key
|
||||
const apiKey = useAppSelector((state: any) => state.auth.apiKey);
|
||||
const upsreamUrl = useAppSelector((state: any) => state.auth.upsreamUrl);
|
||||
const [quill, setQuill] = useState(null);
|
||||
//询问ai,用户输入
|
||||
const [userInput, setUserInput] = useState("robot");
|
||||
|
@ -151,7 +152,14 @@ const QEditor = () => {
|
|||
quill.setSelection(cursorPosition, 0); // 将光标移动到原来的位置
|
||||
|
||||
const prompt = "请帮助用户完成论文写作,使用用户所说的语言完成";
|
||||
await sendMessageToOpenAI(userInput, quill, selectedModel, apiKey, prompt);
|
||||
await sendMessageToOpenAI(
|
||||
userInput,
|
||||
quill,
|
||||
selectedModel,
|
||||
apiKey,
|
||||
upsreamUrl,
|
||||
prompt
|
||||
);
|
||||
};
|
||||
|
||||
// 处理paper2AI
|
||||
|
@ -248,7 +256,7 @@ const QEditor = () => {
|
|||
quill,
|
||||
500
|
||||
)},搜索到的论文内容:${trimmedMessage},需要完成的论文主题:${topic},请根据搜索到的论文内容完成用户的论文`;
|
||||
sendMessageToOpenAI(content, quill, selectedModel, apiKey);
|
||||
sendMessageToOpenAI(content, quill, selectedModel, apiKey, upsreamUrl);
|
||||
} catch (error) {
|
||||
console.error("Error fetching data:", error);
|
||||
// 在处理错误后,再次抛出这个错误
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
// Settings.tsx
|
||||
import { useAppDispatch, useAppSelector } from "@/app/store";
|
||||
import { setApiKey } from "@/app/store/slices/authSlice";
|
||||
import { setApiKey, setUpsreamUrl } from "@/app/store/slices/authSlice";
|
||||
|
||||
const Settings = () => {
|
||||
const dispatch = useAppDispatch();
|
||||
const apiKey = useAppSelector((state) => state.auth.apiKey);
|
||||
const upstreamUrl = useAppSelector((state) => state.auth.upsreamUrl);
|
||||
|
||||
return (
|
||||
<div className="max-w-md mx-auto p-4">
|
||||
|
@ -22,6 +23,22 @@ const Settings = () => {
|
|||
onChange={(event) => dispatch(setApiKey(event.target.value))}
|
||||
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||
/>
|
||||
{/* upstream-url */}
|
||||
<div className="mb-4">
|
||||
<label
|
||||
className="block text-gray-700 text-sm font-bold mb-2"
|
||||
htmlFor="upstream-url"
|
||||
>
|
||||
Upstream URL:
|
||||
</label>
|
||||
<input
|
||||
id="upstream-url"
|
||||
type="text"
|
||||
value={upstreamUrl} // 这里假设你有一个upstreamUrl状态
|
||||
onChange={(event) => dispatch(setUpsreamUrl(event.target.value))}
|
||||
className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -5,6 +5,8 @@ import {
|
|||
updateBracketNumbersInDeltaKeepSelection,
|
||||
convertToSuperscript,
|
||||
} from "@/utils/others/quillutils";
|
||||
//redux不能在普通函数使用
|
||||
|
||||
interface ChatData {
|
||||
choices: Array<{
|
||||
delta: {
|
||||
|
@ -21,6 +23,7 @@ const sendMessageToOpenAI = async (
|
|||
editor: Editor,
|
||||
selectedModel: "gpt3.5",
|
||||
apiKey: string,
|
||||
upsreamUrl: string,
|
||||
prompt?: string
|
||||
) => {
|
||||
// console.log("apiKey", apiKey);
|
||||
|
@ -32,12 +35,13 @@ const sendMessageToOpenAI = async (
|
|||
// );
|
||||
//识别应该使用的模型
|
||||
let model = selectedModel === "gpt3.5" ? "gpt-3.5-turbo" : "gpt-4";
|
||||
|
||||
console.log("upsreamUrl", upsreamUrl);
|
||||
// 设置API请求参数
|
||||
const requestOptions = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"Upstream-Url": upsreamUrl,
|
||||
Authorization:
|
||||
"Bearer " +
|
||||
(isValidApiKey(apiKey)
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
// pages/api/hello.js
|
||||
|
||||
export default function handler(req, res) {
|
||||
res.status(200).json({ message: "Hello from the API!" });
|
||||
}
|
|
@ -23,6 +23,6 @@
|
|||
"@/*": ["./*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts", "app/api/[...slug]"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user