Spaces:
Running
Running
muxi feng
commited on
Commit
·
3444669
1
Parent(s):
9c10adf
增加账号管理和收费系统
Browse files- app/api/auth.ts +5 -3
- app/api/common.ts +11 -0
- app/api/lemur/route.ts +148 -30
- app/api/newbing/route.ts +6 -4
- app/api/user/findpwd/route.ts +22 -0
- app/api/user/info/route.ts +30 -0
- app/api/user/kami/route.ts +31 -0
- app/api/user/login/route.ts +19 -0
- app/api/user/mail/route.ts +19 -0
- app/api/user/register/route.ts +22 -0
- app/api/user/set/route.ts +33 -0
- app/api/user/sig/route.ts +30 -0
- app/api/wanjuan/route.ts +10 -1
- app/components/findpwd.module.scss +74 -0
- app/components/findpwd.tsx +64 -0
- app/components/home.tsx +20 -0
- app/components/login.module.scss +74 -0
- app/components/login.tsx +77 -0
- app/components/register.module.scss +103 -0
- app/components/register.tsx +152 -0
- app/components/settings.tsx +6 -4
- app/components/sidebar.tsx +2 -2
- app/components/ui-lib.tsx +1 -1
- app/components/user.module.scss +84 -0
- app/components/user.tsx +180 -0
- app/config/server.ts +2 -2
- app/constant.ts +5 -0
- app/locales/cn.ts +16 -1
- app/locales/de.ts +11 -0
- app/locales/en.ts +11 -0
- app/locales/es.ts +11 -0
- app/locales/it.ts +11 -0
- app/locales/jp.ts +11 -0
- app/locales/tr.ts +11 -0
- app/locales/tw.ts +11 -0
- app/requests.ts +42 -6
- app/store/access.ts +11 -5
- app/store/index.ts +1 -0
- app/store/user.ts +227 -0
app/api/auth.ts
CHANGED
@@ -28,6 +28,7 @@ function parseApiKey(bearToken: string) {
|
|
28 |
|
29 |
export function auth(req: NextRequest) {
|
30 |
const authToken = req.headers.get("Authorization") ?? "";
|
|
|
31 |
|
32 |
// check if it is openai api key or user token
|
33 |
const { accessCode, apiKey: token } = parseApiKey(authToken);
|
@@ -37,14 +38,15 @@ export function auth(req: NextRequest) {
|
|
37 |
console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]);
|
38 |
console.log("[Auth] got access code:", accessCode);
|
39 |
console.log("[Auth] hashed access code:", hashedCode);
|
|
|
40 |
console.log("[User IP] ", getIP(req));
|
41 |
console.log("[Time] ", new Date().toLocaleString());
|
42 |
-
|
43 |
-
if (
|
44 |
return {
|
45 |
error: true,
|
46 |
needAccessCode: true,
|
47 |
-
msg: "Please go
|
48 |
};
|
49 |
}
|
50 |
|
|
|
28 |
|
29 |
export function auth(req: NextRequest) {
|
30 |
const authToken = req.headers.get("Authorization") ?? "";
|
31 |
+
const auth = req.headers.get("auth") ?? "";
|
32 |
|
33 |
// check if it is openai api key or user token
|
34 |
const { accessCode, apiKey: token } = parseApiKey(authToken);
|
|
|
38 |
console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]);
|
39 |
console.log("[Auth] got access code:", accessCode);
|
40 |
console.log("[Auth] hashed access code:", hashedCode);
|
41 |
+
console.log("[Auth] get auth:",auth);
|
42 |
console.log("[User IP] ", getIP(req));
|
43 |
console.log("[Time] ", new Date().toLocaleString());
|
44 |
+
// serverConfig.needCode && !serverConfig.codes.has(hashedCode) &&
|
45 |
+
if (!token && !auth) {
|
46 |
return {
|
47 |
error: true,
|
48 |
needAccessCode: true,
|
49 |
+
msg: "Please go login page to login.",
|
50 |
};
|
51 |
}
|
52 |
|
app/api/common.ts
CHANGED
@@ -42,3 +42,14 @@ export async function requestOpenai(req: NextRequest) {
|
|
42 |
body: req.body,
|
43 |
});
|
44 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
42 |
body: req.body,
|
43 |
});
|
44 |
}
|
45 |
+
|
46 |
+
export async function requestLemur(req: NextRequest) {
|
47 |
+
return fetch('http://lemurchat.anfans.cn/api/chat/conversation-trial', {
|
48 |
+
headers: {
|
49 |
+
"Content-Type": "application/json"
|
50 |
+
},
|
51 |
+
cache: "no-store",
|
52 |
+
method: req.method,
|
53 |
+
body: req.body,
|
54 |
+
});
|
55 |
+
}
|
app/api/lemur/route.ts
CHANGED
@@ -1,26 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
import { createParser } from "eventsource-parser";
|
2 |
import { NextRequest, NextResponse } from "next/server";
|
3 |
import { auth } from "../auth";
|
|
|
4 |
|
5 |
-
async function createStream(
|
6 |
-
const authResult = auth(req);
|
7 |
-
if (authResult.error) {
|
8 |
-
return authResult.msg;
|
9 |
-
}
|
10 |
const encoder = new TextEncoder();
|
11 |
const decoder = new TextDecoder();
|
12 |
|
13 |
-
const res = await fetch(
|
14 |
-
"http://lemurchat.anfans.cn/api/chat/conversation-trial",
|
15 |
-
{
|
16 |
-
headers: {
|
17 |
-
"Content-Type": "application/json",
|
18 |
-
},
|
19 |
-
method: "POST",
|
20 |
-
body: req.body,
|
21 |
-
},
|
22 |
-
);
|
23 |
-
|
24 |
const stream = new ReadableStream({
|
25 |
async start(controller) {
|
26 |
function onParse(event: any) {
|
@@ -36,40 +106,88 @@ async function createStream(req: NextRequest) {
|
|
36 |
// https://beta.openai.com/docs/api-reference/completions/create#completions/create-stream
|
37 |
try {
|
38 |
const json = JSON.parse(data);
|
39 |
-
// console.log(
|
40 |
if (data.indexOf("content") == -1) {
|
41 |
controller.close();
|
42 |
return;
|
43 |
}
|
44 |
-
|
45 |
-
const text = JSON.parse(json.data.slice(5)).choices[0].delta
|
46 |
-
.content;
|
47 |
const queue = encoder.encode(text);
|
48 |
controller.enqueue(queue);
|
49 |
} catch (e) {
|
50 |
-
controller.error(
|
51 |
}
|
52 |
}
|
53 |
}
|
54 |
|
55 |
const parser = createParser(onParse);
|
56 |
for await (const chunk of res.body as any) {
|
57 |
-
parser.feed(decoder.decode(chunk));
|
58 |
}
|
59 |
},
|
60 |
});
|
61 |
return stream;
|
62 |
}
|
63 |
|
64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
65 |
try {
|
66 |
-
const
|
67 |
-
|
68 |
-
|
69 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
70 |
}
|
71 |
}
|
72 |
|
73 |
-
export const
|
74 |
-
|
75 |
-
|
|
|
|
1 |
+
// import { createParser } from "eventsource-parser";
|
2 |
+
// import { NextRequest, NextResponse } from "next/server";
|
3 |
+
// import { auth } from "../auth";
|
4 |
+
|
5 |
+
// async function createStream(req: NextRequest) {
|
6 |
+
// const authResult = auth(req);
|
7 |
+
// if (authResult.error) {
|
8 |
+
// return authResult.msg;
|
9 |
+
// }
|
10 |
+
// const encoder = new TextEncoder();
|
11 |
+
// const decoder = new TextDecoder();
|
12 |
+
|
13 |
+
// const res = await fetch(
|
14 |
+
// "http://lemurchat.anfans.cn/api/chat/conversation-trial",
|
15 |
+
// {
|
16 |
+
// headers: {
|
17 |
+
// "Content-Type": "application/json",
|
18 |
+
// },
|
19 |
+
// method: "POST",
|
20 |
+
// body: req.body,
|
21 |
+
// },
|
22 |
+
// );
|
23 |
+
|
24 |
+
// const stream = new ReadableStream({
|
25 |
+
// async start(controller) {
|
26 |
+
// function onParse(event: any) {
|
27 |
+
// if (event.type === "event") {
|
28 |
+
// const data = event.data;
|
29 |
+
// if (event.id == "1") {
|
30 |
+
// let text1 = data.slice(data.indexOf("content"));
|
31 |
+
// const text = text1.slice(12, text1.indexOf("index") - 6);
|
32 |
+
// const queue = encoder.encode(text);
|
33 |
+
// controller.enqueue(queue);
|
34 |
+
// return;
|
35 |
+
// }
|
36 |
+
// // https://beta.openai.com/docs/api-reference/completions/create#completions/create-stream
|
37 |
+
// try {
|
38 |
+
// const json = JSON.parse(data);
|
39 |
+
// // console.log(data.indexOf("content"))
|
40 |
+
// if (data.indexOf("content") == -1) {
|
41 |
+
// controller.close();
|
42 |
+
// return;
|
43 |
+
// }
|
44 |
+
// // console.log(event.data)
|
45 |
+
// const text = JSON.parse(json.data.slice(5)).choices[0].delta
|
46 |
+
// .content;
|
47 |
+
// const queue = encoder.encode(text);
|
48 |
+
// controller.enqueue(queue);
|
49 |
+
// } catch (e) {
|
50 |
+
// controller.error(e);
|
51 |
+
// }
|
52 |
+
// }
|
53 |
+
// }
|
54 |
+
|
55 |
+
// const parser = createParser(onParse);
|
56 |
+
// for await (const chunk of res.body as any) {
|
57 |
+
// parser.feed(decoder.decode(chunk));
|
58 |
+
// }
|
59 |
+
// },
|
60 |
+
// });
|
61 |
+
// return stream;
|
62 |
+
// }
|
63 |
+
|
64 |
+
// export async function POST(req: NextRequest) {
|
65 |
+
// try {
|
66 |
+
// const authResult = auth(req);
|
67 |
+
// if (authResult.error) {
|
68 |
+
// return NextResponse.json(authResult, {
|
69 |
+
// status: 401,
|
70 |
+
// });
|
71 |
+
// }
|
72 |
+
// const stream = await createStream(req);
|
73 |
+
// return new Response(stream);
|
74 |
+
// } catch (error) {
|
75 |
+
// console.error("[Chat Stream]", error);
|
76 |
+
// }
|
77 |
+
// }
|
78 |
+
|
79 |
+
// export const config = {
|
80 |
+
// runtime: "edge",
|
81 |
+
// };
|
82 |
+
|
83 |
+
|
84 |
+
|
85 |
import { createParser } from "eventsource-parser";
|
86 |
import { NextRequest, NextResponse } from "next/server";
|
87 |
import { auth } from "../auth";
|
88 |
+
import { requestLemur} from "../common";
|
89 |
|
90 |
+
async function createStream(res: Response) {
|
|
|
|
|
|
|
|
|
91 |
const encoder = new TextEncoder();
|
92 |
const decoder = new TextDecoder();
|
93 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
const stream = new ReadableStream({
|
95 |
async start(controller) {
|
96 |
function onParse(event: any) {
|
|
|
106 |
// https://beta.openai.com/docs/api-reference/completions/create#completions/create-stream
|
107 |
try {
|
108 |
const json = JSON.parse(data);
|
109 |
+
// console.log(json)
|
110 |
if (data.indexOf("content") == -1) {
|
111 |
controller.close();
|
112 |
return;
|
113 |
}
|
114 |
+
const text = JSON.parse(json.data.slice(6)).choices[0].delta.content;
|
|
|
|
|
115 |
const queue = encoder.encode(text);
|
116 |
controller.enqueue(queue);
|
117 |
} catch (e) {
|
118 |
+
controller.error(JSON.parse(data));
|
119 |
}
|
120 |
}
|
121 |
}
|
122 |
|
123 |
const parser = createParser(onParse);
|
124 |
for await (const chunk of res.body as any) {
|
125 |
+
parser.feed(decoder.decode(chunk, { stream: true }));
|
126 |
}
|
127 |
},
|
128 |
});
|
129 |
return stream;
|
130 |
}
|
131 |
|
132 |
+
function formatResponse(msg: any) {
|
133 |
+
const jsonMsg = ["```json\n", JSON.stringify(msg, null, " "), "\n```"].join(
|
134 |
+
"",
|
135 |
+
);
|
136 |
+
return new Response(jsonMsg);
|
137 |
+
}
|
138 |
+
|
139 |
+
async function handle(
|
140 |
+
req: NextRequest,
|
141 |
+
{ params }: { params: { path: string[] } },
|
142 |
+
) {
|
143 |
+
console.log("[Lemur Route] params ", params);
|
144 |
+
|
145 |
+
const authResult = auth(req);
|
146 |
+
if (authResult.error) {
|
147 |
+
return NextResponse.json(authResult, {
|
148 |
+
status: 401,
|
149 |
+
});
|
150 |
+
}
|
151 |
+
|
152 |
try {
|
153 |
+
const api = await requestLemur(req);
|
154 |
+
|
155 |
+
const contentType = api.headers.get("Content-Type") ?? "";
|
156 |
+
|
157 |
+
// streaming response
|
158 |
+
if (contentType.includes("stream")) {
|
159 |
+
const stream = await createStream(api);
|
160 |
+
const res = new Response(stream);
|
161 |
+
res.headers.set("Content-Type", contentType);
|
162 |
+
return res;
|
163 |
+
}
|
164 |
+
|
165 |
+
// try to parse error msg
|
166 |
+
try {
|
167 |
+
const mayBeErrorBody = await api.json();
|
168 |
+
if (mayBeErrorBody.error) {
|
169 |
+
console.error("[Lemur Response] ", mayBeErrorBody);
|
170 |
+
return formatResponse(mayBeErrorBody);
|
171 |
+
} else {
|
172 |
+
const res = new Response(JSON.stringify(mayBeErrorBody));
|
173 |
+
res.headers.set("Content-Type", "application/json");
|
174 |
+
res.headers.set("Cache-Control", "no-cache");
|
175 |
+
return res;
|
176 |
+
}
|
177 |
+
} catch (e) {
|
178 |
+
console.error("[Lemur Parse] ", e);
|
179 |
+
return formatResponse({
|
180 |
+
msg: "invalid response from Lemur server",
|
181 |
+
error: e,
|
182 |
+
});
|
183 |
+
}
|
184 |
+
} catch (e) {
|
185 |
+
console.error("[Lemur] ", e);
|
186 |
+
return formatResponse(e);
|
187 |
}
|
188 |
}
|
189 |
|
190 |
+
export const GET = handle;
|
191 |
+
export const POST = handle;
|
192 |
+
|
193 |
+
export const runtime = "edge";
|
app/api/newbing/route.ts
CHANGED
@@ -1,12 +1,14 @@
|
|
1 |
-
import { NextRequest } from "next/server";
|
2 |
import { BingChat, ChatMessage } from "../../bing-chat/index";
|
3 |
import { auth } from "../auth";
|
4 |
|
5 |
export async function POST(req: NextRequest) {
|
6 |
const authResult = auth(req);
|
7 |
-
|
8 |
-
|
9 |
-
|
|
|
|
|
10 |
try {
|
11 |
let cookies = process.env.COOKIES;
|
12 |
const api = new BingChat({
|
|
|
1 |
+
import { NextRequest, NextResponse } from "next/server";
|
2 |
import { BingChat, ChatMessage } from "../../bing-chat/index";
|
3 |
import { auth } from "../auth";
|
4 |
|
5 |
export async function POST(req: NextRequest) {
|
6 |
const authResult = auth(req);
|
7 |
+
if (authResult.error) {
|
8 |
+
return NextResponse.json(authResult, {
|
9 |
+
status: 401,
|
10 |
+
});
|
11 |
+
}
|
12 |
try {
|
13 |
let cookies = process.env.COOKIES;
|
14 |
const api = new BingChat({
|
app/api/user/findpwd/route.ts
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { NextRequest } from "next/server";
|
2 |
+
|
3 |
+
export async function GET(req: NextRequest) {
|
4 |
+
try {
|
5 |
+
const token=req.headers.get("auth") ?? ""
|
6 |
+
const admin=process.env.ADMIN
|
7 |
+
const key=process.env.KEY
|
8 |
+
const user=req.nextUrl.searchParams.get("user")
|
9 |
+
let res=await fetch("https://dujiaoka.dwzynj.top/main/api/user/find_password.php?admin="+admin+"&key="+key+"&user="+user, {
|
10 |
+
method: "GET",
|
11 |
+
headers:{
|
12 |
+
"token":token
|
13 |
+
},
|
14 |
+
})
|
15 |
+
let msg=await res.json()
|
16 |
+
// console.log(msg)
|
17 |
+
return new Response(JSON.stringify(msg))
|
18 |
+
} catch (e) {
|
19 |
+
console.error("[shuixian] ", e);
|
20 |
+
return new Response(JSON.stringify(e));
|
21 |
+
}
|
22 |
+
}
|
app/api/user/info/route.ts
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { NextRequest, NextResponse } from "next/server";
|
2 |
+
import { auth } from "../../auth";
|
3 |
+
|
4 |
+
export async function GET(req: NextRequest) {
|
5 |
+
try {
|
6 |
+
const authResult = auth(req);
|
7 |
+
if (authResult.error) {
|
8 |
+
return NextResponse.json(authResult, {
|
9 |
+
status: 401,
|
10 |
+
});
|
11 |
+
}
|
12 |
+
const token=req.headers.get("auth") ?? ""
|
13 |
+
const admin=process.env.ADMIN
|
14 |
+
const key=process.env.KEY
|
15 |
+
const user=req.nextUrl.searchParams.get("user")
|
16 |
+
const password=req.nextUrl.searchParams.get("password")
|
17 |
+
let res=await fetch("https://dujiaoka.dwzynj.top/main/api/user/user_data.php?admin="+admin+"&key="+key+"&user="+user+"&password="+password, {
|
18 |
+
method: "GET",
|
19 |
+
headers:{
|
20 |
+
"token":token
|
21 |
+
}
|
22 |
+
})
|
23 |
+
let msg=await res.json()
|
24 |
+
// console.log(msg)
|
25 |
+
return new Response(JSON.stringify(msg))
|
26 |
+
} catch (e) {
|
27 |
+
console.error("[shuixian] ", e);
|
28 |
+
return new Response(JSON.stringify(e));
|
29 |
+
}
|
30 |
+
}
|
app/api/user/kami/route.ts
ADDED
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { NextRequest, NextResponse } from "next/server";
|
2 |
+
import { auth } from "../../auth";
|
3 |
+
|
4 |
+
export async function GET(req: NextRequest) {
|
5 |
+
try {
|
6 |
+
const authResult = auth(req);
|
7 |
+
if (authResult.error) {
|
8 |
+
return NextResponse.json(authResult, {
|
9 |
+
status: 401,
|
10 |
+
});
|
11 |
+
}
|
12 |
+
const token=req.headers.get("auth") ?? ""
|
13 |
+
const admin=process.env.ADMIN
|
14 |
+
const key=process.env.KEY
|
15 |
+
const user=req.nextUrl.searchParams.get("user")
|
16 |
+
const password=req.nextUrl.searchParams.get("password")
|
17 |
+
const code=req.nextUrl.searchParams.get("code")
|
18 |
+
let res=await fetch("https://dujiaoka.dwzynj.top/main/api/kami/use.php?admin="+admin+"&key="+key+"&user="+user+"&password="+password+"&code="+code, {
|
19 |
+
method: "GET",
|
20 |
+
headers:{
|
21 |
+
"token":token
|
22 |
+
},
|
23 |
+
})
|
24 |
+
let msg=await res.json()
|
25 |
+
console.log(msg)
|
26 |
+
return new Response(JSON.stringify(msg))
|
27 |
+
} catch (e) {
|
28 |
+
console.error("[shuixian] ", e);
|
29 |
+
return new Response(JSON.stringify(e));
|
30 |
+
}
|
31 |
+
}
|
app/api/user/login/route.ts
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { NextRequest } from "next/server";
|
2 |
+
|
3 |
+
export async function GET(req: NextRequest) {
|
4 |
+
try {
|
5 |
+
const admin=process.env.ADMIN
|
6 |
+
const key=process.env.KEY
|
7 |
+
const user=req.nextUrl.searchParams.get("user")
|
8 |
+
const password=req.nextUrl.searchParams.get("password")
|
9 |
+
let res=await fetch("https://dujiaoka.dwzynj.top/main/api/user/login.php?admin="+admin+"&key="+key+"&user="+user+"&password="+password, {
|
10 |
+
method: "GET"
|
11 |
+
})
|
12 |
+
let msg=await res.json()
|
13 |
+
// console.log(msg)
|
14 |
+
return new Response(JSON.stringify(msg))
|
15 |
+
} catch (e) {
|
16 |
+
console.error("[shuixian] ", e);
|
17 |
+
return new Response(JSON.stringify(e));
|
18 |
+
}
|
19 |
+
}
|
app/api/user/mail/route.ts
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { NextRequest } from "next/server";
|
2 |
+
|
3 |
+
export async function GET(req: NextRequest) {
|
4 |
+
try {
|
5 |
+
const admin=process.env.ADMIN
|
6 |
+
const key=process.env.KEY
|
7 |
+
const user=req.nextUrl.searchParams.get("user")
|
8 |
+
const mail=req.nextUrl.searchParams.get("mail")
|
9 |
+
let res=await fetch("https://dujiaoka.dwzynj.top/main/api/user/code.php?admin="+admin+"&key="+key+"&user="+user+"&mail="+mail, {
|
10 |
+
method: "GET"
|
11 |
+
})
|
12 |
+
let msg=await res.json()
|
13 |
+
// console.log(msg)
|
14 |
+
return new Response(JSON.stringify(msg))
|
15 |
+
} catch (e) {
|
16 |
+
console.error("[shuixian] ", e);
|
17 |
+
return new Response(JSON.stringify(e));
|
18 |
+
}
|
19 |
+
}
|
app/api/user/register/route.ts
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { NextRequest } from "next/server";
|
2 |
+
|
3 |
+
export async function GET(req: NextRequest) {
|
4 |
+
try {
|
5 |
+
const admin=process.env.ADMIN
|
6 |
+
const key=process.env.KEY
|
7 |
+
const user=req.nextUrl.searchParams.get("user")
|
8 |
+
const password=req.nextUrl.searchParams.get("password")
|
9 |
+
const name=req.nextUrl.searchParams.get("name")
|
10 |
+
const mail=req.nextUrl.searchParams.get("mail")
|
11 |
+
const code=req.nextUrl.searchParams.get("code")
|
12 |
+
let res=await fetch("https://dujiaoka.dwzynj.top/main/api/user/register.php?admin="+admin+"&key="+key+"&user="+user+"&password="+password+"&name="+name+"&mail="+mail+"&code="+code, {
|
13 |
+
method: "GET"
|
14 |
+
})
|
15 |
+
let msg=await res.json()
|
16 |
+
// console.log(msg)
|
17 |
+
return new Response(JSON.stringify(msg))
|
18 |
+
} catch (e) {
|
19 |
+
console.error("[shuixian] ", e);
|
20 |
+
return new Response(JSON.stringify(e));
|
21 |
+
}
|
22 |
+
}
|
app/api/user/set/route.ts
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { NextRequest, NextResponse } from "next/server";
|
2 |
+
import { auth } from "../../auth";
|
3 |
+
|
4 |
+
export async function GET(req: NextRequest) {
|
5 |
+
try {
|
6 |
+
const authResult = auth(req);
|
7 |
+
if (authResult.error) {
|
8 |
+
return NextResponse.json(authResult, {
|
9 |
+
status: 401,
|
10 |
+
});
|
11 |
+
}
|
12 |
+
const token=req.headers.get("auth") ?? ""
|
13 |
+
const admin=process.env.ADMIN
|
14 |
+
const key=process.env.KEY
|
15 |
+
const user=req.nextUrl.searchParams.get("user")
|
16 |
+
const project=req.nextUrl.searchParams.get("project")
|
17 |
+
const projectName=req.nextUrl.searchParams.get("projectName")
|
18 |
+
const data=req.nextUrl.searchParams.get("data")
|
19 |
+
let res=await fetch("https://dujiaoka.dwzynj.top/main/api/user/user_set.php?admin="+admin+"&key="+key+"&user="+user+"&project="+project+"&"+projectName+"="+data, {
|
20 |
+
method: "GET",
|
21 |
+
headers:{
|
22 |
+
"token":token
|
23 |
+
},
|
24 |
+
})
|
25 |
+
|
26 |
+
let msg=await res.json()
|
27 |
+
// console.log(msg)
|
28 |
+
return new Response(JSON.stringify(msg))
|
29 |
+
} catch (e) {
|
30 |
+
console.error("[shuixian] ", e);
|
31 |
+
return new Response(JSON.stringify(e));
|
32 |
+
}
|
33 |
+
}
|
app/api/user/sig/route.ts
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { NextRequest, NextResponse } from "next/server";
|
2 |
+
import { auth } from "../../auth";
|
3 |
+
|
4 |
+
export async function GET(req: NextRequest) {
|
5 |
+
try {
|
6 |
+
const authResult = auth(req);
|
7 |
+
if (authResult.error) {
|
8 |
+
return NextResponse.json(authResult, {
|
9 |
+
status: 401,
|
10 |
+
});
|
11 |
+
}
|
12 |
+
const token=req.headers.get("auth") ?? ""
|
13 |
+
const admin=process.env.ADMIN
|
14 |
+
const key=process.env.KEY
|
15 |
+
const user=req.nextUrl.searchParams.get("user")
|
16 |
+
const password=req.nextUrl.searchParams.get("password")
|
17 |
+
let res=await fetch("https://dujiaoka.dwzynj.top/main/api/user/user_sig.php?admin="+admin+"&key="+key+"&user="+user+"&password="+password, {
|
18 |
+
method: "GET",
|
19 |
+
headers:{
|
20 |
+
"token":token
|
21 |
+
}
|
22 |
+
})
|
23 |
+
let msg=await res.json()
|
24 |
+
console.log(msg)
|
25 |
+
return new Response(JSON.stringify(msg))
|
26 |
+
} catch (e) {
|
27 |
+
console.error("[shuixian] ", e);
|
28 |
+
return new Response(JSON.stringify(e));
|
29 |
+
}
|
30 |
+
}
|
app/api/wanjuan/route.ts
CHANGED
@@ -1,8 +1,17 @@
|
|
1 |
-
|
|
|
|
|
|
|
2 |
try {
|
3 |
let token = process.env.WANJUAN_TOKEN;
|
4 |
let body = { message: await req.json() };
|
5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
6 |
console.log(JSON.stringify(body));
|
7 |
let res = "";
|
8 |
await fetch("http://47.94.237.159:8080/v1/wanjuan", {
|
|
|
1 |
+
import { NextRequest, NextResponse } from "next/server";
|
2 |
+
import { auth } from "../auth";
|
3 |
+
|
4 |
+
export async function POST(req: NextRequest) {
|
5 |
try {
|
6 |
let token = process.env.WANJUAN_TOKEN;
|
7 |
let body = { message: await req.json() };
|
8 |
|
9 |
+
const authResult = auth(req);
|
10 |
+
if (authResult.error) {
|
11 |
+
return NextResponse.json(authResult, {
|
12 |
+
status: 401,
|
13 |
+
});
|
14 |
+
}
|
15 |
console.log(JSON.stringify(body));
|
16 |
let res = "";
|
17 |
await fetch("http://47.94.237.159:8080/v1/wanjuan", {
|
app/components/findpwd.module.scss
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.name{
|
2 |
+
border: var(--border-in-light);
|
3 |
+
padding: 10px;
|
4 |
+
border-radius: 10px;
|
5 |
+
-webkit-appearance: none;
|
6 |
+
-moz-appearance: none;
|
7 |
+
appearance: none;
|
8 |
+
cursor: pointer;
|
9 |
+
background-color: var(--white);
|
10 |
+
color: var(--black);
|
11 |
+
text-align: left;
|
12 |
+
}
|
13 |
+
|
14 |
+
.user {
|
15 |
+
padding: 20px;
|
16 |
+
overflow: auto;
|
17 |
+
}
|
18 |
+
|
19 |
+
.font{
|
20 |
+
font-size: 15px;
|
21 |
+
padding-right: 5px;
|
22 |
+
text-align: center;
|
23 |
+
.wallet{
|
24 |
+
color: #2279ca;
|
25 |
+
};
|
26 |
+
.vipTime{
|
27 |
+
font-size: 8px;
|
28 |
+
padding-top: 5px;
|
29 |
+
};
|
30 |
+
.vipState{
|
31 |
+
color: #dc423c;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
.sigState{
|
36 |
+
color: #2279ca;
|
37 |
+
padding-right: 10px;
|
38 |
+
}
|
39 |
+
|
40 |
+
.sigButton{
|
41 |
+
border: var(--border-in-light);
|
42 |
+
padding: 10px;
|
43 |
+
border-radius: 10px;
|
44 |
+
cursor: pointer;
|
45 |
+
&:hover{
|
46 |
+
border-color: #2279ca;
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
.login{
|
51 |
+
text-align: center;
|
52 |
+
div{
|
53 |
+
padding: 10px;
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
.wangji{
|
58 |
+
padding-right: 60px;
|
59 |
+
font-size: 12px;
|
60 |
+
}
|
61 |
+
|
62 |
+
.zhuce{
|
63 |
+
padding-left: 60px;
|
64 |
+
font-size: 12px;
|
65 |
+
}
|
66 |
+
|
67 |
+
.loginButton{
|
68 |
+
background-color: #1db144;
|
69 |
+
color: white;
|
70 |
+
padding: 0;
|
71 |
+
width: 183px;
|
72 |
+
display: block;
|
73 |
+
margin: 0 auto;
|
74 |
+
}
|
app/components/findpwd.tsx
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ErrorBoundary } from "./error";
|
2 |
+
import Locale, { AllLangs, changeLang, getLang } from "../locales";
|
3 |
+
import ChatIcon from "../icons/chatgpt.svg"
|
4 |
+
import styles from "./findpwd.module.scss";
|
5 |
+
import { IconButton } from "./button";
|
6 |
+
import { useUserStore } from "../store";
|
7 |
+
import { useState } from "react";
|
8 |
+
|
9 |
+
|
10 |
+
export function findpwd(){
|
11 |
+
const userStore=useUserStore()
|
12 |
+
const [user, setUser] = useState("");
|
13 |
+
|
14 |
+
const onUser = (text: string) => {
|
15 |
+
setUser(text)
|
16 |
+
};
|
17 |
+
|
18 |
+
const findpwd=()=>{
|
19 |
+
userStore.findpwd(user)
|
20 |
+
}
|
21 |
+
|
22 |
+
return (
|
23 |
+
<ErrorBoundary>
|
24 |
+
<div className="window-header">
|
25 |
+
<div className="window-header-title">
|
26 |
+
<div className="window-header-main-title">
|
27 |
+
{Locale.User.Login}
|
28 |
+
</div>
|
29 |
+
<div className="window-header-sub-title">
|
30 |
+
{Locale.User.LoginTitle}
|
31 |
+
</div>
|
32 |
+
</div>
|
33 |
+
</div>
|
34 |
+
|
35 |
+
<div>
|
36 |
+
<div className={styles.login}>
|
37 |
+
<div><ChatIcon></ChatIcon></div>
|
38 |
+
<div>
|
39 |
+
<input
|
40 |
+
type="input"
|
41 |
+
className={styles.name}
|
42 |
+
placeholder="账号"
|
43 |
+
onInput={(e) => onUser(e.currentTarget.value)}
|
44 |
+
value={user}
|
45 |
+
></input>
|
46 |
+
</div>
|
47 |
+
<div>
|
48 |
+
<span className={styles.wangji}><a href="/#/login">登录</a></span>
|
49 |
+
<span className={styles.zhuce}><a href="/#/register">注册</a></span>
|
50 |
+
</div>
|
51 |
+
<div>
|
52 |
+
<IconButton
|
53 |
+
text="找回密码"
|
54 |
+
className={styles.loginButton}
|
55 |
+
onClick={()=>{
|
56 |
+
findpwd()
|
57 |
+
}}
|
58 |
+
></IconButton>
|
59 |
+
</div>
|
60 |
+
</div>
|
61 |
+
</div>
|
62 |
+
</ErrorBoundary>
|
63 |
+
);
|
64 |
+
}
|
app/components/home.tsx
CHANGED
@@ -42,6 +42,22 @@ const Chat = dynamic(async () => (await import("./chat")).Chat, {
|
|
42 |
loading: () => <Loading noLogo />,
|
43 |
});
|
44 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
45 |
const NewChat = dynamic(async () => (await import("./new-chat")).NewChat, {
|
46 |
loading: () => <Loading noLogo />,
|
47 |
});
|
@@ -116,6 +132,10 @@ function Screen() {
|
|
116 |
<Route path={Path.NewChat} element={<NewChat />} />
|
117 |
<Route path={Path.Masks} element={<MaskPage />} />
|
118 |
<Route path={Path.Chat} element={<Chat />} />
|
|
|
|
|
|
|
|
|
119 |
<Route path={Path.Settings} element={<Settings />} />
|
120 |
</Routes>
|
121 |
</div>
|
|
|
42 |
loading: () => <Loading noLogo />,
|
43 |
});
|
44 |
|
45 |
+
const User = dynamic(async () => (await import("./user")).User, {
|
46 |
+
loading: () => <Loading noLogo />,
|
47 |
+
});
|
48 |
+
|
49 |
+
const Findpwd = dynamic(async () => (await import("./findpwd")).findpwd, {
|
50 |
+
loading: () => <Loading noLogo />,
|
51 |
+
});
|
52 |
+
|
53 |
+
const Login = dynamic(async () => (await import("./login")).Login, {
|
54 |
+
loading: () => <Loading noLogo />,
|
55 |
+
});
|
56 |
+
|
57 |
+
const Register = dynamic(async () => (await import("./register")).Register, {
|
58 |
+
loading: () => <Loading noLogo />,
|
59 |
+
});
|
60 |
+
|
61 |
const NewChat = dynamic(async () => (await import("./new-chat")).NewChat, {
|
62 |
loading: () => <Loading noLogo />,
|
63 |
});
|
|
|
132 |
<Route path={Path.NewChat} element={<NewChat />} />
|
133 |
<Route path={Path.Masks} element={<MaskPage />} />
|
134 |
<Route path={Path.Chat} element={<Chat />} />
|
135 |
+
<Route path={Path.User} element={<User />} />
|
136 |
+
<Route path={Path.Login} element={<Login />} />
|
137 |
+
<Route path={Path.Findpwd} element={<Findpwd />} />
|
138 |
+
<Route path={Path.Register} element={<Register />} />
|
139 |
<Route path={Path.Settings} element={<Settings />} />
|
140 |
</Routes>
|
141 |
</div>
|
app/components/login.module.scss
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.name{
|
2 |
+
border: var(--border-in-light);
|
3 |
+
padding: 10px;
|
4 |
+
border-radius: 10px;
|
5 |
+
-webkit-appearance: none;
|
6 |
+
-moz-appearance: none;
|
7 |
+
appearance: none;
|
8 |
+
cursor: pointer;
|
9 |
+
background-color: var(--white);
|
10 |
+
color: var(--black);
|
11 |
+
text-align: left;
|
12 |
+
}
|
13 |
+
|
14 |
+
.user {
|
15 |
+
padding: 20px;
|
16 |
+
overflow: auto;
|
17 |
+
}
|
18 |
+
|
19 |
+
.font{
|
20 |
+
font-size: 15px;
|
21 |
+
padding-right: 5px;
|
22 |
+
text-align: center;
|
23 |
+
.wallet{
|
24 |
+
color: #2279ca;
|
25 |
+
};
|
26 |
+
.vipTime{
|
27 |
+
font-size: 8px;
|
28 |
+
padding-top: 5px;
|
29 |
+
};
|
30 |
+
.vipState{
|
31 |
+
color: #dc423c;
|
32 |
+
}
|
33 |
+
}
|
34 |
+
|
35 |
+
.sigState{
|
36 |
+
color: #2279ca;
|
37 |
+
padding-right: 10px;
|
38 |
+
}
|
39 |
+
|
40 |
+
.sigButton{
|
41 |
+
border: var(--border-in-light);
|
42 |
+
padding: 10px;
|
43 |
+
border-radius: 10px;
|
44 |
+
cursor: pointer;
|
45 |
+
&:hover{
|
46 |
+
border-color: #2279ca;
|
47 |
+
}
|
48 |
+
}
|
49 |
+
|
50 |
+
.login{
|
51 |
+
text-align: center;
|
52 |
+
div{
|
53 |
+
padding: 10px;
|
54 |
+
}
|
55 |
+
}
|
56 |
+
|
57 |
+
.wangji{
|
58 |
+
padding-right: 50px;
|
59 |
+
font-size: 12px;
|
60 |
+
}
|
61 |
+
|
62 |
+
.zhuce{
|
63 |
+
padding-left: 50px;
|
64 |
+
font-size: 12px;
|
65 |
+
}
|
66 |
+
|
67 |
+
.loginButton{
|
68 |
+
background-color: #1db144;
|
69 |
+
color: white;
|
70 |
+
padding: 0;
|
71 |
+
width: 183px;
|
72 |
+
display: block;
|
73 |
+
margin: 0 auto;
|
74 |
+
}
|
app/components/login.tsx
ADDED
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ErrorBoundary } from "./error";
|
2 |
+
import Locale, { AllLangs, changeLang, getLang } from "../locales";
|
3 |
+
import ChatIcon from "../icons/chatgpt.svg"
|
4 |
+
import styles from "./login.module.scss";
|
5 |
+
import { IconButton } from "./button";
|
6 |
+
import { useUserStore } from "../store";
|
7 |
+
import { useState } from "react";
|
8 |
+
|
9 |
+
|
10 |
+
export function Login(){
|
11 |
+
const userStore=useUserStore()
|
12 |
+
const [user, setUser] = useState("");
|
13 |
+
const [password, setPassword] = useState("");
|
14 |
+
|
15 |
+
const onUser = (text: string) => {
|
16 |
+
setUser(text)
|
17 |
+
};
|
18 |
+
const onPassword = (text: string) => {
|
19 |
+
setPassword(text)
|
20 |
+
};
|
21 |
+
|
22 |
+
const loginTo=()=>{
|
23 |
+
userStore.login(user,password)
|
24 |
+
}
|
25 |
+
|
26 |
+
return (
|
27 |
+
<ErrorBoundary>
|
28 |
+
<div className="window-header">
|
29 |
+
<div className="window-header-title">
|
30 |
+
<div className="window-header-main-title">
|
31 |
+
{Locale.User.Login}
|
32 |
+
</div>
|
33 |
+
<div className="window-header-sub-title">
|
34 |
+
{Locale.User.LoginTitle}
|
35 |
+
</div>
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
|
39 |
+
<div>
|
40 |
+
<div className={styles.login}>
|
41 |
+
<div><ChatIcon></ChatIcon></div>
|
42 |
+
<div>
|
43 |
+
<input
|
44 |
+
type="input"
|
45 |
+
className={styles.name}
|
46 |
+
placeholder="账号"
|
47 |
+
onInput={(e) => onUser(e.currentTarget.value)}
|
48 |
+
value={user}
|
49 |
+
></input>
|
50 |
+
</div>
|
51 |
+
<div>
|
52 |
+
<input
|
53 |
+
type="password"
|
54 |
+
className={styles.name}
|
55 |
+
placeholder="密码"
|
56 |
+
onInput={(e) => onPassword(e.currentTarget.value)}
|
57 |
+
value={password}
|
58 |
+
></input>
|
59 |
+
</div>
|
60 |
+
<div>
|
61 |
+
<span className={styles.wangji}><a href="/#/findpwd">忘记密码</a></span>
|
62 |
+
<span className={styles.zhuce}><a href="/#/register">注册</a></span>
|
63 |
+
</div>
|
64 |
+
<div>
|
65 |
+
<IconButton
|
66 |
+
text="登录"
|
67 |
+
className={styles.loginButton}
|
68 |
+
onClick={()=>{
|
69 |
+
loginTo()
|
70 |
+
}}
|
71 |
+
></IconButton>
|
72 |
+
</div>
|
73 |
+
</div>
|
74 |
+
</div>
|
75 |
+
</ErrorBoundary>
|
76 |
+
);
|
77 |
+
}
|
app/components/register.module.scss
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.name{
|
2 |
+
border: var(--border-in-light);
|
3 |
+
padding: 10px;
|
4 |
+
border-radius: 10px;
|
5 |
+
-webkit-appearance: none;
|
6 |
+
-moz-appearance: none;
|
7 |
+
appearance: none;
|
8 |
+
cursor: pointer;
|
9 |
+
background-color: var(--white);
|
10 |
+
color: var(--black);
|
11 |
+
text-align: left;
|
12 |
+
}
|
13 |
+
|
14 |
+
.code{
|
15 |
+
border: var(--border-in-light);
|
16 |
+
padding: 10px;
|
17 |
+
margin-right: 10px;
|
18 |
+
border-radius: 10px;
|
19 |
+
-webkit-appearance: none;
|
20 |
+
-moz-appearance: none;
|
21 |
+
appearance: none;
|
22 |
+
cursor: pointer;
|
23 |
+
background-color: var(--white);
|
24 |
+
color: var(--black);
|
25 |
+
text-align: left;
|
26 |
+
width: 58px;
|
27 |
+
}
|
28 |
+
|
29 |
+
.codebox{
|
30 |
+
vertical-align: bottom;
|
31 |
+
}
|
32 |
+
|
33 |
+
.user {
|
34 |
+
padding: 20px;
|
35 |
+
overflow: auto;
|
36 |
+
}
|
37 |
+
|
38 |
+
.font{
|
39 |
+
font-size: 15px;
|
40 |
+
padding-right: 5px;
|
41 |
+
text-align: center;
|
42 |
+
.wallet{
|
43 |
+
color: #2279ca;
|
44 |
+
};
|
45 |
+
.vipTime{
|
46 |
+
font-size: 8px;
|
47 |
+
padding-top: 5px;
|
48 |
+
};
|
49 |
+
.vipState{
|
50 |
+
color: #dc423c;
|
51 |
+
}
|
52 |
+
}
|
53 |
+
|
54 |
+
.sigState{
|
55 |
+
color: #2279ca;
|
56 |
+
padding-right: 10px;
|
57 |
+
}
|
58 |
+
|
59 |
+
.sigButton{
|
60 |
+
border: var(--border-in-light);
|
61 |
+
padding: 10px;
|
62 |
+
border-radius: 10px;
|
63 |
+
cursor: pointer;
|
64 |
+
&:hover{
|
65 |
+
border-color: #2279ca;
|
66 |
+
}
|
67 |
+
}
|
68 |
+
|
69 |
+
.register{
|
70 |
+
text-align: center;
|
71 |
+
div{
|
72 |
+
padding: 10px;
|
73 |
+
}
|
74 |
+
}
|
75 |
+
|
76 |
+
.wangji{
|
77 |
+
padding-right: 50px;
|
78 |
+
font-size: 12px;
|
79 |
+
}
|
80 |
+
|
81 |
+
.zhuce{
|
82 |
+
padding-left: 50px;
|
83 |
+
font-size: 12px;
|
84 |
+
}
|
85 |
+
|
86 |
+
.registerButton{
|
87 |
+
background-color: #1db144;
|
88 |
+
color: white;
|
89 |
+
padding: 0;
|
90 |
+
width: 183px;
|
91 |
+
display: block;
|
92 |
+
margin: 0 auto;
|
93 |
+
}
|
94 |
+
|
95 |
+
.codeButton{
|
96 |
+
background-color: #1db144;
|
97 |
+
color: white;
|
98 |
+
padding: 0;
|
99 |
+
width: 93px;
|
100 |
+
display: inline;
|
101 |
+
margin: 0 auto;
|
102 |
+
font-size: 8px;
|
103 |
+
}
|
app/components/register.tsx
ADDED
@@ -0,0 +1,152 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { ErrorBoundary } from "./error";
|
2 |
+
import Locale, { AllLangs, changeLang, getLang } from "../locales";
|
3 |
+
import { useUserStore } from "../store";
|
4 |
+
import { useEffect, useState } from "react";
|
5 |
+
import styles from "./register.module.scss";
|
6 |
+
import ChatIcon from "../icons/chatgpt.svg"
|
7 |
+
import { IconButton } from "./button";
|
8 |
+
import { showToast } from "./ui-lib";
|
9 |
+
|
10 |
+
export function Register(){
|
11 |
+
const userStore=useUserStore()
|
12 |
+
const [userName, setUserName] = useState("");
|
13 |
+
const [getcode, setgetcode] = useState("");
|
14 |
+
const [codeStatus, setcodeStatus] = useState("");
|
15 |
+
const [name, setName] = useState("");
|
16 |
+
const [password, setPassword] = useState("");
|
17 |
+
const [mail, setMail] = useState("");
|
18 |
+
const [code, setCode] = useState("");
|
19 |
+
|
20 |
+
const onUserName = (text: string) => {
|
21 |
+
setUserName(text)
|
22 |
+
};
|
23 |
+
const onName = (text: string) => {
|
24 |
+
setName(text)
|
25 |
+
};
|
26 |
+
const onPassword = (text: string) => {
|
27 |
+
setPassword(text)
|
28 |
+
};
|
29 |
+
const onMail = (text: string) => {
|
30 |
+
setMail(text)
|
31 |
+
};
|
32 |
+
const onCode = (text: string) => {
|
33 |
+
setCode(text)
|
34 |
+
};
|
35 |
+
|
36 |
+
const loginTo=()=>{
|
37 |
+
userStore.register(userName,password,name,mail,code)
|
38 |
+
}
|
39 |
+
|
40 |
+
const getMailCode=()=>{
|
41 |
+
userStore.getMailCode(userName,mail)
|
42 |
+
getCode()
|
43 |
+
}
|
44 |
+
var countdown=60;
|
45 |
+
const getCode=()=>{
|
46 |
+
if (countdown == 0) {
|
47 |
+
setcodeStatus("")
|
48 |
+
setgetcode("发送验证码")
|
49 |
+
countdown = 60;
|
50 |
+
return;
|
51 |
+
} else {
|
52 |
+
setcodeStatus("true")
|
53 |
+
setgetcode("(" + countdown + ")")
|
54 |
+
countdown--;
|
55 |
+
}
|
56 |
+
setTimeout(function() {
|
57 |
+
getCode() }
|
58 |
+
,1000)
|
59 |
+
}
|
60 |
+
|
61 |
+
useEffect(()=>{
|
62 |
+
setcodeStatus("")
|
63 |
+
setgetcode("发送验证码")
|
64 |
+
},[])
|
65 |
+
|
66 |
+
return (
|
67 |
+
<ErrorBoundary>
|
68 |
+
<div className="window-header">
|
69 |
+
<div className="window-header-title">
|
70 |
+
<div className="window-header-main-title">
|
71 |
+
{Locale.User.Login}
|
72 |
+
</div>
|
73 |
+
<div className="window-header-sub-title">
|
74 |
+
{Locale.User.LoginTitle}
|
75 |
+
</div>
|
76 |
+
</div>
|
77 |
+
</div>
|
78 |
+
|
79 |
+
<div>
|
80 |
+
<div className={styles.register}>
|
81 |
+
<div><ChatIcon></ChatIcon></div>
|
82 |
+
<div>
|
83 |
+
<input
|
84 |
+
type="input"
|
85 |
+
className={styles.name}
|
86 |
+
placeholder="呢称"
|
87 |
+
onInput={(e) => onName(e.currentTarget.value)}
|
88 |
+
value={name}
|
89 |
+
></input>
|
90 |
+
</div>
|
91 |
+
<div>
|
92 |
+
<input
|
93 |
+
type="input"
|
94 |
+
className={styles.name}
|
95 |
+
placeholder="账号 (纯数字)"
|
96 |
+
onInput={(e) => onUserName(e.currentTarget.value)}
|
97 |
+
value={userName}
|
98 |
+
></input>
|
99 |
+
</div>
|
100 |
+
<div>
|
101 |
+
<input
|
102 |
+
type="password"
|
103 |
+
className={styles.name}
|
104 |
+
placeholder="密码 (最少六位)"
|
105 |
+
onInput={(e) => onPassword(e.currentTarget.value)}
|
106 |
+
value={password}
|
107 |
+
></input>
|
108 |
+
</div>
|
109 |
+
<div>
|
110 |
+
<input
|
111 |
+
type="input"
|
112 |
+
className={styles.name}
|
113 |
+
placeholder="邮箱"
|
114 |
+
onInput={(e) => onMail(e.currentTarget.value)}
|
115 |
+
value={mail}
|
116 |
+
></input>
|
117 |
+
</div>
|
118 |
+
<div className={styles.codebox}>
|
119 |
+
<input
|
120 |
+
type="input"
|
121 |
+
className={styles.code}
|
122 |
+
placeholder="验证码"
|
123 |
+
onInput={(e) => onCode(e.currentTarget.value)}
|
124 |
+
value={code}
|
125 |
+
></input>
|
126 |
+
<IconButton
|
127 |
+
disabled={!!codeStatus}
|
128 |
+
text={getcode}
|
129 |
+
className={styles.codeButton}
|
130 |
+
onClick={()=>{
|
131 |
+
getMailCode()
|
132 |
+
}}
|
133 |
+
></IconButton>
|
134 |
+
</div>
|
135 |
+
<div>
|
136 |
+
<span className={styles.wangji}><a href="/#/findpwd">忘记密码</a></span>
|
137 |
+
<span className={styles.zhuce}><a href="/#/login">登录</a></span>
|
138 |
+
</div>
|
139 |
+
<div>
|
140 |
+
<IconButton
|
141 |
+
text="注册"
|
142 |
+
className={styles.registerButton}
|
143 |
+
onClick={()=>{
|
144 |
+
loginTo()
|
145 |
+
}}
|
146 |
+
></IconButton>
|
147 |
+
</div>
|
148 |
+
</div>
|
149 |
+
</div>
|
150 |
+
</ErrorBoundary>
|
151 |
+
);
|
152 |
+
}
|
app/components/settings.tsx
CHANGED
@@ -23,6 +23,7 @@ import {
|
|
23 |
useAppConfig,
|
24 |
ALL_BOT,
|
25 |
ModalConfigValidator,
|
|
|
26 |
} from "../store";
|
27 |
|
28 |
import Locale, { AllLangs, changeLang, getLang } from "../locales";
|
@@ -292,6 +293,7 @@ export function Settings() {
|
|
292 |
onClick={() => {
|
293 |
if (confirm(Locale.Settings.Actions.ConfirmClearAll)) {
|
294 |
chatStore.clearAllData();
|
|
|
295 |
}
|
296 |
}}
|
297 |
bordered
|
@@ -322,7 +324,7 @@ export function Settings() {
|
|
322 |
</div>
|
323 |
<div className={styles["settings"]}>
|
324 |
<List>
|
325 |
-
<ListItem title={Locale.Settings.Avatar}>
|
326 |
<Popover
|
327 |
onClose={() => setShowEmojiPicker(false)}
|
328 |
content={
|
@@ -342,7 +344,7 @@ export function Settings() {
|
|
342 |
<Avatar avatar={config.avatar} />
|
343 |
</div>
|
344 |
</Popover>
|
345 |
-
</ListItem>
|
346 |
|
347 |
{/* <ListItem
|
348 |
title={Locale.Settings.Update.Version(currentVersion ?? "unknown")}
|
@@ -507,7 +509,7 @@ export function Settings() {
|
|
507 |
</ListItem>
|
508 |
) : null}
|
509 |
|
510 |
-
<ListItem
|
511 |
title={Locale.Settings.Usage.Title}
|
512 |
subTitle={
|
513 |
showUsage
|
@@ -529,7 +531,7 @@ export function Settings() {
|
|
529 |
onClick={() => checkUsage(true)}
|
530 |
/>
|
531 |
)}
|
532 |
-
</ListItem>
|
533 |
</List>
|
534 |
|
535 |
<List>
|
|
|
23 |
useAppConfig,
|
24 |
ALL_BOT,
|
25 |
ModalConfigValidator,
|
26 |
+
useUserStore,
|
27 |
} from "../store";
|
28 |
|
29 |
import Locale, { AllLangs, changeLang, getLang } from "../locales";
|
|
|
293 |
onClick={() => {
|
294 |
if (confirm(Locale.Settings.Actions.ConfirmClearAll)) {
|
295 |
chatStore.clearAllData();
|
296 |
+
useUserStore.getState().reset();
|
297 |
}
|
298 |
}}
|
299 |
bordered
|
|
|
324 |
</div>
|
325 |
<div className={styles["settings"]}>
|
326 |
<List>
|
327 |
+
{/* <ListItem title={Locale.Settings.Avatar}>
|
328 |
<Popover
|
329 |
onClose={() => setShowEmojiPicker(false)}
|
330 |
content={
|
|
|
344 |
<Avatar avatar={config.avatar} />
|
345 |
</div>
|
346 |
</Popover>
|
347 |
+
</ListItem> */}
|
348 |
|
349 |
{/* <ListItem
|
350 |
title={Locale.Settings.Update.Version(currentVersion ?? "unknown")}
|
|
|
509 |
</ListItem>
|
510 |
) : null}
|
511 |
|
512 |
+
{/* <ListItem
|
513 |
title={Locale.Settings.Usage.Title}
|
514 |
subTitle={
|
515 |
showUsage
|
|
|
531 |
onClick={() => checkUsage(true)}
|
532 |
/>
|
533 |
)}
|
534 |
+
</ListItem> */}
|
535 |
</List>
|
536 |
|
537 |
<List>
|
app/components/sidebar.tsx
CHANGED
@@ -174,9 +174,9 @@ export function SideBar(props: { className?: string }) {
|
|
174 |
</Link>
|
175 |
</div>
|
176 |
<div className={styles["sidebar-action"]}>
|
177 |
-
|
178 |
<IconButton icon={<GithubIcon />} shadow />
|
179 |
-
</
|
180 |
</div>
|
181 |
</div>
|
182 |
<div>
|
|
|
174 |
</Link>
|
175 |
</div>
|
176 |
<div className={styles["sidebar-action"]}>
|
177 |
+
<Link to={Path.User}>
|
178 |
<IconButton icon={<GithubIcon />} shadow />
|
179 |
+
</Link>
|
180 |
</div>
|
181 |
</div>
|
182 |
<div>
|
app/components/ui-lib.tsx
CHANGED
@@ -5,7 +5,7 @@ import EyeIcon from "../icons/eye.svg";
|
|
5 |
import EyeOffIcon from "../icons/eye-off.svg";
|
6 |
|
7 |
import { createRoot } from "react-dom/client";
|
8 |
-
import React, { HTMLProps, useEffect, useState
|
9 |
import { IconButton } from "./button";
|
10 |
|
11 |
export function Popover(props: {
|
|
|
5 |
import EyeOffIcon from "../icons/eye-off.svg";
|
6 |
|
7 |
import { createRoot } from "react-dom/client";
|
8 |
+
import React, { HTMLProps, useEffect, useState} from "react";
|
9 |
import { IconButton } from "./button";
|
10 |
|
11 |
export function Popover(props: {
|
app/components/user.module.scss
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.name{
|
2 |
+
border: var(--border-in-light);
|
3 |
+
padding: 10px;
|
4 |
+
border-radius: 10px;
|
5 |
+
-webkit-appearance: none;
|
6 |
+
-moz-appearance: none;
|
7 |
+
appearance: none;
|
8 |
+
cursor: pointer;
|
9 |
+
background-color: var(--white);
|
10 |
+
color: var(--black);
|
11 |
+
text-align: right;
|
12 |
+
}
|
13 |
+
|
14 |
+
.kamicode{
|
15 |
+
border: var(--border-in-light);
|
16 |
+
padding: 10px;
|
17 |
+
border-radius: 10px;
|
18 |
+
-webkit-appearance: none;
|
19 |
+
-moz-appearance: none;
|
20 |
+
appearance: none;
|
21 |
+
cursor: pointer;
|
22 |
+
background-color: var(--white);
|
23 |
+
color: var(--black);
|
24 |
+
text-align: right;
|
25 |
+
}
|
26 |
+
|
27 |
+
.kamiButton{
|
28 |
+
display: inline;
|
29 |
+
margin-left: 10px;
|
30 |
+
background-color: #e8f8ff;
|
31 |
+
div{
|
32 |
+
margin-right: 5px;
|
33 |
+
}
|
34 |
+
}
|
35 |
+
|
36 |
+
.avatar {
|
37 |
+
cursor: pointer;
|
38 |
+
}
|
39 |
+
|
40 |
+
.user {
|
41 |
+
padding: 20px;
|
42 |
+
overflow: auto;
|
43 |
+
height: 100%;
|
44 |
+
}
|
45 |
+
|
46 |
+
.font{
|
47 |
+
font-size: 15px;
|
48 |
+
padding-right: 5px;
|
49 |
+
text-align: center;
|
50 |
+
.wallet{
|
51 |
+
color: #2279ca;
|
52 |
+
};
|
53 |
+
.vipTime{
|
54 |
+
font-size: 8px;
|
55 |
+
padding-top: 5px;
|
56 |
+
};
|
57 |
+
.vipState{
|
58 |
+
color: #dc423c;
|
59 |
+
}
|
60 |
+
}
|
61 |
+
|
62 |
+
.sigState{
|
63 |
+
color: #2279ca;
|
64 |
+
padding-right: 10px;
|
65 |
+
}
|
66 |
+
|
67 |
+
.sigButton{
|
68 |
+
border: var(--border-in-light);
|
69 |
+
padding: 10px;
|
70 |
+
border-radius: 10px;
|
71 |
+
cursor: pointer;
|
72 |
+
&:hover{
|
73 |
+
border-color: #2279ca;
|
74 |
+
}
|
75 |
+
}
|
76 |
+
|
77 |
+
.logoutButton{
|
78 |
+
background-color: #fc090f;
|
79 |
+
text-align: center;
|
80 |
+
color: white;
|
81 |
+
div{
|
82 |
+
margin-right: 5px;
|
83 |
+
}
|
84 |
+
}
|
app/components/user.tsx
ADDED
@@ -0,0 +1,180 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { useState, useEffect } from "react";
|
2 |
+
|
3 |
+
import styles from "./user.module.scss";
|
4 |
+
import EditIcon from "../icons/edit.svg";
|
5 |
+
import { List, ListItem, Popover, showModal, showToast } from "./ui-lib";
|
6 |
+
|
7 |
+
import { IconButton } from "./button";
|
8 |
+
import {
|
9 |
+
useAccessStore,
|
10 |
+
useAppConfig,
|
11 |
+
} from "../store";
|
12 |
+
|
13 |
+
import Locale from "../locales";
|
14 |
+
import { Path } from "../constant";
|
15 |
+
import { ErrorBoundary } from "./error";
|
16 |
+
import { useNavigate } from "react-router-dom";
|
17 |
+
import { Avatar, AvatarPicker } from "./emoji";
|
18 |
+
import { useUserStore } from "../store/user";
|
19 |
+
|
20 |
+
export function User() {
|
21 |
+
const navigate = useNavigate();
|
22 |
+
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
23 |
+
const config = useAppConfig();
|
24 |
+
const updateConfig = config.update;
|
25 |
+
|
26 |
+
const accessStore = useAccessStore();
|
27 |
+
const userStor = useUserStore()
|
28 |
+
|
29 |
+
const [userName, setUserName] = useState("");
|
30 |
+
const [kami, setKami] = useState("");
|
31 |
+
const onUserName = (text: string) => {
|
32 |
+
setUserName(text)
|
33 |
+
userStor.updateName(userName)
|
34 |
+
};
|
35 |
+
|
36 |
+
function getVipTime(){
|
37 |
+
if(!userStor.vip_time_stmp){
|
38 |
+
return ""
|
39 |
+
}
|
40 |
+
let time=new Date().getTime();
|
41 |
+
console.log(time)
|
42 |
+
time=time+Number(userStor.vip_time_stmp)*1000
|
43 |
+
console.log(time)
|
44 |
+
const date = new Date(time)
|
45 |
+
const Y = date.getFullYear()
|
46 |
+
const M = date.getMonth() + 1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1
|
47 |
+
const D = date.getDate()
|
48 |
+
return `${Y} - ${M} - ${D}`
|
49 |
+
}
|
50 |
+
|
51 |
+
useEffect(()=>{
|
52 |
+
setUserName(()=>{return userStor.name;})
|
53 |
+
},[])
|
54 |
+
|
55 |
+
useEffect(() => {
|
56 |
+
const keydownEvent = (e: KeyboardEvent) => {
|
57 |
+
if (e.key === "Escape") {
|
58 |
+
navigate(Path.Home);
|
59 |
+
}
|
60 |
+
};
|
61 |
+
document.addEventListener("keydown", keydownEvent);
|
62 |
+
return () => {
|
63 |
+
document.removeEventListener("keydown", keydownEvent);
|
64 |
+
};
|
65 |
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
66 |
+
}, []);
|
67 |
+
|
68 |
+
return (
|
69 |
+
<ErrorBoundary>
|
70 |
+
<div className="window-header">
|
71 |
+
<div className="window-header-title">
|
72 |
+
<div className="window-header-main-title">
|
73 |
+
{Locale.User.Title}
|
74 |
+
</div>
|
75 |
+
<div className="window-header-sub-title">
|
76 |
+
{Locale.User.SubTitle}
|
77 |
+
</div>
|
78 |
+
</div>
|
79 |
+
</div>
|
80 |
+
<div className={styles["user"]}>
|
81 |
+
<List>
|
82 |
+
<ListItem title={Locale.Settings.Avatar}>
|
83 |
+
<Popover
|
84 |
+
onClose={() => setShowEmojiPicker(false)}
|
85 |
+
content={
|
86 |
+
<AvatarPicker
|
87 |
+
onEmojiClick={(avatar: string) => {
|
88 |
+
updateConfig((config) => (config.avatar = avatar));
|
89 |
+
setShowEmojiPicker(false);
|
90 |
+
}}
|
91 |
+
/>
|
92 |
+
}
|
93 |
+
open={showEmojiPicker}
|
94 |
+
>
|
95 |
+
<div
|
96 |
+
className={styles.avatar}
|
97 |
+
onClick={() => setShowEmojiPicker(true)}
|
98 |
+
>
|
99 |
+
<Avatar avatar={config.avatar} />
|
100 |
+
</div>
|
101 |
+
</Popover>
|
102 |
+
</ListItem>
|
103 |
+
|
104 |
+
<ListItem title={Locale.User.Name}>
|
105 |
+
<input
|
106 |
+
type="input"
|
107 |
+
className={styles.name}
|
108 |
+
value={userName}
|
109 |
+
disabled={!accessStore.auth}
|
110 |
+
onBlur={(e)=>{onUserName(e.currentTarget.value)}}
|
111 |
+
onChange={(e)=>{setUserName(e.currentTarget.value)}}
|
112 |
+
></input>
|
113 |
+
</ListItem>
|
114 |
+
|
115 |
+
<ListItem title={Locale.User.Mail}>
|
116 |
+
<span>{userStor.mail}</span>
|
117 |
+
</ListItem>
|
118 |
+
|
119 |
+
<ListItem title={Locale.User.Wallet}>
|
120 |
+
<div className={styles.font} >
|
121 |
+
剩余积分:<span className={styles.wallet}>{userStor.wallet}</span>
|
122 |
+
</div>
|
123 |
+
</ListItem>
|
124 |
+
|
125 |
+
<ListItem title={Locale.User.Vip}>
|
126 |
+
<div className={styles.font}>
|
127 |
+
<div className={styles.vipState}>{userStor.vip_state=="已开通"?"VIP":"非VIP"}</div>
|
128 |
+
<div className={styles.vipTime}>{getVipTime()}</div>
|
129 |
+
</div>
|
130 |
+
</ListItem>
|
131 |
+
|
132 |
+
<ListItem title={Locale.User.kami}>
|
133 |
+
<div>
|
134 |
+
<input
|
135 |
+
type="input"
|
136 |
+
className={styles.kamicode}
|
137 |
+
value={kami}
|
138 |
+
onChange={(e)=>{setKami(e.currentTarget.value)}}>
|
139 |
+
</input>
|
140 |
+
<IconButton
|
141 |
+
className={styles.kamiButton}
|
142 |
+
disabled={!accessStore.auth}
|
143 |
+
text="兑换"
|
144 |
+
onClick={()=>{
|
145 |
+
userStor.useKami(kami)
|
146 |
+
setKami("")
|
147 |
+
}}
|
148 |
+
/>
|
149 |
+
</div>
|
150 |
+
</ListItem>
|
151 |
+
|
152 |
+
<ListItem title={Locale.User.SigState}>
|
153 |
+
<IconButton
|
154 |
+
icon={<EditIcon />}
|
155 |
+
disabled={!accessStore.auth || userStor.sig_state=="已签到"}
|
156 |
+
text="签到"
|
157 |
+
onClick={()=>{
|
158 |
+
userStor.userSig()
|
159 |
+
}}
|
160 |
+
/>
|
161 |
+
</ListItem>
|
162 |
+
|
163 |
+
<ListItem title={Locale.User.Ststus}>
|
164 |
+
<IconButton
|
165 |
+
className={styles.logoutButton}
|
166 |
+
disabled={!accessStore.auth}
|
167 |
+
text="登出"
|
168 |
+
onClick={()=>{
|
169 |
+
accessStore.updateAuth("")
|
170 |
+
userStor.reset()
|
171 |
+
showToast("登出成功!")
|
172 |
+
}}
|
173 |
+
/>
|
174 |
+
</ListItem>
|
175 |
+
</List>
|
176 |
+
</div>
|
177 |
+
</ErrorBoundary>
|
178 |
+
);
|
179 |
+
}
|
180 |
+
|
app/config/server.ts
CHANGED
@@ -37,10 +37,10 @@ export const getServerSideConfig = () => {
|
|
37 |
apiKey: process.env.OPENAI_API_KEY,
|
38 |
code: process.env.CODE,
|
39 |
codes: ACCESS_CODES,
|
40 |
-
needCode: ACCESS_CODES.size
|
41 |
proxyUrl: process.env.PROXY_URL,
|
42 |
isVercel: !!process.env.VERCEL,
|
43 |
-
hideUserApiKey: !!process.env.HIDE_USER_API_KEY,
|
44 |
enableGPT4: !process.env.DISABLE_GPT4,
|
45 |
};
|
46 |
};
|
|
|
37 |
apiKey: process.env.OPENAI_API_KEY,
|
38 |
code: process.env.CODE,
|
39 |
codes: ACCESS_CODES,
|
40 |
+
needCode: ACCESS_CODES.size,
|
41 |
proxyUrl: process.env.PROXY_URL,
|
42 |
isVercel: !!process.env.VERCEL,
|
43 |
+
hideUserApiKey: !!process.env.HIDE_USER_API_KEY || true,
|
44 |
enableGPT4: !process.env.DISABLE_GPT4,
|
45 |
};
|
46 |
};
|
app/constant.ts
CHANGED
@@ -10,9 +10,13 @@ export const RUNTIME_CONFIG_DOM = "danger-runtime-config";
|
|
10 |
export enum Path {
|
11 |
Home = "/",
|
12 |
Chat = "/chat",
|
|
|
13 |
Settings = "/settings",
|
14 |
NewChat = "/new-chat",
|
15 |
Masks = "/masks",
|
|
|
|
|
|
|
16 |
}
|
17 |
|
18 |
export enum SlotID {
|
@@ -31,6 +35,7 @@ export enum StoreKey {
|
|
31 |
Mask = "mask-store",
|
32 |
Prompt = "prompt-store",
|
33 |
Update = "chat-update",
|
|
|
34 |
}
|
35 |
|
36 |
export const MAX_SIDEBAR_WIDTH = 500;
|
|
|
10 |
export enum Path {
|
11 |
Home = "/",
|
12 |
Chat = "/chat",
|
13 |
+
User = "/user",
|
14 |
Settings = "/settings",
|
15 |
NewChat = "/new-chat",
|
16 |
Masks = "/masks",
|
17 |
+
Login = "/login",
|
18 |
+
Register = "/register",
|
19 |
+
Findpwd = "/findpwd"
|
20 |
}
|
21 |
|
22 |
export enum SlotID {
|
|
|
35 |
Mask = "mask-store",
|
36 |
Prompt = "prompt-store",
|
37 |
Update = "chat-update",
|
38 |
+
User = "chat-user"
|
39 |
}
|
40 |
|
41 |
export const MAX_SIDEBAR_WIDTH = 500;
|
app/locales/cn.ts
CHANGED
@@ -4,7 +4,7 @@ const cn = {
|
|
4 |
WIP: "该功能仍在开发中……",
|
5 |
Error: {
|
6 |
Unauthorized:
|
7 |
-
"
|
8 |
},
|
9 |
ChatItem: {
|
10 |
ChatItemCount: (count: number) => `${count} 条对话`,
|
@@ -56,6 +56,21 @@ const cn = {
|
|
56 |
DeleteToast: "已删除会话",
|
57 |
Revert: "撤销",
|
58 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
Settings: {
|
60 |
Title: "设置",
|
61 |
SubTitle: "设置选项",
|
|
|
4 |
WIP: "该功能仍在开发中……",
|
5 |
Error: {
|
6 |
Unauthorized:
|
7 |
+
"尚未登录,请前往[登录](/#/login)页登录。",
|
8 |
},
|
9 |
ChatItem: {
|
10 |
ChatItemCount: (count: number) => `${count} 条对话`,
|
|
|
56 |
DeleteToast: "已删除会话",
|
57 |
Revert: "撤销",
|
58 |
},
|
59 |
+
User:{
|
60 |
+
Title: "用户",
|
61 |
+
SubTitle: "用户信息界面",
|
62 |
+
Login:"登录",
|
63 |
+
LoginTitle:"用户登录",
|
64 |
+
Register:"注册",
|
65 |
+
RegisterTitle:"注册新用户",
|
66 |
+
Name:"用户名",
|
67 |
+
Wallet:"用户积分",
|
68 |
+
Mail:"用户邮箱",
|
69 |
+
SigState:"签到状态",
|
70 |
+
Ststus:"登出",
|
71 |
+
Vip:"会员",
|
72 |
+
kami:"兑换码"
|
73 |
+
},
|
74 |
Settings: {
|
75 |
Title: "设置",
|
76 |
SubTitle: "设置选项",
|
app/locales/de.ts
CHANGED
@@ -58,6 +58,17 @@ const de: LocaleType = {
|
|
58 |
DeleteToast: "Chat gelöscht",
|
59 |
Revert: "Zurücksetzen",
|
60 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
Settings: {
|
62 |
Title: "Einstellungen",
|
63 |
SubTitle: "Alle Einstellungen",
|
|
|
58 |
DeleteToast: "Chat gelöscht",
|
59 |
Revert: "Zurücksetzen",
|
60 |
},
|
61 |
+
User:{
|
62 |
+
Title: "用户",
|
63 |
+
SubTitle: "用户信息界面",
|
64 |
+
Actions: {
|
65 |
+
ClearAll: "清除所有数据",
|
66 |
+
ResetAll: "重置所有选项",
|
67 |
+
Close: "关闭",
|
68 |
+
ConfirmResetAll: "确认重置所有配置?",
|
69 |
+
ConfirmClearAll: "确认清除所有数据?",
|
70 |
+
},
|
71 |
+
},
|
72 |
Settings: {
|
73 |
Title: "Einstellungen",
|
74 |
SubTitle: "Alle Einstellungen",
|
app/locales/en.ts
CHANGED
@@ -58,6 +58,17 @@ const en: LocaleType = {
|
|
58 |
DeleteToast: "Chat Deleted",
|
59 |
Revert: "Revert",
|
60 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
Settings: {
|
62 |
Title: "Settings",
|
63 |
SubTitle: "All Settings",
|
|
|
58 |
DeleteToast: "Chat Deleted",
|
59 |
Revert: "Revert",
|
60 |
},
|
61 |
+
User:{
|
62 |
+
Title: "用户",
|
63 |
+
SubTitle: "用户信息界面",
|
64 |
+
Actions: {
|
65 |
+
ClearAll: "清除所有数据",
|
66 |
+
ResetAll: "重置所有选项",
|
67 |
+
Close: "关闭",
|
68 |
+
ConfirmResetAll: "确认重置所有配置?",
|
69 |
+
ConfirmClearAll: "确认清除所有数据?",
|
70 |
+
},
|
71 |
+
},
|
72 |
Settings: {
|
73 |
Title: "Settings",
|
74 |
SubTitle: "All Settings",
|
app/locales/es.ts
CHANGED
@@ -58,6 +58,17 @@ const es: LocaleType = {
|
|
58 |
DeleteToast: "Chat Deleted",
|
59 |
Revert: "Revert",
|
60 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
Settings: {
|
62 |
Title: "Configuración",
|
63 |
SubTitle: "Todas las configuraciones",
|
|
|
58 |
DeleteToast: "Chat Deleted",
|
59 |
Revert: "Revert",
|
60 |
},
|
61 |
+
User:{
|
62 |
+
Title: "用户",
|
63 |
+
SubTitle: "用户信息界面",
|
64 |
+
Actions: {
|
65 |
+
ClearAll: "清除所有数据",
|
66 |
+
ResetAll: "重置所有选项",
|
67 |
+
Close: "关闭",
|
68 |
+
ConfirmResetAll: "确认重置所有配置?",
|
69 |
+
ConfirmClearAll: "确认清除所有数据?",
|
70 |
+
},
|
71 |
+
},
|
72 |
Settings: {
|
73 |
Title: "Configuración",
|
74 |
SubTitle: "Todas las configuraciones",
|
app/locales/it.ts
CHANGED
@@ -58,6 +58,17 @@ const it: LocaleType = {
|
|
58 |
DeleteToast: "Chat Cancellata",
|
59 |
Revert: "Revert",
|
60 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
Settings: {
|
62 |
Title: "Impostazioni",
|
63 |
SubTitle: "Tutte le impostazioni",
|
|
|
58 |
DeleteToast: "Chat Cancellata",
|
59 |
Revert: "Revert",
|
60 |
},
|
61 |
+
User:{
|
62 |
+
Title: "用户",
|
63 |
+
SubTitle: "用户信息界面",
|
64 |
+
Actions: {
|
65 |
+
ClearAll: "清除所有数据",
|
66 |
+
ResetAll: "重置所有选项",
|
67 |
+
Close: "关闭",
|
68 |
+
ConfirmResetAll: "确认重置所有配置?",
|
69 |
+
ConfirmClearAll: "确认清除所有数据?",
|
70 |
+
},
|
71 |
+
},
|
72 |
Settings: {
|
73 |
Title: "Impostazioni",
|
74 |
SubTitle: "Tutte le impostazioni",
|
app/locales/jp.ts
CHANGED
@@ -58,6 +58,17 @@ const jp: LocaleType = {
|
|
58 |
DeleteToast: "チャットが削除されました",
|
59 |
Revert: "元に戻す",
|
60 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
Settings: {
|
62 |
Title: "設定",
|
63 |
SubTitle: "設定オプション",
|
|
|
58 |
DeleteToast: "チャットが削除されました",
|
59 |
Revert: "元に戻す",
|
60 |
},
|
61 |
+
User:{
|
62 |
+
Title: "用户",
|
63 |
+
SubTitle: "用户信息界面",
|
64 |
+
Actions: {
|
65 |
+
ClearAll: "清除所有数据",
|
66 |
+
ResetAll: "重置所有选项",
|
67 |
+
Close: "关闭",
|
68 |
+
ConfirmResetAll: "确认重置所有配置?",
|
69 |
+
ConfirmClearAll: "确认清除所有数据?",
|
70 |
+
},
|
71 |
+
},
|
72 |
Settings: {
|
73 |
Title: "設定",
|
74 |
SubTitle: "設定オプション",
|
app/locales/tr.ts
CHANGED
@@ -58,6 +58,17 @@ const tr: LocaleType = {
|
|
58 |
DeleteToast: "Sohbet Silindi",
|
59 |
Revert: "Geri Al",
|
60 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
Settings: {
|
62 |
Title: "Ayarlar",
|
63 |
SubTitle: "Tüm Ayarlar",
|
|
|
58 |
DeleteToast: "Sohbet Silindi",
|
59 |
Revert: "Geri Al",
|
60 |
},
|
61 |
+
User:{
|
62 |
+
Title: "用户",
|
63 |
+
SubTitle: "用户信息界面",
|
64 |
+
Actions: {
|
65 |
+
ClearAll: "清除所有数据",
|
66 |
+
ResetAll: "重置所有选项",
|
67 |
+
Close: "关闭",
|
68 |
+
ConfirmResetAll: "确认重置所有配置?",
|
69 |
+
ConfirmClearAll: "确认清除所有数据?",
|
70 |
+
},
|
71 |
+
},
|
72 |
Settings: {
|
73 |
Title: "Ayarlar",
|
74 |
SubTitle: "Tüm Ayarlar",
|
app/locales/tw.ts
CHANGED
@@ -56,6 +56,17 @@ const tw: LocaleType = {
|
|
56 |
DeleteToast: "已刪除對話",
|
57 |
Revert: "撤銷",
|
58 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
59 |
Settings: {
|
60 |
Title: "設定",
|
61 |
SubTitle: "設定選項",
|
|
|
56 |
DeleteToast: "已刪除對話",
|
57 |
Revert: "撤銷",
|
58 |
},
|
59 |
+
User:{
|
60 |
+
Title: "用户",
|
61 |
+
SubTitle: "用户信息界面",
|
62 |
+
Actions: {
|
63 |
+
ClearAll: "清除所有数据",
|
64 |
+
ResetAll: "重置所有选项",
|
65 |
+
Close: "关闭",
|
66 |
+
ConfirmResetAll: "确认重置所有配置?",
|
67 |
+
ConfirmClearAll: "确认清除所有数据?",
|
68 |
+
},
|
69 |
+
},
|
70 |
Settings: {
|
71 |
Title: "設定",
|
72 |
SubTitle: "設定選項",
|
app/requests.ts
CHANGED
@@ -6,6 +6,7 @@ import {
|
|
6 |
useAccessStore,
|
7 |
useAppConfig,
|
8 |
useChatStore,
|
|
|
9 |
} from "./store";
|
10 |
import { showToast } from "./components/ui-lib";
|
11 |
import { ACCESS_CODE_PREFIX } from "./constant";
|
@@ -87,6 +88,8 @@ function getHeaders() {
|
|
87 |
);
|
88 |
}
|
89 |
|
|
|
|
|
90 |
return headers;
|
91 |
}
|
92 |
|
@@ -170,6 +173,21 @@ export async function requestUsage() {
|
|
170 |
};
|
171 |
}
|
172 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
173 |
export async function requestChatStream(
|
174 |
messages: Message[],
|
175 |
options?: {
|
@@ -180,6 +198,12 @@ export async function requestChatStream(
|
|
180 |
onController?: (controller: AbortController) => void;
|
181 |
},
|
182 |
) {
|
|
|
|
|
|
|
|
|
|
|
|
|
183 |
const Bot = useAppConfig.getState().bot;
|
184 |
if (Bot == "OpenAI") {
|
185 |
const req = makeRequestParam(messages, {
|
@@ -238,7 +262,6 @@ export async function requestChatStream(
|
|
238 |
break;
|
239 |
}
|
240 |
}
|
241 |
-
|
242 |
finish();
|
243 |
} else if (res.status === 401) {
|
244 |
console.error("Unauthorized");
|
@@ -267,7 +290,8 @@ export async function requestChatStream(
|
|
267 |
});
|
268 |
|
269 |
clearTimeout(reqTimeoutId);
|
270 |
-
|
|
|
271 |
const response = (await res.json()) as ChatImagesResponse;
|
272 |
options?.onMessage(
|
273 |
"![image](" +
|
@@ -276,6 +300,10 @@ export async function requestChatStream(
|
|
276 |
true,
|
277 |
);
|
278 |
controller.abort();
|
|
|
|
|
|
|
|
|
279 |
} catch (err) {
|
280 |
console.error("NetWork Error", err);
|
281 |
options?.onMessage("请换一个问题试试吧", true);
|
@@ -295,8 +323,8 @@ export async function requestChatStream(
|
|
295 |
});
|
296 |
|
297 |
clearTimeout(reqTimeoutId);
|
298 |
-
|
299 |
-
|
300 |
// let responseText = "";
|
301 |
// for (let i = 1; i <= message.length; i++) {
|
302 |
// // handle time out, will stop if no response in 10 secs
|
@@ -307,6 +335,10 @@ export async function requestChatStream(
|
|
307 |
// }
|
308 |
options?.onMessage(message, true);
|
309 |
controller.abort();
|
|
|
|
|
|
|
|
|
310 |
} catch (err) {
|
311 |
console.error("NetWork Error", err);
|
312 |
options?.onMessage("请换一个问题试试吧", true);
|
@@ -326,8 +358,13 @@ export async function requestChatStream(
|
|
326 |
});
|
327 |
|
328 |
clearTimeout(reqTimeoutId);
|
329 |
-
|
|
|
330 |
controller.abort();
|
|
|
|
|
|
|
|
|
331 |
} catch (err) {
|
332 |
console.error("NetWork Error", err);
|
333 |
options?.onMessage("请换一个问题试试吧", true);
|
@@ -407,7 +444,6 @@ export async function requestChatStream(
|
|
407 |
break;
|
408 |
}
|
409 |
}
|
410 |
-
|
411 |
finish();
|
412 |
} else if (res.status === 401) {
|
413 |
console.error("Unauthorized");
|
|
|
6 |
useAccessStore,
|
7 |
useAppConfig,
|
8 |
useChatStore,
|
9 |
+
useUserStore,
|
10 |
} from "./store";
|
11 |
import { showToast } from "./components/ui-lib";
|
12 |
import { ACCESS_CODE_PREFIX } from "./constant";
|
|
|
88 |
);
|
89 |
}
|
90 |
|
91 |
+
headers.Auth=useAccessStore.getState().auth.trim()
|
92 |
+
|
93 |
return headers;
|
94 |
}
|
95 |
|
|
|
173 |
};
|
174 |
}
|
175 |
|
176 |
+
function updateWallet() {
|
177 |
+
fetch("/api/user/set?user="+useUserStore.getState().user+"&project=wallet&projectName=num&data=1",{
|
178 |
+
method:"GET",
|
179 |
+
headers:{
|
180 |
+
...getHeaders()
|
181 |
+
}
|
182 |
+
})
|
183 |
+
if(useUserStore.getState().wallet>0){
|
184 |
+
useUserStore.getState().updateWallet(1)
|
185 |
+
return true
|
186 |
+
}else{
|
187 |
+
return false
|
188 |
+
}
|
189 |
+
}
|
190 |
+
|
191 |
export async function requestChatStream(
|
192 |
messages: Message[],
|
193 |
options?: {
|
|
|
198 |
onController?: (controller: AbortController) => void;
|
199 |
},
|
200 |
) {
|
201 |
+
if(useUserStore.getState().vip_state=="未开通"){
|
202 |
+
if(!updateWallet()){
|
203 |
+
options?.onMessage("积分不足请充值或开通会员!", true);
|
204 |
+
return
|
205 |
+
}
|
206 |
+
}
|
207 |
const Bot = useAppConfig.getState().bot;
|
208 |
if (Bot == "OpenAI") {
|
209 |
const req = makeRequestParam(messages, {
|
|
|
262 |
break;
|
263 |
}
|
264 |
}
|
|
|
265 |
finish();
|
266 |
} else if (res.status === 401) {
|
267 |
console.error("Unauthorized");
|
|
|
290 |
});
|
291 |
|
292 |
clearTimeout(reqTimeoutId);
|
293 |
+
if(res.ok){
|
294 |
+
const reg = /^['|"](.*)['|"]$/;
|
295 |
const response = (await res.json()) as ChatImagesResponse;
|
296 |
options?.onMessage(
|
297 |
"![image](" +
|
|
|
300 |
true,
|
301 |
);
|
302 |
controller.abort();
|
303 |
+
}else if(res.status === 401){
|
304 |
+
console.error("Unauthorized");
|
305 |
+
options?.onError(new Error("Unauthorized"), res.status);
|
306 |
+
}
|
307 |
} catch (err) {
|
308 |
console.error("NetWork Error", err);
|
309 |
options?.onMessage("请换一个问题试试吧", true);
|
|
|
323 |
});
|
324 |
|
325 |
clearTimeout(reqTimeoutId);
|
326 |
+
if(res.ok){
|
327 |
+
let message = await res.text();
|
328 |
// let responseText = "";
|
329 |
// for (let i = 1; i <= message.length; i++) {
|
330 |
// // handle time out, will stop if no response in 10 secs
|
|
|
335 |
// }
|
336 |
options?.onMessage(message, true);
|
337 |
controller.abort();
|
338 |
+
}else if(res.status === 401){
|
339 |
+
console.error("Unauthorized");
|
340 |
+
options?.onError(new Error("Unauthorized"), res.status);
|
341 |
+
}
|
342 |
} catch (err) {
|
343 |
console.error("NetWork Error", err);
|
344 |
options?.onMessage("请换一个问题试试吧", true);
|
|
|
358 |
});
|
359 |
|
360 |
clearTimeout(reqTimeoutId);
|
361 |
+
if(res.ok){
|
362 |
+
options?.onMessage(await res.text(), true);
|
363 |
controller.abort();
|
364 |
+
}else if(res.status === 401){
|
365 |
+
console.error("Unauthorized");
|
366 |
+
options?.onError(new Error("Unauthorized"), res.status);
|
367 |
+
}
|
368 |
} catch (err) {
|
369 |
console.error("NetWork Error", err);
|
370 |
options?.onMessage("请换一个问题试试吧", true);
|
|
|
444 |
break;
|
445 |
}
|
446 |
}
|
|
|
447 |
finish();
|
448 |
} else if (res.status === 401) {
|
449 |
console.error("Unauthorized");
|
app/store/access.ts
CHANGED
@@ -7,14 +7,16 @@ import { ALL_MODELS } from "./config";
|
|
7 |
export interface AccessControlStore {
|
8 |
accessCode: string;
|
9 |
token: string;
|
|
|
10 |
|
11 |
-
needCode:
|
12 |
hideUserApiKey: boolean;
|
13 |
openaiUrl: string;
|
14 |
|
15 |
updateToken: (_: string) => void;
|
16 |
updateCode: (_: string) => void;
|
17 |
-
|
|
|
18 |
isAuthorized: () => boolean;
|
19 |
fetch: () => void;
|
20 |
}
|
@@ -25,9 +27,10 @@ export const useAccessStore = create<AccessControlStore>()(
|
|
25 |
persist(
|
26 |
(set, get) => ({
|
27 |
token: "",
|
|
|
28 |
accessCode: "",
|
29 |
-
needCode:
|
30 |
-
hideUserApiKey:
|
31 |
openaiUrl: "/api/openai/",
|
32 |
|
33 |
enabledAccessControl() {
|
@@ -41,11 +44,14 @@ export const useAccessStore = create<AccessControlStore>()(
|
|
41 |
updateToken(token: string) {
|
42 |
set(() => ({ token }));
|
43 |
},
|
|
|
|
|
|
|
44 |
isAuthorized() {
|
45 |
get().fetch();
|
46 |
// has token or has code or disabled access control
|
47 |
return (
|
48 |
-
!!get().token || !!get().accessCode ||
|
49 |
);
|
50 |
},
|
51 |
fetch() {
|
|
|
7 |
export interface AccessControlStore {
|
8 |
accessCode: string;
|
9 |
token: string;
|
10 |
+
auth: string;
|
11 |
|
12 |
+
needCode: number;
|
13 |
hideUserApiKey: boolean;
|
14 |
openaiUrl: string;
|
15 |
|
16 |
updateToken: (_: string) => void;
|
17 |
updateCode: (_: string) => void;
|
18 |
+
updateAuth: (_: string) => void;
|
19 |
+
enabledAccessControl: () => number;
|
20 |
isAuthorized: () => boolean;
|
21 |
fetch: () => void;
|
22 |
}
|
|
|
27 |
persist(
|
28 |
(set, get) => ({
|
29 |
token: "",
|
30 |
+
auth: "",
|
31 |
accessCode: "",
|
32 |
+
needCode: 0,
|
33 |
+
hideUserApiKey: true,
|
34 |
openaiUrl: "/api/openai/",
|
35 |
|
36 |
enabledAccessControl() {
|
|
|
44 |
updateToken(token: string) {
|
45 |
set(() => ({ token }));
|
46 |
},
|
47 |
+
updateAuth(token: string) {
|
48 |
+
set(() => ({ auth:token }));
|
49 |
+
},
|
50 |
isAuthorized() {
|
51 |
get().fetch();
|
52 |
// has token or has code or disabled access control
|
53 |
return (
|
54 |
+
!!get().token || !!get().accessCode || !!get().enabledAccessControl() || !!get().auth
|
55 |
);
|
56 |
},
|
57 |
fetch() {
|
app/store/index.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
export * from "./chat";
|
|
|
2 |
export * from "./update";
|
3 |
export * from "./access";
|
4 |
export * from "./config";
|
|
|
1 |
export * from "./chat";
|
2 |
+
export * from "./user";
|
3 |
export * from "./update";
|
4 |
export * from "./access";
|
5 |
export * from "./config";
|
app/store/user.ts
ADDED
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { create } from "zustand";
|
2 |
+
import { persist } from "zustand/middleware";
|
3 |
+
import { StoreKey } from "../constant";
|
4 |
+
import { showToast } from "../components/ui-lib";
|
5 |
+
import { useAccessStore } from "./access";
|
6 |
+
|
7 |
+
function getHeaders() {
|
8 |
+
let headers: Record<string, string> = {};
|
9 |
+
headers.Auth=useAccessStore.getState().auth.trim()
|
10 |
+
return headers;
|
11 |
+
}
|
12 |
+
|
13 |
+
export interface shuixianRes{
|
14 |
+
code: number,
|
15 |
+
msg: string,
|
16 |
+
token: string,
|
17 |
+
data:{
|
18 |
+
name:string,
|
19 |
+
head:string,
|
20 |
+
signatrue:string,
|
21 |
+
wallet:number,
|
22 |
+
vip_state:string,
|
23 |
+
vip_time_stmp:string,
|
24 |
+
ban:string,
|
25 |
+
sig_state:string,
|
26 |
+
title:string,
|
27 |
+
mail:string
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
export interface UserStore {
|
32 |
+
user:string;
|
33 |
+
password:string;
|
34 |
+
name:string;
|
35 |
+
wallet:number;
|
36 |
+
vip_state:string;
|
37 |
+
vip_time_stmp:string;
|
38 |
+
mail:string;
|
39 |
+
sig_state:string;
|
40 |
+
head:string;
|
41 |
+
update: (updater: (user: UserInfo) => void) => void;
|
42 |
+
login: (userName:string,password:string) => void;
|
43 |
+
register: (user:string,password:string,name:string,mail:string,code:string) => void;
|
44 |
+
getMailCode: (user:string,mail:string) => void;
|
45 |
+
userSig: () => void;
|
46 |
+
reset: () => void;
|
47 |
+
updateUser: (user:string) => void;
|
48 |
+
updatePassword: (password:string) => void;
|
49 |
+
updateInfo: (
|
50 |
+
name:string,
|
51 |
+
wallet:number,
|
52 |
+
vip_state:string,
|
53 |
+
vip_time_stmp:string,
|
54 |
+
mail:string,
|
55 |
+
sig_state:string,
|
56 |
+
head:string) => void;
|
57 |
+
updateWallet:(wallet:number) => void;
|
58 |
+
updateName:(name:string) => void;
|
59 |
+
getUserInfo:() => void;
|
60 |
+
findpwd:(user:string) => void;
|
61 |
+
useKami:(code:string) => void;
|
62 |
+
}
|
63 |
+
export const DEFAULT_USER = {
|
64 |
+
user:"",
|
65 |
+
password:"",
|
66 |
+
name:"",
|
67 |
+
wallet:0,
|
68 |
+
vip_state:"",
|
69 |
+
vip_time_stmp:"",
|
70 |
+
mail:"",
|
71 |
+
sig_state:"",
|
72 |
+
head:""
|
73 |
+
}
|
74 |
+
export type UserInfo = typeof DEFAULT_USER;
|
75 |
+
export const useUserStore = create<UserStore>()(
|
76 |
+
persist(
|
77 |
+
(set, get) => ({
|
78 |
+
...DEFAULT_USER,
|
79 |
+
update(updater) {
|
80 |
+
const config = { ...get() };
|
81 |
+
updater(config);
|
82 |
+
set(() => config);
|
83 |
+
},
|
84 |
+
updateInfo(
|
85 |
+
name:string,
|
86 |
+
wallet:number,
|
87 |
+
vip_state:string,
|
88 |
+
vip_time_stmp:string,
|
89 |
+
mail:string,
|
90 |
+
sig_state:string,
|
91 |
+
head:string){
|
92 |
+
set(()=>({
|
93 |
+
name:name,
|
94 |
+
wallet:wallet,
|
95 |
+
vip_state:vip_state,
|
96 |
+
vip_time_stmp:vip_time_stmp,
|
97 |
+
mail:mail,
|
98 |
+
sig_state:sig_state,
|
99 |
+
head:head
|
100 |
+
}))
|
101 |
+
},
|
102 |
+
reset() {
|
103 |
+
set(() => ({ ...DEFAULT_USER }));
|
104 |
+
},
|
105 |
+
updateUser(user:string){
|
106 |
+
set(() => ({ user:user }));
|
107 |
+
},
|
108 |
+
async updateName(name:string){
|
109 |
+
let res=await fetch("/api/user/set?user="+get().user+"&project=name&projectName=name&data="+name,{
|
110 |
+
method: "GET",
|
111 |
+
headers:{
|
112 |
+
...getHeaders()
|
113 |
+
}
|
114 |
+
});
|
115 |
+
let response = await res.json() as shuixianRes
|
116 |
+
console.log(response)
|
117 |
+
showToast(response.msg)
|
118 |
+
if(response.code==1){
|
119 |
+
await this.getUserInfo()
|
120 |
+
}
|
121 |
+
},
|
122 |
+
updatePassword(password:string){
|
123 |
+
set(() => ({ password:password }));
|
124 |
+
},
|
125 |
+
updateWallet(wallet:number){
|
126 |
+
set(() => ({ wallet:get().wallet-wallet }));
|
127 |
+
},
|
128 |
+
async login(user, password) {
|
129 |
+
let res=await fetch("/api/user/login?user="+user+"&password="+password,{
|
130 |
+
method: "GET"
|
131 |
+
});
|
132 |
+
let response = await res.json() as shuixianRes
|
133 |
+
console.log(response)
|
134 |
+
if(response.code=1){
|
135 |
+
useUserStore.getState().updateUser(user)
|
136 |
+
useUserStore.getState().updatePassword(password)
|
137 |
+
useAccessStore.getState().updateAuth(response.token)
|
138 |
+
window.location.href="/#/chat"
|
139 |
+
await this.getUserInfo()
|
140 |
+
}else{
|
141 |
+
showToast(response.msg)
|
142 |
+
}
|
143 |
+
},
|
144 |
+
async register(user, password, name, mail, code) {
|
145 |
+
let res=await fetch("/api/user/register?user="+user+"&password="+password+"&name="+name+"&mail="+mail+"&code="+code,{
|
146 |
+
method: "GET"
|
147 |
+
});
|
148 |
+
let response = await res.json() as shuixianRes
|
149 |
+
console.log(response)
|
150 |
+
if(response.code=1){
|
151 |
+
showToast(response.msg)
|
152 |
+
window.location.href="/#/login"
|
153 |
+
}else{
|
154 |
+
showToast(response.msg)
|
155 |
+
}
|
156 |
+
},
|
157 |
+
async getMailCode(user:string,mail:string) {
|
158 |
+
let res=await fetch("/api/user/mail?user="+user+"&mail="+mail,{
|
159 |
+
method: "GET"
|
160 |
+
});
|
161 |
+
let response = await res.json() as shuixianRes
|
162 |
+
console.log(response)
|
163 |
+
showToast(response.msg)
|
164 |
+
},
|
165 |
+
async userSig() {
|
166 |
+
let res=await fetch("/api/user/sig?user="+get().user+"&password="+get().password,{
|
167 |
+
method: "GET",
|
168 |
+
headers:{
|
169 |
+
...getHeaders()
|
170 |
+
}
|
171 |
+
});
|
172 |
+
let response = await res.json() as shuixianRes
|
173 |
+
console.log(response)
|
174 |
+
showToast(response.msg)
|
175 |
+
if(response.code==1){
|
176 |
+
await this.getUserInfo()
|
177 |
+
}
|
178 |
+
},
|
179 |
+
async getUserInfo() {
|
180 |
+
let resdata=await fetch("/api/user/info?user="+get().user+"&password="+get().password,{
|
181 |
+
method: "GET",
|
182 |
+
headers:{
|
183 |
+
...getHeaders()
|
184 |
+
}
|
185 |
+
});
|
186 |
+
let responsedata=await resdata.json() as shuixianRes
|
187 |
+
let data=responsedata.data
|
188 |
+
this.updateInfo(data.name,data.wallet,data.vip_state,data.vip_time_stmp,data.mail,data.sig_state,data.head)
|
189 |
+
},
|
190 |
+
async findpwd(user) {
|
191 |
+
let res=await fetch("/api/user/findpwd?user="+user,{
|
192 |
+
method: "GET",
|
193 |
+
headers:{
|
194 |
+
...getHeaders()
|
195 |
+
}
|
196 |
+
});
|
197 |
+
let response = await res.json() as shuixianRes
|
198 |
+
console.log(response)
|
199 |
+
if(response.code==1){
|
200 |
+
showToast("密码已发送至您的邮箱,请注意查收!")
|
201 |
+
}else{
|
202 |
+
showToast(response.msg)
|
203 |
+
}
|
204 |
+
},
|
205 |
+
async useKami(code) {
|
206 |
+
let res=await fetch("/api/user/kami?user="+get().user+"&password="+get().password+"&code="+code,{
|
207 |
+
method: "GET",
|
208 |
+
headers:{
|
209 |
+
...getHeaders()
|
210 |
+
}
|
211 |
+
});
|
212 |
+
let response = await res.json() as shuixianRes
|
213 |
+
console.log(response)
|
214 |
+
if(response.code==1){
|
215 |
+
showToast(response.msg)
|
216 |
+
await this.getUserInfo()
|
217 |
+
}else{
|
218 |
+
showToast(response.msg)
|
219 |
+
}
|
220 |
+
},
|
221 |
+
}),
|
222 |
+
{
|
223 |
+
name: StoreKey.User,
|
224 |
+
version: 1,
|
225 |
+
},
|
226 |
+
),
|
227 |
+
);
|