From 61d43cd5f64494410f924fa7e04372e267538535 Mon Sep 17 00:00:00 2001 From: officer Date: Wed, 19 Aug 2026 09:14:45 +0000 Subject: [PATCH] match the appSubUrl shape Gitea actually emits MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The regex expected JSON — "appSubUrl":"…" — and Gitea emits a JavaScript object literal: unquoted key, single quotes, `appSubUrl: ''`. So the single highest-value rewrite in this file silently did nothing on every real page, while every attribute around it rewrote perfectly. The test used the shape I guessed rather than the shape Gitea produces, which is why it passed. Now covers both, with the real form taken verbatim from a live response, and asserts the quote style survives — swapping it would stop the literal parsing. --- sidecar/rewrite.test.ts | 11 +++++++++++ sidecar/rewrite.ts | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/sidecar/rewrite.test.ts b/sidecar/rewrite.test.ts index 7119c6b..79894e2 100644 --- a/sidecar/rewrite.test.ts +++ b/sidecar/rewrite.test.ts @@ -40,6 +40,17 @@ describe('appSubUrl — the line that makes Gitea correct itself', () => { test('an existing subpath is replaced, not appended to', () => { expect(rewriteAppSubUrl('{"appSubUrl":"/git"}', BASE)).toBe(`{"appSubUrl":"${BASE}"}`); }); + + // The shape Gitea ACTUALLY emits — a JavaScript object literal, unquoted key, single quotes. The first + // version of this only matched the JSON form, so it silently did nothing on every real page while the + // attributes around it rewrote perfectly. Taken verbatim from a live response. + test('the JS object-literal form Gitea really emits', () => { + const real = "window.config = {\n appUrl: 'https://gitea.officer.dev/',\n appSubUrl: '',\n};"; + const out = rewriteAppSubUrl(real, BASE); + expect(out).toContain(`appSubUrl: '${BASE}'`); + // The quote style is preserved, or the literal stops parsing. + expect(out).not.toContain(`appSubUrl: "${BASE}"`); + }); }); describe('absolute URLs built from ROOT_URL', () => { diff --git a/sidecar/rewrite.ts b/sidecar/rewrite.ts index d14faf7..ea6ec8b 100644 --- a/sidecar/rewrite.ts +++ b/sidecar/rewrite.ts @@ -74,7 +74,14 @@ export function rewriteAttributes(html: string, base: string): string { * Matched narrowly on the key so a repository whose CONTENT contains the string is untouched. */ export function rewriteAppSubUrl(html: string, base: string): string { - return html.replace(/"appSubUrl"\s*:\s*"[^"]*"/g, `"appSubUrl":"${base}"`); + // Both shapes, because Gitea emits the SECOND one and this originally only matched the first: + // "appSubUrl":"…" JSON, which is what an API response looks like + // appSubUrl: '…' a JavaScript object literal, unquoted key and single quotes — the real page + // Guessing the shape is why this silently did nothing while every attribute around it was rewritten + // correctly. The quote style is captured and reused so the emitted literal stays valid either way. + return html.replace(/(["']?appSubUrl["']?\s*:\s*)(["'])[^"']*\2/g, (_m, head: string, quote: string) => + `${head}${quote}${base}${quote}`, + ); } /**