put the plugin's own routes ahead of the proxy's catch-all

/session and /_web/* were both dead. createSidecarProxy registers all('/*')
when it is CALLED, so anything added to proxy.router afterwards never matches —
hono goes in registration order and the wildcard had already claimed
everything.

The symptom was `[gitea] proxy POST /session -> 500`: forwarded to a sidecar
with no such route, answered "internal error", and looking exactly like the
sidecar being broken. Which is what the panel showed.

A comment two lines above the bug claimed these were "registered BEFORE the
proxy's /*". They were not, and writing that down did not make it so.

Now a router of our own with the proxy's mounted under it at the end, so
everything above genuinely wins.
This commit is contained in:
officer
2026-08-19 08:51:46 +00:00
parent e7f05f66d2
commit 0d88a1259b
+19 -6
View File
@@ -1,4 +1,5 @@
import { randomBytes } from 'node:crypto';
import { createRouter } from '@@/create-router';
import { createSidecarProxy } from '@@/sidecar/create-proxy';
import { mountPrefix } from '@@/plugins/manifest';
import { manifest } from '../manifest';
@@ -31,11 +32,20 @@ const PREFIX = `/api${mountPrefix({ appName: 'gitea', manifest })}`;
const proxy = createSidecarProxy({ name: 'gitea', prefix: PREFIX });
/**
* Mint the frame cookie. Registered BEFORE the proxy's `/*`, because hono matches in registration order
* and the wildcard would otherwise swallow it and forward `/session` to the sidecar, which has no such
* route and would answer 404 — a 404 that looks exactly like the plugin being broken.
* OUR routes first, then the proxy's catch-all.
*
* `createSidecarProxy` registers `all('/*')` when it is CALLED, so anything added to `proxy.router`
* afterwards is dead — hono matches in registration order and the wildcard has already claimed
* everything. Both routes below were added that way at first, and the symptom was
* `POST /session -> 500`: forwarded to a sidecar that has no such route, reported as "internal error",
* and looking for all the world like the sidecar being broken.
*
* So this is a router of our own, with the proxy's mounted UNDER it at the end.
*/
proxy.router.post('/session', (ctx) => {
const router_ = createRouter();
/** Mint the frame cookie for the iframe, which cannot carry a bearer token. */
router_.post('/session', (ctx) => {
const user = ctx.get('user') as { id: number } | undefined;
if (!user) return ctx.json({ error: 'not authenticated' }, 401);
ctx.header('set-cookie', mintFrameCookie(user.id, PREFIX));
@@ -53,7 +63,7 @@ proxy.router.post('/session', (ctx) => {
* So this one path forwards the browser's request itself, headers and all. Everything else about the
* plugin still goes through the shared proxy.
*/
proxy.router.all('/_web/*', async (ctx) => {
router_.all('/_web/*', async (ctx) => {
const userId = frameUser(ctx.req.header('cookie') ?? null);
// Deliberately terse. Whoever reaches this without a cookie is a stray request, not a person — the
// panel mints one before the frame loads.
@@ -93,7 +103,10 @@ proxy.router.all('/_web/*', async (ctx) => {
}
});
export const router = proxy.router;
// Last, so everything above wins.
router_.route('/', proxy.router);
export const router = router_;
/** Base URL of the sidecar's HTTP server, or null if it has not reported in yet. */
export const getGiteaServerUrl = proxy.getHttpUrl;