Skip to content

feat(avatars): image upload for users and bots#98

Merged
haowei2000 merged 1 commit into
developfrom
feat/avatar-upload
Jul 7, 2026
Merged

feat(avatars): image upload for users and bots#98
haowei2000 merged 1 commit into
developfrom
feat/avatar-upload

Conversation

@haowei2000

Copy link
Copy Markdown
Collaborator

What

Full avatar image upload for users and bots (optimization #1). Users set their own avatar; a bot's is owner/admin-only. Images are stored in object storage and served over a public URL.

Backend

  • POST /api/v1/users/me/avatar (self) and POST /api/v1/bots/:bot_id/avatar (owner/admin) — body is raw image bytes. Stores to avatars/{kind}/{id}/{uuid}.{ext} and writes the serving URL into the existing avatar_url column.
  • Public GET /api/v1/{users,bots}/:id/avatar/:file — serves the bytes inline with a 1-day cache. Public because an <img src> can't attach a Bearer token and an avatar isn't sensitive; the path is validated (owner = uuid, file = {hex-uuid}.{known-ext}) so a crafted key can't escape the avatars/ prefix.
  • Accepts raster only (png/jpeg/webp/gif), ≤ 5 MiB. SVG is rejected (file_response forces SVG to download, so it'd never render).

Frontend

  • New AvatarUpload component: hover camera overlay → file picker → optimistic object-URL preview while the upload runs, then persists.
  • Wired into the profile editor (Settings) and the bot status editor.

Verification

Tested end-to-end against the local kind stack:

Case Result
User upload 200, byte-perfect round-trip (198 → 198)
Public serve 200 image/png, content-disposition: inline, cache-control: max-age=86400
Persisted GET /users/me returns the new avatar_url
Bot upload (owner/admin) 200
Non-image content-type 400
Empty body 400
No auth 401
UI (Settings) camera-hover affordance renders; picked image uploads and, after a fresh reload, renders from the persisted server URL

🤖 Generated with Claude Code

Add avatar image upload backed by object storage:
- POST /api/v1/users/me/avatar (self) and POST /api/v1/bots/:bot_id/avatar
  (owner/admin) accept raw image bytes, store to avatars/{kind}/{id}/{uuid}.{ext},
  and write the serving URL into the existing avatar_url column.
- Public GET /api/v1/{users,bots}/:id/avatar/:file serves the bytes with an
  inline content-disposition and 1-day cache (an <img src> can't send a Bearer
  token, and the path is validated to stay inside the avatars/ prefix).
- Only raster images (png/jpeg/webp/gif) up to 5 MiB; SVG is rejected because
  file_response forces it to download.

Frontend: AvatarUpload component (hover camera + file picker + optimistic
preview) wired into the profile editor and the bot status editor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@haowei2000
haowei2000 merged commit a5cd2d4 into develop Jul 7, 2026
10 checks passed
@haowei2000
haowei2000 deleted the feat/avatar-upload branch July 7, 2026 00:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant