cap nhat giao dien

This commit is contained in:
quangdn-ght 2025-06-25 23:25:07 +07:00
parent b07760fbc9
commit ee25e4ac82
10 changed files with 20370 additions and 1165 deletions

View File

@ -6,16 +6,8 @@ export default function SyncOnFirstLoad() {
const syncStore = useSyncStore();
useEffect(() => {
// if (syncStore.lastSyncTime === 0) {
// // If this is the first time syncing, call sync()
// alert("[SyncOnFirstLoad] Dong bo hoa du lieu lan dau tien");
console.log("[SyncOnFirstLoad] Dong bo hoa du lieu lan dau tien");
console.log("Thoi gian dong bo lan cuoi: ", syncStore.lastSyncTime);
syncStore.sync();
// }
// Parse cookies using the custom function
// syncStore.sync();
}, []);
return null;

View File

@ -15,6 +15,7 @@ import { handle as siliconflowHandler } from "../../siliconflow";
import { handle as xaiHandler } from "../../xai";
import { handle as chatglmHandler } from "../../glm";
import { handle as proxyHandler } from "../../proxy";
import { handle as supabaseHandler } from "../../supabase";
async function handle(
req: NextRequest,
@ -27,6 +28,9 @@ async function handle(
console.log(`[${params.provider} Route] params `, params);
switch (apiPath) {
case ApiPath.Supabase:
console.log("[Supabase Route] params ", params);
return supabaseHandler(req, { params });
case ApiPath.Azure:
return azureHandler(req, { params });
case ApiPath.Google:

51
app/api/supabase.ts Normal file
View File

@ -0,0 +1,51 @@
import { createClient } from "@supabase/supabase-js";
import { NextRequest, NextResponse } from "next/server";
import cookie from "cookie";
const SUPABASE_URL = process.env.SUPABASE_URL!;
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY!;
const AUTHEN_PAGE = process.env.AUTHEN_PAGE!;
export async function handle(
req: NextRequest,
{ params }: { params: { path: string[] } },
) {
// Parse cookies using the 'cookie' library
const cookies = cookie.parse(req.headers.get("cookie") || "");
const authToken = cookies["sb-zzgkylsbdgwoohcbompi-auth-token"];
console.log("[Supabase] authToken", authToken);
if (!authToken) {
return NextResponse.redirect(AUTHEN_PAGE, 302);
}
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
global: {
headers: {
Authorization: `Bearer ${authToken}`,
},
},
});
try {
const { data, error } = await supabase.auth.getUser();
console.log("[Supabase] user", data?.user);
if (error || !data?.user) {
return NextResponse.json(
{ error: error?.message || "Error fetching user data" },
{ status: 401 },
);
}
return NextResponse.json({ user: data.user }, { status: 200 });
} catch (err) {
console.error("Error fetching user data from Supabase:", err);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 },
);
}
}

View File

@ -132,10 +132,16 @@ interface ChatProvider {
usage: () => void;
}
// Khởi tạo API client dựa trên nhà cung cấp mô hình được chỉ định
export class ClientApi {
public llm: LLMApi;
// Hàm khởi tạo nhận vào một provider (nhà cung cấp mô hình AI)
// Mặc định là ModelProvider.GPT nếu không được chỉ định
constructor(provider: ModelProvider = ModelProvider.GPT) {
console.log("[ClientApi] provider ", provider);
// Sử dụng switch để khởi tạo instance tương ứng với provider được chọn
switch (provider) {
case ModelProvider.GeminiPro:
this.llm = new GeminiProApi();
@ -178,13 +184,18 @@ export class ClientApi {
}
}
// Hàm cấu hình (chưa triển khai chi tiết)
config() {}
// Hàm lấy prompts (chưa triển khai chi tiết)
prompts() {}
// Hàm lấy masks (chưa triển khai chi tiết)
masks() {}
// Hàm chia sẻ cuộc trò chuyện
async share(messages: ChatMessage[], avatarUrl: string | null = null) {
// Chuẩn bị dữ liệu tin nhắn để chia sẻ
const msgs = messages
.map((m) => ({
from: m.role === "user" ? "human" : "gpt",
@ -197,14 +208,20 @@ export class ClientApi {
"Share from [NextChat]: https://github.com/Yidadaa/ChatGPT-Next-Web",
},
]);
// 敬告二开开发者们,为了开源大模型的发展,请不要修改上述消息,此消息用于后续数据清洗使用
// Lưu ý: Không nên sửa đổi dòng thông báo cuối cùng này vì nó dùng cho việc làm sạch dữ liệu sau này
// Please do not modify this message
console.log("[Share]", messages, msgs);
// Lấy cấu hình client
const clientConfig = getClientConfig();
// Xác định URL để chia sẻ dựa trên môi trường (app hay web)
const proxyUrl = "/sharegpt";
const rawUrl = "https://sharegpt.com/api/conversations";
const shareUrl = clientConfig?.isApp ? rawUrl : proxyUrl;
// Gửi yêu cầu POST để chia sẻ cuộc trò chuyện
const res = await fetch(shareUrl, {
body: JSON.stringify({
avatarUrl,
@ -216,6 +233,7 @@ export class ClientApi {
method: "POST",
});
// Xử lý phản hồi và trả về link chia sẻ
const resJson = await res.json();
console.log("[Share]", resJson);
if (resJson.id) {
@ -224,6 +242,7 @@ export class ClientApi {
}
}
// Hàm tạo token xác thực Bearer
export function getBearerToken(
apiKey: string,
noBearer: boolean = false,
@ -233,14 +252,21 @@ export function getBearerToken(
: "";
}
// Hàm kiểm tra chuỗi có hợp lệ không (có độ dài > 0)
export function validString(x: string): boolean {
return x?.length > 0;
}
// Hàm lấy các header cần thiết cho yêu cầu API
export function getHeaders(ignoreHeaders: boolean = false) {
// Lấy store để truy cập các trạng thái liên quan đến quyền truy cập và chat
const accessStore = useAccessStore.getState();
const chatStore = useChatStore.getState();
// Khởi tạo đối tượng headers rỗng
let headers: Record<string, string> = {};
// Nếu không bỏ qua headers thì thêm các header mặc định
if (!ignoreHeaders) {
headers = {
"Content-Type": "application/json",
@ -248,10 +274,15 @@ export function getHeaders(ignoreHeaders: boolean = false) {
};
}
// Lấy cấu hình client
const clientConfig = getClientConfig();
// Hàm getConfig sẽ xác định nhà cung cấp hiện tại và API key tương ứng
function getConfig() {
// Lấy cấu hình mô hình từ session hiện tại
const modelConfig = chatStore.currentSession().mask.modelConfig;
// Kiểm tra loại nhà cung cấp đang được sử dụng
const isGoogle = modelConfig.providerName === ServiceProvider.Google;
const isAzure = modelConfig.providerName === ServiceProvider.Azure;
const isAnthropic = modelConfig.providerName === ServiceProvider.Anthropic;
@ -265,7 +296,11 @@ export function getHeaders(ignoreHeaders: boolean = false) {
const isChatGLM = modelConfig.providerName === ServiceProvider.ChatGLM;
const isSiliconFlow =
modelConfig.providerName === ServiceProvider.SiliconFlow;
// Kiểm tra xem có bật kiểm soát truy cập không
const isEnabledAccessControl = accessStore.enabledAccessControl();
// Xác định API key dựa trên nhà cung cấp đang được sử dụng
const apiKey = isGoogle
? accessStore.googleApiKey
: isAzure
@ -309,6 +344,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
};
}
// Hàm xác định header nào sẽ được sử dụng để xác thực
function getAuthHeader(): string {
return isAzure
? "api-key"
@ -319,6 +355,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
: "Authorization";
}
// Lấy các giá trị đã được xác định trong getConfig
const {
isGoogle,
isAzure,
@ -335,19 +372,24 @@ export function getHeaders(ignoreHeaders: boolean = false) {
apiKey,
isEnabledAccessControl,
} = getConfig();
// when using baidu api in app, not set auth header
// Khi sử dụng API của Baidu trong ứng dụng, không đặt header xác thực
if (isBaidu && clientConfig?.isApp) return headers;
// Xác định tên header xác thực
const authHeader = getAuthHeader();
// Tạo token xác thực
const bearerToken = getBearerToken(
apiKey,
isAzure || isAnthropic || isGoogle,
);
// Nếu có bearer token thì thêm vào headers
if (bearerToken) {
headers[authHeader] = bearerToken;
} else if (isEnabledAccessControl && validString(accessStore.accessCode)) {
// Nếu có mã truy cập thì sử dụng nó để tạo bearer token
headers["Authorization"] = getBearerToken(
ACCESS_CODE_PREFIX + accessStore.accessCode,
);
@ -356,6 +398,7 @@ export function getHeaders(ignoreHeaders: boolean = false) {
return headers;
}
// Hàm tạo instance của ClientApi dựa trên nhà cung cấp dịch vụ
export function getClientApi(provider: ServiceProvider): ClientApi {
switch (provider) {
case ServiceProvider.Google:

View File

@ -1813,7 +1813,7 @@ function _Chat() {
const shouldShowClearContextDivider =
i === clearContextIndex - 1;
console.log(message.role);
// console.log(message.role);
return (
<Fragment key={message.id}>
@ -1868,9 +1868,9 @@ function _Chat() {
}}
></IconButton>
</div>
{isUser ? (
<Avatar avatar={config.avatar} />
) : (
{/* Neu la user thi khong hien thi avatar */}
{isUser ? null : (
<>
{["system"].includes(message.role) ? (
<Avatar avatar="2699-fe0f" />

View File

@ -5,7 +5,8 @@ require("../polyfill");
import { useEffect, useState } from "react";
import styles from "./home.module.scss";
import BotIcon from "../icons/bot.svg";
//icon chebichat logo
import BotIcon from "../icons/chebichat.svg";
import LoadingIcon from "../icons/three-dots.svg";
import { getCSSVar, useMobileScreen } from "../utils";
@ -14,7 +15,7 @@ import dynamic from "next/dynamic";
import { Path, SlotID } from "../constant";
import { ErrorBoundary } from "./error";
import { getISOLang, getLang } from "../locales";
import { getISOLang } from "../locales";
import {
HashRouter as Router,
@ -212,7 +213,7 @@ function Screen() {
<div
className={clsx(styles.container, {
[styles["tight-container"]]: shouldTightBorder,
[styles["rtl-screen"]]: getLang() === "ar",
// [styles["rtl-screen"]]: getLang() === "ar", // Removed because "ar" is not a possible return value
})}
>
{renderContent()}

View File

@ -61,6 +61,7 @@ export enum Path {
export enum ApiPath {
Cors = "",
Supabase = "/api/supabase",
Azure = "/api/azure",
OpenAI = "/api/alibaba", // Use Alibaba path for OpenAI API
Anthropic = "/api/anthropic",

17622
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -25,22 +25,28 @@
"@hello-pangea/dnd": "^16.5.0",
"@modelcontextprotocol/sdk": "^1.0.4",
"@next/third-parties": "^14.1.0",
"@supabase/auth-helpers-nextjs": "^0.10.0",
"@supabase/ssr": "^0.6.1",
"@supabase/supabase-js": "^2.50.1",
"@svgr/webpack": "^6.5.1",
"@vercel/analytics": "^0.1.11",
"@vercel/speed-insights": "^1.0.2",
"axios": "^1.7.5",
"clsx": "^2.1.1",
"cookie": "^1.0.2",
"emoji-picker-react": "^4.9.2",
"fuse.js": "^7.0.0",
"heic2any": "^0.0.4",
"html-to-image": "^1.11.11",
"idb-keyval": "^6.2.1",
"install": "^0.13.0",
"lodash-es": "^4.17.21",
"markdown-to-txt": "^2.0.1",
"mermaid": "^10.6.1",
"nanoid": "^5.0.3",
"next": "^14.1.1",
"node-fetch": "^3.3.1",
"npm": "^11.4.2",
"openapi-client-axios": "^7.5.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@ -54,6 +60,7 @@
"rt-client": "https://github.com/Azure-Samples/aoai-realtime-audio-sdk/releases/download/js/v0.5.0/rt-client-0.5.0.tgz",
"sass": "^1.59.2",
"spark-md5": "^3.0.2",
"supabase": "^2.26.9",
"use-debounce": "^9.0.4",
"zod": "^3.24.1",
"zustand": "^4.3.8"

3776
yarn.lock

File diff suppressed because it is too large Load Diff