the extension door answered 500, because the shared proxy wants a user
`createSidecarProxy` is for AUTHENTICATED proxying. Its forwarder does
`ctx.get('user').id`, because every other sidecar behind it is reached by
somebody holding an Officer session. This door is declared in
`unauthenticatedPaths`, so there is no user, and mounting the factory's router
there answered every request with `TypeError: undefined is not an object` as a
500.
Found on alpha with a real request rather than by reading: the authenticated door
returned 401 as it should and the extension door returned 500.
Vault hand-writes its public router for exactly this reason. I reached for the
shared thing because it was shared, which is the wrong reason - it is shared
among routes that have a caller, and this one deliberately does not.
The factory is still constructed and still earns it: getHttpUrl(), so the port
the sidecar announced is learned in one place, and registerProxiedPrefix, which
runs at construction and keeps the body parser off /api/linkwarden/lw. Neither of
those is the router.
This commit is contained in:
+46
-1
@@ -1,6 +1,7 @@
|
||||
import { createRouter } from '@@/create-router';
|
||||
import { createSidecarProxy } from '@@/sidecar/create-proxy';
|
||||
import { getLinkwardenConfig, setLinkwardenConfig, getAccount } from '../db/queries';
|
||||
import { stripHopByHop } from '../sidecar/upstream';
|
||||
|
||||
/**
|
||||
* Mounted at `/api/linkwarden`.
|
||||
@@ -32,7 +33,51 @@ const proxy = createSidecarProxy({
|
||||
timeoutSeconds: 120,
|
||||
});
|
||||
|
||||
router.route('/lw', proxy.router);
|
||||
/*
|
||||
── The factory's ROUTER is not used, and that is the whole reason this is written out ──
|
||||
|
||||
`createSidecarProxy` is for AUTHENTICATED proxying: its forwarder does `ctx.get('user').id`, because
|
||||
every other sidecar behind it is reached by somebody with an Officer session. This door is declared in
|
||||
`unauthenticatedPaths`, so there is no user, and mounting the factory's router here answered every
|
||||
request with `TypeError: undefined is not an object` as a 500. Measured on alpha, not reasoned about.
|
||||
|
||||
Vault hand-writes its public router for exactly this reason and I should have followed it rather than
|
||||
reaching for the shared thing because it was shared.
|
||||
|
||||
What the factory IS still used for, and why it is still constructed: `getHttpUrl()`, so the port the
|
||||
sidecar announced is discovered in one place; and `registerProxiedPrefix`, which runs at construction
|
||||
and tells the body parser to leave `/api/linkwarden/lw` alone. Both matter, and neither is the router.
|
||||
*/
|
||||
router.all('/lw/*', async (ctx) => {
|
||||
const base = proxy.getHttpUrl();
|
||||
if (!base) return ctx.text('the Linkwarden sidecar is not running', 503);
|
||||
|
||||
const url = new URL(ctx.req.url);
|
||||
const subpath = url.pathname.slice('/api/linkwarden/lw'.length) || '/';
|
||||
|
||||
/*
|
||||
Authorization is FORWARDED, not replaced — the opposite of the door below.
|
||||
|
||||
The client's own credential is the only one in play here, and Linkwarden is the only thing entitled
|
||||
to judge it. Officer adds no security to a stock extension because it has nothing to check: this is
|
||||
the same exposure through one address instead of two, which is what lets Linkwarden stop listening
|
||||
on its own.
|
||||
*/
|
||||
const hasBody = ctx.req.method !== 'GET' && ctx.req.method !== 'HEAD';
|
||||
const init: RequestInit & { duplex?: 'half' } = {
|
||||
method: ctx.req.method,
|
||||
headers: stripHopByHop(ctx.req.raw.headers),
|
||||
redirect: 'manual',
|
||||
...(hasBody ? { body: ctx.req.raw.body, duplex: 'half' as const } : {}),
|
||||
};
|
||||
|
||||
try {
|
||||
const upstream = await fetch(`${base}${subpath}${url.search}`, init);
|
||||
return new Response(upstream.body, { status: upstream.status, headers: stripHopByHop(upstream.headers) });
|
||||
} catch {
|
||||
return ctx.text('Linkwarden unreachable', 502);
|
||||
}
|
||||
});
|
||||
|
||||
// ── where the instance is ────────────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
Reference in New Issue
Block a user