fix(review): land cleanCoordinate server-side guard; surface geocode failures
Two code-review follow-ups. cleanCoordinate() — the server-side coordinate sanitizer the design doc and plan both describe as already shipped — had never actually been committed; it existed only as uncommitted work in another checkout, so this branch had no server-side validation of lat/lng at all (the blueprint fields are plain `type: text` with no `validate:` key). Landing it here makes the spec's stated safety net real. Also corrected its onAdminSave comment, which justified that hook by saying the public form's lat/lng inputs are CSS-hidden and GPS-filled — true before this feature, inverted by it. Both hooks are needed: this branch makes /post the primary hand-entry path, not Admin2. The geocode lookup swallowed every failure and reset the button, leaving the DOM byte-identical to the pre-click state — a traveller on flaky mobile data could not distinguish a failed lookup from a broken button. It now shows a distinct hint, and checks r.ok first so a 4xx/5xx body no longer parses as "no results" and tells the traveller their city does not exist. R8's actual guarantee (fields untouched on failure) is preserved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -719,7 +719,14 @@ function initLocationDetails() {
|
||||
// button stuck disabled on "Searching…" forever.
|
||||
var controller = new AbortController();
|
||||
var timeoutId = setTimeout(function () { controller.abort(); }, 10000);
|
||||
fetch(url, { signal: controller.signal }).then(function (r) { return r.json(); }).then(function (data) {
|
||||
fetch(url, { signal: controller.signal }).then(function (r) {
|
||||
// Without this a 4xx/5xx body (rate limit, upstream error) parses as
|
||||
// JSON with no `results` key and the traveller is told their city
|
||||
// does not exist — sending them off to hunt a spelling mistake that
|
||||
// isn't there. Route real failures to the catch instead.
|
||||
if (!r.ok) throw new Error('geocode http ' + r.status);
|
||||
return r.json();
|
||||
}).then(function (data) {
|
||||
var list = (data && data.results) || [];
|
||||
if (!list.length) {
|
||||
setHint('No matches — try adding a country, or drag the pin on the map.');
|
||||
@@ -746,8 +753,13 @@ function initLocationDetails() {
|
||||
}
|
||||
showResults(list);
|
||||
}).catch(function () {
|
||||
// R8: network failure (including our own timeout abort) degrades
|
||||
// silently — fields untouched.
|
||||
// R8 revised: the fields stay untouched on failure (that part of R8
|
||||
// is the actual guarantee), but the failure is no longer invisible.
|
||||
// Silence left the DOM byte-identical to the pre-click state — empty
|
||||
// hint, enabled button — so a traveller on flaky mobile data could
|
||||
// not tell "lookup failed" from "the button is broken". Distinct
|
||||
// from the no-match message above, which means the service answered.
|
||||
setHint('Couldn’t reach the lookup service — check your connection and try again, or drag the pin on the map.');
|
||||
}).then(function () {
|
||||
clearTimeout(timeoutId);
|
||||
lookupBtn.disabled = false;
|
||||
|
||||
Reference in New Issue
Block a user