mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
fix(welcome): include bearer token in core RPC test-connection probe (#1301)
This commit is contained in:
@@ -3,7 +3,7 @@ import { useState } from 'react';
|
||||
import OAuthProviderButton from '../components/oauth/OAuthProviderButton';
|
||||
import { oauthProviderConfigs } from '../components/oauth/providerConfigs';
|
||||
import RotatingTetrahedronCanvas from '../components/RotatingTetrahedronCanvas';
|
||||
import { clearCoreRpcUrlCache } from '../services/coreRpcClient';
|
||||
import { clearCoreRpcUrlCache, testCoreRpcConnection } from '../services/coreRpcClient';
|
||||
import { useDeepLinkAuthState } from '../store/deepLinkAuthState';
|
||||
import {
|
||||
clearStoredRpcUrl,
|
||||
@@ -65,11 +65,7 @@ const Welcome = () => {
|
||||
setRpcUrlError(null);
|
||||
|
||||
try {
|
||||
const response = await fetch(normalized, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'openhuman.ping', params: {} }),
|
||||
});
|
||||
const response = await testCoreRpcConnection(normalized);
|
||||
|
||||
if (response.ok || response.status === 405) {
|
||||
setSaveSuccess(true);
|
||||
|
||||
@@ -421,4 +421,76 @@ describe('coreRpcClient', () => {
|
||||
expect(tokenCalls).toBe(1);
|
||||
expect(fetch).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe('testCoreRpcConnection', () => {
|
||||
test('POSTs an openhuman.ping JSON-RPC envelope to the supplied URL', async () => {
|
||||
vi.resetModules();
|
||||
vi.mocked(isTauri).mockReturnValue(false);
|
||||
const { testCoreRpcConnection } = await import('../coreRpcClient');
|
||||
const fetchMock = vi.mocked(fetch);
|
||||
fetchMock.mockResolvedValueOnce({ ok: true, status: 200 } as Response);
|
||||
|
||||
await testCoreRpcConnection('http://example.test:7788/rpc');
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
const [url, init] = fetchMock.mock.calls[0];
|
||||
expect(url).toBe('http://example.test:7788/rpc');
|
||||
const requestInit = init as RequestInit;
|
||||
expect(requestInit.method).toBe('POST');
|
||||
expect(JSON.parse(requestInit.body as string)).toMatchObject({
|
||||
jsonrpc: '2.0',
|
||||
id: 1,
|
||||
method: 'openhuman.ping',
|
||||
params: {},
|
||||
});
|
||||
});
|
||||
|
||||
test('omits Authorization header when no bearer token is available (non-Tauri)', async () => {
|
||||
vi.resetModules();
|
||||
vi.mocked(isTauri).mockReturnValue(false);
|
||||
const { testCoreRpcConnection } = await import('../coreRpcClient');
|
||||
const fetchMock = vi.mocked(fetch);
|
||||
fetchMock.mockResolvedValueOnce({ ok: true, status: 200 } as Response);
|
||||
|
||||
await testCoreRpcConnection('http://example.test:7788/rpc');
|
||||
|
||||
const requestInit = fetchMock.mock.calls[0][1] as RequestInit;
|
||||
const headers = requestInit.headers as Record<string, string>;
|
||||
expect(headers).toMatchObject({ 'Content-Type': 'application/json' });
|
||||
expect(headers).not.toHaveProperty('Authorization');
|
||||
});
|
||||
|
||||
test('attaches Authorization: Bearer when the Tauri bearer token resolves', async () => {
|
||||
vi.resetModules();
|
||||
vi.mocked(isTauri).mockReturnValue(true);
|
||||
vi.mocked(invoke).mockImplementation(async (cmd: string) => {
|
||||
if (cmd === 'core_rpc_token') return 'deadbeef';
|
||||
throw new Error(`unexpected command: ${cmd}`);
|
||||
});
|
||||
const { testCoreRpcConnection } = await import('../coreRpcClient');
|
||||
const fetchMock = vi.mocked(fetch);
|
||||
fetchMock.mockResolvedValueOnce({ ok: true, status: 200 } as Response);
|
||||
|
||||
await testCoreRpcConnection('http://example.test:7788/rpc');
|
||||
|
||||
const requestInit = fetchMock.mock.calls[0][1] as RequestInit;
|
||||
const headers = requestInit.headers as Record<string, string>;
|
||||
expect(headers.Authorization).toBe('Bearer deadbeef');
|
||||
expect(headers['Content-Type']).toBe('application/json');
|
||||
});
|
||||
|
||||
test('returns the raw fetch Response so callers can inspect status/ok', async () => {
|
||||
vi.resetModules();
|
||||
vi.mocked(isTauri).mockReturnValue(false);
|
||||
const { testCoreRpcConnection } = await import('../coreRpcClient');
|
||||
const fetchMock = vi.mocked(fetch);
|
||||
const probe = { ok: false, status: 405, statusText: 'Method Not Allowed' } as Response;
|
||||
fetchMock.mockResolvedValueOnce(probe);
|
||||
|
||||
const response = await testCoreRpcConnection('http://example.test:7788/rpc');
|
||||
|
||||
expect(response).toBe(probe);
|
||||
expect(response.status).toBe(405);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -167,6 +167,30 @@ async function getCoreRpcToken(): Promise<string | null> {
|
||||
return resolvingCoreRpcToken;
|
||||
}
|
||||
|
||||
/**
|
||||
* Probe an arbitrary core RPC URL with `openhuman.ping`. Used by the
|
||||
* Welcome page's "Test Connection" affordance to validate a user-entered
|
||||
* RPC URL without going through the cached `getCoreRpcUrl` resolution.
|
||||
*
|
||||
* Encapsulates the bearer-token + JSON-RPC envelope assembly that would
|
||||
* otherwise sit in the calling component, keeping all RPC client behavior
|
||||
* inside the service per the project guideline ("Keep Tauri IPC and RPC
|
||||
* client calls localized to services … do not scatter `invoke()` or
|
||||
* direct RPC calls throughout components").
|
||||
*/
|
||||
export async function testCoreRpcConnection(url: string): Promise<Response> {
|
||||
const token = await getCoreRpcToken();
|
||||
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
||||
if (token) {
|
||||
headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return fetch(url, {
|
||||
method: 'POST',
|
||||
headers,
|
||||
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'openhuman.ping', params: {} }),
|
||||
});
|
||||
}
|
||||
|
||||
export async function getCoreHttpBaseUrl(): Promise<string> {
|
||||
const rpcUrl = await getCoreRpcUrl();
|
||||
const url = new URL(rpcUrl);
|
||||
|
||||
Reference in New Issue
Block a user