16 Commits

Author SHA1 Message Date
Oliver Hofmann
70fd61608b Log actual tokens and elapsed time for all endpoints incl. streaming
For streaming /v1/chat/completions: inject stream_options.include_usage,
parse usage from SSE chunks, log actual ↑↓ tokens and wall time in the
generator's finally block. Add elapsed time to all second log entries.
2026-05-08 09:47:32 +02:00
Oliver Hofmann
07f6fec4bf Show app version in admin UI and /version endpoint
Embed APP_VERSION build arg in Docker image (default: dev).
build_push.sh passes the git tag as build arg. Proxy exposes
GET /version, admin UI shows it as read-only field in settings.
2026-05-08 09:30:23 +02:00
Oliver Hofmann
0d1ce96c99 Expose /api/ps to show currently loaded model 2026-05-08 09:22:17 +02:00
Oliver Hofmann
f823e7d314 Return 422 when model field is missing and no force_model is set
Prevents an opaque Ollama error from reaching the client by failing fast
with a clear message before the request is forwarded.
2026-05-08 08:04:52 +02:00
Oliver Hofmann
34b108f4df Replace default_model with force_model (model lock)
Removes DEFAULT_MODEL in favour of a force_model setting configurable
via the admin UI. When set, every proxy request's model field is
overridden, preventing uncoordinated model switches during lab sessions.
Updates schemas, admin API, all three proxy endpoints, frontend,
init_db, and docs (README, DOCKERHUB, KURZANLEITUNG).
2026-05-08 08:02:16 +02:00
Oliver Hofmann
cced65693c Log actual Ollama token counts and add user guide
Add a second usage log line after each proxy response with actual ↑prompt ↓completion
token counts from Ollama (prompt_eval_count/eval_count for native endpoints,
usage object for OpenAI endpoint). Also adds KURZANLEITUNG.md for students and
colleagues covering API access, model selection, Python examples, opencode setup,
and quota/admin information.
2026-05-08 07:21:36 +02:00
8d3f9a7661 Fix OpenAI array content, add error logging, Ollama reachability warning
- Normalize OpenAI array-format content to string to fix connection reset
- Add error.log with rotating handler for proxy and stream errors
- Add global unhandled exception handler returning JSON 500
- Write OLLAMA_URL/DEFAULT_MODEL env vars to DB on startup (reset on restart)
- Add extra_hosts to docker-compose.yml for host.docker.internal on Linux
- Show warning in admin UI when Ollama URL is unreachable
- Return reachable: true/false from /api/ollama-models endpoint
2026-05-07 11:43:17 +02:00
Oliver Hofmann
25f19b6ada Show reset date below quota progress bars in admin UI 2026-04-29 09:55:25 +02:00
Oliver Hofmann
dd8f69ecb6 Proxy fixes, streaming support, Admin-UI overhaul
Backend:
- Fix Content-Length mismatch by not forwarding client headers to Ollama
- Proxy /v1/chat/completions directly to Ollama's OpenAI-compatible endpoint
  (eliminates manual Ollama↔OpenAI format conversion, fixes tool use)
- Add streaming support via SSE passthrough
- Fix ollama_url /v1 suffix stripped on save
- Replace BaseHTTPMiddleware with FastAPI global dependency (fixes double logging)
- Add rotating usage log (8 KB, logs key name + model + token estimate + prompt preview)
- Add httpx timeout 300s
- Add activate and delete endpoints for API keys
- Return usage data (tokens/requests) in GET /api/api-keys

Frontend:
- Admin table: remove ID column, status as icon, icon-only action buttons with CSS tooltips
- Add activate + delete buttons; edit available for inactive keys too
- Quota columns: fixed equal width, progress bars with k-unit formatting
- Create form: structured layout matching edit form style
- Edit form: token inputs in k units (÷1000 display, ×1000 on save)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-29 07:48:10 +02:00
Oliver Hofmann
c62cafc202 Store key_prefix for readable key display instead of masked hash
The last-4 of the SHA-256 hash was meaningless for identification.
Now storing the first 12 chars of the plaintext key as key_prefix,
displayed as 'sk-aBcDeFgH••••••••' — consistent with what the user
sees at creation time and how GitHub/OpenAI handle it.
2026-04-28 10:23:37 +02:00
Oliver Hofmann
94368670b7 Reload Ollama models on URL change, pre-select current model
- /api/ollama-models accepts optional url query param to query a different endpoint
- Frontend fetches models on load and on Ollama URL blur
- Keeps current model selected if available, otherwise selects first in list
- Shows loading indicator while fetching
2026-04-28 09:07:53 +02:00
Oliver Hofmann
317c7f0340 Add Docker production build and update README
- Multi-stage Dockerfile: builds frontend, packages with Python backend
- admin.py serves frontend/dist as StaticFiles in production
- docker-entrypoint.sh runs proxy + admin-api, exits cleanly if either dies
- .dockerignore excludes .env, venv, tests, node_modules
- Split requirements.txt (prod) / requirements-dev.txt (dev+test)
- aiofiles added for StaticFiles support
- start.sh: port checks before startup, venv auto-activation, trap cleanup
- vite.config.js: clearScreen disabled
- README rewritten to reflect current architecture

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:34:45 +02:00
Oliver Hofmann
c8235ec274 Refactor to flat APIKey model with quota, admin UI, .env config, and Berlin timezone
- Remove User/Quota models; quota fields now live directly on APIKey
- Admin UI: login, API key management, settings (Ollama URL/model), proxy info display
- .env/.env.example: ADMIN_PASSWORD, PROXY_HOST/PORT, DATABASE_URL, APP_TZ
- Admin API runs on 127.0.0.1 only; proxy host/port configurable
- API keys support optional expires_at; verified against Europe/Berlin timezone
- Daily/monthly quota resets use Europe/Berlin midnight boundary
- Fix all tests to use new flat model; add expiry tests

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 08:21:42 +02:00
Oliver Hofmann
cfa874a4c3 Fix medium/low priority review items; update README
Medium:
- Frontend: Error-Handling in fetchUsers/fetchApiKeys (try/catch)
- Frontend: Loading-Race behoben (Promise.all + .finally)
- Frontend: API-Keys maskiert (nur letzte 4 Zeichen sichtbar)
- Tests: Setup-Code aus test_auth.py in conftest.py konsolidiert
- Tests: Fixture-Scope vereinheitlicht (function statt session)

Low:
- bare except in database.py → except Exception
- datetime.utcnow → datetime.now(timezone.utc) durchgängig
- DateTime(timezone=True) in allen Modell-Spalten
- .gitignore hinzugefügt (.env, *.db, __pycache__, .idea, node_modules)

Docs:
- README aktualisiert (Sicherheit, Konfiguration, Projektstruktur, Tests)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 21:48:26 +02:00
Oliver Hofmann
bf694b79e2 Fix critical/high security and correctness issues from code review
Critical (all fixed):
- bcrypt statt SHA-256 für Passwörter
- API-Keys gehasht in DB, Plaintext nur einmalig zurückgegeben
- DB-Session-Leak behoben (SessionLocal + try/finally, Depends(get_db))
- Admin-Check via is_admin-Spalte statt Hardcoded-Username
- CORS: konfigurierbare Origins via ALLOWED_ORIGINS, kein Wildcard mit Credentials

High (all fixed):
- TOCTOU-Race: check_and_increment_quota mit SELECT FOR UPDATE atomar
- Getrennte Tages-/Monatszähler in Usage + automatische Reset-Logik
- Token-Zählung mit tiktoken (cl100k_base) statt .split()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-27 21:34:17 +02:00
Oliver Hofmann
562f6ecd9c Init 2026-04-27 18:54:27 +02:00