Compare commits

...

3 Commits

Author SHA1 Message Date
quangdn-ght
dc2948f408
Merge 8af4d7a6d9 into 673f907ea4 2025-06-25 17:48:01 +00:00
quangdn-ght
8af4d7a6d9 Xu ly dong bo nguoi dung thanh cong 2025-06-26 00:47:54 +07:00
quangdn-ght
ee25e4ac82 cap nhat giao dien 2025-06-25 23:25:07 +07:00
17 changed files with 20424 additions and 1172 deletions

View File

@ -1,21 +1,53 @@
"use client";
import { useEffect } from "react";
import { useSyncStore } from "./store/sync";
import { showToast } from "./components/ui-lib";
import { AUTHEN_PAGE } from "./constant";
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");
// Parse cookies using the cookie library
// const cookies = cookie.parse(document.cookie || "");
// const authToken = cookies["sb-zzgkylsbdgwoohcbompi-auth-token"] || null;
console.log("[SyncOnFirstLoad] Dong bo hoa du lieu lan dau tien");
console.log("[Auth Check] Checking user authentication status");
console.log("Thoi gian dong bo lan cuoi: ", syncStore.lastSyncTime);
fetch("/api/auth/check")
.then((res) => {
if (res.status === 401) {
console.log("[Auth Check] User is not authenticated");
// Handle unauthenticated user - redirect or show login modal
syncStore.sync();
// }
showToast("Please login first");
setTimeout(() => {
window.location.href = AUTHEN_PAGE;
}, 500);
return;
}
return res.json();
})
.then((data) => {
if (data) {
console.log("[Auth Check] User is authenticated:", data.user);
// Assuming data.user contains the user information(user email)
const email = data.user.email || "";
// Only update upstash.username, keep other params
syncStore.update((config) => (config.upstash.username = email));
// You can now use the user data as needed
// syncStore.sync();
// console.log("[Sync] User data synced successfully");
}
})
.catch((e) => {
console.error("[Auth Check] Error checking authentication:", e);
// Handle error appropriately
});
}, []);
return null;

View File

@ -0,0 +1,21 @@
// /app/api/auth/check/route.ts
import { NextRequest } from "next/server";
import { checkAuth } from "../../supabase";
export async function GET(req: NextRequest) {
const user = await checkAuth(req);
console.log("[Auth] user ", user);
if (!user) {
return new Response(JSON.stringify({ authenticated: false }), {
status: 401,
headers: { "Content-Type": "application/json" },
});
}
return new Response(JSON.stringify({ authenticated: true, user }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}

View File

@ -30,14 +30,17 @@ export async function requestOpenai(req: NextRequest) {
authHeaderName = "api-key";
} else {
// Nếu là OpenAI thường, giữ nguyên header Authorization
authValue = req.headers.get("Authorization") ?? "";
authHeaderName = "Authorization";
console.log("[Auth] ", authValue);
}
// Xử lý lại đường dẫn endpoint cho phù hợp với OpenAI/Azure
let path = `${req.nextUrl.pathname}`.replaceAll("/api/openai/", "");
console.log("[Proxy] mac dinh ", path);
// console.log("[Proxy] mac dinh ", path);
// Lấy baseUrl từ config, ưu tiên Azure nếu là request Azure
let baseUrl =

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

@ -0,0 +1,45 @@
import { createClient } from "@supabase/supabase-js";
import { NextRequest } from "next/server";
const SUPABASE_URL = process.env.SUPABASE_URL!;
const SUPABASE_ANON_KEY = process.env.SUPABASE_ANON_KEY!;
export async function checkAuth(req: NextRequest) {
// Use NextRequest.cookies API
const authToken = req.cookies.get("sb-zzgkylsbdgwoohcbompi-auth-token")
?.value;
// console.log("[Supabase] authToken", authToken);
if (!authToken) {
// Không tìm thấy token xác thực
console.log("[Supabase] No auth token found");
return null;
}
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
global: {
headers: {
Authorization: `Bearer ${authToken}`,
},
},
});
try {
const { data, error } = await supabase.auth.getUser();
if (error || !data?.user) {
// Lỗi khi lấy thông tin người dùng
console.error("[Supabase] Error getting user:", error);
return null;
}
// Đã xác thực thành công, trả về thông tin người dùng
console.log("[Supabase] Authenticated user:", data.user);
return data.user;
} catch (err) {
// Lỗi khi lấy dữ liệu người dùng
console.error("[Supabase] Error fetching user data:", err);
return null;
}
}

View File

@ -5,3 +5,4 @@ export const UPSTASH_ENDPOINT = "https://fine-baboon-52580.upstash.io";
export const UPSTASH_APIKEY =
"Ac1kAAIjcDE2YjM4YmY3OGI4YzA0MTU2YjZhNmQyNzc5Yzc3NzEwYnAxMA";
export const STORAGE_KEY = "chebichat-backup";
export const AUTHEN_PAGE = "https://chinese.giahungtech.com.vn";

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

@ -529,6 +529,8 @@ function SyncItems() {
setShowSyncConfigModal(true);
}}
/>
{/* thuc hien dong bo voi cloud */}
{couldSync && (
<IconButton
icon={<ResetIcon />}

View File

@ -29,13 +29,15 @@ import {
import { Link, useNavigate } from "react-router-dom";
import { isIOS, useMobileScreen } from "../utils";
import dynamic from "next/dynamic";
import { Selector, showConfirm } from "./ui-lib";
import { Selector, showConfirm, showToast } from "./ui-lib";
import clsx from "clsx";
import { isMcpEnabled } from "../mcp/actions";
import { useSyncStore } from "../store/sync";
const DISCOVERY = [
// { name: Locale.Plugin.Name, path: Path.Plugins },
// { name: "Stable Diffusion", path: Path.Sd },
{ name: Locale.UI.Sync, path: "/sync" },
{ name: Locale.SearchChat.Page.Title, path: Path.SearchChat },
];
@ -233,6 +235,8 @@ export function SideBar(props: { className?: string }) {
const chatStore = useChatStore();
const [mcpEnabled, setMcpEnabled] = useState(false);
const syncStore = useSyncStore();
useEffect(() => {
// 检查 MCP 是否启用
const checkMcpStatus = async () => {
@ -306,9 +310,20 @@ export function SideBar(props: { className?: string }) {
}),
]}
onClose={() => setshowDiscoverySelector(false)}
onSelection={(s) => {
// Điều hướng đến trang được chọn
onSelection={async (s) => {
console.log(s[0]);
if (s[0] == "/sync") {
try {
await syncStore.sync();
console.log("Dong bo thanh cong ");
showToast(Locale.Settings.Sync.Success);
} catch (e) {
showToast(Locale.Settings.Sync.Fail);
console.error("[Sync]", e);
}
} else {
navigate(s[0], { state: { fromHome: true } });
}
}}
/>
)}

View File

@ -57,10 +57,12 @@ export enum Path {
Artifacts = "/artifacts",
SearchChat = "/search-chat",
McpMarket = "/mcp-market",
Sync = "/sync",
}
export enum ApiPath {
Cors = "",
Supabase = "/api/supabase",
Azure = "/api/azure",
OpenAI = "/api/alibaba", // Use Alibaba path for OpenAI API
Anthropic = "/api/anthropic",

View File

@ -37,7 +37,7 @@ export default function RootLayout({
const serverConfig = getServerSideConfig();
// Log server configuration for debugging
console.log("Server Configuration:");
// console.log("Server Configuration:");
// console.log(serverConfig);
return (

View File

@ -35,10 +35,10 @@ export function compressImage(file: Blob, maxSize: number): Promise<string> {
if (dataUrl.length < maxSize) break;
if (quality > 0.5) {
// Prioritize quality reduction
// Ưu tiên giảm chất lượng
quality -= 0.1;
} else {
// Then reduce the size
// Sau đó giảm kích thước
width *= 0.9;
height *= 0.9;
}

2
git.sh
View File

@ -2,7 +2,7 @@
# git config --global user.name "quangdn-ght"
git add .
git commit -m "cap nhat giao dien"
git commit -m "Xu ly dong bo nguoi dung thanh cong"
git push
# mdZddHXcuzsB0Akk

17622
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -25,22 +25,27 @@
"@hello-pangea/dnd": "^16.5.0",
"@modelcontextprotocol/sdk": "^1.0.4",
"@next/third-parties": "^14.1.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 +59,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"

3751
yarn.lock

File diff suppressed because it is too large Load Diff