import Member from '../../models/member.js' import { requireAuth } from '../../utils/auth.js' export default defineEventHandler(async (event) => { const member = await requireAuth(event) const memberId = member._id const topics = member.board?.topics || [] if (!topics.length) { return { suggestions: [] } } const query = getQuery(event) const filterTag = query.tag || null let myTopics = topics if (filterTag) { myTopics = myTopics.filter((t) => t.tagSlug === filterTag) } if (!myTopics.length) { return { suggestions: [] } } const mySlugs = myTopics.map((t) => t.tagSlug) const candidates = await Member.find({ _id: { $ne: memberId }, status: 'active', 'board.topics.tagSlug': { $in: mySlugs }, }) .select('name avatar craftTags circle board privacy') .lean() if (!candidates.length) { return { suggestions: [] } } const myTopicMap = {} for (const t of myTopics) { myTopicMap[t.tagSlug] = t.state } const suggestions = [] for (const candidate of candidates) { const theirTopics = candidate.board?.topics || [] const matchingTags = [] for (const theirTopic of theirTopics) { const myState = myTopicMap[theirTopic.tagSlug] if (!myState) continue matchingTags.push({ tagSlug: theirTopic.tagSlug, yourState: myState, theirState: theirTopic.state, }) } if (!matchingTags.length) continue // Privacy filter: only expose fields the candidate allows to other members const privacy = candidate.privacy || {} const filtered = { _id: candidate._id, name: candidate.name, circle: candidate.circle, } const avatarPrivacy = privacy.avatar || 'public' if (avatarPrivacy === 'public' || avatarPrivacy === 'members') { filtered.avatar = candidate.avatar } const craftTagsPrivacy = privacy.craftTags || 'members' if (craftTagsPrivacy === 'public' || craftTagsPrivacy === 'members') { filtered.craftTags = candidate.craftTags } // Expose slackHandle only when the candidate has opted into peer support. // Slack handle is the contact-in-place path — without it, there is no way // for the current member to reach out. if (candidate.board?.offerPeerSupport && candidate.board?.slackHandle) { filtered.slackHandle = candidate.board.slackHandle } suggestions.push({ member: filtered, matchingTags, matchCount: matchingTags.length, }) } suggestions.sort((a, b) => b.matchCount - a.matchCount) return { suggestions } })