Skip to content

feat(server): add fallback URL with automatic failover#109

Open
yashpatil27 wants to merge 1 commit into
taylorcox75:mainfrom
yashpatil27:feat/server-fallback-url
Open

feat(server): add fallback URL with automatic failover#109
yashpatil27 wants to merge 1 commit into
taylorcox75:mainfrom
yashpatil27:feat/server-fallback-url

Conversation

@yashpatil27
Copy link
Copy Markdown
Contributor

Closes #14

Summary

Adds a fallback endpoint to each server. When the primary URL is unreachable, the app automatically tries the configured fallback (LAN ↔ WAN, DDNS vs static IP, reverse proxy vs direct, etc.), so the user doesn't have to manually edit the server every time the network context changes.

The feature is fully optional — existing servers continue to work exactly as before with no fallback configured, no migration required.

Behavior

Save / Edit

  • New "FALLBACK URL" section in the Add Server and Edit Server screens, between Server Info and Authentication.
  • Toggle "Use fallback URL". When enabled, the section reveals fallback host, fallback port, and a fallback HTTPS switch.
  • Authentication credentials are shared between primary and fallback (fallback is treated as an alternate route to the same qBittorrent instance).
  • Save validates: fallback host required when enabled, fallback port must be 1–65535 when provided.
  • Edit screen loads and round-trips every fallback field.

Connect

  • The connection flow attempts the primary endpoint first.
  • On a network error (timeout, ECONNABORTED, ERR_NETWORK, 5xx, etc.) the fallback is tried next.
  • On an authentication / permission failure the fallback is not tried because credentials are shared — failing again would just waste time.
  • Whichever endpoint connects becomes the active one for the session. The stored current_server_id is the same regardless of which endpoint won, so the rest of the app sees a single logical server.
  • reconnect() and checkAndReconnect() reuse the same flow — fallback happens automatically on stale-connection recovery.

Test Connection

  • Primary-only servers: unchanged toast ("Connection test successful!" or error message).
  • Fallback-enabled servers: both endpoints are tested and a compact per-endpoint toast is shown, e.g. "Primary: OK · Fallback: OK" or "Primary: failed · Fallback: OK".

Settings tab

  • When a server with fallback is connected, the connection card shows a small subtitle: "Connected via primary" or "Connected via fallback".
  • Servers without fallback render exactly as before.

What changed

Data model — types/api.ts

  • ServerConfig extended with optional fields: useFallback, fallbackHost, fallbackPort, fallbackUseHttps, fallbackBasePath (last one is reserved for a future UI; data-model parity only).
  • New ServerEndpointKind = 'primary' | 'fallback' union.

Helpers — utils/server.ts

  • hasFallback(server) — true when fallback is enabled and a host is set.
  • resolveServerEndpoint(server, endpoint) — returns a transient ServerConfig whose host/port/useHttps/basePath are swapped for the chosen endpoint, leaving id/credentials intact so cookies and stored IDs remain coherent.
  • getServerEndpointLabel(server, endpoint) — display string helper.
  • getActiveEndpoint(server, activeServer) — derives which endpoint the API client is currently on by comparing host/port/HTTPS against both resolved endpoints.

Connection logic — services/server-manager.ts

  • connectToServer(server) refactored: tries primary via a shared private connectToEndpoint(server, resolved, endpoint) path, catches network errors, and re-runs the same path against the resolved fallback when configured. Non-network errors propagate unchanged. The most informative error (fallback's, when both fail) is surfaced.
  • testConnection(server, signal) returns the new ConnectionTestResult shape (extends the old { success, error }) with optional primary/fallback keys when fallback is enabled. A new internal testEndpoint runs the per-endpoint check and translates auth/network errors into the consistent shape. Cancellation propagates between the two endpoint tests.
  • New exported types: EndpointTestResult, ConnectionTestResult.

Context — context/ServerContext.tsx

  • New state: activeEndpoint: 'primary' | 'fallback' | null.
  • refreshActiveEndpoint(server, connected) derives the active endpoint from apiClient.getServer() and is called after every successful connect/reconnect/auto-connect path. Cleared on disconnect and on connection errors.

Storage — services/storage.ts

  • saveServer() persists useFallback, fallbackHost (with stripProtocol), fallbackPort, fallbackUseHttps, and fallbackBasePath.
  • getServers() strips any stray protocol on fallbackHost for legacy safety.
  • No migration is needed; missing fields naturally mean fallback is disabled.

UI

  • app/server/add.tsx: new section, new state, validation in both Save and Test paths, save includes fallback fields, per-endpoint test-result toast.
  • app/server/[id].tsx: same UI section, plus loads/round-trips all fallback fields and preserves fallbackBasePath even though the UI does not surface it.
  • app/(tabs)/settings.tsx: imports hasFallback, consumes activeEndpoint, shows the connected-via subtitle when applicable.

i18n — locales/{en,es,zh,fr,de,ru}/translation.json

12 new keys per locale:

  • server.fallbackUrl, server.useFallback, server.useFallbackHint, server.fallbackUseHttps
  • server.endpointPrimary, server.endpointFallback
  • server.testEndpointOk, server.testEndpointFail
  • server.connectedViaPrimary, server.connectedViaFallback
  • placeholders.fallbackHost
  • errors.fillFallbackHost

Backward compatibility

  • Existing servers stored in AsyncStorage have no fallback fields → useFallback reads as undefined → hasFallback() returns false → all flows behave exactly like before.
  • The legacy theme / other preferences are unaffected (this PR only touches server-related types and code).
  • Saved password remains in SecureStore as before.

Tested

  • Existing primary-only server: connects, edits, tests as before.
  • Fallback enabled and primary reachable → connects via primary; settings subtitle "Connected via primary".
  • Primary unreachable, fallback reachable → connects via fallback; subtitle "Connected via fallback".
  • Both unreachable → meaningful error.
  • Auth failure on primary → does not attempt fallback.
  • Add Server validation: missing fallback host when enabled is blocked; invalid fallback port is blocked.
  • Edit Server: fallback fields load and round-trip correctly.
  • Test Connection toast: per-endpoint outcomes display correctly.
  • npx tsc --noEmit passes clean.

Out of scope (intentionally deferred)

  • Mid-session failover (switching endpoints during an already-connected session).
  • More than one fallback endpoint.
  • Per-endpoint credentials.
  • UI for fallbackBasePath (field exists for data-model parity).

Adds a fallback endpoint to each server so the app can keep connecting when the primary URL is unreachable. Supports common LAN/WAN, DDNS, and reverse-proxy setups, and reduces manual intervention when switching networks.

- ServerConfig: new optional fields useFallback, fallbackHost, fallbackPort, fallbackUseHttps, fallbackBasePath. New ServerEndpointKind union.

- utils/server.ts: hasFallback, resolveServerEndpoint, getServerEndpointLabel, getActiveEndpoint helpers.

- ServerManager.connectToServer: try primary first, then fallback only on network errors. Auth/permission errors do not trigger fallback because credentials are shared with the primary.

- ServerManager.testConnection: returns ConnectionTestResult with optional primary/fallback per-endpoint outcomes when fallback is configured; preserves the simple shape for primary-only servers.

- ServerContext: exposes activeEndpoint ('primary' | 'fallback' | null), updated on connect, reconnect, auto-connect, and disconnect.

- storage.ts: persists the new fallback fields alongside existing server data; missing fields naturally mean fallback is disabled, no migration needed.

- app/server/add.tsx and app/server/[id].tsx: new 'Fallback URL' section with switch + conditional host/port/HTTPS fields, validation, save, and edit round-trip. Test Connection now reports per-endpoint outcomes when fallback is enabled.

- app/(tabs)/settings.tsx: connection card shows a 'Connected via primary' / 'Connected via fallback' subtitle when fallback is configured.

- i18n: 12 new strings added across en, es, zh, fr, de, ru (fallback section title, switch + hint, fallback HTTPS, primary/fallback endpoint labels, per-endpoint test result text, connected-via labels, fallback host placeholder, fallback host validation error).

Closes taylorcox75#14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fallback Option for Server URL on Add Server Page

1 participant