use Gitea's public health endpoint to check the URL
/api/v1/version is not reliably public — an instance with REQUIRE_SIGNIN_VIEW answers 403 — so naming a perfectly good URL was refused with "the server answered 403" and whoever typed it had nothing to act on. /api/healthz is Gitea's own health endpoint and answers before any authentication, which is the whole point of it. Tried first, with version as the fallback. A 401 or 403 from either now counts as reachable. This step only asks whether there is a Gitea at the address; whether the caller may READ it is the token step's question, and failing here for that reason sends people looking for the wrong problem.
This commit is contained in:
+28
-7
@@ -124,14 +124,35 @@ export type ProbeResult = { ok: boolean; version: string | null; user: string |
|
||||
* up", which is exactly what this step needs to establish.
|
||||
*/
|
||||
export async function reachable(base: string): Promise<ProbeResult> {
|
||||
try {
|
||||
const res = await fetch(`${base}/api/v1/version`, { signal: AbortSignal.timeout(8000) });
|
||||
if (!res.ok) return { ok: false, version: null, user: null, error: `the server answered ${res.status}` };
|
||||
const body = (await res.json().catch(() => null)) as { version?: string } | null;
|
||||
return { ok: true, version: body?.version ?? null, user: null };
|
||||
} catch {
|
||||
return { ok: false, version: null, user: null, error: 'could not reach that URL from the server' };
|
||||
// `/api/healthz` FIRST. It is Gitea's own health endpoint and it is public by design — it answers
|
||||
// before any authentication, which is the whole point of it.
|
||||
//
|
||||
// `/api/v1/version` was tried first and is NOT reliably public: an instance with
|
||||
// `REQUIRE_SIGNIN_VIEW` answers 403, so a perfectly good URL was refused with "the server answered
|
||||
// 403" and whoever typed it had no idea what to do about it.
|
||||
//
|
||||
// A 401 or 403 from EITHER endpoint still proves something answered at that address. This step only
|
||||
// asks "is there a Gitea here"; whether the caller may read it is the token step's question, and
|
||||
// failing here for that reason sends people looking for the wrong problem.
|
||||
for (const path of ['/api/healthz', '/api/v1/version']) {
|
||||
try {
|
||||
const res = await fetch(`${base}${path}`, { signal: AbortSignal.timeout(8000) });
|
||||
if (res.ok) {
|
||||
const body = (await res.json().catch(() => null)) as { version?: string } | null;
|
||||
return { ok: true, version: body?.version ?? null, user: null };
|
||||
}
|
||||
if (res.status === 401 || res.status === 403) return { ok: true, version: null, user: null };
|
||||
// Anything else — 404, 502, a login page — is worth trying the other path for before giving up.
|
||||
} catch {
|
||||
// Network-level. Try the other path; if both fail the error below is the honest one.
|
||||
}
|
||||
}
|
||||
return {
|
||||
ok: false,
|
||||
version: null,
|
||||
user: null,
|
||||
error: 'no Gitea answered at that URL — check it is reachable from the server, not just from your browser',
|
||||
};
|
||||
}
|
||||
|
||||
export async function probe(config: GiteaConfig): Promise<ProbeResult> {
|
||||
|
||||
Reference in New Issue
Block a user