Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c480facadc | ||
|
|
ed40b6798c |
+3
-3
@@ -1,7 +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';
|
||||
import { stripHopByHop, stripResponseHeaders } from '../sidecar/upstream';
|
||||
|
||||
/**
|
||||
* Mounted at `/api/linkwarden`.
|
||||
@@ -73,7 +73,7 @@ router.all('/lw/*', async (ctx) => {
|
||||
|
||||
try {
|
||||
const upstream = await fetch(`${base}${subpath}${url.search}`, init);
|
||||
return new Response(upstream.body, { status: upstream.status, headers: stripHopByHop(upstream.headers) });
|
||||
return new Response(upstream.body, { status: upstream.status, headers: stripResponseHeaders(upstream.headers) });
|
||||
} catch {
|
||||
return ctx.text('Linkwarden unreachable', 502);
|
||||
}
|
||||
@@ -174,7 +174,7 @@ router.all('/api/*', async (ctx) => {
|
||||
|
||||
try {
|
||||
const upstream = await fetch(`${base}${subpath}${url.search}`, init);
|
||||
return new Response(upstream.body, { status: upstream.status, headers: upstream.headers });
|
||||
return new Response(upstream.body, { status: upstream.status, headers: stripResponseHeaders(upstream.headers) });
|
||||
} catch {
|
||||
return ctx.json({ error: 'Linkwarden unreachable' }, 502);
|
||||
}
|
||||
|
||||
+1
-1
@@ -32,7 +32,7 @@ import type { PluginManifest } from '@@/plugins/manifest';
|
||||
*/
|
||||
export const manifest: PluginManifest = {
|
||||
publisher: 'officerdev',
|
||||
version: '2026.09.05.02',
|
||||
version: '2026.09.05.03',
|
||||
platform: '>=1.0.0',
|
||||
|
||||
label: 'Linkwarden',
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import { createSidecarConnector } from '@@/sidecar/connect';
|
||||
import { API_URL } from '@@/officer-url.mjs';
|
||||
import { getLinkwardenBase, stripHopByHop, redactPath } from './upstream';
|
||||
import { getLinkwardenBase, stripHopByHop, stripResponseHeaders, redactPath } from './upstream';
|
||||
|
||||
// The officer-linkwarden sidecar: a singleton process that registers with the API server and owns a
|
||||
// transparent reverse-proxy to a self-hosted Linkwarden. The platform API is a thin forwarder to this;
|
||||
@@ -86,7 +86,7 @@ Bun.serve({
|
||||
|
||||
return new Response(upstream.body, {
|
||||
status: upstream.status,
|
||||
headers: stripHopByHop(upstream.headers),
|
||||
headers: stripResponseHeaders(upstream.headers),
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
@@ -79,6 +79,29 @@ export function stripHopByHop(src: Headers): Headers {
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Headers for a response we are handing back, which is NOT the same as headers for a request.
|
||||
*
|
||||
* ── `content-encoding` has to go, and finding out why cost a 502 ──
|
||||
*
|
||||
* `fetch` decompresses a gzipped response body before anyone sees it. The headers it returns still say
|
||||
* `content-encoding: gzip`, so copying them onto the decoded bytes tells the client to gunzip plain
|
||||
* JSON — `Decompression error: ZlibError`, surfacing as a 502 from the catch below.
|
||||
*
|
||||
* It hides well. A 401 body is a few bytes and is not compressed, so every unauthenticated probe passes
|
||||
* and the first REAL request is the one that fails. Measured exactly that way on alpha: the door
|
||||
* answered 401 correctly without a token and 502 with one.
|
||||
*
|
||||
* `content-length` goes with it for the same reason — it describes the compressed length, and the body
|
||||
* being handed on is not that length.
|
||||
*/
|
||||
export function stripResponseHeaders(src: Headers): Headers {
|
||||
const out = stripHopByHop(src);
|
||||
out.delete('content-encoding');
|
||||
out.delete('content-length');
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
Paths are logged; tokens are not.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user