mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-04-28 03:30:06 +00:00
Revert "add authentication (login/signup) and improve search history management" (#1120)
This commit is contained in:
parent
bd2f2a1c9e
commit
3291fdf0eb
14 changed files with 223 additions and 462 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -19,7 +19,7 @@ release
|
|||
backend/context_files/
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/
|
||||
.vscode/.debug.env
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
|
|
|
|||
23
.vscode/.debug.script.mjs
vendored
Normal file
23
.vscode/.debug.script.mjs
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { createRequire } from 'node:module'
|
||||
import { spawn } from 'node:child_process'
|
||||
|
||||
const pkg = createRequire(import.meta.url)('../package.json')
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
||||
|
||||
// write .debug.env
|
||||
const envContent = Object.entries(pkg.debug.env).map(([key, val]) => `${key}=${val}`)
|
||||
fs.writeFileSync(path.join(__dirname, '.debug.env'), envContent.join('\n'))
|
||||
|
||||
// bootstrap
|
||||
spawn(
|
||||
// TODO: terminate `npm run dev` when Debug exits.
|
||||
process.platform === 'win32' ? 'npm.cmd' : 'npm',
|
||||
['run', 'dev'],
|
||||
{
|
||||
stdio: 'inherit',
|
||||
env: Object.assign(process.env, { VSCODE_DEBUG: 'true' }),
|
||||
},
|
||||
)
|
||||
13
.vscode/extensions.json
vendored
Normal file
13
.vscode/extensions.json
vendored
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
// See http://go.microsoft.com/fwlink/?LinkId=827846
|
||||
// for the documentation about the extensions.json format
|
||||
"recommendations": [
|
||||
"mrmlnc.vscode-json5",
|
||||
"ms-python.python",
|
||||
"ms-python.debugpy",
|
||||
// Linting / Formatting
|
||||
"esbenp.prettier-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"bradlc.vscode-tailwindcss"
|
||||
]
|
||||
}
|
||||
70
.vscode/launch.json
vendored
Normal file
70
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"compounds": [
|
||||
{
|
||||
"name": "Debug App",
|
||||
"preLaunchTask": "Before Debug",
|
||||
"configurations": [
|
||||
"Debug Main Process",
|
||||
"Debug Renderer Process"
|
||||
],
|
||||
"presentation": {
|
||||
"hidden": false,
|
||||
"group": "",
|
||||
"order": 1
|
||||
},
|
||||
"stopAll": true
|
||||
}
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Main Process",
|
||||
"type": "node",
|
||||
"request": "launch",
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
|
||||
"windows": {
|
||||
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd"
|
||||
},
|
||||
"runtimeArgs": [
|
||||
"--no-sandbox",
|
||||
"--remote-debugging-port=9229",
|
||||
"."
|
||||
],
|
||||
"envFile": "${workspaceFolder}/.vscode/.debug.env",
|
||||
"console": "integratedTerminal"
|
||||
},
|
||||
{
|
||||
"name": "Debug Renderer Process",
|
||||
"port": 9229,
|
||||
"request": "attach",
|
||||
"type": "chrome",
|
||||
"timeout": 60000,
|
||||
"skipFiles": [
|
||||
"<node_internals>/**",
|
||||
"${workspaceRoot}/node_modules/**",
|
||||
"${workspaceRoot}/dist-electron/**",
|
||||
// Skip files in host(VITE_DEV_SERVER_URL)
|
||||
"http://127.0.0.1:7777/**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Debug Python Backend (Attach)",
|
||||
"type": "debugpy",
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
"host": "localhost",
|
||||
"port": 5678
|
||||
},
|
||||
"pathMappings": [
|
||||
{
|
||||
"localRoot": "${workspaceFolder}/backend",
|
||||
"remoteRoot": "."
|
||||
}
|
||||
],
|
||||
"justMyCode": false
|
||||
}
|
||||
]
|
||||
}
|
||||
54
.vscode/settings.json
vendored
Normal file
54
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"typescript.tsc.autoDetect": "off",
|
||||
"json.schemas": [
|
||||
{
|
||||
"fileMatch": [
|
||||
"/*electron-builder.json5",
|
||||
"/*electron-builder.json"
|
||||
],
|
||||
"url": "https://json.schemastore.org/electron-builder"
|
||||
}
|
||||
],
|
||||
"cSpell.words": [
|
||||
"Eigent"
|
||||
],
|
||||
"i18n-ally.localesPaths": [
|
||||
"backend/lang",
|
||||
"server/lang",
|
||||
"src/i18n",
|
||||
"src/i18n/locales"
|
||||
],
|
||||
// Prettier & ESLint configuration
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit",
|
||||
"source.organizeImports": "explicit"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[javascriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescriptreact]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"prettier.requireConfig": true,
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"typescriptreact"
|
||||
]
|
||||
}
|
||||
2
.vscode/tasks.json
vendored
2
.vscode/tasks.json
vendored
|
|
@ -6,7 +6,7 @@
|
|||
{
|
||||
"label": "Before Debug",
|
||||
"type": "shell",
|
||||
"command": "node .vscode/.debug.script.js",
|
||||
"command": "node .vscode/.debug.script.mjs",
|
||||
"isBackground": true,
|
||||
"problemMatcher": {
|
||||
"owner": "typescript",
|
||||
|
|
|
|||
|
|
@ -14,10 +14,9 @@
|
|||
|
||||
'use client';
|
||||
|
||||
import { ScanFace, Search, Trash2 } from 'lucide-react';
|
||||
import { ScanFace, Search } from 'lucide-react';
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import { proxyFetchDelete } from '@/api/http';
|
||||
import GroupedHistoryView from '@/components/GroupedHistoryView';
|
||||
import {
|
||||
CommandDialog,
|
||||
|
|
@ -31,7 +30,6 @@ import {
|
|||
import useChatStoreAdapter from '@/hooks/useChatStoreAdapter';
|
||||
import { replayProject } from '@/lib';
|
||||
import { fetchHistoryTasks } from '@/service/historyApi';
|
||||
import { getAuthStore } from '@/store/authStore';
|
||||
import { useGlobalStore } from '@/store/globalStore';
|
||||
import { VisuallyHidden } from '@radix-ui/react-visually-hidden';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
|
@ -76,35 +74,9 @@ export function SearchHistoryDialog() {
|
|||
await replayProject(projectStore, navigate, projectId, question, historyId);
|
||||
};
|
||||
|
||||
const handleDelete = async (historyId: string, callback?: () => void) => {
|
||||
try {
|
||||
await proxyFetchDelete(`/api/chat/history/${historyId}`);
|
||||
|
||||
// Also delete local files for this task if available (via Electron IPC)
|
||||
const history = historyTasks.find(
|
||||
(item) => String(item.id) === String(historyId)
|
||||
);
|
||||
const { email } = getAuthStore();
|
||||
if (history?.task_id && (window as any).ipcRenderer) {
|
||||
try {
|
||||
await (window as any).ipcRenderer.invoke(
|
||||
'delete-task-files',
|
||||
email,
|
||||
history.task_id,
|
||||
history.project_id ?? undefined
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn('Local file cleanup failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
setHistoryTasks((list) =>
|
||||
list.filter((item) => String(item.id) !== String(historyId))
|
||||
);
|
||||
callback?.();
|
||||
} catch (error) {
|
||||
console.error('Failed to delete history task:', error);
|
||||
}
|
||||
const handleDelete = (taskId: string) => {
|
||||
// TODO: Implement delete functionality similar to HistorySidebar
|
||||
console.log('Delete task:', taskId);
|
||||
};
|
||||
|
||||
const handleShare = (taskId: string) => {
|
||||
|
|
@ -165,20 +137,6 @@ export function SearchHistoryDialog() {
|
|||
<div className="overflow-hidden text-ellipsis whitespace-nowrap">
|
||||
{task.question}
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="text-muted-foreground hover:text-foreground ml-auto"
|
||||
aria-label="Delete history"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
void handleDelete(String(task.id));
|
||||
}}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</Button>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ import github2 from '@/assets/github2.svg';
|
|||
import google from '@/assets/google.svg';
|
||||
import WindowControls from '@/components/WindowControls';
|
||||
import { hasStackKeys } from '@/lib';
|
||||
import { loginByStackWithAutoCreate } from '@/service/stackAuthApi';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const HAS_STACK_KEYS = hasStackKeys();
|
||||
|
|
@ -159,20 +158,7 @@ export default function Login() {
|
|||
return;
|
||||
}
|
||||
|
||||
const token = (data as any)?.token as string | undefined;
|
||||
const email =
|
||||
((data as any)?.email as string | undefined) ?? formData.email;
|
||||
if (!token) {
|
||||
setGeneralError(t('layout.login-failed-please-try-again'));
|
||||
return;
|
||||
}
|
||||
|
||||
setAuth({
|
||||
token,
|
||||
email,
|
||||
username: (data as any)?.username ?? null,
|
||||
user_id: (data as any)?.user_id ?? null,
|
||||
});
|
||||
setAuth({ email: formData.email, ...data });
|
||||
setModelType('cloud');
|
||||
// Record VITE_USE_LOCAL_PROXY value at login
|
||||
const localProxyValue = import.meta.env.VITE_USE_LOCAL_PROXY || null;
|
||||
|
|
@ -191,14 +177,12 @@ export default function Login() {
|
|||
const handleLoginByStack = useCallback(
|
||||
async (token: string) => {
|
||||
try {
|
||||
// 1) Try normal login (existing profile)
|
||||
// 2) If not found, auto-create profile via signup and continue
|
||||
const data = await loginByStackWithAutoCreate(token);
|
||||
|
||||
if (!data) {
|
||||
setGeneralError(t('layout.login-failed-please-try-again'));
|
||||
return;
|
||||
}
|
||||
const data = await proxyFetchPost(
|
||||
'/api/login-by_stack?token=' + token,
|
||||
{
|
||||
token: token,
|
||||
}
|
||||
);
|
||||
|
||||
const errorMessage = getLoginErrorMessage(data);
|
||||
if (errorMessage) {
|
||||
|
|
@ -207,21 +191,7 @@ export default function Login() {
|
|||
}
|
||||
console.log('data', data);
|
||||
setModelType('cloud');
|
||||
|
||||
const authToken = (data as any)?.token as string | undefined;
|
||||
const email =
|
||||
((data as any)?.email as string | undefined) ?? formData.email;
|
||||
if (!authToken) {
|
||||
setGeneralError(t('layout.login-failed-please-try-again'));
|
||||
return;
|
||||
}
|
||||
|
||||
setAuth({
|
||||
token: authToken,
|
||||
email,
|
||||
username: (data as any)?.username ?? null,
|
||||
user_id: (data as any)?.user_id ?? null,
|
||||
});
|
||||
setAuth({ email: formData.email, ...data });
|
||||
// Record VITE_USE_LOCAL_PROXY value at login
|
||||
const localProxyValue = import.meta.env.VITE_USE_LOCAL_PROXY || null;
|
||||
setLocalProxyValue(localProxyValue);
|
||||
|
|
@ -250,11 +220,6 @@ export default function Login() {
|
|||
|
||||
const handleReloadBtn = async (type: string) => {
|
||||
if (!app) {
|
||||
// Keep the buttons visible so users discover the option, but make it
|
||||
// clear when Stack OAuth isn't configured for local builds.
|
||||
setGeneralError(
|
||||
'Social sign-in is not configured for this build. Set VITE_STACK_PROJECT_ID, VITE_STACK_PUBLISHABLE_CLIENT_KEY, and VITE_STACK_SECRET_SERVER_KEY.'
|
||||
);
|
||||
console.error('Stack app not initialized');
|
||||
return;
|
||||
}
|
||||
|
|
@ -322,12 +287,7 @@ export default function Login() {
|
|||
|
||||
lock = true;
|
||||
setIsLoading(true);
|
||||
const accessToken = await handleGetToken(code);
|
||||
if (!accessToken) {
|
||||
setGeneralError(t('layout.login-failed-please-try-again'));
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
let accessToken = await handleGetToken(code);
|
||||
handleLoginByStack(accessToken);
|
||||
setTimeout(() => {
|
||||
lock = false;
|
||||
|
|
@ -442,35 +402,39 @@ export default function Login() {
|
|||
{t('layout.sign-up')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="w-full pt-6">
|
||||
<Button
|
||||
variant="primary"
|
||||
size="lg"
|
||||
onClick={() => handleReloadBtn('google')}
|
||||
className="mb-4 w-full justify-center rounded-[24px] text-center font-inter text-[15px] font-bold leading-[22px] text-[#F5F5F5] transition-all duration-300 ease-in-out"
|
||||
disabled={isLoading || !HAS_STACK_KEYS}
|
||||
>
|
||||
<img src={google} className="h-5 w-5" />
|
||||
<span className="ml-2">
|
||||
{t('layout.continue-with-google-login')}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="lg"
|
||||
onClick={() => handleReloadBtn('github')}
|
||||
className="mb-4 w-full justify-center rounded-[24px] text-center font-inter text-[15px] font-bold leading-[22px] text-[#F5F5F5] transition-all duration-300 ease-in-out"
|
||||
disabled={isLoading || !HAS_STACK_KEYS}
|
||||
>
|
||||
<img src={github2} className="h-5 w-5" />
|
||||
<span className="ml-2">
|
||||
{t('layout.continue-with-github-login')}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="mb-6 mt-2 w-full text-center font-inter text-[15px] font-medium leading-[22px] text-[#222]">
|
||||
{t('layout.or')}
|
||||
</div>
|
||||
{HAS_STACK_KEYS && (
|
||||
<div className="w-full pt-6">
|
||||
<Button
|
||||
variant="primary"
|
||||
size="lg"
|
||||
onClick={() => handleReloadBtn('google')}
|
||||
className="mb-4 w-full justify-center rounded-[24px] text-center font-inter text-[15px] font-bold leading-[22px] text-[#F5F5F5] transition-all duration-300 ease-in-out"
|
||||
disabled={isLoading}
|
||||
>
|
||||
<img src={google} className="h-5 w-5" />
|
||||
<span className="ml-2">
|
||||
{t('layout.continue-with-google-login')}
|
||||
</span>
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
size="lg"
|
||||
onClick={() => handleReloadBtn('github')}
|
||||
className="mb-4 w-full justify-center rounded-[24px] text-center font-inter text-[15px] font-bold leading-[22px] text-[#F5F5F5] transition-all duration-300 ease-in-out"
|
||||
disabled={isLoading}
|
||||
>
|
||||
<img src={github2} className="h-5 w-5" />
|
||||
<span className="ml-2">
|
||||
{t('layout.continue-with-github-login')}
|
||||
</span>
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
{HAS_STACK_KEYS && (
|
||||
<div className="mb-6 mt-2 w-full text-center font-inter text-[15px] font-medium leading-[22px] text-[#222]">
|
||||
{t('layout.or')}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex w-full flex-col gap-4">
|
||||
{generalError && (
|
||||
<p className="mb-4 mt-1 text-label-md text-text-cuation">
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ import eye from '@/assets/eye.svg';
|
|||
import github2 from '@/assets/github2.svg';
|
||||
import google from '@/assets/google.svg';
|
||||
import { hasStackKeys } from '@/lib';
|
||||
import { loginByStackToken } from '@/service/stackAuthApi';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const HAS_STACK_KEYS = hasStackKeys();
|
||||
|
|
@ -151,16 +150,13 @@ export default function SignUp() {
|
|||
const handleLoginByStack = useCallback(
|
||||
async (token: string) => {
|
||||
try {
|
||||
if (!token) {
|
||||
setGeneralError(t('layout.login-failed-please-try-again'));
|
||||
return;
|
||||
}
|
||||
const inviteCode = localStorage.getItem('invite_code') || '';
|
||||
const data = await loginByStackToken({
|
||||
token,
|
||||
type: 'signup',
|
||||
inviteCode: inviteCode || undefined,
|
||||
});
|
||||
const data = await proxyFetchPost(
|
||||
'/api/login-by_stack?token=' + token,
|
||||
{
|
||||
token: token,
|
||||
invite_code: localStorage.getItem('invite_code') || '',
|
||||
}
|
||||
);
|
||||
|
||||
if (data.code === 10) {
|
||||
setGeneralError(
|
||||
|
|
@ -169,21 +165,7 @@ export default function SignUp() {
|
|||
return;
|
||||
}
|
||||
console.log('data', data);
|
||||
|
||||
const authToken = (data as any)?.token as string | undefined;
|
||||
const email =
|
||||
((data as any)?.email as string | undefined) ?? formData.email;
|
||||
if (!authToken) {
|
||||
setGeneralError(t('layout.login-failed-please-try-again'));
|
||||
return;
|
||||
}
|
||||
|
||||
setAuth({
|
||||
token: authToken,
|
||||
email,
|
||||
username: (data as any)?.username ?? null,
|
||||
user_id: (data as any)?.user_id ?? null,
|
||||
});
|
||||
setAuth({ email: formData.email, ...data });
|
||||
navigate('/');
|
||||
} catch (error: any) {
|
||||
console.error('Login failed:', error);
|
||||
|
|
@ -264,17 +246,12 @@ export default function SignUp() {
|
|||
lock = true;
|
||||
setIsLoading(true);
|
||||
const accessToken = await handleGetToken(code);
|
||||
if (!accessToken) {
|
||||
setGeneralError(t('layout.login-failed-please-try-again'));
|
||||
setIsLoading(false);
|
||||
return;
|
||||
}
|
||||
await handleLoginByStack(accessToken);
|
||||
setTimeout(() => {
|
||||
lock = false;
|
||||
}, 1500);
|
||||
},
|
||||
[location.pathname, handleLoginByStack, handleGetToken, setIsLoading, t]
|
||||
[location.pathname, handleLoginByStack, handleGetToken, setIsLoading]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import { proxyFetchPost } from '@/api/http';
|
||||
|
||||
export type StackAuthFlowType = 'login' | 'signup';
|
||||
|
||||
type StackLoginResponse = {
|
||||
code?: number;
|
||||
text?: string;
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
function isUserNotFoundResponse(
|
||||
res: StackLoginResponse | null | undefined
|
||||
): boolean {
|
||||
if (!res || typeof res !== 'object') return false;
|
||||
if (res.code !== 1) return false;
|
||||
const text = String(res.text ?? '').toLowerCase();
|
||||
return text.includes('user not found');
|
||||
}
|
||||
|
||||
export async function loginByStackToken(params: {
|
||||
token: string;
|
||||
type: StackAuthFlowType;
|
||||
inviteCode?: string;
|
||||
}): Promise<StackLoginResponse> {
|
||||
const searchParams = new URLSearchParams();
|
||||
searchParams.set('token', params.token);
|
||||
searchParams.set('type', params.type);
|
||||
if (params.inviteCode) {
|
||||
searchParams.set('invite_code', params.inviteCode);
|
||||
}
|
||||
|
||||
// Endpoint is defined as POST, but consumes query params.
|
||||
return proxyFetchPost(`/api/login-by_stack?${searchParams.toString()}`, {
|
||||
token: params.token,
|
||||
invite_code: params.inviteCode ?? '',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts a passwordless SSO login first, and auto-creates the user if not found.
|
||||
* This matches the UX request: “check existing profile; if missing, create like signup”.
|
||||
*/
|
||||
export async function loginByStackWithAutoCreate(
|
||||
token: string
|
||||
): Promise<StackLoginResponse> {
|
||||
const loginRes = await loginByStackToken({ token, type: 'login' });
|
||||
if (!isUserNotFoundResponse(loginRes)) return loginRes;
|
||||
return loginByStackToken({ token, type: 'signup' });
|
||||
}
|
||||
|
|
@ -32,9 +32,9 @@ type CloudModelType =
|
|||
// auth info interface
|
||||
interface AuthInfo {
|
||||
token: string;
|
||||
username?: string | null;
|
||||
username: string;
|
||||
email: string;
|
||||
user_id?: number | null;
|
||||
user_id: number;
|
||||
}
|
||||
|
||||
// auth state interface
|
||||
|
|
@ -107,12 +107,7 @@ const authStore = create<AuthState>()(
|
|||
|
||||
// auth related methods
|
||||
setAuth: ({ token, username, email, user_id }) =>
|
||||
set({
|
||||
token,
|
||||
email,
|
||||
username: username ?? null,
|
||||
user_id: user_id ?? null,
|
||||
}),
|
||||
set({ token, username, email, user_id }),
|
||||
|
||||
logout: () =>
|
||||
set({
|
||||
|
|
|
|||
|
|
@ -1,148 +0,0 @@
|
|||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { SearchHistoryDialog } from '../../../src/components/SearchHistoryDialog';
|
||||
|
||||
// ---- Mocks ----
|
||||
|
||||
vi.mock('react-router-dom', () => ({
|
||||
useNavigate: () => vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('@/hooks/useChatStoreAdapter', () => ({
|
||||
default: () => ({
|
||||
chatStore: { activeTaskId: undefined },
|
||||
projectStore: {
|
||||
getProjectById: vi.fn(() => null),
|
||||
setHistoryId: vi.fn(),
|
||||
setActiveProject: vi.fn(),
|
||||
},
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock('@/store/globalStore', () => ({
|
||||
useGlobalStore: () => ({ history_type: 'list' }),
|
||||
}));
|
||||
|
||||
const proxyFetchDeleteMock = vi.fn().mockResolvedValue({ code: 0 });
|
||||
vi.mock('@/api/http', () => ({
|
||||
proxyFetchDelete: (...args: any[]) => proxyFetchDeleteMock(...args),
|
||||
}));
|
||||
|
||||
vi.mock('@/store/authStore', () => ({
|
||||
getAuthStore: () => ({ email: 'test@example.com' }),
|
||||
}));
|
||||
|
||||
vi.mock('@/service/historyApi', () => ({
|
||||
fetchHistoryTasks: (setter: (tasks: any[]) => void) => {
|
||||
setter([
|
||||
{
|
||||
id: '1',
|
||||
task_id: 'task-1',
|
||||
project_id: 'project-1',
|
||||
question: 'My history item',
|
||||
},
|
||||
]);
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock('@/components/ui/command', () => ({
|
||||
CommandDialog: ({ open, children }: any) =>
|
||||
open ? <div data-testid="cmd-dialog">{children}</div> : null,
|
||||
CommandEmpty: ({ children }: any) => <div>{children}</div>,
|
||||
CommandGroup: ({ children }: any) => <div>{children}</div>,
|
||||
CommandInput: (props: any) => <input aria-label="command-input" {...props} />,
|
||||
CommandItem: ({ children, onSelect }: any) => (
|
||||
<div role="button" tabIndex={0} onClick={() => onSelect?.('')}>
|
||||
{children}
|
||||
</div>
|
||||
),
|
||||
CommandList: ({ children }: any) => <div>{children}</div>,
|
||||
CommandSeparator: () => <hr />,
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/components/ui/button', () => ({
|
||||
Button: ({ children, onClick, ...rest }: any) => (
|
||||
<button onClick={onClick} {...rest}>
|
||||
{children}
|
||||
</button>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock('../../../src/components/ui/dialog', () => ({
|
||||
DialogTitle: ({ children }: any) => <div>{children}</div>,
|
||||
}));
|
||||
|
||||
vi.mock('@radix-ui/react-visually-hidden', () => ({
|
||||
VisuallyHidden: ({ children }: any) => <span>{children}</span>,
|
||||
}));
|
||||
|
||||
vi.mock('lucide-react', () => ({
|
||||
Search: (props: any) => <div data-testid="search-icon" {...props} />,
|
||||
ScanFace: (props: any) => <div data-testid="scanface-icon" {...props} />,
|
||||
Trash2: (props: any) => <div data-testid="trash-icon" {...props} />,
|
||||
}));
|
||||
|
||||
vi.mock('@/components/GroupedHistoryView', () => ({
|
||||
default: () => <div data-testid="grouped-history-view" />,
|
||||
}));
|
||||
|
||||
vi.mock('@/lib', () => ({
|
||||
replayProject: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('SearchHistoryDialog', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
(window as any).ipcRenderer = {
|
||||
invoke: vi.fn().mockResolvedValue(undefined),
|
||||
};
|
||||
});
|
||||
|
||||
it('deletes a history item from list view', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<SearchHistoryDialog />);
|
||||
|
||||
// Open dialog
|
||||
await user.click(screen.getByRole('button'));
|
||||
|
||||
// History item should appear
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('My history item')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
// Click delete button
|
||||
const deleteButton = screen.getByRole('button', { name: 'Delete history' });
|
||||
await user.click(deleteButton);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(proxyFetchDeleteMock).toHaveBeenCalledWith('/api/chat/history/1');
|
||||
});
|
||||
|
||||
expect((window as any).ipcRenderer.invoke).toHaveBeenCalledWith(
|
||||
'delete-task-files',
|
||||
'test@example.com',
|
||||
'task-1',
|
||||
'project-1'
|
||||
);
|
||||
|
||||
await waitFor(() => {
|
||||
expect(screen.queryByText('My history item')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -1,84 +0,0 @@
|
|||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
loginByStackToken,
|
||||
loginByStackWithAutoCreate,
|
||||
} from '../../../src/service/stackAuthApi';
|
||||
|
||||
vi.mock('@/api/http', () => ({
|
||||
proxyFetchPost: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('stackAuthApi', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
});
|
||||
|
||||
it('falls back to signup when login returns user not found', async () => {
|
||||
const { proxyFetchPost } = await import('@/api/http');
|
||||
|
||||
vi.mocked(proxyFetchPost)
|
||||
.mockResolvedValueOnce({ code: 1, text: 'User not found' })
|
||||
.mockResolvedValueOnce({ code: 0, token: 't', email: 'e@example.com' });
|
||||
|
||||
const res = await loginByStackWithAutoCreate('stack-token');
|
||||
|
||||
expect(res.code).toBe(0);
|
||||
expect(vi.mocked(proxyFetchPost)).toHaveBeenCalledTimes(2);
|
||||
|
||||
const firstUrl = vi.mocked(proxyFetchPost).mock.calls[0][0] as string;
|
||||
const secondUrl = vi.mocked(proxyFetchPost).mock.calls[1][0] as string;
|
||||
|
||||
expect(firstUrl).toContain('/api/login-by_stack?');
|
||||
expect(firstUrl).toContain('type=login');
|
||||
expect(secondUrl).toContain('type=signup');
|
||||
});
|
||||
|
||||
it('does not fall back to signup for account/password error', async () => {
|
||||
const { proxyFetchPost } = await import('@/api/http');
|
||||
|
||||
vi.mocked(proxyFetchPost).mockResolvedValueOnce({
|
||||
code: 10,
|
||||
text: 'Account or password error',
|
||||
});
|
||||
|
||||
const res = await loginByStackWithAutoCreate('stack-token');
|
||||
|
||||
expect(res.code).toBe(10);
|
||||
expect(res.text).toBe('Account or password error');
|
||||
expect(vi.mocked(proxyFetchPost)).toHaveBeenCalledTimes(1);
|
||||
|
||||
const firstUrl = vi.mocked(proxyFetchPost).mock.calls[0][0] as string;
|
||||
expect(firstUrl).toContain('/api/login-by_stack?');
|
||||
expect(firstUrl).toContain('type=login');
|
||||
});
|
||||
|
||||
it('includes invite_code in query when provided', async () => {
|
||||
const { proxyFetchPost } = await import('@/api/http');
|
||||
|
||||
vi.mocked(proxyFetchPost).mockResolvedValueOnce({ code: 0 });
|
||||
|
||||
await loginByStackToken({
|
||||
token: 'stack-token',
|
||||
type: 'signup',
|
||||
inviteCode: 'INV123',
|
||||
});
|
||||
|
||||
const url = vi.mocked(proxyFetchPost).mock.calls[0][0] as string;
|
||||
expect(url).toContain('invite_code=INV123');
|
||||
});
|
||||
});
|
||||
|
|
@ -45,7 +45,9 @@ export default defineConfig(({ command, mode }) => {
|
|||
entry: 'electron/main/index.ts',
|
||||
onstart(args) {
|
||||
if (process.env.VSCODE_DEBUG) {
|
||||
console.log('[startup] Electron App');
|
||||
console.log(
|
||||
/* For `.vscode/.debug.script.mjs` */ '[startup] Electron App'
|
||||
);
|
||||
} else {
|
||||
args.startup();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue