mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-05-12 14:11:07 +00:00
Feature upload logs (#124)
This commit is contained in:
commit
b4b1d897f6
12 changed files with 196 additions and 113 deletions
|
|
@ -1,5 +1,5 @@
|
|||
VITE_BASE_URL=/api
|
||||
|
||||
VITE_PROXY_URL=https://dev.eigent.ai
|
||||
VITE_PROXY_URL=http://localhost:3001
|
||||
|
||||
VITE_USE_LOCAL_PROXY=false
|
||||
VITE_USE_LOCAL_PROXY=true
|
||||
|
|
|
|||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -36,6 +36,7 @@ yarn.lock
|
|||
.env
|
||||
.env.local
|
||||
.env.production
|
||||
.env.development
|
||||
|
||||
.cursor
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import path from 'node:path'
|
|||
import os, { homedir } from 'node:os'
|
||||
import log from 'electron-log'
|
||||
import { update, registerUpdateIpcHandlers } from './update'
|
||||
import { checkToolInstalled, installDependencies, startBackend } from './init'
|
||||
import { checkToolInstalled, installDependencies, killProcessOnPort, startBackend } from './init'
|
||||
import { WebViewManager } from './webview'
|
||||
import { FileReader } from './fileReader'
|
||||
import { ChildProcessWithoutNullStreams } from 'node:child_process'
|
||||
|
|
@ -15,6 +15,9 @@ import { getEnvPath, updateEnvBlock, removeEnvKey, getEmailFolderPath } from './
|
|||
import { copyBrowserData } from './copy'
|
||||
import { findAvailablePort } from './init'
|
||||
import kill from 'tree-kill';
|
||||
import { zipFolder } from './utils/log'
|
||||
import axios from 'axios';
|
||||
import FormData from 'form-data';
|
||||
|
||||
const userData = app.getPath('userData');
|
||||
const versionFile = path.join(userData, 'version.txt');
|
||||
|
|
@ -318,7 +321,7 @@ function registerIpcHandlers() {
|
|||
});
|
||||
ipcMain.handle('execute-command', async (event, command: string, email: string) => {
|
||||
log.info("execute-command", command);
|
||||
const {MCP_REMOTE_CONFIG_DIR} = getEmailFolderPath(email);
|
||||
const { MCP_REMOTE_CONFIG_DIR } = getEmailFolderPath(email);
|
||||
|
||||
try {
|
||||
const { spawn } = await import('child_process');
|
||||
|
|
@ -440,6 +443,72 @@ function registerIpcHandlers() {
|
|||
}
|
||||
});
|
||||
|
||||
ipcMain.handle('upload-log', async (event, email: string, taskId: string, baseUrl: string, token: string) => {
|
||||
let zipPath: string | null = null;
|
||||
|
||||
try {
|
||||
// Validate required parameters
|
||||
if (!email || !taskId || !baseUrl || !token) {
|
||||
return { success: false, error: 'Missing required parameters' };
|
||||
}
|
||||
|
||||
// Sanitize taskId to prevent path traversal attacks
|
||||
const sanitizedTaskId = taskId.replace(/[^a-zA-Z0-9_-]/g, '');
|
||||
if (!sanitizedTaskId) {
|
||||
return { success: false, error: 'Invalid task ID' };
|
||||
}
|
||||
|
||||
const { MCP_REMOTE_CONFIG_DIR } = getEmailFolderPath(email);
|
||||
const logFolderName = `task_${sanitizedTaskId}`;
|
||||
const logFolderPath = path.join(MCP_REMOTE_CONFIG_DIR, logFolderName);
|
||||
|
||||
// Check if log folder exists
|
||||
if (!fs.existsSync(logFolderPath)) {
|
||||
return { success: false, error: 'Log folder not found' };
|
||||
}
|
||||
|
||||
zipPath = path.join(MCP_REMOTE_CONFIG_DIR, `${logFolderName}.zip`);
|
||||
await zipFolder(logFolderPath, zipPath);
|
||||
|
||||
// Create form data with file stream
|
||||
const formData = new FormData();
|
||||
const fileStream = fs.createReadStream(zipPath);
|
||||
formData.append('file', fileStream);
|
||||
formData.append('task_id', sanitizedTaskId);
|
||||
|
||||
// Upload with timeout
|
||||
const response = await axios.post(baseUrl + '/api/chat/logs', formData, {
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
'Authorization': `Bearer ${token}`
|
||||
},
|
||||
timeout: 60000, // 60 second timeout
|
||||
maxContentLength: Infinity,
|
||||
maxBodyLength: Infinity
|
||||
});
|
||||
|
||||
fileStream.destroy();
|
||||
|
||||
if (response.status === 200) {
|
||||
return { success: true, data: response.data };
|
||||
} else {
|
||||
return { success: false, error: response.data };
|
||||
}
|
||||
} catch (error: any) {
|
||||
log.error('Failed to upload log:', error);
|
||||
return { success: false, error: error.message || 'Upload failed' };
|
||||
} finally {
|
||||
// Clean up zip file
|
||||
if (zipPath && fs.existsSync(zipPath)) {
|
||||
try {
|
||||
fs.unlinkSync(zipPath);
|
||||
} catch (cleanupError) {
|
||||
log.error('Failed to clean up zip file:', cleanupError);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// ==================== MCP manage handler ====================
|
||||
ipcMain.handle('mcp-install', async (event, name, mcp) => {
|
||||
addMcp(name, mcp);
|
||||
|
|
@ -609,7 +678,7 @@ function registerIpcHandlers() {
|
|||
|
||||
// ==================== delete folder handler ====================
|
||||
ipcMain.handle('delete-folder', async (event, email: string) => {
|
||||
const {MCP_REMOTE_CONFIG_DIR} = getEmailFolderPath(email);
|
||||
const { MCP_REMOTE_CONFIG_DIR } = getEmailFolderPath(email);
|
||||
try {
|
||||
log.info('Deleting folder:', MCP_REMOTE_CONFIG_DIR);
|
||||
|
||||
|
|
@ -646,7 +715,7 @@ function registerIpcHandlers() {
|
|||
// ==================== get MCP config path handler ====================
|
||||
ipcMain.handle('get-mcp-config-path', async (event, email: string) => {
|
||||
try {
|
||||
const {MCP_REMOTE_CONFIG_DIR,tempEmail} = getEmailFolderPath(email);
|
||||
const { MCP_REMOTE_CONFIG_DIR, tempEmail } = getEmailFolderPath(email);
|
||||
log.info('Getting MCP config path for email:', email);
|
||||
log.info('MCP config path:', MCP_REMOTE_CONFIG_DIR);
|
||||
return {
|
||||
|
|
@ -664,7 +733,7 @@ function registerIpcHandlers() {
|
|||
});
|
||||
|
||||
// ==================== env handler ====================
|
||||
|
||||
|
||||
ipcMain.handle('get-env-path', async (_event, email) => {
|
||||
return getEnvPath(email);
|
||||
});
|
||||
|
|
@ -957,6 +1026,7 @@ const checkAndStartBackend = async () => {
|
|||
});
|
||||
|
||||
python_process?.on('exit', (code, signal) => {
|
||||
|
||||
log.info('Python process exited', { code, signal });
|
||||
});
|
||||
} else {
|
||||
|
|
@ -965,20 +1035,41 @@ const checkAndStartBackend = async () => {
|
|||
};
|
||||
|
||||
// ==================== process cleanup ====================
|
||||
const cleanupPythonProcess = () => {
|
||||
const cleanupPythonProcess = async () => {
|
||||
try {
|
||||
// First attempt: Try to kill using PID
|
||||
if (python_process?.pid) {
|
||||
log.info('Cleaning up Python process', { pid: python_process.pid });
|
||||
kill(python_process.pid, 'SIGINT', (err) => {
|
||||
if (err) {
|
||||
log.error('Failed to clean up process tree:', err);
|
||||
} else {
|
||||
log.info('Successfully cleaned up Python process tree');
|
||||
}
|
||||
const pid = python_process.pid;
|
||||
log.info('Cleaning up Python process', { pid });
|
||||
|
||||
await new Promise<void>((resolve) => {
|
||||
kill(pid, 'SIGINT', (err) => {
|
||||
if (err) {
|
||||
log.error('Failed to clean up process tree:', err);
|
||||
} else {
|
||||
log.info('Successfully cleaned up Python process tree');
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
log.info('No Python process to clean up');
|
||||
}
|
||||
|
||||
// Second attempt: Use port-based cleanup as fallback
|
||||
const portFile = path.join(userData, 'port.txt');
|
||||
if (fs.existsSync(portFile)) {
|
||||
try {
|
||||
const port = parseInt(fs.readFileSync(portFile, 'utf-8').trim(), 10);
|
||||
if (!isNaN(port) && port > 0 && port < 65536) {
|
||||
log.info(`Attempting to kill process on port: ${port}`);
|
||||
await killProcessOnPort(port);
|
||||
}
|
||||
fs.unlinkSync(portFile);
|
||||
} catch (error) {
|
||||
log.error('Error handling port file:', error);
|
||||
}
|
||||
}
|
||||
|
||||
python_process = null;
|
||||
} catch (error) {
|
||||
log.error('Error occurred while cleaning up process:', error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -423,7 +423,7 @@ function checkPortAvailable(port: number): Promise<boolean> {
|
|||
});
|
||||
}
|
||||
|
||||
async function killProcessOnPort(port: number): Promise<boolean> {
|
||||
export async function killProcessOnPort(port: number): Promise<boolean> {
|
||||
try {
|
||||
const platform = process.platform;
|
||||
let command: string;
|
||||
|
|
|
|||
|
|
@ -90,9 +90,9 @@ export function getEmailFolderPath(email: string) {
|
|||
hasToken = false;
|
||||
}
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
hasToken = false;
|
||||
}
|
||||
|
||||
return { MCP_REMOTE_CONFIG_DIR, MCP_CONFIG_DIR, tempEmail, hasToken };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
25
electron/main/utils/log.ts
Normal file
25
electron/main/utils/log.ts
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import fs from 'node:fs'
|
||||
// @ts-ignore
|
||||
import archiver from 'archiver'
|
||||
import log from 'electron-log'
|
||||
|
||||
export function zipFolder(folderPath: string, outputZipPath: string): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const output = fs.createWriteStream(outputZipPath)
|
||||
const archive = archiver('zip', { zlib: { level: 9 } })
|
||||
|
||||
output.on('close', () => resolve(outputZipPath))
|
||||
|
||||
archive.on('error', (err: any) => {
|
||||
log.error('Archive error:', err);
|
||||
reject(err);
|
||||
})
|
||||
|
||||
archive.pipe(output)
|
||||
archive.directory(folderPath, false)
|
||||
archive.finalize()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
|
@ -44,6 +44,7 @@ contextBridge.exposeInMainWorld('electronAPI', {
|
|||
getShowWebview: () => ipcRenderer.invoke('get-show-webview'),
|
||||
webviewDestroy: (webviewId: string) => ipcRenderer.invoke('webview-destroy', webviewId),
|
||||
exportLog: () => ipcRenderer.invoke('export-log'),
|
||||
uploadLog: (email: string, taskId: string, baseUrl: string, token: string) => ipcRenderer.invoke('upload-log', email, taskId, baseUrl, token),
|
||||
// mcp
|
||||
mcpInstall: (name: string, mcp: any) => ipcRenderer.invoke('mcp-install', name, mcp),
|
||||
mcpRemove: (name: string) => ipcRenderer.invoke('mcp-remove', name),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "eigent",
|
||||
"version": "0.0.46",
|
||||
"version": "0.0.47",
|
||||
"main": "dist-electron/main/index.js",
|
||||
"description": "Eigent",
|
||||
"author": "Eigent.AI",
|
||||
|
|
@ -49,6 +49,7 @@
|
|||
"@xterm/xterm": "^5.5.0",
|
||||
"@xyflow/react": "^12.6.4",
|
||||
"adm-zip": "^0.5.16",
|
||||
"archiver": "^7.0.1",
|
||||
"axios": "^1.9.0",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
|
|
@ -82,6 +83,7 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@playwright/test": "^1.48.2",
|
||||
"@types/archiver": "^6.0.3",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/papaparse": "^5.3.16",
|
||||
"@types/react": "^18.3.12",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,14 @@
|
|||
import { useState, useRef, useEffect, useMemo } from "react";
|
||||
import { Settings, Minus, Square, X, FileDown, Menu, Plus } from "lucide-react";
|
||||
import {
|
||||
Settings,
|
||||
Minus,
|
||||
Square,
|
||||
X,
|
||||
FileDown,
|
||||
Menu,
|
||||
Plus,
|
||||
Import,
|
||||
} from "lucide-react";
|
||||
import "./index.css";
|
||||
import folderIcon from "@/assets/Folder.svg";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
|
@ -7,6 +16,7 @@ import { useLocation, useNavigate } from "react-router-dom";
|
|||
import { useChatStore } from "@/store/chatStore";
|
||||
import { useSidebarStore } from "@/store/sidebarStore";
|
||||
import chevron_left from "@/assets/chevron_left.svg";
|
||||
import { getAuthStore } from "@/store/authStore";
|
||||
function HeaderWin() {
|
||||
const titlebarRef = useRef<HTMLDivElement>(null);
|
||||
const controlsRef = useRef<HTMLDivElement>(null);
|
||||
|
|
@ -16,6 +26,7 @@ function HeaderWin() {
|
|||
const chatStore = useChatStore();
|
||||
const { toggle } = useSidebarStore();
|
||||
const [isFullscreen, setIsFullscreen] = useState(false);
|
||||
const { token } = getAuthStore();
|
||||
useEffect(() => {
|
||||
const p = window.electronAPI.getPlatform();
|
||||
setPlatform(p);
|
||||
|
|
@ -140,20 +151,20 @@ function HeaderWin() {
|
|||
<>
|
||||
{activeTaskTitle === "New Project" ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="font-bold text-base no-drag"
|
||||
onClick={createNewProject}
|
||||
>
|
||||
{activeTaskTitle}
|
||||
</Button>
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="font-bold text-base no-drag"
|
||||
onClick={createNewProject}
|
||||
>
|
||||
{activeTaskTitle}
|
||||
</Button>
|
||||
) : (
|
||||
<div className="font-bold leading-10 text-base ">{activeTaskTitle}</div>
|
||||
<div className="font-bold leading-10 text-base ">
|
||||
{activeTaskTitle}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
|
||||
</div>
|
||||
<div id="maximize-window" className="flex-1 h-10"></div>
|
||||
{/* right */}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
import { getAuthStore } from "@/store/authStore"
|
||||
|
||||
export function getProxyBaseURL() {
|
||||
const isDev = import.meta.env.DEV
|
||||
|
||||
|
|
@ -56,4 +58,22 @@ export function hasStackKeys() {
|
|||
return import.meta.env.VITE_STACK_PROJECT_ID &&
|
||||
import.meta.env.VITE_STACK_PUBLISHABLE_CLIENT_KEY &&
|
||||
import.meta.env.VITE_STACK_SECRET_SERVER_KEY;
|
||||
}
|
||||
|
||||
export async function uploadLog(taskId: string, type?: string | undefined) {
|
||||
if (import.meta.env.VITE_USE_LOCAL_PROXY !== "true" && !type) {
|
||||
try {
|
||||
const { email, token } = getAuthStore()
|
||||
const baseUrl = import.meta.env.DEV ? import.meta.env.VITE_PROXY_URL : import.meta.env.VITE_BASE_URL
|
||||
|
||||
await window.electronAPI.uploadLog(
|
||||
email,
|
||||
taskId,
|
||||
baseUrl,
|
||||
token
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('Failed to upload log:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,10 @@
|
|||
import { fetchPost, fetchPut, getBaseURL, proxyFetchPost, proxyFetchPut, proxyFetchGet, uploadFile } from '@/api/http';
|
||||
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
||||
import { create } from 'zustand';
|
||||
import { generateUniqueId } from "@/lib";
|
||||
import { generateUniqueId, uploadLog } from "@/lib";
|
||||
import { FileText } from 'lucide-react';
|
||||
import { getAuthStore, useWorkerList } from './authStore';
|
||||
import { showCreditsToast } from '@/components/Toast/creditsToast';
|
||||
import { OAuth } from '@/lib/oauth';
|
||||
import { showStorageToast } from '@/components/Toast/storageToast';
|
||||
|
||||
|
||||
|
|
@ -184,8 +183,7 @@ const chatStore = create<ChatStore>()(
|
|||
}
|
||||
const base_Url = import.meta.env.DEV ? import.meta.env.VITE_PROXY_URL : import.meta.env.VITE_BASE_URL
|
||||
const api = type == 'share' ? `${base_Url}/api/chat/share/playback/${shareToken}?delay_time=${delayTime}` : type == 'replay' ? `${base_Url}/api/chat/steps/playback/${taskId}?delay_time=${delayTime}` : `${baseURL}/chat`
|
||||
const isInChina = await getIsInChina(systemLanguage)
|
||||
console.log("isInChina", isInChina);
|
||||
|
||||
const { tasks } = get()
|
||||
let historyId: string | null = null;
|
||||
let snapshots: any = [];
|
||||
|
|
@ -265,7 +263,7 @@ const chatStore = create<ChatStore>()(
|
|||
} catch (error) {
|
||||
console.log('get-env-path error', error)
|
||||
}
|
||||
|
||||
|
||||
// create history
|
||||
if (!type) {
|
||||
const authStore = getAuthStore();
|
||||
|
|
@ -766,7 +764,7 @@ const chatStore = create<ChatStore>()(
|
|||
addFileList(taskId, agentMessages.data.process_task_id as string, fileInfo);
|
||||
|
||||
// Async file upload
|
||||
if (!type && file_path && import.meta.env.VITE_USE_LOCAL_PROXY!=='true') {
|
||||
if (!type && file_path && import.meta.env.VITE_USE_LOCAL_PROXY !== 'true') {
|
||||
(async () => {
|
||||
try {
|
||||
// Read file content using Electron API
|
||||
|
|
@ -805,13 +803,14 @@ const chatStore = create<ChatStore>()(
|
|||
console.log('error', agentMessages.data)
|
||||
showCreditsToast()
|
||||
setStatus(taskId, 'pause');
|
||||
uploadLog(taskId, type)
|
||||
return
|
||||
}
|
||||
|
||||
if (agentMessages.step === "error") {
|
||||
console.error('Model error:', agentMessages.data)
|
||||
const errorMessage = agentMessages.data.message || 'An error occurred while processing your request';
|
||||
|
||||
|
||||
// Create a new task to avoid "Task already exists" error
|
||||
// and completely reset the interface
|
||||
const newTaskId = create();
|
||||
|
|
@ -825,7 +824,7 @@ const chatStore = create<ChatStore>()(
|
|||
role: "agent",
|
||||
content: `❌ **Error**: ${errorMessage}`,
|
||||
});
|
||||
|
||||
uploadLog(taskId, type)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -846,6 +845,8 @@ const chatStore = create<ChatStore>()(
|
|||
}
|
||||
proxyFetchPut(`/api/chat/history/${historyId}`, obj)
|
||||
}
|
||||
uploadLog(taskId, type)
|
||||
|
||||
|
||||
let taskRunning = [...tasks[taskId].taskRunning];
|
||||
let taskAssigning = [...tasks[taskId].taskAssigning];
|
||||
|
|
@ -1561,47 +1562,14 @@ const chatStore = create<ChatStore>()(
|
|||
})
|
||||
);
|
||||
|
||||
// const filterMessage = (message: string, method_name: string = '') => {
|
||||
// if (!message!.includes("=======================") && !message?.includes("Original Query") && !message?.startsWith('You need to process one given task') && method_name !== 'browser_take_screenshot' && message !== '{}' && !message?.startsWith('{"query"') && !message?.startsWith('{"entity"') && message !== '' && !message?.startsWith("{'warning':") && !message?.startsWith("{'results':") && !message?.startsWith(`{"index"`) && !message?.startsWith('- Ran Playwright code')) {
|
||||
// if (message?.includes(`{"content"`)) {
|
||||
// message = JSON.parse(message)?.content || ''
|
||||
// }
|
||||
// if (message?.startsWith('{"element"')) {
|
||||
// message = JSON.parse(message)?.element || ''
|
||||
// }
|
||||
// if (message?.startsWith('{"url"')) {
|
||||
// message = 'Open URL: ' + JSON.parse(message)?.url || ''
|
||||
// }
|
||||
// if (message?.startsWith('{"filename"')) {
|
||||
// message = JSON.parse(message)?.filename || ''
|
||||
// }
|
||||
// if (method_name === 'browser_click' && message?.startsWith('{"element"')) {
|
||||
// message = 'Click Element: ' + JSON.parse(message)?.element || ''
|
||||
// }
|
||||
// if (message?.startsWith('{"query"')) {
|
||||
// message = 'Search: ' + JSON.parse(message)?.query || ''
|
||||
// }
|
||||
// if (message?.startsWith('{"result"')) {
|
||||
// message = JSON.parse(message)?.result || ''
|
||||
// }
|
||||
|
||||
// // && !message?.startsWith("{'error':")
|
||||
// if (message?.startsWith("{'error':")) {
|
||||
// message = JSON.parse(message.replace(/'error'/g, '"error"'))?.error || ''
|
||||
// }
|
||||
// console.log(message)
|
||||
// return message
|
||||
// }
|
||||
// return ''
|
||||
// }
|
||||
const filterMessage = (message: AgentMessage) => {
|
||||
if (message.data.toolkit_name?.includes('Search ')) {
|
||||
message.data.toolkit_name='Search Toolkit'
|
||||
message.data.toolkit_name = 'Search Toolkit'
|
||||
}
|
||||
if (message.data.method_name?.includes('search')) {
|
||||
message.data.method_name='search'
|
||||
message.data.method_name = 'search'
|
||||
}
|
||||
|
||||
|
||||
if (message.data.toolkit_name === 'Note Taking Toolkit') {
|
||||
message.data.message = message.data.message!.replace(/content='/g, '').replace(/', update=False/g, '').replace(/', update=True/g, '')
|
||||
}
|
||||
|
|
@ -1610,45 +1578,8 @@ const filterMessage = (message: AgentMessage) => {
|
|||
}
|
||||
return message
|
||||
}
|
||||
let isInChinaCache: boolean | null = null;
|
||||
|
||||
|
||||
const getIsInChina = async (systemLanguage: string): Promise<boolean> => {
|
||||
if (isInChinaCache !== null) {
|
||||
return isInChinaCache;
|
||||
}
|
||||
const fetchWithTimeout = (url: string, timeout = 3000): Promise<Response> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const timer = setTimeout(() => {
|
||||
reject(new Error('Timeout'));
|
||||
}, timeout);
|
||||
|
||||
fetch(url)
|
||||
.then((response) => {
|
||||
clearTimeout(timer);
|
||||
resolve(response);
|
||||
})
|
||||
.catch((err) => {
|
||||
clearTimeout(timer);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetchWithTimeout('https://ipinfo.io/json', 3000);
|
||||
if (!response.ok) throw new Error('Network response was not ok');
|
||||
|
||||
const info = await response.json();
|
||||
console.log('country', info?.country)
|
||||
isInChinaCache = info?.country === 'CN';
|
||||
return isInChinaCache;
|
||||
} catch (error) {
|
||||
console.warn('IP Timeout', error);
|
||||
isInChinaCache = systemLanguage === 'zh-cn';
|
||||
return isInChinaCache;
|
||||
}
|
||||
};
|
||||
|
||||
export const useChatStore = chatStore;
|
||||
|
||||
|
|
|
|||
|
|
@ -96,3 +96,4 @@ process.on('SIGINT', () => {
|
|||
console.log(e)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue