The stored apiUrl was required to already carry the `/api` suffix, since
api.js builds requests as `${baseUrl}/credentials`. The options label read
"FC base URL", so entering the instance root -- the natural reading --
sent every request one path segment short: POST /credentials hit the Vue
SPA catch-all and came back 405, and GET /extension/manifest 404'd.
Worse, Test Connection reported success on it: the catch-all answers GET
/credentials with 200 HTML, so `r.ok` was true and the only affordance
meant to catch this misconfiguration actively masked it.
Normalize instead of validate (rules 92, 26):
- New lib/url.js: normalizeApiUrl / webRootFromApiUrl, one source shared
by the background client and the options page. Accepts either the
instance root or the API root.
- api.js normalizes on read, so configs already stored in the broken form
heal themselves without the operator reopening Settings.
- options.js stores the canonical form, echoes back what it saved, and
the test now asserts a JSON content-type -- killing the false green.
- 404/405 in request() now names the URL and points at the setting.
- Options label/placeholder state that both forms work.
Version 1.0.9 -> 1.0.10 in BOTH manifest.json and package.json; build.yml
resolves the release version from package.json, and a stale value there
would hit the cached ext-1.0.9 asset and republish the old XPI unsigned
against the new code.
Refs #2393
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
33 lines
1.2 KiB
JavaScript
33 lines
1.2 KiB
JavaScript
/**
|
|
* Canonical FC endpoint derivation, shared by the background client and the
|
|
* options page so a URL entered either way behaves identically.
|
|
*
|
|
* FC serves two things on one origin: the JSON API under `/api`, and the Vue
|
|
* SPA from the root. `api.js` builds requests as `${baseUrl}/credentials`, so
|
|
* the stored base URL has to carry the `/api` suffix.
|
|
*/
|
|
|
|
/**
|
|
* Accept what an operator would naturally type — the instance root
|
|
* (`http://curator.example.com`) or the API root (`.../api`) — and return the
|
|
* API root either way.
|
|
*
|
|
* Worth normalizing rather than validating: a root-form URL doesn't fail
|
|
* loudly, it lands on the SPA catch-all, which answers `GET /credentials` with
|
|
* 200 HTML and rejects `POST /credentials` with 405. The operator sees a
|
|
* working Test Connection and a broken export.
|
|
*/
|
|
function normalizeApiUrl(raw) {
|
|
const trimmed = (raw || '').trim().replace(/\/+$/, '');
|
|
if (!trimmed) return '';
|
|
return /\/api$/i.test(trimmed) ? trimmed : `${trimmed}/api`;
|
|
}
|
|
|
|
/**
|
|
* The SPA root — where the Vue router (artist pages) and the served XPI live,
|
|
* NOT the JSON API. Accepts either input form, same as normalizeApiUrl.
|
|
*/
|
|
function webRootFromApiUrl(raw) {
|
|
return normalizeApiUrl(raw).replace(/\/api$/i, '');
|
|
}
|