mirror of
https://github.com/arsvendg/Stirling-PDF.git
synced 2026-07-16 11:23:10 +02:00
Desktop: Fallback to local backend if self-hosted server is offline (#5880)
* Adds a fallback mechanism so the desktop app routes tool operations to the local bundled backend when the user's self-hosted Stirling-PDF server goes offline, and disables tools in the UI that aren't supported locally. * `selfHostedServerMonitor.ts` independently polls the self-hosted server every 15s and exposes which tool endpoints are unavailable when it goes offline * `operationRouter.ts` intercepts operations destined for the self-hosted server and reroutes them to the local bundled backend when the monitor reports it offline * `useSelfHostedToolAvailability.ts` feeds the offline tool set into useToolManagement, disabling affected tools in the UI with a selfHostedOffline reason and banner warning - `SelfHostedOfflineBanner `is a dismissable (session-only) gray bar shown at the top of the UI when in self-hosted mode and the server goes offline. It shows:
This commit is contained in:
@@ -2,14 +2,22 @@ import i18n from '@app/i18n';
|
||||
import { alert } from '@app/components/toast';
|
||||
import { tauriBackendService } from '@app/services/tauriBackendService';
|
||||
import { operationRouter } from '@app/services/operationRouter';
|
||||
import { connectionModeService } from '@app/services/connectionModeService';
|
||||
import { selfHostedServerMonitor } from '@app/services/selfHostedServerMonitor';
|
||||
|
||||
const BACKEND_TOAST_COOLDOWN_MS = 4000;
|
||||
let lastBackendToast = 0;
|
||||
|
||||
/**
|
||||
* Desktop-specific guard that ensures the embedded backend is healthy
|
||||
* before tools attempt to call any API endpoints.
|
||||
* Enhanced to skip checks for endpoints routed to SaaS backend.
|
||||
* Desktop-specific guard that ensures the relevant backend is ready before
|
||||
* tools attempt to call any API endpoints.
|
||||
*
|
||||
* - SaaS mode: checks the local bundled backend via tauriBackendService.isOnline
|
||||
* - Self-hosted mode (server online/checking): allows through — the operation
|
||||
* targets the remote server and will surface network errors naturally
|
||||
* - Self-hosted mode (server confirmed offline): allows through if local port is
|
||||
* known (operationRouter falls back to local); suppresses toast since
|
||||
* SelfHostedOfflineBanner already communicates the outage
|
||||
*
|
||||
* @param endpoint - Optional endpoint path to check if it needs local backend
|
||||
* @returns true if backend is ready OR endpoint will be routed to SaaS
|
||||
@@ -24,14 +32,40 @@ export async function ensureBackendReady(endpoint?: string): Promise<boolean> {
|
||||
}
|
||||
}
|
||||
|
||||
// Check local backend health
|
||||
if (tauriBackendService.isBackendHealthy()) {
|
||||
const mode = await connectionModeService.getCurrentMode();
|
||||
if (mode === 'selfhosted') {
|
||||
let { status } = selfHostedServerMonitor.getSnapshot();
|
||||
|
||||
// 'checking' means the first poll hasn't returned yet. Wait briefly (up to
|
||||
// 1.5 s) for it to resolve so we don't surface raw network errors during the
|
||||
// first few seconds after launch. If it doesn't resolve in time we fall
|
||||
// through and allow the operation — the HTTP layer will handle any error.
|
||||
if (status === 'checking') {
|
||||
await Promise.race([
|
||||
selfHostedServerMonitor.checkNow(),
|
||||
new Promise<void>(resolve => setTimeout(resolve, 1500)),
|
||||
]);
|
||||
status = selfHostedServerMonitor.getSnapshot().status;
|
||||
}
|
||||
|
||||
if (status === 'offline') {
|
||||
// Server offline: allow through if local backend port is known.
|
||||
// operationRouter will route to local for supported endpoints.
|
||||
// Suppress the toast — SelfHostedOfflineBanner communicates the outage.
|
||||
return !!tauriBackendService.getBackendUrl();
|
||||
}
|
||||
// Server online: allow through — the operation targets the remote server.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Trigger a health check so we get the freshest status
|
||||
// SaaS mode: check local bundled backend
|
||||
if (tauriBackendService.isOnline) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Trigger a fresh check so we get the latest status
|
||||
await tauriBackendService.checkBackendHealth();
|
||||
if (tauriBackendService.isBackendHealthy()) {
|
||||
if (tauriBackendService.isOnline) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user