Add OpenAI (GPT-5.6 Sol) as a second vision provider; gate server settings to a named admin

vision.py now dispatches per-model to _call_anthropic or _call_openai --
same prompt, same schema, same cardimage.py measurements either way, only
the request/response shape differs. Confirmed the existing GRADING_SCHEMA
already satisfies OpenAI's strict-mode requirement (every property listed
in required, additionalProperties:false at every level) with no changes.

Settings gained a second axis: which server key applies now depends on the
selected model's provider, and friends' personal keys are stored per
provider (with a one-time migration from the old single-key localStorage
slot) since a Claude key and an OpenAI key aren't interchangeable.

CARD_GRADER_ADMIN_USER names one username (read from the proxy's forwarded
basic-auth header) who alone may write server settings; everyone else keeps
the same read-only view CARD_GRADER_LOCK used to give everyone, while still
being able to set their own personal key. Deployed here as ninja_hippo.
CARD_GRADER_LOCK remains the fallback when no admin is named.
This commit is contained in:
Barely Removable 2026-08-23 07:39:03 -07:00
parent 9d5c68b093
commit 034761e145
7 changed files with 351 additions and 122 deletions

View file

@ -27,10 +27,18 @@ services:
volumes:
- ./data:/data
environment:
# Refuses settings writes from anyone but the host, so a visitor
# can't overwrite the API key or switch to a pricier model. Set to 0
# temporarily (on the live server only, not here) while setting the
# API key via the app's own Settings screen, then back to 1.
# Only this ONE username (from NPM's basic auth, forwarded upstream)
# may write server settings — model choice, both provider API keys.
# Everyone else gets the same read-only view CARD_GRADER_LOCK used to
# give everyone; they can still set their OWN personal key, which
# never touches server settings at all. Set with the app's Settings
# screen unlocked for ninja_hippo only, so paste keys in there, not
# here.
- CARD_GRADER_ADMIN_USER=ninja_hippo
# CARD_GRADER_LOCK is now redundant with CARD_GRADER_ADMIN_USER set
# (the admin check takes priority) — kept only as the fallback for
# anyone who unsets the admin var and wants the old all-or-nothing
# behaviour back.
- CARD_GRADER_LOCK=1
# This app is reverse-proxied at hippofam.com/cards, not the domain
# root — see app.py's BASE_PATH handling.