Fix #359: org profile parity + avatar #366

Merged
crueber merged 1 commit from fix/issue-359 into main 2026-09-12 01:17:24 +00:00
Owner

Fixes #359 (org profile parity + avatar: location/timezone/bio fields + avatar storage).

Backend (internal/identity): org.json gains append-only location/timezone/bio_markdown (owner-profile spelling + limits, duplicated constants per law 8) plus avatar_content_type/avatar_updated_at pointer; PUT /api/v1/orgs/{org} carries all five profile fields (full-document replace, body cap 128 KiB); new GET/PUT/DELETE /api/v1/orgs/{org}/avatar serves bucket-backed bytes at orgs//avatar (2 MiB cap → 413, PNG/JPEG/GIF/WebP magic-sniff → 415, owner-only writes, public GET, immutable max-age); DeleteOrg removes the avatar; old readers ignore new keys (law 5, pinned both directions).

Frontend: SDK orgs.avatar.url/upload/remove; lib/org-profile.js helpers; Org.jsx ProfileTab edits all fields + avatar upload/remove; avatar + fields render on /:owner and the settings header. No new deps.

Decisions (docs/features/01 §Decisions): NO website field (parity target has none); bytes-first-pointer-second so GET org stays 1 round trip.

Tests: identity -race green, 96.3% coverage (≥95% gate); node 700/703 (3 smoke fails are environmental — live :8080 instance in setup-only mode, identical on clean main); vite+esbuild clean; gofmt/vet clean.

Fixes #359 (org profile parity + avatar: location/timezone/bio fields + avatar storage). Backend (internal/identity): org.json gains append-only location/timezone/bio_markdown (owner-profile spelling + limits, duplicated constants per law 8) plus avatar_content_type/avatar_updated_at pointer; PUT /api/v1/orgs/{org} carries all five profile fields (full-document replace, body cap 128 KiB); new GET/PUT/DELETE /api/v1/orgs/{org}/avatar serves bucket-backed bytes at orgs/<org>/avatar (2 MiB cap → 413, PNG/JPEG/GIF/WebP magic-sniff → 415, owner-only writes, public GET, immutable max-age); DeleteOrg removes the avatar; old readers ignore new keys (law 5, pinned both directions). Frontend: SDK orgs.avatar.url/upload/remove; lib/org-profile.js helpers; Org.jsx ProfileTab edits all fields + avatar upload/remove; avatar + fields render on /:owner and the settings header. No new deps. Decisions (docs/features/01 §Decisions): NO website field (parity target has none); bytes-first-pointer-second so GET org stays 1 round trip. Tests: identity -race green, 96.3% coverage (≥95% gate); node 700/703 (3 smoke fails are environmental — live :8080 instance in setup-only mode, identical on clean main); vite+esbuild clean; gofmt/vet clean.
org.json gains append-only location/timezone/bio_markdown with the
owner-profile spelling and limits, plus avatar_content_type/
avatar_updated_at pointer; PUT /api/v1/orgs/{org} carries all five
profile fields (full-document replace); GET/PUT/DELETE
/api/v1/orgs/{org}/avatar serves bucket-backed bytes (2 MiB cap,
PNG/JPEG/GIF/WebP magic-sniff, owner-only writes); DeleteOrg removes
the avatar; UI renders avatar + fields on /:owner and /:org/settings.
Decision: no website field (owner profile has none). Back-compat:
old readers ignore new keys (pinned by test).
Sign in to join this conversation.
No description provided.