muxi feng commited on
Commit
3444669
·
1 Parent(s): 9c10adf

增加账号管理和收费系统

Browse files
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 (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
44
  return {
45
  error: true,
46
  needAccessCode: true,
47
- msg: "Please go settings page and fill your access code.",
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(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) {
@@ -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(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 stream = await createStream(req);
67
- return new Response(stream);
68
- } catch (error) {
69
- console.error("[Chat Stream]", error);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  }
71
  }
72
 
73
- export const config = {
74
- runtime: "edge",
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
- if (authResult.error) {
8
- return authResult.msg;
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
- export async function POST(req: Request) {
 
 
 
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
- <a target="_blank">
178
  <IconButton icon={<GithubIcon />} shadow />
179
- </a>
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 } from "react";
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 > 0,
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
- "访问密码不正确或为空,请前往[设置](/#/settings)页输入正确的访问密码,或者填入你自己的 OpenAI API Key。",
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
- const reg = /^['|"](.*)['|"]$/;
 
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
- let message = await res.text();
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
- options?.onMessage(await res.text(), true);
 
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: boolean;
12
  hideUserApiKey: boolean;
13
  openaiUrl: string;
14
 
15
  updateToken: (_: string) => void;
16
  updateCode: (_: string) => void;
17
- enabledAccessControl: () => boolean;
 
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: true,
30
- hideUserApiKey: false,
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 || !get().enabledAccessControl()
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
+ );