Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 48 additions & 5 deletions app/main-axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
export async function setCookie(
name: string,
value: string,
days = 7,

Check warning on line 64 in app/main-axios.ts

View workflow job for this annotation

GitHub Actions / checks

'days' is assigned a value but never used. Allowed unused args must match /^_/u
): Promise<void> {
try {
await AsyncStorage.setItem(name, value);
Expand Down Expand Up @@ -279,7 +279,7 @@
updateApiInstances();
await detectAndUpdateApiInstances();
return true;
} catch (error) {

Check warning on line 282 in app/main-axios.ts

View workflow job for this annotation

GitHub Actions / checks

'error' is defined but never used

Check warning on line 282 in app/main-axios.ts

View workflow job for this annotation

GitHub Actions / checks

'error' is defined but never used
return false;
}
}
Expand Down Expand Up @@ -350,7 +350,7 @@
try {
const token = await getCookie("jwt");
return !!token;
} catch (error) {

Check warning on line 353 in app/main-axios.ts

View workflow job for this annotation

GitHub Actions / checks

'error' is defined but never used
return false;
}
}
Expand Down Expand Up @@ -2291,6 +2291,37 @@
return null;
}

function isLocalNetworkHttpsUrl(url: string): boolean {
if (!url.startsWith("https://")) return false;

try {
const host = new URL(url).hostname.replace(/^\[|\]$/g, "");
if (host === "localhost" || host === "::1") return true;

const parts = host.split(".").map((part) => Number(part));
if (parts.length !== 4 || parts.some((part) => !Number.isInteger(part))) {
return false;
}

const [first, second] = parts;
return (
first === 10 ||
first === 127 ||
(first === 169 && second === 254) ||
(first === 172 && second >= 16 && second <= 31) ||
(first === 192 && second === 168)
);
} catch {
return false;
}
}

function isNativeNetworkFailure(error: unknown): boolean {
return (
error instanceof Error && error.message.includes("Network request failed")
);
}

/**
* Detects whether the server URL sits behind a reverse-proxy authentication
* gate (Cloudflare Access, Authelia, etc.) that intercepts requests and serves
Expand Down Expand Up @@ -2353,11 +2384,23 @@
password: string,
): Promise<{ data: any; token: string | null }> {
const url = `${baseUrl.replace(/\/$/, "")}/users/login`;
const fetchResponse = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
});
let fetchResponse: Response;
try {
fetchResponse = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, password }),
});
} catch (error) {
if (isLocalNetworkHttpsUrl(url) && isNativeNetworkFailure(error)) {
throw new ApiError(
"The app could not trust HTTPS for this local IP. Use a DNS name with a valid certificate, a certificate for this IP, or HTTP on a trusted local/VPN network.",
0,
"LOCAL_IP_HTTPS_CERTIFICATE_ERROR",
);
}
throw error;
}

const contentType = (
fetchResponse.headers.get("content-type") || ""
Expand Down
Loading