refactor(jellyseerr): keep the server version out of the field UI; enforce it at login

The resolver field only needs to find the working URL — the Jellyseerr version requirement is irrelevant there and only polluted the UI.

- jellyseerrProbe: validate reachability + that it's a jellyseerr (no version gate, no version-too-low outcome).

- Drop the version-too-low reason from the whole resolver stack (types, resolve, hook, status text, i18n).

- Min version 2.0.0 stays enforced in JellyseerrApi.test() at login: now writes an error log + toast, and uses numeric isVersionBelow (fixes the "2.10.0" < "2.0.0" string-compare bug).
This commit is contained in:
Gauvain
2026-06-04 21:24:40 +02:00
parent ef27674010
commit 7fc74df0aa
9 changed files with 24 additions and 58 deletions

View File

@@ -1,14 +1,11 @@
import axios from "axios";
import { isVersionBelow } from "../semver";
import type { ServerProbe } from "../types";
/** Jellyseerr/Overseerr minimum supported version. */
const MIN_VERSION = "2.0.0";
/**
* Probe for a Jellyseerr server. `/api/v1/status` is jellyseerr/overseerr
* specific and unauthenticated, so it both proves reachability and confirms
* we hit the right service.
* specific and unauthenticated, so it both proves reachability and confirms we
* hit the right service. The minimum-version requirement is enforced at login
* time (see JellyseerrApi.test) — not surfaced here, to keep the field UI clean.
*/
export const jellyseerrProbe: ServerProbe = async (url, signal) => {
try {
@@ -19,22 +16,14 @@ export const jellyseerrProbe: ServerProbe = async (url, signal) => {
if (status < 200 || status >= 300) return { status: "unreachable" };
const version: string | undefined =
typeof data?.version === "string" ? data.version : undefined;
// A JSON body carrying version/commitTag identifies a real jellyseerr.
if (
!version &&
!(data && typeof data === "object" && "commitTag" in data)
) {
return { status: "wrong-service" };
}
const looksLikeJellyseerr =
!!data &&
typeof data === "object" &&
(typeof data.version === "string" || "commitTag" in data);
if (!looksLikeJellyseerr) return { status: "wrong-service" };
if (version && isVersionBelow(version, MIN_VERSION)) {
return { status: "version-too-low", version };
}
return { status: "ok", meta: { version } };
return { status: "ok", meta: { version: data.version } };
} catch {
return { status: "unreachable" };
}

View File

@@ -4,13 +4,12 @@ import type { ServerProbe, ServerProbeOutcome } from "./types";
export type ResolveFailureReason =
| "empty"
| "invalid"
| "version-too-low"
| "wrong-service"
| "unreachable";
export type ResolveResult =
| { ok: true; url: string; meta?: Record<string, unknown> }
| { ok: false; reason: ResolveFailureReason; version?: string };
| { ok: false; reason: ResolveFailureReason };
export interface ResolveOptions {
/** Per-candidate probe timeout in ms. Default 5000. */
@@ -22,7 +21,6 @@ export interface ResolveOptions {
// Order in which to surface a failure when no candidate validated:
// the more specific/actionable the reason, the earlier it is reported.
const FAILURE_PRIORITY = [
"version-too-low",
"wrong-service",
"unreachable",
] as const satisfies ReadonlyArray<ResolveFailureReason>;
@@ -62,11 +60,7 @@ export async function resolveServerUrl(
for (const reason of FAILURE_PRIORITY) {
const hit = outcomes.find((outcome) => outcome.status === reason);
if (hit) {
return {
ok: false,
reason,
version: hit.status === "version-too-low" ? hit.version : undefined,
};
return { ok: false, reason };
}
}
return { ok: false, reason: "unreachable" };

View File

@@ -1,7 +1,6 @@
/** Result of probing a single candidate URL for a specific service. */
export type ServerProbeOutcome =
| { status: "ok"; meta?: Record<string, unknown> }
| { status: "version-too-low"; version?: string }
| { status: "wrong-service" }
| { status: "unreachable" };