feat(mail): 전사 메일함 수집 + 요청자 참여(from/to/cc) 가시성 필터
build-and-push / build (push) Successful in 32s

- 수집 대상을 프로젝트 팀 → 전사 모든 구성원 메일함으로 변경(프로젝트 캐시)
- Message.Cc 수집 + Involves(email): 요청자가 수신·참조된 메일만 노출
- 공동 메모는 그대로(프로젝트 구성원 공유)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
theorose49
2026-06-30 11:11:23 +09:00
co-authored by Claude Opus 4.8
parent 751aa8ed97
commit b4b47e5ed1
2 changed files with 43 additions and 27 deletions
+29 -26
View File
@@ -507,38 +507,41 @@ func (s *Server) handleListProjectMails(w http.ResponseWriter, r *http.Request)
writeJSON(w, http.StatusOK, resp)
return
}
// The cache holds the COMPANY-WIDE aggregate for the domain; we then filter
// per-viewer below (a member only sees mail they're a party to).
var agg []mailsync.Message
if v, ok := s.mailCache.Load(id); ok {
if e := v.(*mailCacheEntry); time.Since(e.at) < mailCacheTTL {
resp["messages"] = e.msgs
writeJSON(w, http.StatusOK, resp)
return
agg = e.msgs
}
}
// impersonation set = project members + PM (the team that corresponds with the client)
var pms []models.ProjectMember
s.db.Where("project_id = ?", id).Find(&pms)
set := map[string]bool{}
for _, m := range pms {
if m.MemberEmail != "" {
set[m.MemberEmail] = true
if agg == nil {
// 전사 모든 구성원 메일함을 impersonate해 도메인 메일을 모은다(구성원은 여러
// 프로젝트에 걸쳐 있으므로 전사 수집 후 프로젝트 도메인으로 필터하는 편이 단순).
var mailboxes []string
s.db.Model(&models.Member{}).Where("status <> ?", "inactive").Pluck("email", &mailboxes)
msgs, err := s.mail.ListForDomain(r.Context(), mailboxes, p.ClientDomain, 200)
if err != nil {
resp["error"] = err.Error()
}
if msgs == nil {
msgs = []mailsync.Message{}
}
agg = msgs
s.mailCache.Store(id, &mailCacheEntry{at: time.Now(), msgs: agg})
}
// per-viewer visibility: only mail the requester sent/received/was CC'd on.
me := s.email(r)
visible := make([]mailsync.Message, 0, len(agg))
for _, m := range agg {
if m.Involves(me) {
visible = append(visible, m)
}
if len(visible) >= 80 {
break
}
}
if p.PMEmail != "" {
set[p.PMEmail] = true
}
mailboxes := make([]string, 0, len(set))
for e := range set {
mailboxes = append(mailboxes, e)
}
msgs, err := s.mail.ListForDomain(r.Context(), mailboxes, p.ClientDomain, 60)
if err != nil {
resp["error"] = err.Error()
}
if msgs == nil {
msgs = []mailsync.Message{}
}
resp["messages"] = msgs
s.mailCache.Store(id, &mailCacheEntry{at: time.Now(), msgs: msgs})
resp["messages"] = visible
writeJSON(w, http.StatusOK, resp)
}