Skip to content

Commit da82ae3

Browse files
committed
v2.1.0
1 parent e2e5e20 commit da82ae3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+15179
-975
lines changed

.env.template

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

2-
# Your openai api key. (required)
3-
OPENAI_API_KEY=sk-xxxx
2+
# Your openai api key, separated by comma. (optional) (this for system which can be used access code)
3+
# Default: Empty
4+
OPENAI_API_KEY=
45

56
# Access passsword, separated by comma. (optional)
67
CODE=your-password
@@ -17,11 +18,6 @@ BASE_URL=
1718
# Default: Empty
1819
OPENAI_ORG_ID=
1920

20-
# (optional)
21-
# Default: Empty
22-
# If you do not want users to use GPT-4, set this value to 1.
23-
DISABLE_GPT4=
24-
2521
# (optional)
2622
# Default: Empty
2723
# If you do not want users to input their own API key, set this value to 1.
@@ -36,3 +32,8 @@ ENABLE_BALANCE_QUERY=
3632
# Default: Empty
3733
# If you want to disable parse settings from url, set this value to 1.
3834
DISABLE_FAST_LINK=
35+
36+
# (optional)
37+
# Default: Empty
38+
# If you want enable vercel web analytics, set this value to 1.
39+
VERCEL_ANALYTICS=

.gitpod.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# This configuration file was automatically generated by Gitpod.
2+
# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml)
3+
# and commit this file to your remote git repository to share the goodness with others.
4+
5+
# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart
6+
7+
tasks:
8+
- init: yarn install && yarn run dev
9+
command: yarn run dev
10+
11+

CODE_OF_CONDUCT.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ representative at an online or offline event.
6060

6161
Instances of abusive, harassing, or otherwise unacceptable behavior may be
6262
reported to the community leaders responsible for enforcement at
63-
.
63+
6464
All complaints will be reviewed and investigated promptly and fairly.
6565

6666
All community leaders are obligated to respect the privacy and security of the

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1818
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1919
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
2020
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21-
SOFTWARE.
21+
SOFTWARE.

app/api/auth.ts

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { NextRequest } from "next/server";
22
import { getServerSideConfig } from "../config/server";
3-
import md5 from "spark-md5";
3+
import binary from "spark-md5";
44
import { ACCESS_CODE_PREFIX } from "../constant";
55

66
function getIP(req: NextRequest) {
@@ -28,9 +28,9 @@ export function auth(req: NextRequest) {
2828
const authToken = req.headers.get("Authorization") ?? "";
2929

3030
// check if it is openai api key or user token
31-
const { accessCode, apiKey } = parseApiKey(authToken);
31+
const { accessCode, apiKey: token } = parseApiKey(authToken);
3232

33-
const hashedCode = md5.hash(accessCode ?? "").trim();
33+
const hashedCode = binary.hash(accessCode ?? "").trim();
3434

3535
const serverConfig = getServerSideConfig();
3636
console.log("[Auth] allowed hashed codes: ", [...serverConfig.codes]);
@@ -39,37 +39,22 @@ export function auth(req: NextRequest) {
3939
console.log("[User IP] ", getIP(req));
4040
console.log("[Time] ", new Date().toLocaleString());
4141

42-
if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !apiKey) {
42+
if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
4343
return {
4444
error: true,
4545
msg: !accessCode ? "empty access code" : "wrong access code",
4646
};
4747
}
4848

49-
if (serverConfig.hideUserApiKey && !!apiKey) {
50-
return {
51-
error: true,
52-
msg: "you are not allowed to access openai with your own api key",
53-
};
54-
}
55-
56-
// if user does not provide an api key, inject system api key
57-
if (!apiKey) {
58-
const serverApiKey = serverConfig.isAzure
59-
? serverConfig.azureApiKey
60-
: serverConfig.apiKey;
61-
62-
if (serverApiKey) {
63-
console.log("[Auth] use system api key");
64-
req.headers.set(
65-
"Authorization",
66-
`${serverConfig.isAzure ? "" : "Bearer "}${serverApiKey}`,
67-
);
68-
} else {
69-
console.log("[Auth] admin did not provide an api key");
70-
}
49+
// Check if the access code has a corresponding API key
50+
const apiKey = serverConfig.apiKeys.get(hashedCode);
51+
if (apiKey) {
52+
console.log("[Auth] use access code-specific API key");
53+
req.headers.set("Authorization", `Bearer ${apiKey}`);
54+
} else if (token) {
55+
console.log("[Auth] use user API key");
7156
} else {
72-
console.log("[Auth] use user api key");
57+
console.log("[Auth] admin did not provide an API key");
7358
}
7459

7560
return {

app/api/common.ts

Lines changed: 10 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,19 @@
11
import { NextRequest, NextResponse } from "next/server";
22
import { getServerSideConfig } from "../config/server";
33
import { DEFAULT_MODELS, OPENAI_BASE_URL } from "../constant";
4-
import { collectModelTable } from "../utils/model";
5-
import { makeAzurePath } from "../azure";
4+
import { collectModelTable, collectModels } from "../utils/model";
65

76
const serverConfig = getServerSideConfig();
87

98
export async function requestOpenai(req: NextRequest) {
109
const controller = new AbortController();
11-
1210
const authValue = req.headers.get("Authorization") ?? "";
13-
const authHeaderName = serverConfig.isAzure ? "api-key" : "Authorization";
14-
15-
let path = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
11+
const openaiPath = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
1612
"/api/openai/",
1713
"",
1814
);
1915

20-
let baseUrl =
21-
serverConfig.azureUrl || serverConfig.baseUrl || OPENAI_BASE_URL;
16+
let baseUrl = serverConfig.baseUrl ?? OPENAI_BASE_URL;
2217

2318
if (!baseUrl.startsWith("http")) {
2419
baseUrl = `https://${baseUrl}`;
@@ -28,12 +23,9 @@ export async function requestOpenai(req: NextRequest) {
2823
baseUrl = baseUrl.slice(0, -1);
2924
}
3025

31-
console.log("[Proxy] ", path);
26+
console.log("[Proxy] ", openaiPath);
3227
console.log("[Base Url]", baseUrl);
33-
// this fix [Org ID] undefined in server side if not using custom point
34-
if (serverConfig.openaiOrgId !== undefined) {
35-
console.log("[Org ID]", serverConfig.openaiOrgId);
36-
}
28+
console.log("[Org ID]", serverConfig.openaiOrgId);
3729

3830
const timeoutId = setTimeout(
3931
() => {
@@ -42,24 +34,14 @@ export async function requestOpenai(req: NextRequest) {
4234
10 * 60 * 1000,
4335
);
4436

45-
if (serverConfig.isAzure) {
46-
if (!serverConfig.azureApiVersion) {
47-
return NextResponse.json({
48-
error: true,
49-
message: `missing AZURE_API_VERSION in server env vars`,
50-
});
51-
}
52-
path = makeAzurePath(path, serverConfig.azureApiVersion);
53-
}
54-
55-
const fetchUrl = `${baseUrl}/${path}`;
37+
const fetchUrl = `${baseUrl}/${openaiPath}`;
5638
const fetchOptions: RequestInit = {
5739
headers: {
5840
"Content-Type": "application/json",
5941
"Cache-Control": "no-store",
60-
[authHeaderName]: authValue,
61-
...(serverConfig.openaiOrgId && {
62-
"OpenAI-Organization": serverConfig.openaiOrgId,
42+
Authorization: authValue,
43+
...(process.env.OPENAI_ORG_ID && {
44+
"OpenAI-Organization": process.env.OPENAI_ORG_ID,
6345
}),
6446
},
6547
method: req.method,
@@ -84,7 +66,7 @@ export async function requestOpenai(req: NextRequest) {
8466
const jsonBody = JSON.parse(clonedBody) as { model?: string };
8567

8668
// not undefined and is false
87-
if (modelTable[jsonBody?.model ?? ""].available === false) {
69+
if (modelTable[jsonBody?.model ?? ""] === false) {
8870
return NextResponse.json(
8971
{
9072
error: true,

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

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,33 @@ async function handle(
1616
method?.toLowerCase() ?? "",
1717
);
1818

19+
function isRealDevicez(userAgent: string | null): boolean {
20+
// Author : @H0llyW00dzZ
21+
// Note : This just an experiment for a prevent suspicious bot
22+
// Modify this function to define your logic for determining if the user-agent belongs to a real device
23+
// For example, you can check if the user-agent contains certain keywords or patterns that indicate a real device
24+
if (userAgent) {
25+
return userAgent.includes("AppleWebKit") && !userAgent.includes("Headless");
26+
}
27+
return false;
28+
}
29+
30+
31+
const userAgent = req.headers.get("User-Agent");
32+
const isRealDevice = isRealDevicez(userAgent);
33+
34+
if (!isRealDevice) {
35+
return NextResponse.json(
36+
{
37+
error: true,
38+
msg: "Access Forbidden",
39+
},
40+
{
41+
status: 403,
42+
},
43+
);
44+
}
45+
1946
const fetchOptions: RequestInit = {
2047
headers: {
2148
authorization: req.headers.get("authorization") ?? "",
@@ -28,7 +55,7 @@ async function handle(
2855

2956
const fetchResult = await fetch(targetUrl, fetchOptions);
3057

31-
console.log("[Any Proxy]", targetUrl, {
58+
console.log("[Cloud Sync]", targetUrl, {
3259
status: fetchResult.status,
3360
statusText: fetchResult.statusText,
3461
});
@@ -40,4 +67,4 @@ export const POST = handle;
4067
export const GET = handle;
4168
export const OPTIONS = handle;
4269

43-
export const runtime = "nodejs";
70+
export const runtime = "edge";

app/api/model-config/route.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { NextResponse } from "next/server";
2+
import { DEFAULT_MODELS } from "@/app/constant";
3+
4+
async function handle() {
5+
const model_list = DEFAULT_MODELS.map((model) => {
6+
return {
7+
name: model.name,
8+
available: model.available,
9+
};
10+
});
11+
return NextResponse.json({ model_list });
12+
}
13+
14+
export const GET = handle;

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

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { NextRequest, NextResponse } from "next/server";
66
import { auth } from "../../auth";
77
import { requestOpenai } from "../../common";
88

9-
const ALLOWD_PATH = new Set(Object.values(OpenaiPath));
9+
const ALLOWED_PATH = new Set(Object.values(OpenaiPath));
1010

1111
function getModels(remoteModelRes: OpenAIListModelResponse) {
1212
const config = getServerSideConfig();
@@ -32,7 +32,7 @@ async function handle(
3232

3333
const subpath = params.path.join("/");
3434

35-
if (!ALLOWD_PATH.has(subpath)) {
35+
if (!ALLOWED_PATH.has(subpath)) {
3636
console.log("[OpenAI Route] forbidden path ", subpath);
3737
return NextResponse.json(
3838
{
@@ -45,6 +45,33 @@ async function handle(
4545
);
4646
}
4747

48+
function isRealDevicez(userAgent: string | null): boolean {
49+
// Author : @H0llyW00dzZ
50+
// Note : This just an experiment for a prevent suspicious bot
51+
// Modify this function to define your logic for determining if the user-agent belongs to a real device
52+
// For example, you can check if the user-agent contains certain keywords or patterns that indicate a real device
53+
if (userAgent) {
54+
return userAgent.includes("AppleWebKit") && !userAgent.includes("Headless");
55+
}
56+
return false;
57+
}
58+
59+
60+
const userAgent = req.headers.get("User-Agent");
61+
const isRealDevice = isRealDevicez(userAgent);
62+
63+
if (!isRealDevice) {
64+
return NextResponse.json(
65+
{
66+
error: true,
67+
msg: "Access Forbidden",
68+
},
69+
{
70+
status: 403,
71+
},
72+
);
73+
}
74+
4875
const authResult = auth(req);
4976
if (authResult.error) {
5077
return NextResponse.json(authResult, {
@@ -75,4 +102,3 @@ export const GET = handle;
75102
export const POST = handle;
76103

77104
export const runtime = "edge";
78-
export const preferredRegion = ['arn1', 'bom1', 'cdg1', 'cle1', 'cpt1', 'dub1', 'fra1', 'gru1', 'hnd1', 'iad1', 'icn1', 'kix1', 'lhr1', 'pdx1', 'sfo1', 'sin1', 'syd1'];

app/client/api.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export interface LLMConfig {
2626
export interface ChatOptions {
2727
messages: RequestMessage[];
2828
config: LLMConfig;
29+
whitelist: boolean;
2930

3031
onUpdate?: (message: string, chunk: string) => void;
3132
onFinish: (message: string) => void;
@@ -93,7 +94,7 @@ export class ClientApi {
9394
{
9495
from: "human",
9596
value:
96-
"Share from [NeuroGPT]: https://github.com/Em1tSan/NeuroGPT",
97+
"Share from [NeuroGPT Web]: https://github.com/Yidadaa/NeuroGPT",
9798
},
9899
]);
99100
// 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用

0 commit comments

Comments
 (0)