Files
openhuman/docs/src/03-services.md
T
Mega MindandGitHub c3718d9e4f Feat/final flow check (#155)
* feat: add mnemonic recovery flow and encryption key management

- Introduced a new Mnemonic page for users to generate or import their recovery phrase.
- Updated routing to redirect users to the Mnemonic page after onboarding.
- Enhanced state management to store and retrieve AES encryption keys derived from the mnemonic.
- Added utility functions for generating and validating BIP39 mnemonic phrases.
- Updated selectors and reducers to handle encryption key state in the auth slice.
- Included new dependencies for cryptographic operations.

* feat: integrate Web3 wallet functionality and enhance state management

- Added WalletInfoSection component to display connected wallet address, network, and balance.
- Updated auth slice to manage primary wallet address derived from mnemonic.
- Enhanced SkillManager to pass wallet address as load parameters for wallet skill.
- Introduced utility function to derive EVM wallet address from mnemonic.
- Updated dependencies for cryptographic operations related to wallet functionality.

* fix: improve error handling and network selection in WalletInfoSection

- Enhanced the parsing of network data to ensure robustness against undefined values.
- Updated network selection logic to prioritize valid entries and provide a fallback.
- Improved error logging for better debugging of wallet information loading issues.
- Adjusted balance retrieval to handle cases where chain_id may be missing.

* feat: add socket event contract for Tauri integration

- Documented the socket event contract for communication between the backend and frontend in Tauri mode.
- Introduced constants for `REQUEST_ENCRYPTION_KEY` and `ENCRYPTION_KEY_EVENT` to facilitate encryption key management.
- Updated `setupTauriSocketListeners` to handle the encryption key request and response flow.

* Refactor wallet information fetching and connection status handling

- Updated `WalletInfoSection` to improve loading state management and error handling during wallet info retrieval.
- Introduced a retry mechanism for fetching wallet information with a maximum of 5 attempts.
- Enhanced connection status logic in `SkillsGrid` to treat missing authentication status as connected.
- Added a new method in `SkillManager` to set the wallet address and notify the wallet skill accordingly.

* feat: add integration token fetching and decryption functionality

- Introduced `fetchIntegrationTokens` API call to retrieve encrypted OAuth tokens for integrations.
- Implemented decryption logic for integration tokens using a provided key.
- Enhanced deep link handling to support fetching and storing integration tokens upon successful OAuth login.
- Added utility functions for hex and base64 conversions to facilitate token decryption.
- Updated `authApi.ts` and `desktopDeepLinkListener.ts` to integrate new functionality.

* refactor: move integration token encryption/decryption logic to a new utility file

- Extracted encryption and decryption functions for integration tokens into `integrationTokensCrypto.ts`.
- Removed redundant functions from `desktopDeepLinkListener.ts` to streamline the code.
- Updated deep link handling to store only the encrypted tokens instead of decrypted ones.
- Improved type definitions for integration tokens and their payloads.

* feat: enhance Gmail skill integration and state management

- Updated `SkillManager` to accept an optional access token for Gmail during OAuth completion.
- Introduced `gmailSlice` to manage Gmail user profile and emails in the Redux store.
- Implemented synchronization of Gmail skill state with the Redux store to keep user data updated.
- Refactored `SkillProvider` to handle Gmail state changes and fetch initial state from the backend.
- Adjusted deep link handling to pass decrypted access tokens for Gmail integration.
- Modified API endpoint for onboarding completion to remove the Telegram prefix.

* feat: implement Gmail metadata synchronization to backend

- Added a new utility function `syncGmailMetadataToBackend` to emit Gmail profile and email summaries to the backend via the `integration:metadata-sync` socket event.
- Updated `SkillProvider` to call the new synchronization function, ensuring Gmail skill state is sent to the backend when updated.

* refactor: update Tauri socket event handling and improve Mnemonic component typing

- Revised the Tauri socket event contract to clarify that encryption key handling is managed via the API instead of the socket.
- Removed outdated socket event constants related to encryption key requests from `tauriSocket.ts`.
- Enhanced type definitions in the `Mnemonic` component by specifying the event type for keyboard events.

* fix: update Gmail provider constant and adjust metadata synchronization logic

- Changed the constant for the Google provider from 'google' to 'gmail' for clarity.
- Commented out the email metadata synchronization logic in `syncGmailMetadataToBackend` to prevent potential issues with undefined email states.

* feat: implement Gmail metadata synchronization service

- Added a new file `metadataSync.ts` to handle the synchronization of Gmail profile and email summaries to the backend via the `integration:metadata-sync` socket event.
- Updated import paths in `SkillProvider` to reflect the new location of the Gmail metadata synchronization function.
- Enhanced type definitions for Gmail profile and email summaries to ensure proper data structure during synchronization.

* refactor: clean up code formatting and improve readability across multiple components

- Standardized import statements and formatting in various files, including `WalletInfoSection`, `AgentChatPanel`, and `SkillsPanel`.
- Enhanced readability by adjusting line breaks and spacing in JSX elements and function definitions.
- Improved consistency in the use of arrow functions and destructuring across components.
- Updated type definitions and ensured proper alignment of code for better maintainability.

* feat: enhance error handling in WalletInfoSection and improve user feedback

- Added error reporting functionality in `WalletInfoSection` to capture and enqueue errors when wallet info fails to load.
- Updated `Mnemonic` component to handle user state more robustly, ensuring encryption key is only set if the user is loaded.
- Refactored `TauriCommandsPanel` to improve string comparisons for loading states by converting to lowercase.
- Adjusted deep link handling in `desktopDeepLinkListener` to use trimmed hex values for encryption key conversion.

* fix: improve balance parsing and error handling in WalletInfoSection

- Updated balance parsing logic in `WalletInfoSection` to ensure that non-finite values default to zero.
- Enhanced error handling in `desktopDeepLinkListener` by adding error reporting for invalid encryption keys and conversion failures, improving user feedback and debugging capabilities.

* Merge remote-tracking branch 'upstream/develop' into feat/final-flow-check

* chore: update submodule URL for skills repository to use HTTPS

* chore: update skills submodule to latest commit and enhance SkillsGrid component

- Updated the skills submodule to the latest commit for improved functionality.
- Refactored `SkillsGrid` to better handle skill setup logic, ensuring accurate representation of skills with setup requirements.
- Cleaned up code formatting and improved readability in various components, including `SkillActionButton` and `desktopDeepLinkListener`.

* feat: implement Notion metadata synchronization and user profile management

- Added a new service `metadataSync.ts` to handle synchronization of Notion user metadata to the backend via the `integration:metadata-sync` socket event.
- Introduced a new Redux slice `notionSlice.ts` for managing the Notion user profile state.
- Updated `SkillProvider` to fetch and sync Notion user profile upon connection, ensuring seamless integration with the backend.
- Enhanced the store configuration to include the new Notion reducer.

* refactor: remove unnecessary console warnings in SkillProvider

- Eliminated console warnings related to Notion user retrieval errors and auto-loading skills, streamlining the logging process for better clarity and reducing noise in the console output.
- Improved code readability by cleaning up commented-out warning messages that were no longer needed.
2026-03-04 14:33:17 +05:30

7.2 KiB

Services Layer

The application uses singleton services for external communication. This prevents connection leaks and provides consistent API access.

Service Architecture

Services Layer
  ├─ apiClient (HTTP REST)
  │   ├─ reads auth.token from Redux
  │   └─ makes requests to BACKEND_URL
  ├─ socketService (Socket.io)
  │   ├─ manages real-time connection
  │   └─ emits/listens for MCP messages
  └─ mtprotoService (Telegram)
      ├─ manages TelegramClient
      └─ stores session in Redux

API Client (services/apiClient.ts)

HTTP REST client for backend communication.

Features

  • Fetch-based implementation
  • Auto-injects JWT from Redux store
  • Typed request/response handling
  • Error handling with typed errors

Usage

import apiClient from '../services/apiClient';

// GET request
const user = await apiClient.get<User>('/users/me');

// POST request
const result = await apiClient.post<LoginResponse>('/auth/login', { email, password });

// With custom headers
const data = await apiClient.get<Data>('/endpoint', { headers: { 'X-Custom': 'value' } });

Configuration

Reads VITE_BACKEND_URL from environment or uses default:

const BACKEND_URL = import.meta.env.VITE_BACKEND_URL || 'https://api.example.com';

API Endpoints (services/api/)

Auth API (services/api/authApi.ts)

Authentication-related endpoints.

import { authApi } from '../services/api/authApi';

// Login
const { token, user } = await authApi.login(credentials);

// Token exchange (for deep link flow)
const { sessionToken, user } = await authApi.exchangeToken(loginToken);

// Logout
await authApi.logout();

User API (services/api/userApi.ts)

User profile endpoints.

import { userApi } from '../services/api/userApi';

// Get current user
const user = await userApi.getCurrentUser();

// Update profile
const updated = await userApi.updateProfile({ firstName, lastName });

// Get settings
const settings = await userApi.getSettings();

Socket Service (services/socketService.ts)

Socket.io client singleton for real-time communication.

Features

  • Singleton pattern - single connection per app
  • Auth token passed in socket auth object
  • Transports: polling first, then WebSocket upgrade
  • Auto-reconnection handling

API

import socketService from '../services/socketService';

// Connect with auth token
socketService.connect(token);

// Disconnect
socketService.disconnect();

// Emit event
socketService.emit('event-name', data);

// Listen for events
socketService.on('event-name', data => {
  // Handle event
});

// Remove listener
socketService.off('event-name', handler);

// One-time listener
socketService.once('event-name', data => {
  // Handle once
});

// Get socket instance
const socket = socketService.getSocket();

// Check connection status
const isConnected = socketService.isConnected();

Connection Flow

// In SocketProvider.tsx
useEffect(() => {
  if (token) {
    socketService.connect(token);

    socketService.on('connect', () => {
      dispatch(setSocketStatus({ userId, status: 'connected' }));
      dispatch(setSocketId({ userId, socketId: socket.id }));
      // Initialize MCP server
      initMCPServer(socketService.getSocket());
    });

    socketService.on('disconnect', () => {
      dispatch(setSocketStatus({ userId, status: 'disconnected' }));
    });
  }

  return () => {
    socketService.disconnect();
  };
}, [token]);

Configuration

const socket = io(BACKEND_URL, {
  auth: { token },
  transports: ['polling', 'websocket'],
  reconnection: true,
  reconnectionAttempts: 5,
  reconnectionDelay: 1000,
});

Socket event contract (Tauri)

In Tauri mode, the Rust socket forwards server events to the frontend via the server:event Tauri event. The frontend listens and can emit back via emitViaRustSocket (see utils/tauriSocket.ts). Encryption key is handled via the API, not the socket.

MTProto Service (services/mtprotoService.ts)

Telegram MTProto client singleton.

Features

  • Singleton pattern - one client per user
  • Session persistence via Redux (not localStorage)
  • Auto-retry for FLOOD_WAIT up to 60s
  • Supports QR login and phone auth

Initialization

import mtprotoService from '../services/mtprotoService';

// Get or create instance for user
const client = await mtprotoService.getInstance().initialize(userId);

// Set session string (from Redux)
await client.setSession(sessionString);

// Connect to Telegram
await client.connect();

Session Management

// Session is stored in Redux, not localStorage
// In TelegramProvider:
const sessionString = useAppSelector(state => state.telegram.byUser[userId]?.sessionString);

// When session updates
useEffect(() => {
  if (client && sessionString) {
    client.setSession(sessionString);
  }
}, [sessionString]);

// Save session after auth
const newSession = await client.getSession();
dispatch(setSessionString({ userId, sessionString: newSession }));

API Operations

// Get current user
const me = await client.getMe();

// Get dialogs (chats)
const dialogs = await client.getDialogs({ limit: 20 });

// Send message
await client.sendMessage(peer, { message: 'Hello!' });

// Get history
const messages = await client.getMessages(peer, { limit: 50 });

Error Handling

try {
  await client.connect();
} catch (error) {
  if (error.message.includes('FLOOD_WAIT')) {
    const seconds = parseInt(error.message.match(/\d+/)?.[0] || '60');
    if (seconds <= 60) {
      // Auto-retry after wait
      await new Promise(r => setTimeout(r, seconds * 1000));
      await client.connect();
    }
  }
  throw error;
}

Service Integration with Providers

SocketProvider

// providers/SocketProvider.tsx
export function SocketProvider({ children }) {
  const token = useAppSelector((state) => state.auth.token);

  useEffect(() => {
    if (token) {
      socketService.connect(token);
      // On connect, initialize MCP
    }
    return () => socketService.disconnect();
  }, [token]);

  return <SocketContext.Provider value={...}>{children}</SocketContext.Provider>;
}

TelegramProvider

// providers/TelegramProvider.tsx
export function TelegramProvider({ children }) {
  const dispatch = useAppDispatch();
  const userId = useAppSelector((state) => state.user.profile?.id);

  useEffect(() => {
    if (userId) {
      // Parallel init + connect for faster startup
      Promise.all([
        dispatch(initializeTelegram(userId)),
        dispatch(connectTelegram(userId))
      ]);
    }
  }, [userId]);

  return <TelegramContext.Provider value={...}>{children}</TelegramContext.Provider>;
}

Best Practices

  1. Use singletons - Never create multiple service instances
  2. Store sessions in Redux - Not localStorage
  3. Clean up on unmount - Disconnect in useEffect cleanup
  4. Handle errors gracefully - Retry for transient failures
  5. Pass auth via proper channels - Socket auth object, not query string

Previous: State Management | Next: MCP System