Skip to content

Commit

Permalink
v2.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
neurogen-dev committed Dec 8, 2023
1 parent e2e5e20 commit da82ae3
Show file tree
Hide file tree
Showing 109 changed files with 15,179 additions and 975 deletions.
15 changes: 8 additions & 7 deletions .env.template
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

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

# Access passsword, separated by comma. (optional)
CODE=your-password
Expand All @@ -17,11 +18,6 @@ BASE_URL=
# Default: Empty
OPENAI_ORG_ID=

# (optional)
# Default: Empty
# If you do not want users to use GPT-4, set this value to 1.
DISABLE_GPT4=

# (optional)
# Default: Empty
# If you do not want users to input their own API key, set this value to 1.
Expand All @@ -36,3 +32,8 @@ ENABLE_BALANCE_QUERY=
# Default: Empty
# If you want to disable parse settings from url, set this value to 1.
DISABLE_FAST_LINK=

# (optional)
# Default: Empty
# If you want enable vercel web analytics, set this value to 1.
VERCEL_ANALYTICS=
11 changes: 11 additions & 0 deletions .gitpod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# This configuration file was automatically generated by Gitpod.
# Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml)
# and commit this file to your remote git repository to share the goodness with others.

# Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart

tasks:
- init: yarn install && yarn run dev
command: yarn run dev


2 changes: 1 addition & 1 deletion CODE_OF_CONDUCT.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ representative at an online or offline event.

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
.
[email protected].
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.
39 changes: 12 additions & 27 deletions app/api/auth.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NextRequest } from "next/server";
import { getServerSideConfig } from "../config/server";
import md5 from "spark-md5";
import binary from "spark-md5";
import { ACCESS_CODE_PREFIX } from "../constant";

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

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

const hashedCode = md5.hash(accessCode ?? "").trim();
const hashedCode = binary.hash(accessCode ?? "").trim();

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

if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !apiKey) {
if (serverConfig.needCode && !serverConfig.codes.has(hashedCode) && !token) {
return {
error: true,
msg: !accessCode ? "empty access code" : "wrong access code",
};
}

if (serverConfig.hideUserApiKey && !!apiKey) {
return {
error: true,
msg: "you are not allowed to access openai with your own api key",
};
}

// if user does not provide an api key, inject system api key
if (!apiKey) {
const serverApiKey = serverConfig.isAzure
? serverConfig.azureApiKey
: serverConfig.apiKey;

if (serverApiKey) {
console.log("[Auth] use system api key");
req.headers.set(
"Authorization",
`${serverConfig.isAzure ? "" : "Bearer "}${serverApiKey}`,
);
} else {
console.log("[Auth] admin did not provide an api key");
}
// Check if the access code has a corresponding API key
const apiKey = serverConfig.apiKeys.get(hashedCode);
if (apiKey) {
console.log("[Auth] use access code-specific API key");
req.headers.set("Authorization", `Bearer ${apiKey}`);
} else if (token) {
console.log("[Auth] use user API key");
} else {
console.log("[Auth] use user api key");
console.log("[Auth] admin did not provide an API key");
}

return {
Expand Down
38 changes: 10 additions & 28 deletions app/api/common.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,19 @@
import { NextRequest, NextResponse } from "next/server";
import { getServerSideConfig } from "../config/server";
import { DEFAULT_MODELS, OPENAI_BASE_URL } from "../constant";
import { collectModelTable } from "../utils/model";
import { makeAzurePath } from "../azure";
import { collectModelTable, collectModels } from "../utils/model";

const serverConfig = getServerSideConfig();

export async function requestOpenai(req: NextRequest) {
const controller = new AbortController();

const authValue = req.headers.get("Authorization") ?? "";
const authHeaderName = serverConfig.isAzure ? "api-key" : "Authorization";

let path = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
const openaiPath = `${req.nextUrl.pathname}${req.nextUrl.search}`.replaceAll(
"/api/openai/",
"",
);

let baseUrl =
serverConfig.azureUrl || serverConfig.baseUrl || OPENAI_BASE_URL;
let baseUrl = serverConfig.baseUrl ?? OPENAI_BASE_URL;

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

console.log("[Proxy] ", path);
console.log("[Proxy] ", openaiPath);
console.log("[Base Url]", baseUrl);
// this fix [Org ID] undefined in server side if not using custom point
if (serverConfig.openaiOrgId !== undefined) {
console.log("[Org ID]", serverConfig.openaiOrgId);
}
console.log("[Org ID]", serverConfig.openaiOrgId);

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

if (serverConfig.isAzure) {
if (!serverConfig.azureApiVersion) {
return NextResponse.json({
error: true,
message: `missing AZURE_API_VERSION in server env vars`,
});
}
path = makeAzurePath(path, serverConfig.azureApiVersion);
}

const fetchUrl = `${baseUrl}/${path}`;
const fetchUrl = `${baseUrl}/${openaiPath}`;
const fetchOptions: RequestInit = {
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store",
[authHeaderName]: authValue,
...(serverConfig.openaiOrgId && {
"OpenAI-Organization": serverConfig.openaiOrgId,
Authorization: authValue,
...(process.env.OPENAI_ORG_ID && {
"OpenAI-Organization": process.env.OPENAI_ORG_ID,
}),
},
method: req.method,
Expand All @@ -84,7 +66,7 @@ export async function requestOpenai(req: NextRequest) {
const jsonBody = JSON.parse(clonedBody) as { model?: string };

// not undefined and is false
if (modelTable[jsonBody?.model ?? ""].available === false) {
if (modelTable[jsonBody?.model ?? ""] === false) {
return NextResponse.json(
{
error: true,
Expand Down
31 changes: 29 additions & 2 deletions app/api/cors/[...path]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,33 @@ async function handle(
method?.toLowerCase() ?? "",
);

function isRealDevicez(userAgent: string | null): boolean {
// Author : @H0llyW00dzZ
// Note : This just an experiment for a prevent suspicious bot
// Modify this function to define your logic for determining if the user-agent belongs to a real device
// For example, you can check if the user-agent contains certain keywords or patterns that indicate a real device
if (userAgent) {
return userAgent.includes("AppleWebKit") && !userAgent.includes("Headless");
}
return false;
}


const userAgent = req.headers.get("User-Agent");
const isRealDevice = isRealDevicez(userAgent);

if (!isRealDevice) {
return NextResponse.json(
{
error: true,
msg: "Access Forbidden",
},
{
status: 403,
},
);
}

const fetchOptions: RequestInit = {
headers: {
authorization: req.headers.get("authorization") ?? "",
Expand All @@ -28,7 +55,7 @@ async function handle(

const fetchResult = await fetch(targetUrl, fetchOptions);

console.log("[Any Proxy]", targetUrl, {
console.log("[Cloud Sync]", targetUrl, {
status: fetchResult.status,
statusText: fetchResult.statusText,
});
Expand All @@ -40,4 +67,4 @@ export const POST = handle;
export const GET = handle;
export const OPTIONS = handle;

export const runtime = "nodejs";
export const runtime = "edge";
14 changes: 14 additions & 0 deletions app/api/model-config/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { NextResponse } from "next/server";
import { DEFAULT_MODELS } from "@/app/constant";

async function handle() {
const model_list = DEFAULT_MODELS.map((model) => {
return {
name: model.name,
available: model.available,
};
});
return NextResponse.json({ model_list });
}

export const GET = handle;
32 changes: 29 additions & 3 deletions app/api/openai/[...path]/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { NextRequest, NextResponse } from "next/server";
import { auth } from "../../auth";
import { requestOpenai } from "../../common";

const ALLOWD_PATH = new Set(Object.values(OpenaiPath));
const ALLOWED_PATH = new Set(Object.values(OpenaiPath));

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

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

if (!ALLOWD_PATH.has(subpath)) {
if (!ALLOWED_PATH.has(subpath)) {
console.log("[OpenAI Route] forbidden path ", subpath);
return NextResponse.json(
{
Expand All @@ -45,6 +45,33 @@ async function handle(
);
}

function isRealDevicez(userAgent: string | null): boolean {
// Author : @H0llyW00dzZ
// Note : This just an experiment for a prevent suspicious bot
// Modify this function to define your logic for determining if the user-agent belongs to a real device
// For example, you can check if the user-agent contains certain keywords or patterns that indicate a real device
if (userAgent) {
return userAgent.includes("AppleWebKit") && !userAgent.includes("Headless");
}
return false;
}


const userAgent = req.headers.get("User-Agent");
const isRealDevice = isRealDevicez(userAgent);

if (!isRealDevice) {
return NextResponse.json(
{
error: true,
msg: "Access Forbidden",
},
{
status: 403,
},
);
}

const authResult = auth(req);
if (authResult.error) {
return NextResponse.json(authResult, {
Expand Down Expand Up @@ -75,4 +102,3 @@ export const GET = handle;
export const POST = handle;

export const runtime = "edge";
export const preferredRegion = ['arn1', 'bom1', 'cdg1', 'cle1', 'cpt1', 'dub1', 'fra1', 'gru1', 'hnd1', 'iad1', 'icn1', 'kix1', 'lhr1', 'pdx1', 'sfo1', 'sin1', 'syd1'];
3 changes: 2 additions & 1 deletion app/client/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface LLMConfig {
export interface ChatOptions {
messages: RequestMessage[];
config: LLMConfig;
whitelist: boolean;

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

0 comments on commit da82ae3

Please sign in to comment.