Skip to content

feat: user profile page with avatar (account info, icon/image avatar, sign out)#573

Merged
pancacake merged 2 commits into
HKUDS:devfrom
IliaAvdeev:feat/profile-page
Jun 20, 2026
Merged

feat: user profile page with avatar (account info, icon/image avatar, sign out)#573
pancacake merged 2 commits into
HKUDS:devfrom
IliaAvdeev:feat/profile-page

Conversation

@IliaAvdeev

Copy link
Copy Markdown

Description

Implements the Profile half of #553: a self-service account page for any
signed-in user. Today a regular user can only see their identity in the
Admin → User Management table, and only if they're an admin — this gives every
user one place to see who they're logged in as, pick an avatar, and sign out.

The Admin → User Management redesign (the other half of #553) will follow as a
separate, independent PR.

Frontend — a new /profile route (under the existing (utility) group):

  • Account info: username, role badge, join date.
  • Avatar: upload your own picture or pick from a predefined icon set
    (16 icons × 8 colors). Uploads are cropped to a square and resized to 256px
    client-side via <canvas> (which also strips EXIF), keeping the stored file
    small.
  • Sign out.
  • A shared UserAvatar component (image → picked icon → deterministic fallback
    by username hash). Admins get an amber ring + shield badge, always paired with
    an aria-label/role badge so the cue is never color-only.

Backend — no new dependencies:

  • UserRecord gains an optional, backward-compatible avatar marker
    ("", "icon:<name>:<color>", or "img:<version>"); surfaced on
    /auth/status and /auth/users.
  • New endpoints: GET/PUT /api/v1/auth/profile,
    PUT/DELETE /api/v1/auth/profile/avatar, GET /api/v1/auth/avatar/{user_id}.
  • Uploaded images are validated by magic bytes (PNG/JPEG/WebP only; SVG
    rejected as a stored-XSS vector), capped at 1 MB, stored on disk under
    multi-user/_system/auth/avatars/<user_id>.<ext> with an atomic
    write-then-replace, and served with nosniff + private caching. Deleting a
    user also removes their avatar file.

Related Issues

Module(s) Affected

  • agents
  • api
  • config
  • core
  • knowledge
  • logging
  • services
  • tools
  • utils
  • web (Frontend)
  • docs (Documentation)
  • scripts
  • tests
  • Other: multi_user (identity store / user model)

Checklist

  • I have read and followed the contribution guidelines.
  • My code follows the project's coding standards.
  • I have run pre-commit run --all-files and fixed any issues.
  • I have added relevant tests for my changes.
  • I have updated the documentation (if necessary).
  • My changes do not introduce any new security vulnerabilities.

Additional Notes

Rebased onto the latest dev and verified end-to-end in the Docker build (auth
enabled): login → Profile link in the sidebar → upload an image, pick an
icon/color, remove photo, and sign out all work. Screenshots below.

Design decisions (answering the open questions in #553):

  • Route, not popover — fits the existing (utility) group.
  • Hybrid avatars (upload and an icon set that also serves as the fallback);
    stored in the user record, with image bytes on disk rather than in the user
    JSON.
  • Profile endpoints require auth but not admin; a user can only edit their
    own record, and img: markers can only be set via the validated upload
    endpoint.

Intentional tradeoffs (happy to change):

  • /auth/status now reads the user store to include the avatar marker so the
    sidebar avatar needs no extra request — could be cached for very large user
    stores.
  • set_avatar takes the existing users-file write lock; the sibling
    delete_user/set_role helpers predate it and don't — I left that asymmetry
    alone to keep the diff scoped, happy to align them.
  • With AUTH_ENABLED=false (default localhost mode) the Profile link is hidden
    and the page redirects, matching the existing admin-area behavior.

This is a proposal, not a fixed design. If you disagree with any of it, or want
something added, reworked, split up differently, renamed, or dropped — no problem
at all, I'm happy to discuss and iterate on whatever direction you prefer.

Screenshots

Profile — uploaded image (admin) Profile — picked icon (admin) Profile — regular user
image 02-profile-admin-icon 01-profile-user
Sidebar — expanded Sidebar — collapsed
03-sidebar-expanded 04-sidebar-collapsed

@pancacake

Copy link
Copy Markdown
Collaborator

Thanks for your contribution! Amazing pr, lemmi take the rest.

@pancacake
pancacake merged commit 2729b75 into HKUDS:dev Jun 20, 2026
11 checks passed
pancacake added a commit that referenced this pull request Jun 20, 2026
…uth-status fetch

Follow-up nits on the #573 profile/avatar work:
- UserAvatar: validate an icon marker's icon/color against the known sets and
  fall back to the deterministic avatar for unknown values. The backend
  _ICON_MARKER_RE accepts any [a-z0-9-] color, so a hand-set marker like
  "icon:star:bogus" previously rendered an invisible avatar
  (AVATAR_COLORS[unknown] === undefined).
- ProfileLink: pass role to UserAvatar so admins get the amber ring in the
  sidebar, matching the "amber ring at every size" intent.
- useAuthStatus: share one in-flight /api/v1/auth/status request across the
  hook's consumers (sidebar Admin/Logout/Profile links) so a page load makes a
  single call instead of one per consumer; cleared once settled so a later
  mount (after login/logout) refetches.

Verified: tsc, eslint, web node tests (142).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
vaskoyudha added a commit to vaskoyudha/deeptutor-for-programmer-fork that referenced this pull request Jul 25, 2026
Self-service /profile page (account info, icon/image avatar, sign out)
implementing the profile half of HKUDS#553. Contributed by @IliaAvdeev.

Integration performed during merge onto current dev:
- Migrate ProfileLink and the profile page off the removed AUTH_ENABLED
  build-time export to runtime auth status (auth.ts was refactored on dev
  by HKUDS#577). ProfileLink reads the full status via fetchAuthStatus (it needs
  username/avatar, which the useAuthStatus hook does not expose); the page
  gates on status.enabled.
- Resolve en/zh locale conflicts as a union of dev and PR keys.
- Honor EXIF orientation in createImageBitmap so the primary decode path
  matches the <img> fallback.
- Drop an unreachable AUTH_ENABLED render guard in the profile page.

Verified locally: tsc, eslint, ruff, web node tests (142), pytest
tests/multi_user (81 passed).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
vaskoyudha added a commit to vaskoyudha/deeptutor-for-programmer-fork that referenced this pull request Jul 25, 2026
…uth-status fetch

Follow-up nits on the HKUDS#573 profile/avatar work:
- UserAvatar: validate an icon marker's icon/color against the known sets and
  fall back to the deterministic avatar for unknown values. The backend
  _ICON_MARKER_RE accepts any [a-z0-9-] color, so a hand-set marker like
  "icon:star:bogus" previously rendered an invisible avatar
  (AVATAR_COLORS[unknown] === undefined).
- ProfileLink: pass role to UserAvatar so admins get the amber ring in the
  sidebar, matching the "amber ring at every size" intent.
- useAuthStatus: share one in-flight /api/v1/auth/status request across the
  hook's consumers (sidebar Admin/Logout/Profile links) so a page load makes a
  single call instead of one per consumer; cleared once settled so a later
  mount (after login/logout) refetches.

Verified: tsc, eslint, web node tests (142).

Co-Authored-By: Claude Opus 4.8 <[email protected]>
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.

2 participants