Merge branch 'main' into main
This commit is contained in:
commit
5bf402710f
|
@ -25,7 +25,7 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4
|
||||||
[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple
|
[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple
|
||||||
[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu
|
[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu
|
||||||
|
|
||||||
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FYidadaa%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&env=GOOGLE_API_KEY&project-name=chatgpt-next-web&repository-name=ChatGPT-Next-Web)
|
[](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FChatGPTNextWeb%2FChatGPT-Next-Web&env=OPENAI_API_KEY&env=CODE&project-name=nextchat&repository-name=NextChat)
|
||||||
|
|
||||||
[](https://zeabur.com/templates/ZBUEFA)
|
[](https://zeabur.com/templates/ZBUEFA)
|
||||||
|
|
||||||
|
|
|
@ -1,43 +0,0 @@
|
||||||
import { NextRequest, NextResponse } from "next/server";
|
|
||||||
|
|
||||||
async function handle(
|
|
||||||
req: NextRequest,
|
|
||||||
{ params }: { params: { path: string[] } },
|
|
||||||
) {
|
|
||||||
if (req.method === "OPTIONS") {
|
|
||||||
return NextResponse.json({ body: "OK" }, { status: 200 });
|
|
||||||
}
|
|
||||||
|
|
||||||
const [protocol, ...subpath] = params.path;
|
|
||||||
const targetUrl = `${protocol}://${subpath.join("/")}`;
|
|
||||||
|
|
||||||
const method = req.headers.get("method") ?? undefined;
|
|
||||||
const shouldNotHaveBody = ["get", "head"].includes(
|
|
||||||
method?.toLowerCase() ?? "",
|
|
||||||
);
|
|
||||||
|
|
||||||
const fetchOptions: RequestInit = {
|
|
||||||
headers: {
|
|
||||||
authorization: req.headers.get("authorization") ?? "",
|
|
||||||
},
|
|
||||||
body: shouldNotHaveBody ? null : req.body,
|
|
||||||
method,
|
|
||||||
// @ts-ignore
|
|
||||||
duplex: "half",
|
|
||||||
};
|
|
||||||
|
|
||||||
const fetchResult = await fetch(targetUrl, fetchOptions);
|
|
||||||
|
|
||||||
console.log("[Any Proxy]", targetUrl, {
|
|
||||||
status: fetchResult.status,
|
|
||||||
statusText: fetchResult.statusText,
|
|
||||||
});
|
|
||||||
|
|
||||||
return fetchResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const POST = handle;
|
|
||||||
export const GET = handle;
|
|
||||||
export const OPTIONS = handle;
|
|
||||||
|
|
||||||
export const runtime = "edge";
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
|
async function handle(
|
||||||
|
req: NextRequest,
|
||||||
|
{ params }: { params: { action: string; key: string[] } },
|
||||||
|
) {
|
||||||
|
const requestUrl = new URL(req.url);
|
||||||
|
const endpoint = requestUrl.searchParams.get("endpoint");
|
||||||
|
|
||||||
|
if (req.method === "OPTIONS") {
|
||||||
|
return NextResponse.json({ body: "OK" }, { status: 200 });
|
||||||
|
}
|
||||||
|
const [...key] = params.key;
|
||||||
|
// only allow to request to *.upstash.io
|
||||||
|
if (!endpoint || !new URL(endpoint).hostname.endsWith(".upstash.io")) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: true,
|
||||||
|
msg: "you are not allowed to request " + params.key.join("/"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 403,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only allow upstash get and set method
|
||||||
|
if (params.action !== "get" && params.action !== "set") {
|
||||||
|
console.log("[Upstash Route] forbidden action ", params.action);
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: true,
|
||||||
|
msg: "you are not allowed to request " + params.action,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 403,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetUrl = `${endpoint}/${params.action}/${params.key.join("/")}`;
|
||||||
|
|
||||||
|
const method = req.method;
|
||||||
|
const shouldNotHaveBody = ["get", "head"].includes(
|
||||||
|
method?.toLowerCase() ?? "",
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchOptions: RequestInit = {
|
||||||
|
headers: {
|
||||||
|
authorization: req.headers.get("authorization") ?? "",
|
||||||
|
},
|
||||||
|
body: shouldNotHaveBody ? null : req.body,
|
||||||
|
method,
|
||||||
|
// @ts-ignore
|
||||||
|
duplex: "half",
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log("[Upstash Proxy]", targetUrl, fetchOptions);
|
||||||
|
const fetchResult = await fetch(targetUrl, fetchOptions);
|
||||||
|
|
||||||
|
console.log("[Any Proxy]", targetUrl, {
|
||||||
|
status: fetchResult.status,
|
||||||
|
statusText: fetchResult.statusText,
|
||||||
|
});
|
||||||
|
|
||||||
|
return fetchResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const POST = handle;
|
||||||
|
export const GET = handle;
|
||||||
|
export const OPTIONS = handle;
|
||||||
|
|
||||||
|
export const runtime = "edge";
|
|
@ -0,0 +1,112 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
import { STORAGE_KEY } from "../../../constant";
|
||||||
|
async function handle(
|
||||||
|
req: NextRequest,
|
||||||
|
{ params }: { params: { path: string[] } },
|
||||||
|
) {
|
||||||
|
if (req.method === "OPTIONS") {
|
||||||
|
return NextResponse.json({ body: "OK" }, { status: 200 });
|
||||||
|
}
|
||||||
|
const folder = STORAGE_KEY;
|
||||||
|
const fileName = `${folder}/backup.json`;
|
||||||
|
|
||||||
|
const requestUrl = new URL(req.url);
|
||||||
|
let endpoint = requestUrl.searchParams.get("endpoint");
|
||||||
|
if (!endpoint?.endsWith("/")) {
|
||||||
|
endpoint += "/";
|
||||||
|
}
|
||||||
|
const endpointPath = params.path.join("/");
|
||||||
|
|
||||||
|
// only allow MKCOL, GET, PUT
|
||||||
|
if (req.method !== "MKCOL" && req.method !== "GET" && req.method !== "PUT") {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: true,
|
||||||
|
msg: "you are not allowed to request " + params.path.join("/"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 403,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for MKCOL request, only allow request ${folder}
|
||||||
|
if (
|
||||||
|
req.method == "MKCOL" &&
|
||||||
|
!new URL(endpointPath).pathname.endsWith(folder)
|
||||||
|
) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: true,
|
||||||
|
msg: "you are not allowed to request " + params.path.join("/"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 403,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for GET request, only allow request ending with fileName
|
||||||
|
if (
|
||||||
|
req.method == "GET" &&
|
||||||
|
!new URL(endpointPath).pathname.endsWith(fileName)
|
||||||
|
) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: true,
|
||||||
|
msg: "you are not allowed to request " + params.path.join("/"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 403,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// for PUT request, only allow request ending with fileName
|
||||||
|
if (
|
||||||
|
req.method == "PUT" &&
|
||||||
|
!new URL(endpointPath).pathname.endsWith(fileName)
|
||||||
|
) {
|
||||||
|
return NextResponse.json(
|
||||||
|
{
|
||||||
|
error: true,
|
||||||
|
msg: "you are not allowed to request " + params.path.join("/"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 403,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const targetUrl = `${endpoint + endpointPath}`;
|
||||||
|
|
||||||
|
const method = req.method;
|
||||||
|
const shouldNotHaveBody = ["get", "head"].includes(
|
||||||
|
method?.toLowerCase() ?? "",
|
||||||
|
);
|
||||||
|
|
||||||
|
const fetchOptions: RequestInit = {
|
||||||
|
headers: {
|
||||||
|
authorization: req.headers.get("authorization") ?? "",
|
||||||
|
},
|
||||||
|
body: shouldNotHaveBody ? null : req.body,
|
||||||
|
method,
|
||||||
|
// @ts-ignore
|
||||||
|
duplex: "half",
|
||||||
|
};
|
||||||
|
|
||||||
|
const fetchResult = await fetch(targetUrl, fetchOptions);
|
||||||
|
|
||||||
|
console.log("[Any Proxy]", targetUrl, {
|
||||||
|
status: fetchResult.status,
|
||||||
|
statusText: fetchResult.statusText,
|
||||||
|
});
|
||||||
|
|
||||||
|
return fetchResult;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const POST = handle;
|
||||||
|
export const GET = handle;
|
||||||
|
export const OPTIONS = handle;
|
||||||
|
|
||||||
|
export const runtime = "edge";
|
|
@ -219,6 +219,8 @@ function useSubmitHandler() {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const shouldSubmit = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
const shouldSubmit = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
|
||||||
|
// Fix Chinese input method "Enter" on Safari
|
||||||
|
if (e.keyCode == 229) return false;
|
||||||
if (e.key !== "Enter") return false;
|
if (e.key !== "Enter") return false;
|
||||||
if (e.key === "Enter" && (e.nativeEvent.isComposing || isComposing.current))
|
if (e.key === "Enter" && (e.nativeEvent.isComposing || isComposing.current))
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -23,7 +23,7 @@ export enum Path {
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ApiPath {
|
export enum ApiPath {
|
||||||
Cors = "/api/cors",
|
Cors = "",
|
||||||
OpenAI = "/api/openai",
|
OpenAI = "/api/openai",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -118,7 +118,7 @@ export const useSyncStore = createPersistStore(
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
name: StoreKey.Sync,
|
name: StoreKey.Sync,
|
||||||
version: 1.1,
|
version: 1.2,
|
||||||
|
|
||||||
migrate(persistedState, version) {
|
migrate(persistedState, version) {
|
||||||
const newState = persistedState as typeof DEFAULT_SYNC_STATE;
|
const newState = persistedState as typeof DEFAULT_SYNC_STATE;
|
||||||
|
@ -127,6 +127,15 @@ export const useSyncStore = createPersistStore(
|
||||||
newState.upstash.username = STORAGE_KEY;
|
newState.upstash.username = STORAGE_KEY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 1.2) {
|
||||||
|
if (
|
||||||
|
(persistedState as typeof DEFAULT_SYNC_STATE).proxyUrl ===
|
||||||
|
"/api/cors/"
|
||||||
|
) {
|
||||||
|
newState.proxyUrl = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return newState as any;
|
return newState as any;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
12
app/utils.ts
12
app/utils.ts
|
@ -292,9 +292,11 @@ export function getMessageImages(message: RequestMessage): string[] {
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isVisionModel(model: string) {
|
export function isVisionModel(model: string) {
|
||||||
return (
|
// Note: This is a better way using the TypeScript feature instead of `&&` or `||` (ts v5.5.0-dev.20240314 I've been using)
|
||||||
// model.startsWith("gpt-4-vision") ||
|
const visionKeywords = [
|
||||||
// model.startsWith("gemini-pro-vision") ||
|
"vision",
|
||||||
model.includes("vision")
|
"claude-3",
|
||||||
);
|
];
|
||||||
|
|
||||||
|
return visionKeywords.some(keyword => model.includes(keyword));
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { STORAGE_KEY } from "@/app/constant";
|
import { STORAGE_KEY } from "@/app/constant";
|
||||||
import { SyncStore } from "@/app/store/sync";
|
import { SyncStore } from "@/app/store/sync";
|
||||||
import { corsFetch } from "../cors";
|
|
||||||
import { chunks } from "../format";
|
import { chunks } from "../format";
|
||||||
|
|
||||||
export type UpstashConfig = SyncStore["upstash"];
|
export type UpstashConfig = SyncStore["upstash"];
|
||||||
|
@ -18,10 +17,9 @@ export function createUpstashClient(store: SyncStore) {
|
||||||
return {
|
return {
|
||||||
async check() {
|
async check() {
|
||||||
try {
|
try {
|
||||||
const res = await corsFetch(this.path(`get/${storeKey}`), {
|
const res = await fetch(this.path(`get/${storeKey}`, proxyUrl), {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
proxyUrl,
|
|
||||||
});
|
});
|
||||||
console.log("[Upstash] check", res.status, res.statusText);
|
console.log("[Upstash] check", res.status, res.statusText);
|
||||||
return [200].includes(res.status);
|
return [200].includes(res.status);
|
||||||
|
@ -32,10 +30,9 @@ export function createUpstashClient(store: SyncStore) {
|
||||||
},
|
},
|
||||||
|
|
||||||
async redisGet(key: string) {
|
async redisGet(key: string) {
|
||||||
const res = await corsFetch(this.path(`get/${key}`), {
|
const res = await fetch(this.path(`get/${key}`, proxyUrl), {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
proxyUrl,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[Upstash] get key = ", key, res.status, res.statusText);
|
console.log("[Upstash] get key = ", key, res.status, res.statusText);
|
||||||
|
@ -45,11 +42,10 @@ export function createUpstashClient(store: SyncStore) {
|
||||||
},
|
},
|
||||||
|
|
||||||
async redisSet(key: string, value: string) {
|
async redisSet(key: string, value: string) {
|
||||||
const res = await corsFetch(this.path(`set/${key}`), {
|
const res = await fetch(this.path(`set/${key}`, proxyUrl), {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
body: value,
|
body: value,
|
||||||
proxyUrl,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[Upstash] set key = ", key, res.status, res.statusText);
|
console.log("[Upstash] set key = ", key, res.status, res.statusText);
|
||||||
|
@ -84,18 +80,28 @@ export function createUpstashClient(store: SyncStore) {
|
||||||
Authorization: `Bearer ${config.apiKey}`,
|
Authorization: `Bearer ${config.apiKey}`,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
path(path: string) {
|
path(path: string, proxyUrl: string = "") {
|
||||||
let url = config.endpoint;
|
if (!path.endsWith("/")) {
|
||||||
|
path += "/";
|
||||||
if (!url.endsWith("/")) {
|
|
||||||
url += "/";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.startsWith("/")) {
|
if (path.startsWith("/")) {
|
||||||
path = path.slice(1);
|
path = path.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return url + path;
|
if (proxyUrl.length > 0 && !proxyUrl.endsWith("/")) {
|
||||||
|
proxyUrl += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
let url;
|
||||||
|
if (proxyUrl.length > 0 || proxyUrl === "/") {
|
||||||
|
let u = new URL(proxyUrl + "/api/upstash/" + path);
|
||||||
|
// add query params
|
||||||
|
u.searchParams.append("endpoint", config.endpoint);
|
||||||
|
url = u.toString();
|
||||||
|
} else {
|
||||||
|
url = "/api/upstash/" + path + "?endpoint=" + config.endpoint;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { STORAGE_KEY } from "@/app/constant";
|
import { STORAGE_KEY } from "@/app/constant";
|
||||||
import { SyncStore } from "@/app/store/sync";
|
import { SyncStore } from "@/app/store/sync";
|
||||||
import { corsFetch } from "../cors";
|
|
||||||
|
|
||||||
export type WebDAVConfig = SyncStore["webdav"];
|
export type WebDAVConfig = SyncStore["webdav"];
|
||||||
export type WebDavClient = ReturnType<typeof createWebDavClient>;
|
export type WebDavClient = ReturnType<typeof createWebDavClient>;
|
||||||
|
@ -15,10 +14,9 @@ export function createWebDavClient(store: SyncStore) {
|
||||||
return {
|
return {
|
||||||
async check() {
|
async check() {
|
||||||
try {
|
try {
|
||||||
const res = await corsFetch(this.path(folder), {
|
const res = await fetch(this.path(folder, proxyUrl), {
|
||||||
method: "MKCOL",
|
method: "MKCOL",
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
proxyUrl,
|
|
||||||
});
|
});
|
||||||
console.log("[WebDav] check", res.status, res.statusText);
|
console.log("[WebDav] check", res.status, res.statusText);
|
||||||
return [201, 200, 404, 301, 302, 307, 308].includes(res.status);
|
return [201, 200, 404, 301, 302, 307, 308].includes(res.status);
|
||||||
|
@ -30,10 +28,9 @@ export function createWebDavClient(store: SyncStore) {
|
||||||
},
|
},
|
||||||
|
|
||||||
async get(key: string) {
|
async get(key: string) {
|
||||||
const res = await corsFetch(this.path(fileName), {
|
const res = await fetch(this.path(fileName, proxyUrl), {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
proxyUrl,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[WebDav] get key = ", key, res.status, res.statusText);
|
console.log("[WebDav] get key = ", key, res.status, res.statusText);
|
||||||
|
@ -42,11 +39,10 @@ export function createWebDavClient(store: SyncStore) {
|
||||||
},
|
},
|
||||||
|
|
||||||
async set(key: string, value: string) {
|
async set(key: string, value: string) {
|
||||||
const res = await corsFetch(this.path(fileName), {
|
const res = await fetch(this.path(fileName, proxyUrl), {
|
||||||
method: "PUT",
|
method: "PUT",
|
||||||
headers: this.headers(),
|
headers: this.headers(),
|
||||||
body: value,
|
body: value,
|
||||||
proxyUrl,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log("[WebDav] set key = ", key, res.status, res.statusText);
|
console.log("[WebDav] set key = ", key, res.status, res.statusText);
|
||||||
|
@ -59,18 +55,28 @@ export function createWebDavClient(store: SyncStore) {
|
||||||
authorization: `Basic ${auth}`,
|
authorization: `Basic ${auth}`,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
path(path: string) {
|
path(path: string, proxyUrl: string = "") {
|
||||||
let url = config.endpoint;
|
if (!path.endsWith("/")) {
|
||||||
|
path += "/";
|
||||||
if (!url.endsWith("/")) {
|
|
||||||
url += "/";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (path.startsWith("/")) {
|
if (path.startsWith("/")) {
|
||||||
path = path.slice(1);
|
path = path.slice(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return url + path;
|
if (proxyUrl.length > 0 && !proxyUrl.endsWith("/")) {
|
||||||
|
proxyUrl += "/";
|
||||||
|
}
|
||||||
|
|
||||||
|
let url;
|
||||||
|
if (proxyUrl.length > 0 || proxyUrl === "/") {
|
||||||
|
let u = new URL(proxyUrl + "/api/webdav/" + path);
|
||||||
|
// add query params
|
||||||
|
u.searchParams.append("endpoint", config.endpoint);
|
||||||
|
url = u.toString();
|
||||||
|
} else {
|
||||||
|
url = "/api/upstash/" + path + "?endpoint=" + config.endpoint;
|
||||||
|
}
|
||||||
|
return url;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,9 @@ import { ApiPath, DEFAULT_API_HOST } from "../constant";
|
||||||
export function corsPath(path: string) {
|
export function corsPath(path: string) {
|
||||||
const baseUrl = getClientConfig()?.isApp ? `${DEFAULT_API_HOST}` : "";
|
const baseUrl = getClientConfig()?.isApp ? `${DEFAULT_API_HOST}` : "";
|
||||||
|
|
||||||
|
if (baseUrl === "" && path === "") {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
if (!path.startsWith("/")) {
|
if (!path.startsWith("/")) {
|
||||||
path = "/" + path;
|
path = "/" + path;
|
||||||
}
|
}
|
||||||
|
@ -14,37 +17,3 @@ export function corsPath(path: string) {
|
||||||
|
|
||||||
return `${baseUrl}${path}`;
|
return `${baseUrl}${path}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function corsFetch(
|
|
||||||
url: string,
|
|
||||||
options: RequestInit & {
|
|
||||||
proxyUrl?: string;
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
if (!url.startsWith("http")) {
|
|
||||||
throw Error("[CORS Fetch] url must starts with http/https");
|
|
||||||
}
|
|
||||||
|
|
||||||
let proxyUrl = options.proxyUrl ?? corsPath(ApiPath.Cors);
|
|
||||||
if (!proxyUrl.endsWith("/")) {
|
|
||||||
proxyUrl += "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
url = url.replace("://", "/");
|
|
||||||
|
|
||||||
const corsOptions = {
|
|
||||||
...options,
|
|
||||||
method: "POST",
|
|
||||||
headers: options.method
|
|
||||||
? {
|
|
||||||
...options.headers,
|
|
||||||
method: options.method,
|
|
||||||
}
|
|
||||||
: options.headers,
|
|
||||||
};
|
|
||||||
|
|
||||||
const corsUrl = proxyUrl + url;
|
|
||||||
console.info("[CORS] target = ", corsUrl);
|
|
||||||
|
|
||||||
return fetch(corsUrl, corsOptions);
|
|
||||||
}
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
},
|
},
|
||||||
"package": {
|
"package": {
|
||||||
"productName": "NextChat",
|
"productName": "NextChat",
|
||||||
"version": "2.11.2"
|
"version": "2.11.3"
|
||||||
},
|
},
|
||||||
"tauri": {
|
"tauri": {
|
||||||
"allowlist": {
|
"allowlist": {
|
||||||
|
|
Loading…
Reference in New Issue