From dfb8a6e9e8a36f3a107e4ea09ad14d1e4494428f Mon Sep 17 00:00:00 2001 From: munzzyy Date: Sat, 4 Jul 2026 21:10:39 -0500 Subject: [PATCH] Distinguish rate limit and network errors from invalid USDA keys testApiKey() only ever returned true/false, so a rate-limited or network-failed request got treated the same as an actually bad key and the user saw "API Key Invalid" either way. Confirmed against the live USDA API that a valid key past its rate limit comes back as HTTP 429 / OVER_RATE_LIMIT, not the 403 / API_KEY_INVALID a bad key gets, and the old code couldn't tell the two apart. The non-ok branch also fell through to invalid-key for anything that wasn't a rate limit, which meant a 5xx USDA outage or a non-JSON response got the same "API Key Invalid" message as a genuinely bad key. The reason is now picked from the response status alongside the error code (403/API_KEY_INVALID for a bad key, 429/OVER_RATE_LIMIT for the rate limit, anything else falls back to network), so an outage or unexpected response reads as "couldn't verify" instead of telling the user their key is wrong. testApiKey now resolves an object with a reason (invalid-key, rate-limit, network) instead of a bare boolean, and saveUSDAKey shows a message that matches the actual failure. Fixes #947. --- www/activities/foodlist/js/usda.js | 35 +++++++++++++++++++------- www/activities/settings/js/settings.js | 10 +++++++- www/assets/locales/locale-en.json | 2 ++ 3 files changed, 37 insertions(+), 10 deletions(-) diff --git a/www/activities/foodlist/js/usda.js b/www/activities/foodlist/js/usda.js index c28260433..8c65483f0 100644 --- a/www/activities/foodlist/js/usda.js +++ b/www/activities/foodlist/js/usda.js @@ -212,18 +212,35 @@ app.USDA = { let url = "https://api.nal.usda.gov/fdc/v1/foods/search?api_key=" + encodeURIComponent(key) + "&query=cheese"; let response = await app.Utils.timeoutFetch(url).catch((err) => { - resolve(false); + resolve({ + valid: false, + reason: "network" + }); }); - if (response && response.ok) { - let data = await response.json(); - if (data.error && data.error.code == "API_KEY_INVALID") - resolve(false); - else - resolve(true); + if (response) { + if (response.ok) { + resolve({ + valid: true + }); + } else { + let data = await response.json().catch((err) => undefined); + let code = data && data.error && data.error.code; + + let reason; + if (code == "OVER_RATE_LIMIT" || response.status == 429) + reason = "rate-limit"; + else if (code == "API_KEY_INVALID" || response.status == 403) + reason = "invalid-key"; + else + reason = "network"; + + resolve({ + valid: false, + reason: reason + }); + } } - - resolve(false); }); } }; \ No newline at end of file diff --git a/www/activities/settings/js/settings.js b/www/activities/settings/js/settings.js index 8376375da..da9cc3851 100644 --- a/www/activities/settings/js/settings.js +++ b/www/activities/settings/js/settings.js @@ -346,11 +346,19 @@ app.Settings = { saveUSDAKey: async function(key) { let screen = document.querySelector(".usda-login"); if (app.Utils.isInternetConnected()) { - if (key == "" || await app.USDA.testApiKey(key)) { + let result = key == "" ? { valid: true } : await app.USDA.testApiKey(key); + + if (result.valid) { this.put("integration", "usda-key", key); app.f7.loginScreen.close(screen); let msg = app.strings.settings.integration["login-success"] || "Login Successful"; app.Utils.toast(msg); + } else if (result.reason == "rate-limit") { + let msg = app.strings.settings.integration["usda-rate-limited"] || "USDA rate limit reached, try again later"; + app.Utils.toast(msg); + } else if (result.reason == "network") { + let msg = app.strings.settings.integration["usda-key-check-failed"] || "Couldn't verify key, check your connection and try again"; + app.Utils.toast(msg); } else { let msg = app.strings.settings.integration["invalid-api-key"] || "API Key Invalid"; app.Utils.toast(msg); diff --git a/www/assets/locales/locale-en.json b/www/assets/locales/locale-en.json index 685c0f6fc..89503d03b 100644 --- a/www/assets/locales/locale-en.json +++ b/www/assets/locales/locale-en.json @@ -395,6 +395,8 @@ "save": "Save", "invalid-credentials": "Invalid Credentials", "invalid-api-key": "API Key Invalid", + "usda-rate-limited": "USDA rate limit reached, try again later", + "usda-key-check-failed": "Couldn't verify key, check your connection and try again", "export-success": "Database Exported", "export-fail": "Export Failed", "import-fail": "Import Failed",