mirror of
https://github.com/Frooodle/Stirling-PDF.git
synced 2025-12-18 20:04:17 +01:00
Attempt 4
This commit is contained in:
parent
60649c095f
commit
3c8e1054ad
@ -1,48 +1,98 @@
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||
import { fetch } from '@tauri-apps/plugin-http';
|
||||
import { invoke } from '@tauri-apps/api/core';
|
||||
import { connectionModeService } from '@app/services/connectionModeService';
|
||||
import { tauriBackendService } from '@app/services/tauriBackendService';
|
||||
|
||||
type BackendStatus = 'up' | 'starting' | 'down';
|
||||
import { tauriBackendService, type BackendStatus as ServiceBackendStatus } from '@app/services/tauriBackendService';
|
||||
|
||||
type ConnectionMode = 'saas' | 'selfhosted';
|
||||
|
||||
type ProbeStatus = 'up' | 'starting' | 'down';
|
||||
|
||||
interface BackendProbeState {
|
||||
status: BackendStatus;
|
||||
status: ProbeStatus;
|
||||
loginDisabled: boolean;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
interface ProbeTarget {
|
||||
baseUrl: string;
|
||||
mode: ConnectionMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Desktop override of useBackendProbe.
|
||||
* Hits the local/remote backend directly via the Tauri HTTP client.
|
||||
* Desktop-specific backend probe.
|
||||
*
|
||||
* In SaaS mode we trust the embedded backend's health status exposed by tauriBackendService,
|
||||
* avoiding redundant HTTP polling that was preventing the Login/Landing screens from advancing.
|
||||
* In self-hosted mode we still hit the remote server directly to detect login-disabled state.
|
||||
*/
|
||||
export function useBackendProbe() {
|
||||
const initialStatus = useMemo(() => mapServiceStatus(tauriBackendService.getBackendStatus()), []);
|
||||
const [state, setState] = useState<BackendProbeState>({
|
||||
status: 'starting',
|
||||
status: initialStatus,
|
||||
loginDisabled: false,
|
||||
loading: true,
|
||||
});
|
||||
const modeRef = useRef<ConnectionMode>('saas');
|
||||
|
||||
// Track connection mode so the probe knows when to fall back to remote HTTP checks.
|
||||
useEffect(() => {
|
||||
let unsubscribe: (() => void) | null = null;
|
||||
connectionModeService.getCurrentConfig()
|
||||
.then((config) => {
|
||||
modeRef.current = config.mode;
|
||||
unsubscribe = connectionModeService.subscribeToModeChanges((nextConfig) => {
|
||||
modeRef.current = nextConfig.mode;
|
||||
});
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('[Desktop useBackendProbe] Failed to load connection mode:', error);
|
||||
});
|
||||
|
||||
return () => {
|
||||
if (unsubscribe) {
|
||||
unsubscribe();
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
// React to backend health updates coming from the shared tauriBackendService.
|
||||
useEffect(() => {
|
||||
const unsubscribe = tauriBackendService.subscribeToStatus((serviceStatus) => {
|
||||
setState((prev) => ({
|
||||
...prev,
|
||||
status: mapServiceStatus(serviceStatus),
|
||||
loading: false,
|
||||
}));
|
||||
});
|
||||
return () => {
|
||||
unsubscribe();
|
||||
};
|
||||
}, []);
|
||||
|
||||
const probe = useCallback(async () => {
|
||||
const target = await resolveProbeBaseUrl();
|
||||
if (!target) {
|
||||
const pendingState: BackendProbeState = {
|
||||
const mode = modeRef.current;
|
||||
const serviceStatus = tauriBackendService.getBackendStatus();
|
||||
|
||||
if (mode === 'saas') {
|
||||
// SaaS desktop always relies on the embedded backend. Kick off a health check if needed.
|
||||
if (serviceStatus !== 'healthy') {
|
||||
void tauriBackendService.checkBackendHealth();
|
||||
}
|
||||
const nextState: BackendProbeState = {
|
||||
status: mapServiceStatus(serviceStatus),
|
||||
loginDisabled: false,
|
||||
loading: false,
|
||||
};
|
||||
setState(nextState);
|
||||
return nextState;
|
||||
}
|
||||
|
||||
const baseUrl = await resolveRemoteBaseUrl();
|
||||
if (!baseUrl) {
|
||||
const pending: BackendProbeState = {
|
||||
status: 'starting',
|
||||
loginDisabled: false,
|
||||
loading: false,
|
||||
};
|
||||
setState(pendingState);
|
||||
return pendingState;
|
||||
setState(pending);
|
||||
return pending;
|
||||
}
|
||||
|
||||
const { baseUrl, mode } = target;
|
||||
const statusUrl = `${baseUrl}/api/v1/info/status`;
|
||||
const loginUrl = `${baseUrl}/api/v1/proprietary/ui-data/login`;
|
||||
|
||||
@ -71,28 +121,24 @@ export function useBackendProbe() {
|
||||
next.status = 'down';
|
||||
}
|
||||
|
||||
// SaaS desktop always runs bundled backend with login disabled,
|
||||
// so only check the login endpoint in self-hosted mode.
|
||||
if (mode === 'selfhosted') {
|
||||
try {
|
||||
const res = await fetch(loginUrl, { method: 'GET', connectTimeout: 5000 });
|
||||
if (res.ok) {
|
||||
next.status = 'up';
|
||||
const data = await res.json().catch(() => null);
|
||||
if (data && data.enableLogin === false) {
|
||||
next.loginDisabled = true;
|
||||
}
|
||||
} else if (res.status === 404) {
|
||||
next.status = 'up';
|
||||
try {
|
||||
const res = await fetch(loginUrl, { method: 'GET', connectTimeout: 5000 });
|
||||
if (res.ok) {
|
||||
next.status = 'up';
|
||||
const data = await res.json().catch(() => null);
|
||||
if (data && data.enableLogin === false) {
|
||||
next.loginDisabled = true;
|
||||
} else if (res.status === 503) {
|
||||
next.status = 'starting';
|
||||
} else {
|
||||
next.status = 'down';
|
||||
}
|
||||
} catch {
|
||||
// keep existing inferred state (down/starting)
|
||||
} else if (res.status === 404) {
|
||||
next.status = 'up';
|
||||
next.loginDisabled = true;
|
||||
} else if (res.status === 503) {
|
||||
next.status = 'starting';
|
||||
} else {
|
||||
next.status = 'down';
|
||||
}
|
||||
} catch {
|
||||
// keep inferred state
|
||||
}
|
||||
|
||||
setState(next);
|
||||
@ -109,38 +155,19 @@ export function useBackendProbe() {
|
||||
};
|
||||
}
|
||||
|
||||
async function resolveProbeBaseUrl(): Promise<ProbeTarget | null> {
|
||||
async function resolveRemoteBaseUrl(): Promise<string | null> {
|
||||
try {
|
||||
const config = await connectionModeService.getCurrentConfig();
|
||||
if (config.mode === 'selfhosted') {
|
||||
const serverUrl = config.server_config?.url;
|
||||
if (!serverUrl) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
baseUrl: stripTrailingSlash(serverUrl),
|
||||
mode: 'selfhosted',
|
||||
};
|
||||
}
|
||||
|
||||
const directUrl = tauriBackendService.getBackendUrl();
|
||||
if (directUrl) {
|
||||
return {
|
||||
baseUrl: stripTrailingSlash(directUrl),
|
||||
mode: 'saas',
|
||||
};
|
||||
}
|
||||
|
||||
const port = await invoke<number | null>('get_backend_port').catch(() => null);
|
||||
if (!port) {
|
||||
if (config.mode !== 'selfhosted') {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
baseUrl: stripTrailingSlash(`http://localhost:${port}`),
|
||||
mode: 'saas',
|
||||
};
|
||||
const serverUrl = config.server_config?.url;
|
||||
if (!serverUrl) {
|
||||
return null;
|
||||
}
|
||||
return stripTrailingSlash(serverUrl);
|
||||
} catch (error) {
|
||||
console.error('[Desktop useBackendProbe] Failed to resolve backend URL:', error);
|
||||
console.error('[Desktop useBackendProbe] Failed to resolve remote backend URL:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@ -148,3 +175,13 @@ async function resolveProbeBaseUrl(): Promise<ProbeTarget | null> {
|
||||
function stripTrailingSlash(value: string): string {
|
||||
return value.replace(/\/$/, '');
|
||||
}
|
||||
|
||||
function mapServiceStatus(status: ServiceBackendStatus): ProbeStatus {
|
||||
if (status === 'healthy') {
|
||||
return 'up';
|
||||
}
|
||||
if (status === 'unhealthy') {
|
||||
return 'down';
|
||||
}
|
||||
return 'starting';
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user