Three small cleanups in the org-listing path. Independent — can land together or separately.
1. Batch joinable-orgs fetch
internal/api/v1beta1connect/user.go:776-790 and :839-854 loop calling orgService.Get(ctx, id) per joinable org ID. For a domain with K trusted orgs, K DB roundtrips.
Replace with a single orgService.GetByIDs(ctx, ids) call. Repository.GetByIDs already exists; expose on the service if needed.
2. Extract joinableOrgsForEmail helper
ListOrganizationsByUser and ListOrganizationsByCurrentUser carry ~30 near-identical lines each (list joinable IDs → fetch → transform → append). Only difference is the SU-skip guard in the current-user variant.
func (h *ConnectHandler) joinableOrgsForEmail(ctx context.Context, email string) ([]*frontierv1beta1.Organization, error)
SU-skip stays in the caller as one line. Pairs naturally with #1 — the batch fetch lands inside this helper.
3. Add membership.IsOrgMember(principal, orgID) (bool, error)
Two callers fetch all of a user's orgs then linear-scan for one ID:
core/invitation/service.go — isUserOrgMember
core/domain/service.go — ListJoinableOrgsByDomain (slices.Contains per trusted domain)
Add a direct indexed lookup on the membership service; swap both call sites.
Three small cleanups in the org-listing path. Independent — can land together or separately.
1. Batch joinable-orgs fetch
internal/api/v1beta1connect/user.go:776-790and:839-854loop callingorgService.Get(ctx, id)per joinable org ID. For a domain with K trusted orgs, K DB roundtrips.Replace with a single
orgService.GetByIDs(ctx, ids)call.Repository.GetByIDsalready exists; expose on the service if needed.2. Extract
joinableOrgsForEmailhelperListOrganizationsByUserandListOrganizationsByCurrentUsercarry ~30 near-identical lines each (list joinable IDs → fetch → transform → append). Only difference is the SU-skip guard in the current-user variant.SU-skip stays in the caller as one line. Pairs naturally with #1 — the batch fetch lands inside this helper.
3. Add
membership.IsOrgMember(principal, orgID) (bool, error)Two callers fetch all of a user's orgs then linear-scan for one ID:
core/invitation/service.go—isUserOrgMembercore/domain/service.go—ListJoinableOrgsByDomain(slices.Containsper trusted domain)Add a direct indexed lookup on the membership service; swap both call sites.