Add hosted API portal: server, EyerollAPIBackend, MCP server, skills#3
Add hosted API portal: server, EyerollAPIBackend, MCP server, skills#3
Conversation
- eyeroll/server/: FastAPI app (main.py) + asyncpg DB helpers (db.py)
+ minimal dark landing page (static/index.html)
Routes: GET / POST /signup POST|GET /api/watch|usage|keys/rotate
PostgreSQL-backed (asyncpg, raw SQL), rate-limited at 20 analyses/day
- eyeroll/backend.py: EyerollAPIBackend — routes watch() calls to the
hosted API when EYEROLL_API_KEY is set; auto-selected in get_backend()
- eyeroll/watch.py: short-circuit for EyerollAPIBackend (calls backend.watch()
directly, skipping local extract/analyze pipeline)
- eyeroll/cli.py: add eyeroll-api to --backend choices; auto-detect
EYEROLL_API_KEY for default backend and parallel worker count
- eyeroll/mcp_server.py: stdio MCP server exposing watch_video,
check_usage, rotate_key tools; entry point: eyeroll-mcp
- plugins/eyeroll/commands/api-{watch,usage,rotate-key}.md: Claude Code
slash command skills wrapping the hosted API endpoints
- pyproject.toml: add [server] optional deps (fastapi, uvicorn, asyncpg,
python-multipart, google-genai, openai); add eyeroll-mcp script entry
https://claude.ai/code/session_01BiJUMiryNmWRbnkzQbzjmH
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f83f171135
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| row = await conn.fetchrow("SELECT id, email, api_key FROM users WHERE email = $1", email) | ||
| if row: | ||
| return dict(row) |
There was a problem hiding this comment.
Stop exposing existing API keys on repeated signup
This branch returns the full existing api_key whenever /signup is called with an email that is already registered, and /signup is unauthenticated. That means anyone who can guess a user's email can retrieve that user's active credential and immediately call protected endpoints as them (including rotating their key). Treat existing-user signups as non-disclosive (or require ownership verification) instead of returning stored keys.
Useful? React with 👍 / 👎.
| import urllib.request | ||
| import json as _json | ||
|
|
||
| payload = _json.dumps({"source": source, "context": context, "max_frames": max_frames}).encode() |
There was a problem hiding this comment.
Upload local media instead of sending filesystem paths
The hosted backend forwards source as a JSON string only, so a call like eyeroll watch ./recording.mp4 sends a client-local path that the API server cannot read from its own filesystem. Because this backend is auto-selected when EYEROLL_API_KEY is set and the docs advertise local-file support, local analyses will fail for those users unless the client uploads file bytes (or explicitly falls back to local processing for local paths).
Useful? React with 👍 / 👎.
| if isinstance(backend, EyerollAPIBackend): | ||
| if verbose: | ||
| print(f"Backend: eyeroll-api (hosted)", file=sys.stderr) | ||
| return backend.watch(source=source, context=context, max_frames=max_frames) |
There was a problem hiding this comment.
Reset backend cache before returning from API fast path
This early return bypasses the normal finally path that calls reset_backend(). Since get_backend() always returns a cached backend once initialized, one watch() call that uses EyerollAPIBackend can pin the process to that backend and cause later watch(..., backend_name="gemini") calls in the same process to still route to the hosted API.
Useful? React with 👍 / 👎.
DB (db.py):
- New api_keys table: users can have multiple named keys
- Auth lookup now joins api_keys → users, returns key_id for usage tracking
- log_usage() tracks key_id per analysis
- Key CRUD: create_key, list_keys, rename_key, delete_key (guards last key)
Server (main.py):
- POST /signup returns {api_key, key_id, key_name}
- GET /api/keys — list all keys for authenticated user
- POST /api/keys — create a new key {name?}
- PATCH /api/keys/{id} — rename a key
- DELETE /api/keys/{id} — revoke a key (400 if last)
- Removed /api/keys/rotate (replaced by create + delete pattern)
MCP server (mcp_server.py):
- Added signup, keys_list, keys_create, keys_rename, keys_delete tools
- signup tool requires no auth (bootstrapping flow)
- Bumped server version to 0.2.0
Claude Code skills (plugins/eyeroll/commands/):
- signup.md — interactive signup flow
- keys-list.md — list keys with masked display
- keys-create.md — create key with optional name
- keys-delete.md — revoke with last-key guard warning
- keys-rename.md — rename by key ID
https://claude.ai/code/session_01BiJUMiryNmWRbnkzQbzjmH
eyeroll/server/: FastAPI app (main.py) + asyncpg DB helpers (db.py)
Routes: GET / POST /signup POST|GET /api/watch|usage|keys/rotate
PostgreSQL-backed (asyncpg, raw SQL), rate-limited at 20 analyses/day
eyeroll/backend.py: EyerollAPIBackend — routes watch() calls to the
hosted API when EYEROLL_API_KEY is set; auto-selected in get_backend()
eyeroll/watch.py: short-circuit for EyerollAPIBackend (calls backend.watch()
directly, skipping local extract/analyze pipeline)
eyeroll/cli.py: add eyeroll-api to --backend choices; auto-detect
EYEROLL_API_KEY for default backend and parallel worker count
eyeroll/mcp_server.py: stdio MCP server exposing watch_video,
check_usage, rotate_key tools; entry point: eyeroll-mcp
plugins/eyeroll/commands/api-{watch,usage,rotate-key}.md: Claude Code
slash command skills wrapping the hosted API endpoints
pyproject.toml: add [server] optional deps (fastapi, uvicorn, asyncpg,
python-multipart, google-genai, openai); add eyeroll-mcp script entry
https://claude.ai/code/session_01BiJUMiryNmWRbnkzQbzjmH