api: five reads that were declared as writes are now GET
The permission model being built reads the HTTP method to decide whether a
non-owner may make a call: safe methods are reads, everything else is a write.
That only works if the method tells the truth. These five read something and
returned it while announcing themselves as writes, so a member would have been
denied a read they are entitled to because of a habit in how the route was
declared.
/api/file-browser/video-info POST {url} -> GET ?url=
/api/file-browser/video-playlist POST {url} -> GET ?url=
/api/server-settings/ocr/models POST {url} -> GET ?url=
/api/transmission/_officer/port-test POST -> GET
/api/jellyfin/_config/:id/test POST|GET -> GET only
The last one already answered to both, which is worse than either: a method that
means nothing cannot be the thing authorisation reads.
Deliberately stops at five. A sweep of all 100 mutating routes found many more
reads wearing POST, and they are staying, for two reasons that are not going
away: some need a request body GET cannot carry (/stt takes multipart audio;
/tts, /ocr, /transcribe take payloads), and some carry a credential, where a
query string is the wrong place — access logs, shell history and Referer headers
all capture those, request bodies do not (/tts/voices takes an apiKey, the four
/test endpoints take connection secrets, /local-providers/probe takes auth).
So the method alone can never carry the permission model, and the registry will
need an explicit per-route classification regardless. Converting these five is
worth it because it is free; converting the rest would be a breaking change
across 117 mobile call sites that buys nothing.
Web callers updated in the same commit; the sidecar contract comments now match.
Mobile has exactly one caller to change — transmissionPortTest in
packages/core/src/services/transmission.ts — and no shim was added, because an
endpoint answering to both methods is the problem this commit exists to fix.
docs/api-method-changes-2026-08-06.md is the handoff for the mobile team: what
changed, the one line to edit, what deliberately did NOT change and why, and how
to verify.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -32,7 +32,9 @@ export const OCRSection = () => {
|
||||
const fetchModels = async (u: string) => {
|
||||
setModelsLoading(true);
|
||||
try {
|
||||
const res = await client.post<{ models?: string[]; error?: string }>('/server-settings/ocr/models', { url: u });
|
||||
const res = await client.get<{ models?: string[]; error?: string }>(
|
||||
`/server-settings/ocr/models?url=${encodeURIComponent(u)}`,
|
||||
);
|
||||
if (res.models) setModels(res.models);
|
||||
else setModels([]);
|
||||
} catch {
|
||||
@@ -111,7 +113,9 @@ export const OCRSection = () => {
|
||||
className="p-0.5 rounded hover:bg-duck-dark/10 dark:hover:bg-foreground/10 cursor-pointer transition-colors disabled:opacity-40"
|
||||
title="Refresh models"
|
||||
>
|
||||
<RefreshCw className={`h-3 w-3 text-duck-dark/50 dark:text-foreground/50 ${modelsLoading ? 'animate-spin' : ''}`} />
|
||||
<RefreshCw
|
||||
className={`h-3 w-3 text-duck-dark/50 dark:text-foreground/50 ${modelsLoading ? 'animate-spin' : ''}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
{models.length > 0 ? (
|
||||
@@ -121,7 +125,9 @@ export const OCRSection = () => {
|
||||
</SelectTrigger>
|
||||
<SelectContent className="z-[600] max-h-[300px]">
|
||||
{models.map((m) => (
|
||||
<SelectItem key={m} value={m}>{m}</SelectItem>
|
||||
<SelectItem key={m} value={m}>
|
||||
{m}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
Reference in New Issue
Block a user