the clever type cost twelve errors; the untested rule was the load-bearing one

four fixes from a review, and the first one was leaking out of the plugin.

TYPING. `respond` derived its context from `Parameters<typeof router.get>[1]`.
hono's .get is overloaded, `Parameters<>` resolves the FIRST overload — `[path:
string]` — so index 1 was `undefined`, ctx was `never`, and all nine handlers
errored. twelve errors from one line, in a tree where `bunx tsgo` is shared with
the platform and expected clean. imports hono's `Context` instead. tsgo now
exits 0 tree-wide, which it has not done since this plugin landed.

READONLYWRITES. `/_config/test` was declared as a read-only write so a member
would not meet a 403 on the test button. they always would: authorize.ts asks
isOwnerOnlyRoute before isRequestAllowedAtLevel, and ownerOnly is prefix
matched, so `/_config` already covered it and the entry was inert. removed, and
the reason it SHOULD be inert written down — that request carries the admin
secret in its body, so a member who could call it would hold an oracle pointed
at somebody else's password.

STATUS WROTE ON EVERY POLL. recordProbe ran unconditionally on a GET whose own
comment invites polling, so a read endpoint issued an UPDATE per poll per open
panel. the probe is cached 30s and the row is written only when the verdict or
the version changes — a flapping NPM still records every transition, which is
the case the column exists for. the cache is dropped when the credential does.

DEAD TERNARY. `likelyAuth ? 502 : 502`.

and the tests. 22 over a stubbed fetch, 99% of npm-client.ts. the two that
matter are the 404-means-credential rule and the mint-once-and-retry: both are
silent when broken, and a refactor of either turns a working panel into one
that says "not found" forever. every test uses its own credential because the
token cache is module state — sharing one would let an earlier test's token
satisfy the retry test, which would then pass for the wrong reason.

still not installed, still no settings form, still no db:push. README says so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Officer-Session: 656f5c85-e49a-4f1f-b9f3-042a04b4d043
This commit is contained in:
2026-08-25 15:26:18 +00:00
co-authored by Claude Opus 5
parent 5914e96cf3
commit 3380a482ed
4 changed files with 378 additions and 23 deletions
+11 -5
View File
@@ -34,12 +34,18 @@ export const manifest: PluginManifest = {
label: 'Proxy Manager',
description: 'Nginx Proxy Manager — proxy hosts, redirections, streams and certificates',
// The connection form and the probe. Everything under here reads or writes the admin credential.
// The connection form and the probe. Everything under here reads or writes the admin credential,
// and the match is by PREFIX — so `/_config/test` is covered too, deliberately.
//
// There is no `readOnlyWrites` here, and the omission is the decision rather than an oversight.
// `POST /_config/test` is a genuine read-only write: it validates a credential and stores nothing.
// Declaring it would still change nothing, because `authorize.ts` asks `isOwnerOnlyRoute` before
// `isRequestAllowedAtLevel` — ownerOnly wins, so the entry would be inert.
//
// And it should be inert. The test request carries the admin secret in its BODY. A member who can
// call it can offer this server a credential and be told whether it works, which is an oracle
// pointed at somebody else's password. Testing a connection belongs to whoever owns it.
ownerOnly: ['/_config'],
// Probes that look like writes and are not. Without declaring these a read-level account meets
// what reads as a broken feature: the "test connection" button 403s.
readOnlyWrites: ['/_config/test'],
},
],
};