mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-28 13:32:23 +00:00
* refactor(core): integrate CoreStateProvider and streamline state management - Replaced UserProvider with CoreStateProvider in App component to centralize state management. - Updated various components to utilize the new CoreStateProvider for accessing session tokens and user data. - Refactored hooks and components to eliminate direct Redux store dependencies, enhancing modularity and maintainability. - Introduced a new core state management structure to improve the handling of user authentication and onboarding states. This refactor aims to simplify state access across the application and improve overall code clarity. * fix: restore polyfills import and clean up component imports - Reintroduced the import of polyfills in main.tsx to ensure compatibility. - Adjusted import statements in PrivacyPanel and SocketProvider for consistency. - Simplified conditional expressions in TeamInvitesPanel and TeamMembersPanel for better readability. - Refactored CoreStateProvider and store.ts to streamline state initialization. - Enhanced formatting in various files for improved code clarity and maintainability. * refactor(tests): streamline onboarding and protected route tests - Updated OnboardingOverlay tests to utilize mock state management and simplify assertions. - Refactored ProtectedRoute tests to improve readability and ensure consistent use of mock state. - Enhanced teamApi tests to replace direct API calls with core RPC method calls, improving test isolation and clarity. - Adjusted socketSelectors tests to utilize a centralized core state snapshot for better state management during tests. * refactor(onboarding): simplify onboarding state management and improve loading logic - Removed unnecessary local state for onboarding completion in OnboardingOverlay, directly utilizing snapshot data. - Enhanced loading logic to prevent unnecessary renders and improve user experience during onboarding. - Updated PrivacyPanel to handle analytics consent persistence with error handling. - Refactored TeamManagementPanel and TeamPanel to improve team data fetching logic and loading states. - Streamlined CoreStateProvider to manage session token synchronization and state updates more effectively. * chore(deps): update tempfile dependency and refactor app state management - Added `tempfile` dependency to Cargo.toml for improved temporary file handling. - Refactored app state loading and saving functions to enhance error handling and ensure data integrity. - Introduced quarantine mechanism for corrupted app state files to prevent application crashes. - Updated URL parsing logic to ensure proper formatting of API URLs. - Adjusted type definitions in schemas for better clarity and consistency. * refactor(tests): simplify mock state usage in onboarding and protected route tests - Consolidated mock state management in OnboardingOverlay and ProtectedRoute tests for improved readability. - Streamlined assertions by reducing unnecessary lines in test setups. - Enhanced consistency in mock return values across tests to ensure clarity and maintainability. * refactor(tests): enhance PublicRoute tests with mock state and routing - Updated PublicRoute tests to utilize MemoryRouter for improved routing simulation. - Simplified mock state management by integrating `useCoreState` for user authentication scenarios. - Streamlined test assertions and removed redundant preloaded state configurations for clarity and maintainability. * refactor(tests): streamline mock state usage in PublicRoute and Mnemonic tests - Simplified mock state management in PublicRoute and Mnemonic tests for improved readability. - Consolidated mock return values to reduce redundancy and enhance clarity in test setups. - Improved consistency in the usage of `useCoreState` across test files. * Refactor billing components for improved readability - Cleaned up import statements in BillingPanel.tsx for better organization. - Enhanced formatting of billing plan descriptions in billingHelpers.ts for consistency. - Improved readability of conditional checks in BillingPanel component. * Refactor API endpoint handling for clarity and consistency - Updated references from `/settings` to `/auth/me` in various modules to standardize user authentication flows. - Renamed `parse_settings_response_json` to `parse_api_response_json` for improved clarity in response handling. - Adjusted user ID extraction functions to reflect the new endpoint structure, enhancing maintainability across the codebase. - Updated test cases to align with the new endpoint naming conventions, ensuring consistency in API interactions.
235 lines
7.4 KiB
TypeScript
235 lines
7.4 KiB
TypeScript
import debug from 'debug';
|
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
|
|
import { useCoreState } from '../providers/CoreStateProvider';
|
|
import { tunnelsApi } from '../services/api/tunnelsApi';
|
|
import { getCoreHttpBaseUrl } from '../services/coreRpcClient';
|
|
import { useAppDispatch, useAppSelector } from '../store/hooks';
|
|
import {
|
|
addActivity,
|
|
addTunnel,
|
|
removeTunnel,
|
|
setError,
|
|
setLoading,
|
|
setRegistrations,
|
|
setTunnels,
|
|
type WebhookActivityEntry,
|
|
} from '../store/webhooksSlice';
|
|
import {
|
|
openhumanWebhooksListLogs,
|
|
openhumanWebhooksListRegistrations,
|
|
openhumanWebhooksRegisterEcho,
|
|
openhumanWebhooksUnregisterEcho,
|
|
type WebhookDebugLogEntry,
|
|
} from '../utils/tauriCommands';
|
|
|
|
const log = debug('webhooks');
|
|
|
|
/** Convert a debug log entry to an activity entry for the ring buffer. */
|
|
function logToActivity(entry: WebhookDebugLogEntry): WebhookActivityEntry {
|
|
return {
|
|
correlation_id: entry.correlation_id,
|
|
tunnel_name: entry.tunnel_name,
|
|
method: entry.method,
|
|
path: entry.path,
|
|
status_code: entry.status_code,
|
|
skill_id: entry.skill_id,
|
|
timestamp: entry.updated_at || entry.timestamp,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Hook for managing webhook tunnels, registrations, and live activity.
|
|
*
|
|
* - Fetches tunnels from the backend API (CRUD)
|
|
* - Fetches registrations + debug logs from the Rust core (via JSON-RPC)
|
|
* - Subscribes to SSE /events/webhooks for real-time activity updates
|
|
*/
|
|
export function useWebhooks() {
|
|
const { snapshot } = useCoreState();
|
|
const dispatch = useAppDispatch();
|
|
const { tunnels, registrations, activity, loading, error } = useAppSelector(
|
|
state => state.webhooks
|
|
);
|
|
const token = snapshot.sessionToken;
|
|
const [coreConnected, setCoreConnected] = useState(false);
|
|
const eventSourceRef = useRef<EventSource | null>(null);
|
|
|
|
// ── Load registrations + logs from core RPC ──────────────────────────────
|
|
const loadCoreData = useCallback(async () => {
|
|
try {
|
|
const [regsResponse, logsResponse] = await Promise.all([
|
|
openhumanWebhooksListRegistrations(),
|
|
openhumanWebhooksListLogs(100),
|
|
]);
|
|
dispatch(setRegistrations(regsResponse.result.result.registrations));
|
|
|
|
// Seed activity from debug logs
|
|
const logs = logsResponse.result.result.logs;
|
|
for (const entry of logs.reverse()) {
|
|
dispatch(addActivity(logToActivity(entry)));
|
|
}
|
|
log(
|
|
'Loaded %d registrations, %d logs from core',
|
|
regsResponse.result.result.registrations.length,
|
|
logs.length
|
|
);
|
|
} catch (err) {
|
|
log(
|
|
'Core RPC not available (registrations/logs): %s',
|
|
err instanceof Error ? err.message : err
|
|
);
|
|
}
|
|
}, [dispatch]);
|
|
|
|
// ── Fetch tunnels from backend API ───────────────────────────────────────
|
|
const fetchTunnels = useCallback(async () => {
|
|
dispatch(setLoading(true));
|
|
try {
|
|
const data = await tunnelsApi.getTunnels();
|
|
dispatch(setTunnels(data));
|
|
log('Fetched %d tunnels', data.length);
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : 'Failed to fetch tunnels';
|
|
dispatch(setError(msg));
|
|
log('Error fetching tunnels: %s', msg);
|
|
}
|
|
}, [dispatch]);
|
|
|
|
// ── Subscribe to SSE for real-time webhook events ────────────────────────
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
|
|
const connect = async () => {
|
|
try {
|
|
const baseUrl = await getCoreHttpBaseUrl();
|
|
if (cancelled) return;
|
|
|
|
const es = new EventSource(`${baseUrl}/events/webhooks`);
|
|
eventSourceRef.current = es;
|
|
|
|
es.addEventListener('webhooks_debug', () => {
|
|
setCoreConnected(true);
|
|
// Reload registrations + logs on any debug event (registration change, new log, etc.)
|
|
void loadCoreData();
|
|
});
|
|
|
|
es.onopen = () => {
|
|
setCoreConnected(true);
|
|
log('SSE connected to /events/webhooks');
|
|
};
|
|
|
|
es.onerror = () => {
|
|
setCoreConnected(false);
|
|
};
|
|
} catch {
|
|
setCoreConnected(false);
|
|
}
|
|
};
|
|
|
|
void connect();
|
|
|
|
return () => {
|
|
cancelled = true;
|
|
if (eventSourceRef.current) {
|
|
eventSourceRef.current.close();
|
|
eventSourceRef.current = null;
|
|
}
|
|
setCoreConnected(false);
|
|
};
|
|
}, [loadCoreData]);
|
|
|
|
// ── Initial data load ────────────────────────────────────────────────────
|
|
useEffect(() => {
|
|
if (!token) return;
|
|
void fetchTunnels();
|
|
void loadCoreData();
|
|
}, [token, fetchTunnels, loadCoreData]);
|
|
|
|
// ── CRUD actions ─────────────────────────────────────────────────────────
|
|
const createTunnel = useCallback(
|
|
async (name: string, description?: string) => {
|
|
try {
|
|
const tunnel = await tunnelsApi.createTunnel({ name, description });
|
|
dispatch(addTunnel(tunnel));
|
|
log('Created tunnel: %s (%s)', tunnel.name, tunnel.uuid);
|
|
return tunnel;
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : 'Failed to create tunnel';
|
|
dispatch(setError(msg));
|
|
throw err;
|
|
}
|
|
},
|
|
[dispatch]
|
|
);
|
|
|
|
const deleteTunnel = useCallback(
|
|
async (id: string) => {
|
|
try {
|
|
await tunnelsApi.deleteTunnel(id);
|
|
dispatch(removeTunnel(id));
|
|
log('Deleted tunnel: %s', id);
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : 'Failed to delete tunnel';
|
|
dispatch(setError(msg));
|
|
throw err;
|
|
}
|
|
},
|
|
[dispatch]
|
|
);
|
|
|
|
const refreshTunnels = useCallback(async () => {
|
|
await fetchTunnels();
|
|
await loadCoreData();
|
|
}, [fetchTunnels, loadCoreData]);
|
|
|
|
// ── Echo registration ────────────────────────────────────────────────────
|
|
const registerEcho = useCallback(
|
|
async (tunnelUuid: string, tunnelName?: string, backendTunnelId?: string) => {
|
|
try {
|
|
const response = await openhumanWebhooksRegisterEcho(
|
|
tunnelUuid,
|
|
tunnelName,
|
|
backendTunnelId
|
|
);
|
|
dispatch(setRegistrations(response.result.result.registrations));
|
|
log('Registered echo for tunnel %s', tunnelUuid);
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : 'Failed to register echo';
|
|
dispatch(setError(msg));
|
|
throw err;
|
|
}
|
|
},
|
|
[dispatch]
|
|
);
|
|
|
|
const unregisterEcho = useCallback(
|
|
async (tunnelUuid: string) => {
|
|
try {
|
|
const response = await openhumanWebhooksUnregisterEcho(tunnelUuid);
|
|
dispatch(setRegistrations(response.result.result.registrations));
|
|
log('Unregistered echo for tunnel %s', tunnelUuid);
|
|
} catch (err) {
|
|
const msg = err instanceof Error ? err.message : 'Failed to unregister echo';
|
|
dispatch(setError(msg));
|
|
throw err;
|
|
}
|
|
},
|
|
[dispatch]
|
|
);
|
|
|
|
return {
|
|
tunnels,
|
|
registrations,
|
|
activity,
|
|
loading,
|
|
error,
|
|
coreConnected,
|
|
createTunnel,
|
|
deleteTunnel,
|
|
refreshTunnels,
|
|
registerEcho,
|
|
unregisterEcho,
|
|
};
|
|
}
|