mirror of
https://github.com/hanzoai/forge.git
synced 2026-10-30 15:00:39 +00:00
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]>
130 lines
3.7 KiB
Go
130 lines
3.7 KiB
Go
// Copyright 2026 Hanzo AI, Inc. All rights reserved.
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package repository
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/hanzoai/git/models/db"
|
|
issues_model "github.com/hanzoai/git/models/issues"
|
|
"github.com/hanzoai/git/models/perm"
|
|
access_model "github.com/hanzoai/git/models/perm/access"
|
|
repo_model "github.com/hanzoai/git/models/repo"
|
|
user_model "github.com/hanzoai/git/models/user"
|
|
|
|
"github.com/hanzoai/builder"
|
|
)
|
|
|
|
func AddOrUpdateCollaborator(ctx context.Context, repo *repo_model.Repository, u *user_model.User, mode perm.AccessMode) error {
|
|
// only allow valid access modes, read, write and admin
|
|
if mode < perm.AccessModeRead || mode > perm.AccessModeAdmin {
|
|
return perm.ErrInvalidAccessMode
|
|
}
|
|
|
|
if err := repo.LoadOwner(ctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
if user_model.IsUserBlockedBy(ctx, u, repo.OwnerID) || user_model.IsUserBlockedBy(ctx, repo.Owner, u.ID) {
|
|
return user_model.ErrBlockedUser
|
|
}
|
|
|
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
|
collaboration, has, err := db.Get[repo_model.Collaboration](ctx, builder.Eq{
|
|
"repo_id": repo.ID,
|
|
"user_id": u.ID,
|
|
})
|
|
if err != nil {
|
|
return err
|
|
} else if has {
|
|
if collaboration.Mode == mode {
|
|
return nil
|
|
}
|
|
if _, err = db.GetEngine(ctx).
|
|
Where("repo_id=?", repo.ID).
|
|
And("user_id=?", u.ID).
|
|
Cols("mode").
|
|
Update(&repo_model.Collaboration{
|
|
Mode: mode,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
} else if err = db.Insert(ctx, &repo_model.Collaboration{
|
|
RepoID: repo.ID,
|
|
UserID: u.ID,
|
|
Mode: mode,
|
|
}); err != nil {
|
|
return err
|
|
}
|
|
|
|
return access_model.RecalculateUserAccess(ctx, repo, u.ID)
|
|
})
|
|
}
|
|
|
|
// DeleteCollaboration removes collaboration relation between the user and repository.
|
|
func DeleteCollaboration(ctx context.Context, repo *repo_model.Repository, collaborator *user_model.User) (err error) {
|
|
collaboration := &repo_model.Collaboration{
|
|
RepoID: repo.ID,
|
|
UserID: collaborator.ID,
|
|
}
|
|
|
|
return db.WithTx(ctx, func(ctx context.Context) error {
|
|
if has, err := db.GetEngine(ctx).Delete(collaboration); err != nil {
|
|
return err
|
|
} else if has == 0 {
|
|
return nil
|
|
}
|
|
|
|
if err := repo.LoadOwner(ctx); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = access_model.RecalculateAccesses(ctx, repo); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = repo_model.WatchRepo(ctx, collaborator, repo, false); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err = ReconsiderWatches(ctx, repo, collaborator); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Unassign a user from any issue (s)he has been assigned to in the repository
|
|
return ReconsiderRepoIssuesAssignee(ctx, repo, collaborator)
|
|
})
|
|
}
|
|
|
|
func ReconsiderRepoIssuesAssignee(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
|
if canAssigned, err := access_model.CanBeAssigned(ctx, user, repo); err != nil || canAssigned {
|
|
return err
|
|
}
|
|
|
|
if _, err := db.GetEngine(ctx).Where(builder.Eq{"assignee_id": user.ID}).
|
|
In("issue_id", builder.Select("id").From("issue").Where(builder.Eq{"repo_id": repo.ID})).
|
|
Delete(&issues_model.IssueAssignees{}); err != nil {
|
|
return fmt.Errorf("Could not delete assignee[%d] %w", user.ID, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ReconsiderWatches(ctx context.Context, repo *repo_model.Repository, user *user_model.User) error {
|
|
if has, err := access_model.HasAnyUnitAccess(ctx, user.ID, repo); err != nil || has {
|
|
return err
|
|
}
|
|
if err := repo_model.WatchRepo(ctx, user, repo, false); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Remove all stopwatches a user has running in the repository
|
|
if err := issues_model.RemoveStopwatchesByRepoID(ctx, user.ID, repo.ID); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Remove all IssueWatches a user has subscribed to in the repository
|
|
return issues_model.RemoveIssueWatchersByRepoID(ctx, user.ID, repo.ID)
|
|
}
|