Skip to content

Commit bd6ed3c

Browse files
authored
Merge branch 'ChatGPTNextWeb:main' into main
2 parents 3448b39 + 3ba984d commit bd6ed3c

File tree

17 files changed

+270
-121
lines changed

17 files changed

+270
-121
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ One-Click to get a well-designed cross-platform ChatGPT web UI, with GPT3, GPT4
2525
[MacOS-image]: https://img.shields.io/badge/-MacOS-black?logo=apple
2626
[Linux-image]: https://img.shields.io/badge/-Linux-333?logo=ubuntu
2727

28-
[![Deploy with Vercel](https://vercel.com/button)](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)
28+
[![Deploy with Vercel](https://vercel.com/button)](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)
2929

3030
[![Deploy on Zeabur](https://zeabur.com/button.svg)](https://zeabur.com/templates/ZBUEFA)
3131

app/api/cors/[...path]/route.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
3+
async function handle(
4+
req: NextRequest,
5+
{ params }: { params: { action: string; key: string[] } },
6+
) {
7+
const requestUrl = new URL(req.url);
8+
const endpoint = requestUrl.searchParams.get("endpoint");
9+
10+
if (req.method === "OPTIONS") {
11+
return NextResponse.json({ body: "OK" }, { status: 200 });
12+
}
13+
const [...key] = params.key;
14+
// only allow to request to *.upstash.io
15+
if (!endpoint || !new URL(endpoint).hostname.endsWith(".upstash.io")) {
16+
return NextResponse.json(
17+
{
18+
error: true,
19+
msg: "you are not allowed to request " + params.key.join("/"),
20+
},
21+
{
22+
status: 403,
23+
},
24+
);
25+
}
26+
27+
// only allow upstash get and set method
28+
if (params.action !== "get" && params.action !== "set") {
29+
console.log("[Upstash Route] forbidden action ", params.action);
30+
return NextResponse.json(
31+
{
32+
error: true,
33+
msg: "you are not allowed to request " + params.action,
34+
},
35+
{
36+
status: 403,
37+
},
38+
);
39+
}
40+
41+
const targetUrl = `${endpoint}/${params.action}/${params.key.join("/")}`;
42+
43+
const method = req.method;
44+
const shouldNotHaveBody = ["get", "head"].includes(
45+
method?.toLowerCase() ?? "",
46+
);
47+
48+
const fetchOptions: RequestInit = {
49+
headers: {
50+
authorization: req.headers.get("authorization") ?? "",
51+
},
52+
body: shouldNotHaveBody ? null : req.body,
53+
method,
54+
// @ts-ignore
55+
duplex: "half",
56+
};
57+
58+
console.log("[Upstash Proxy]", targetUrl, fetchOptions);
59+
const fetchResult = await fetch(targetUrl, fetchOptions);
60+
61+
console.log("[Any Proxy]", targetUrl, {
62+
status: fetchResult.status,
63+
statusText: fetchResult.statusText,
64+
});
65+
66+
return fetchResult;
67+
}
68+
69+
export const POST = handle;
70+
export const GET = handle;
71+
export const OPTIONS = handle;
72+
73+
export const runtime = "edge";

app/api/webdav/[...path]/route.ts

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
import { NextRequest, NextResponse } from "next/server";
2+
import { STORAGE_KEY } from "../../../constant";
3+
async function handle(
4+
req: NextRequest,
5+
{ params }: { params: { path: string[] } },
6+
) {
7+
if (req.method === "OPTIONS") {
8+
return NextResponse.json({ body: "OK" }, { status: 200 });
9+
}
10+
const folder = STORAGE_KEY;
11+
const fileName = `${folder}/backup.json`;
12+
13+
const requestUrl = new URL(req.url);
14+
let endpoint = requestUrl.searchParams.get("endpoint");
15+
if (!endpoint?.endsWith("/")) {
16+
endpoint += "/";
17+
}
18+
const endpointPath = params.path.join("/");
19+
20+
// only allow MKCOL, GET, PUT
21+
if (req.method !== "MKCOL" && req.method !== "GET" && req.method !== "PUT") {
22+
return NextResponse.json(
23+
{
24+
error: true,
25+
msg: "you are not allowed to request " + params.path.join("/"),
26+
},
27+
{
28+
status: 403,
29+
},
30+
);
31+
}
32+
33+
// for MKCOL request, only allow request ${folder}
34+
if (
35+
req.method == "MKCOL" &&
36+
!new URL(endpointPath).pathname.endsWith(folder)
37+
) {
38+
return NextResponse.json(
39+
{
40+
error: true,
41+
msg: "you are not allowed to request " + params.path.join("/"),
42+
},
43+
{
44+
status: 403,
45+
},
46+
);
47+
}
48+
49+
// for GET request, only allow request ending with fileName
50+
if (
51+
req.method == "GET" &&
52+
!new URL(endpointPath).pathname.endsWith(fileName)
53+
) {
54+
return NextResponse.json(
55+
{
56+
error: true,
57+
msg: "you are not allowed to request " + params.path.join("/"),
58+
},
59+
{
60+
status: 403,
61+
},
62+
);
63+
}
64+
65+
// for PUT request, only allow request ending with fileName
66+
if (
67+
req.method == "PUT" &&
68+
!new URL(endpointPath).pathname.endsWith(fileName)
69+
) {
70+
return NextResponse.json(
71+
{
72+
error: true,
73+
msg: "you are not allowed to request " + params.path.join("/"),
74+
},
75+
{
76+
status: 403,
77+
},
78+
);
79+
}
80+
81+
const targetUrl = `${endpoint + endpointPath}`;
82+
83+
const method = req.method;
84+
const shouldNotHaveBody = ["get", "head"].includes(
85+
method?.toLowerCase() ?? "",
86+
);
87+
88+
const fetchOptions: RequestInit = {
89+
headers: {
90+
authorization: req.headers.get("authorization") ?? "",
91+
},
92+
body: shouldNotHaveBody ? null : req.body,
93+
method,
94+
// @ts-ignore
95+
duplex: "half",
96+
};
97+
98+
const fetchResult = await fetch(targetUrl, fetchOptions);
99+
100+
console.log("[Any Proxy]", targetUrl, {
101+
status: fetchResult.status,
102+
statusText: fetchResult.statusText,
103+
});
104+
105+
return fetchResult;
106+
}
107+
108+
export const POST = handle;
109+
export const GET = handle;
110+
export const OPTIONS = handle;
111+
112+
export const runtime = "edge";

app/components/chat.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ function useSubmitHandler() {
219219
}, []);
220220

221221
const shouldSubmit = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {
222+
// Fix Chinese input method "Enter" on Safari
223+
if (e.keyCode == 229) return false;
222224
if (e.key !== "Enter") return false;
223225
if (e.key === "Enter" && (e.nativeEvent.isComposing || isComposing.current))
224226
return false;

app/components/emoji.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export function AvatarPicker(props: {
2121
}) {
2222
return (
2323
<EmojiPicker
24+
width={"100%"}
2425
lazyLoadEmojis
2526
theme={EmojiTheme.AUTO}
2627
getEmojiUrl={getEmojiUrl}

app/components/settings.module.scss

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
.avatar {
77
cursor: pointer;
8+
position: relative;
9+
z-index: 1;
810
}
911

1012
.edit-prompt-modal {

app/components/settings.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,7 +693,9 @@ export function Settings() {
693693
>
694694
<div
695695
className={styles.avatar}
696-
onClick={() => setShowEmojiPicker(true)}
696+
onClick={() => {
697+
setShowEmojiPicker(!showEmojiPicker);
698+
}}
697699
>
698700
<Avatar avatar={config.avatar} />
699701
</div>

app/components/ui-lib.module.scss

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,17 +14,24 @@
1414

1515
.popover-content {
1616
position: absolute;
17+
width: 350px;
1718
animation: slide-in 0.3s ease;
1819
right: 0;
1920
top: calc(100% + 10px);
2021
}
21-
22+
@media screen and (max-width: 600px) {
23+
.popover-content {
24+
width: auto;
25+
}
26+
}
2227
.popover-mask {
2328
position: fixed;
2429
top: 0;
2530
left: 0;
2631
width: 100vw;
2732
height: 100vh;
33+
background-color: rgba(0, 0, 0, 0.3);
34+
backdrop-filter: blur(5px);
2835
}
2936

3037
.list-item {

app/components/ui-lib.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,10 @@ export function Popover(props: {
2626
<div className={styles.popover}>
2727
{props.children}
2828
{props.open && (
29-
<div className={styles["popover-content"]}>
30-
<div className={styles["popover-mask"]} onClick={props.onClose}></div>
31-
{props.content}
32-
</div>
29+
<div className={styles["popover-mask"]} onClick={props.onClose}></div>
30+
)}
31+
{props.open && (
32+
<div className={styles["popover-content"]}>{props.content}</div>
3333
)}
3434
</div>
3535
);

0 commit comments

Comments
 (0)