From dacb172661c4a632bbe62ff4f88385f1240c0fa5 Mon Sep 17 00:00:00 2001 From: hanafish <1106510024@qq.com> Date: Fri, 24 Jul 2026 23:05:52 +0800 Subject: [PATCH] refactor(auth): retire legacy login modal --- src/api/http/auth/index.ts | 15 -- src/api/http/auth/login.ts | 138 ---------- src/modules/shared/layouts/GlobalModals.tsx | 19 +- .../ModalSystem/component-modal-0129.md | 2 - .../Login/component-login-modal-1225.md | 199 --------------- .../ModalSystem/variants/Login/index.tsx | 236 ------------------ src/scaffold/ModalSystem/variants/index.ts | 1 - src/store/ui/uiAtom.ts | 8 - src/types/core/user.ts | 7 - 9 files changed, 5 insertions(+), 620 deletions(-) delete mode 100644 src/api/http/auth/login.ts delete mode 100644 src/scaffold/ModalSystem/variants/Login/component-login-modal-1225.md delete mode 100644 src/scaffold/ModalSystem/variants/Login/index.tsx diff --git a/src/api/http/auth/index.ts b/src/api/http/auth/index.ts index 99fa79309..0e19caca0 100644 --- a/src/api/http/auth/index.ts +++ b/src/api/http/auth/index.ts @@ -1,16 +1,3 @@ -export { - getLoginUrl, - completeLogin, - getCurrentUserInfo, - getUserInfoByAuthingId, - deleteGitHubAccount, - deleteGitLabAccount, - addGitHubClassicToken, - addGitLabClassicToken, - setUserAPIKey, - authApi, -} from "./login"; - export { getSupabaseAuthClient, signInWithSupabase, @@ -31,5 +18,3 @@ export { type AuthState, type TokenResponse as SecureTokenResponse, } from "./secure"; - -export { default } from "./login"; diff --git a/src/api/http/auth/login.ts b/src/api/http/auth/login.ts deleted file mode 100644 index dfee958c4..000000000 --- a/src/api/http/auth/login.ts +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Authentication API Endpoints - * - * Handles user authentication, authorization, and profile management. - * - * Base URLs: - * - /api/v2/login - Authentication endpoints - * - /api/v2/user - User profile endpoints - */ -import { ISetUserAPIKeyParam, IUserInfo } from "@src/types/core/user"; - -import { getApi, postApi } from "../client"; - -// ============================================ -// Authentication Endpoints -// ============================================ - -const LOGIN_URL = "/api/v2/login"; -const USER_URL = "/api/v2/user"; - -/** - * Get login URL for OAuth flow - */ -export async function getLoginUrl() { - return getApi<{ url: string }>(LOGIN_URL + "/url"); -} - -/** - * Complete login with OAuth code - */ -export async function completeLogin(params: { code: string }) { - return postApi<{ - user: IUserInfo; - id_token: string; - }>(LOGIN_URL + "/complete", params); -} - -// ============================================ -// User Profile Endpoints -// ============================================ - -/** - * Get current user info - */ -export async function getCurrentUserInfo() { - return getApi<{ - user_public: IUserInfo; - github_token: string; - access_token: string; - }>(USER_URL + "/me", {}, true); -} - -/** - * Get user info by Authing ID - */ -export async function getUserInfoByAuthingId(params: { authing_id: string }) { - return getApi( - USER_URL + "/get_user_info_by_authing_id", - params, - true - ); -} - -// ============================================ -// Git Account Management -// ============================================ - -/** - * Delete GitHub account - */ -export async function deleteGitHubAccount(params: { - user_id: string; - github_info_uuid: string; -}) { - return postApi(USER_URL + "/delete_github_info", params, true); -} - -/** - * Delete GitLab account - */ -export async function deleteGitLabAccount(params: { - user_id: string; - gitlab_info_uuid: string; -}) { - return postApi(USER_URL + "/delete_gitlab_info", params, true); -} - -/** - * Add GitHub classic token - */ -export async function addGitHubClassicToken(params: { - user_id: string; - user_name: string; - token: string; -}) { - return postApi(USER_URL + "/add_classic_token", params, true); -} - -/** - * Add GitLab classic token - */ -export async function addGitLabClassicToken(params: { - user_id: string; - user_name: string; - token: string; -}) { - return postApi(USER_URL + "/add_classic_gitlab_token", params, true); -} - -/** - * Set user API key configuration - */ -export async function setUserAPIKey(params: ISetUserAPIKeyParam) { - return postApi(USER_URL + "/set_user_config", params, true); -} - -// ============================================ -// Exports -// ============================================ - -export const authApi = { - // Authentication - getLoginUrl, - completeLogin, - - // User profile - getCurrentUserInfo, - getUserInfoByAuthingId, - - // Git account management - deleteGitHubAccount, - deleteGitLabAccount, - addGitHubClassicToken, - addGitLabClassicToken, - setUserAPIKey, -}; - -export default authApi; diff --git a/src/modules/shared/layouts/GlobalModals.tsx b/src/modules/shared/layouts/GlobalModals.tsx index 5d18f60c6..59df2fd88 100644 --- a/src/modules/shared/layouts/GlobalModals.tsx +++ b/src/modules/shared/layouts/GlobalModals.tsx @@ -8,11 +8,7 @@ import { useAtomValue } from "jotai"; import React, { Suspense, useEffect, useState } from "react"; import { componentIssueModalOpenAtom } from "@src/store/ui/overlayAtom"; -import { loginModalVisibleAtom } from "@src/store/ui/uiAtom"; -const LoginModal = React.lazy( - () => import("@/src/scaffold/ModalSystem/variants/Login") -); const ComponentIssueModalProvider = React.lazy(() => import("@src/modules/shared/DevTools/ComponentIssueModal").then((module) => ({ default: module.ComponentIssueModalProvider, @@ -43,13 +39,8 @@ const ComponentIssueModalLoader: React.FC = () => { return ; }; -export const GlobalModals: React.FC = () => { - const loginModalVisible = useAtomValue(loginModalVisibleAtom); - - return ( - - {loginModalVisible && } - - - ); -}; +export const GlobalModals: React.FC = () => ( + + + +); diff --git a/src/scaffold/ModalSystem/component-modal-0129.md b/src/scaffold/ModalSystem/component-modal-0129.md index 01af23db6..6a83110d2 100644 --- a/src/scaffold/ModalSystem/component-modal-0129.md +++ b/src/scaffold/ModalSystem/component-modal-0129.md @@ -49,7 +49,6 @@ Used extensively throughout the app for modals: - `src/components/DeleteModal/index.tsx` - Used in delete modal - `src/components/CreateBranchModal/index.tsx` - Used in create branch modal -- `src/components/LoginModal/index.tsx` - Used in login modal - `src/components/GlobalModal/component/FormModal.tsx` - Used in form modal - And many other components @@ -363,7 +362,6 @@ interface ConfirmModalConfig { The ModalSystem includes several specialized modal variants: - `variants/QuickUpload/` - File upload modal -- `variants/Login/` - Login modal - `variants/Rename/` - Rename modal - `variants/AddFunds/` - Add funds modal - `variants/ContentView/` - Content viewing modal diff --git a/src/scaffold/ModalSystem/variants/Login/component-login-modal-1225.md b/src/scaffold/ModalSystem/variants/Login/component-login-modal-1225.md deleted file mode 100644 index 949485090..000000000 --- a/src/scaffold/ModalSystem/variants/Login/component-login-modal-1225.md +++ /dev/null @@ -1,199 +0,0 @@ -# LoginModal Component - -**Status**: ✅ Complete -**Location**: `src/components/LoginModal/` -**Last Updated**: December 25, 2025 - ---- - -## Overview - -The LoginModal component provides a login modal interface with OAuth authentication support. It handles authentication flow, user profile creation, and login state management. Supports both Tauri desktop app (with OAuth server) and browser (with redirect) authentication flows. - -### Key Features - -- ✅ **OAuth Authentication**: OAuth authentication flow via Authing -- ✅ **Tauri Support**: Tauri desktop app support with OAuth server -- ✅ **Browser Support**: Browser fallback with redirect -- ✅ **User Profile**: User profile creation and management -- ✅ **Login State**: Login state management via Jotai atoms -- ✅ **Loading State**: Loading state during authentication -- ✅ **Modal Control**: Modal visibility and fix state control - ---- - -## Functions - -The LoginModal component serves as: - -1. **Authentication**: Handle user authentication via OAuth -2. **Login Interface**: Provide login UI with branding -3. **OAuth Flow**: Manage OAuth authentication flow (Tauri or browser) -4. **User Management**: Create and manage user profiles after login - ---- - -## Where It's Used - -### Primary Usage - -- `src/page/Orgii/index.tsx` - Used in main layout (via GlobalModals) -- `src/page/Payment/Prices/index.tsx` - Used in pricing page - -```tsx -// Used in main app -import LoginModal from "@src/components/LoginModal"; - -// Modal visibility controlled by loginModalVisibleAtom -; -``` - ---- - -## How to Use - -### Basic Usage - -The component uses Jotai atoms for state management, so no props are needed: - -```tsx -import { useSetAtom } from "jotai"; - -import LoginModal from "@src/components/LoginModal"; -import { loginModalVisibleAtom } from "@src/store/allAtom"; - -function MyComponent() { - const setLoginVisible = useSetAtom(loginModalVisibleAtom); - - return ( - <> - - - - ); -} -``` - ---- - -## API Reference - -### Component Props - -No props required. Component reads from Jotai atoms: - -- `loginModalVisibleAtom` - Controls modal visibility -- `loginModalFixAtom` - Controls if modal can be closed -- `userAtom` - User state - -### Atoms - -**loginModalVisibleAtom:** - -- Type: `Atom` -- Controls: Modal visibility - -**loginModalFixAtom:** - -- Type: `Atom` -- Controls: Whether modal can be closed (if true, modal cannot be closed) - -**userAtom:** - -- Type: `Atom` -- Stores: Current user information - ---- - -## Implementation Details - -### OAuth Flow - -**Tauri Desktop:** - -1. Starts OAuth server on port 54031 -2. Opens login URL in system browser -3. Listens for OAuth callback URL -4. Extracts authorization code from callback -5. Completes login with code -6. Stops OAuth server - -**Browser:** - -1. Gets login URL from API -2. Redirects window to login URL -3. User completes login on Authing -4. Redirects back to app - -### Authentication Process - -1. **Get Login URL**: Calls `getLoginUrl()` API -2. **Start OAuth**: - - Tauri: Starts OAuth server and opens URL - - Browser: Redirects to URL -3. **Handle Callback**: - - Extracts `code` from callback URL - - Calls `completeLogin({ code })` -4. **Get User Info**: Calls `getCurrentUserInfo()` API -5. **Create Profile**: Calls `createUserProfile()` with user data -6. **Update State**: Updates `userAtom` with user information -7. **Close Modal**: Closes modal after successful login - -### Login State Management - -- **Session Storage**: Uses `sessionStorage` to track `login_in_progress` flag -- **Prevents Duplicates**: Skips duplicate login requests if already in progress -- **Cleanup**: Clears flag after login completes - -### Modal Behavior - -- **Visibility**: Controlled by `loginModalVisibleAtom` -- **Closable**: Can be prevented from closing via `loginModalFixAtom` -- **Mask Closable**: `maskClosable={!loginModalFix}` -- **ESC to Exit**: `escToExit={!loginModalFix}` -- **Size**: `h-[500px] w-[400px]` -- **Rounded**: `rounded-xl` - -### UI Elements - -- **Branding**: Atlas XP logo (`IconAtlasXp`) -- **Title**: "Unlock the power of Atlas XP" -- **Login Button**: - - Text: "Login in / Sign Up" - - Size: `h-8 w-[320px]` - - Loading state during authentication -- **Terms**: Links to Terms of Service and Privacy Policy - -### Error Handling - -- **OAuth Server**: Tries to stop existing server before starting new one -- **URL Parsing**: Handles errors when parsing callback URL -- **API Errors**: Logs errors but doesn't show user-facing error messages -- **Login In Progress**: Prevents duplicate login attempts - -### Dependencies - -- `@fabianlars/tauri-plugin-oauth` - OAuth plugin for Tauri -- `@tauri-apps/plugin-opener` - Open URLs in system browser -- `@src/api/loginApi` - getLoginUrl, completeLogin -- `@src/api/userApi` - getCurrentUserInfo, createUserProfile -- `@src/components/Modal` - Modal component -- `@src/components/Button` - Button component -- `@src/store/allAtom` - loginModalVisibleAtom, loginModalFixAtom -- `@src/store/userAtom` - userAtom -- `@src/util/tauri` - isTauriDesktop utility - ---- - -## Related Files - -- `src/components/LoginModal/index.tsx` - Main component implementation -- `src/components/LoginModal/index.scss` - Component styles -- `src/api/loginApi.ts` - Login API functions -- `src/api/userApi.ts` - User API functions -- `src/components/TitleBar/callback.template` - OAuth callback HTML template - ---- - -**Last Updated**: December 25, 2025 -**Status**: ✅ Production Ready diff --git a/src/scaffold/ModalSystem/variants/Login/index.tsx b/src/scaffold/ModalSystem/variants/Login/index.tsx deleted file mode 100644 index 8ec017bd6..000000000 --- a/src/scaffold/ModalSystem/variants/Login/index.tsx +++ /dev/null @@ -1,236 +0,0 @@ -import Button from "@/src/components/Button"; -import Modal from "@/src/scaffold/ModalSystem"; -import { cancel, onUrl, start } from "@fabianlars/tauri-plugin-oauth"; -import { openPath } from "@tauri-apps/plugin-opener"; -import { useAtom, useAtomValue } from "jotai"; -import { Sparkles } from "lucide-react"; -import { useState } from "react"; - -import { - completeLogin, - getCurrentUserInfo, - getLoginUrl, -} from "@src/api/http/auth/login"; -import { SERVICE_AUTH_STORAGE_KEYS } from "@src/config/serviceAuth"; -import { createLogger } from "@src/hooks/logger"; -import { loginModalFixAtom, loginModalVisibleAtom } from "@src/store"; -import { userAtom } from "@src/store/user"; -import type { IUserInfo } from "@src/types/core/user"; -import { isTauriDesktop } from "@src/util/platform/tauri"; - -const log = createLogger("Login"); - -// OAuth callback template for Tauri desktop -const callbackTemplate = ` - - - - - - - - - - My Notes - - - -

You can now open the app

- - -`; - -async function stopOAuthServer() { - try { - await cancel(54031); - } catch (error) { - log.error("Error stopping OAuth server:", error); - } -} - -function completeOAuthSignIn( - payload: string, - setUser: (user: IUserInfo) => void, - setVisible: (visible: boolean) => void -) { - const url = new URL(payload); - let code; - try { - code = new URLSearchParams(url.search).get("code"); - } catch (error) { - log.error("Error parsing URL for code:", error); - code = null; - } - if (!code) { - log.error("No code found in URL"); - return; - } - - // Check if login is already in progress to prevent duplicate requests - const loginInProgress = sessionStorage.getItem("login_in_progress"); - if (loginInProgress) { - return; - } - - // Mark login as in progress - sessionStorage.setItem("login_in_progress", "true"); - completeLogin({ code }) - .then(async (res) => { - if (res && res?.status !== 200) { - setUser(res.data.user); - // Legacy Authing SSO path (not used in OSS BYOK mode). When it does - // run, write the issued id_token into the same hosted_access_token - // slot that headers.ts/getOrRefreshHostedToken read so we don't keep - // a parallel `id_token` localStorage key around. - localStorage.setItem( - SERVICE_AUTH_STORAGE_KEYS.accessToken, - res.data.id_token - ); - localStorage.setItem("user_id", res.data.user.uuid); - // Notify localStorage listeners of the auth update - window.dispatchEvent(new Event("localStorageChange")); - - try { - const userInfoRes = await getCurrentUserInfo(); - if (userInfoRes && userInfoRes.status === 0) { - setUser(userInfoRes.data.user_public); - } - } catch (err) { - log.error("Failed to get current user info:", err); - } - - // Close the modal after all user info is updated - setTimeout(() => { - setVisible(false); - }, 300); - } - }) - .catch((err) => { - log.error(err); - }) - .finally(() => { - // Clear login in progress flag - sessionStorage.removeItem("login_in_progress"); - // Ensure modal stays closed after login attempt - setTimeout(() => {}, 500); - }); -} - -const openOAuthSignIn = async (loginUrl: string) => { - try { - await openPath(loginUrl); - } catch (error) { - throw new Error("Failed to open path: " + error); - } -}; - -async function startOAuthFlow( - login_url: string, - setUser: (user: IUserInfo) => void, - setVisible: (visible: boolean) => void -) { - try { - // Try to stop any existing OAuth server on this port before starting a new one - try { - await cancel(54031); - } catch (error) { - // It's okay if nothing was running - } - - await start({ - ports: [54031], - response: callbackTemplate, - }); - // Set up listeners for OAuth results - await onUrl((url) => { - completeOAuthSignIn(url, setUser, setVisible); - stopOAuthServer(); - }); - - openOAuthSignIn(login_url); - } catch (error) { - log.error("Error starting OAuth server:", error); - } -} - -const LoginModal = () => { - const [visible, setVisible] = useAtom(loginModalVisibleAtom); - const loginModalFix = useAtomValue(loginModalFixAtom); - const [_user, setUser] = useAtom(userAtom); - const [isLoading, setIsLoading] = useState(false); - return ( - setVisible(false)} - onCancel={loginModalFix ? () => {} : () => setVisible(false)} - maskClosable={!loginModalFix} - escToExit={!loginModalFix} - className="login__modal h-[500px] w-[400px] rounded-xl bg-bg-2" - > -
- -

- Unlock the power of Atlas XP -

- -

- By clicking any of the above buttons, you agree to the - -  ATLAS AI TOS  - {" "} - and - -  Privacy Policy - - . -

-
-
- ); -}; -export default LoginModal; diff --git a/src/scaffold/ModalSystem/variants/index.ts b/src/scaffold/ModalSystem/variants/index.ts index 054d01119..b6076c6fd 100644 --- a/src/scaffold/ModalSystem/variants/index.ts +++ b/src/scaffold/ModalSystem/variants/index.ts @@ -11,5 +11,4 @@ */ export { default as ContentViewModal } from "./ContentView"; -export { default as LoginModal } from "./Login"; export { default as RenameModal } from "./Rename"; diff --git a/src/store/ui/uiAtom.ts b/src/store/ui/uiAtom.ts index 003ac0491..ed1510a96 100644 --- a/src/store/ui/uiAtom.ts +++ b/src/store/ui/uiAtom.ts @@ -186,18 +186,10 @@ userDisplayNameAtom.debugLabel = "userDisplayNameAtom"; // Modal & Dialog State // ============================================ -/** Login modal visibility */ -export const loginModalVisibleAtom = atom(false); -loginModalVisibleAtom.debugLabel = "loginModalVisibleAtom"; - /** Route debug trigger — set to true by Cmd+0; resets to false after toast fires */ export const routeDebugModalOpenAtom = atom(false); routeDebugModalOpenAtom.debugLabel = "routeDebugModalOpenAtom"; -/** Login modal fixed position */ -export const loginModalFixAtom = atom(false); -loginModalFixAtom.debugLabel = "loginModalFixAtom"; - /** * Session expired state * When true, user will be blocked and redirected to login page diff --git a/src/types/core/user.ts b/src/types/core/user.ts index 10a500034..84ce5373e 100644 --- a/src/types/core/user.ts +++ b/src/types/core/user.ts @@ -9,13 +9,6 @@ // Account Types (from shared/user.ts) // ============================================ -export type ISetUserAPIKeyParam = { - user_id: string; - openai_api_key?: string; - deepseek_api_key?: string; - login_token?: string; -}; - export type IUserInfo = { uuid: string; name: string;