Files
forge/services/convert/notification.go
zeekayandHanzo Dev aa87442fdd Record upstream authorship centrally in NOTICE instead of in every file header
Source files keep their SPDX-License-Identifier and Hanzo copyright line; the
Gitea, Gogs and LFS Test Server copyrights they used to repeat now live once in
NOTICE, alongside LICENSE. The header lint accepts any copyright line above the
SPDX line, so it no longer demands an upstream one.

Co-authored-by: Hanzo Dev <[email protected]>
2026-09-10 17:50:35 -07:00

108 lines
3.8 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package convert
import (
"context"
"net/url"
activities_model "github.com/hanzoai/git/models/activities"
access_model "github.com/hanzoai/git/models/perm/access"
"github.com/hanzoai/git/modules/log"
api "github.com/hanzoai/git/modules/structs"
)
// ToNotificationThread convert a Notification to api.NotificationThread
func ToNotificationThread(ctx context.Context, n *activities_model.Notification) *api.NotificationThread {
result := &api.NotificationThread{
ID: n.ID,
Unread: !(n.Status == activities_model.NotificationStatusRead || n.Status == activities_model.NotificationStatusPinned),
Pinned: n.Status == activities_model.NotificationStatusPinned,
UpdatedAt: n.UpdatedUnix.AsTime(),
URL: n.APIURL(),
}
// since user only get notifications when he has access to use minimal access mode
if n.Repository == nil {
return result
}
perm, err := access_model.GetIndividualUserRepoPermission(ctx, n.Repository, n.User)
if err != nil {
log.Error("GetIndividualUserRepoPermission failed: %v", err)
return result
}
// if the user has been revoked access to the repo, do not leak repo or subject info
if !perm.HasAnyUnitAccessOrPublicAccess() {
return result
}
result.Repository = ToRepo(ctx, n.Repository, perm)
// This permission is not correct and we should not be reporting it
for repository := result.Repository; repository != nil; repository = repository.Parent {
repository.Permissions = nil
}
// handle Subject
switch n.Source {
case activities_model.NotificationSourceIssue:
result.Subject = &api.NotificationSubject{Type: api.NotifySubjectIssue}
if n.Issue != nil {
result.Subject.Title = n.Issue.Title
result.Subject.URL = n.Issue.APIURL(ctx)
result.Subject.HTMLURL = n.Issue.HTMLURL(ctx)
result.Subject.State = api.NotifySubjectStateType(n.Issue.State())
comment, err := n.Issue.GetLastComment(ctx)
if err == nil && comment != nil {
result.Subject.LatestCommentURL = comment.APIURL(ctx)
result.Subject.LatestCommentHTMLURL = comment.HTMLURL(ctx)
}
}
case activities_model.NotificationSourcePullRequest:
result.Subject = &api.NotificationSubject{Type: api.NotifySubjectPull}
if n.Issue != nil {
result.Subject.Title = n.Issue.Title
result.Subject.URL = n.Issue.APIURL(ctx)
result.Subject.HTMLURL = n.Issue.HTMLURL(ctx)
result.Subject.State = api.NotifySubjectStateType(n.Issue.State())
comment, err := n.Issue.GetLastComment(ctx)
if err == nil && comment != nil {
result.Subject.LatestCommentURL = comment.APIURL(ctx)
result.Subject.LatestCommentHTMLURL = comment.HTMLURL(ctx)
}
if err := n.Issue.LoadPullRequest(ctx); err == nil &&
n.Issue.PullRequest != nil &&
n.Issue.PullRequest.HasMerged {
result.Subject.State = api.NotifySubjectStateMerged
}
}
case activities_model.NotificationSourceCommit:
url := n.Repository.HTMLURL() + "/commit/" + url.PathEscape(n.CommitID)
result.Subject = &api.NotificationSubject{
Type: api.NotifySubjectCommit,
Title: n.CommitID,
URL: url,
HTMLURL: url,
}
case activities_model.NotificationSourceRepository:
result.Subject = &api.NotificationSubject{
Type: api.NotifySubjectRepository,
Title: n.Repository.FullName(),
// FIXME: this is a relative URL, rather useless and inconsistent, but keeping for backwards compat
URL: n.Repository.Link(),
HTMLURL: n.Repository.HTMLURL(),
}
}
return result
}
// ToNotifications convert list of Notification to api.NotificationThread list
func ToNotifications(ctx context.Context, nl activities_model.NotificationList) []*api.NotificationThread {
result := make([]*api.NotificationThread, 0, len(nl))
for _, n := range nl {
result = append(result, ToNotificationThread(ctx, n))
}
return result
}