Skip to main content

max / makenotwork

Return group member email from list_members (Groups p4 M4) The group members endpoint now joins users and returns each member's email alongside role and added-at, so a consumer's admin UI can identify members instead of showing a bare user id. Threads email through DbSyncGroupMember, GroupMemberResponse, and the synckit-client GroupMember type. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-07-23 23:51 UTC
Commit: e33082c72b167b1365702214962ba8f1636108af
Parent: 30b420e
5 files changed, +14 insertions, -4 deletions
@@ -118,6 +118,9 @@ pub struct DbSyncGroupMember {
118 118 pub group_id: SyncGroupId,
119 119 /// The member.
120 120 pub user_id: UserId,
121 + /// The member's account email, joined from `users`. Surfaced so an admin panel
122 + /// can show who a member is rather than a bare user id.
123 + pub email: String,
121 124 /// `admin` | `member`. Reserved for the later per-key permission system; MVP
122 125 /// treats every member as a reader and writer.
123 126 pub role: String,
@@ -115,10 +115,11 @@ pub async fn list_groups_for_user(
115 115 pub async fn list_members(pool: &PgPool, group_id: SyncGroupId) -> Result<Vec<DbSyncGroupMember>> {
116 116 let members = sqlx::query_as::<_, DbSyncGroupMember>(
117 117 r#"
118 - SELECT group_id, user_id, role, sealed_gck, gck_version, added_at
119 - FROM sync_group_members
120 - WHERE group_id = $1
121 - ORDER BY added_at ASC
118 + SELECT m.group_id, m.user_id, u.email, m.role, m.sealed_gck, m.gck_version, m.added_at
119 + FROM sync_group_members m
120 + JOIN users u ON u.id = m.user_id
121 + WHERE m.group_id = $1
122 + ORDER BY m.added_at ASC
122 123 "#,
123 124 )
124 125 .bind(group_id)
@@ -258,6 +258,7 @@ pub(super) async fn list_members(
258 258 .into_iter()
259 259 .map(|m| GroupMemberResponse {
260 260 user_id: m.user_id,
261 + email: m.email,
261 262 role: m.role,
262 263 added_at: m.added_at,
263 264 })
@@ -266,6 +266,8 @@ pub(crate) struct GroupMemberPubkey {
266 266 pub(crate) struct GroupMemberResponse {
267 267 #[schema(value_type = String)]
268 268 user_id: UserId,
269 + /// The member's account email, so an admin panel can identify them.
270 + email: String,
269 271 role: String,
270 272 #[schema(value_type = String)]
271 273 added_at: DateTime<Utc>,
@@ -272,6 +272,9 @@ pub struct SyncGroup {
272 272 pub struct GroupMember {
273 273 /// The member's account id.
274 274 pub user_id: UserId,
275 + /// The member's account email, so a consumer's admin UI can identify them
276 + /// rather than showing a bare user id.
277 + pub email: String,
275 278 /// `"admin"` or `"member"`.
276 279 pub role: String,
277 280 /// When they were added.