Files
forge/models/repo/org_repo.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

203 lines
5.8 KiB
Go

// Copyright 2026 Hanzo AI, Inc. All rights reserved.
// SPDX-License-Identifier: MIT
package repo
import (
"context"
"fmt"
"strings"
"github.com/hanzoai/git/models/db"
org_model "github.com/hanzoai/git/models/organization"
user_model "github.com/hanzoai/git/models/user"
"github.com/hanzoai/builder"
)
// GetOrgRepositories get repos belonging to the given organization
func GetOrgRepositories(ctx context.Context, orgID int64) (RepositoryList, error) {
var orgRepos []*Repository
err := db.GetEngine(ctx).Where("owner_id = ?", orgID).Find(&orgRepos)
return orgRepos, err
}
// GetOrgRepositoryIDs get repo IDs belonging to the given organization
func GetOrgRepositoryIDs(ctx context.Context, orgID int64) (repoIDs []int64, _ error) {
err := db.GetEngine(ctx).Table("repository").Where("owner_id = ?", orgID).Cols("id").Find(&repoIDs)
return repoIDs, err
}
type SearchTeamRepoOptions struct {
db.ListOptions
TeamID int64
// PublicOnly restricts the result (and count) to non-private repositories.
PublicOnly bool
}
func (opts *SearchTeamRepoOptions) toCond() builder.Cond {
cond := builder.NewCond()
if opts.TeamID > 0 {
cond = cond.And(builder.In("id",
builder.Select("repo_id").
From("team_repo").
Where(builder.Eq{"team_id": opts.TeamID}),
))
}
if opts.PublicOnly {
cond = cond.And(builder.Eq{"is_private": false})
}
return cond
}
// GetTeamRepositories returns paginated repositories in team of organization.
func GetTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (RepositoryList, error) {
sess := db.GetEngine(ctx).Where(opts.toCond())
if opts.PageSize > 0 {
sess.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize)
}
var repos []*Repository
return repos, sess.OrderBy("repository.name").
Find(&repos)
}
// CountTeamRepositories returns the number of repositories in team of organization matching opts.
func CountTeamRepositories(ctx context.Context, opts *SearchTeamRepoOptions) (int64, error) {
return db.GetEngine(ctx).Where(opts.toCond()).Count(new(Repository))
}
// AccessibleReposEnvironment operations involving the repositories that are
// accessible to a particular user
type AccessibleReposEnvironment interface {
CountRepos(ctx context.Context) (int64, error)
RepoIDs(ctx context.Context) ([]int64, error)
MirrorRepos(ctx context.Context) (RepositoryList, error)
AddKeyword(keyword string)
SetSort(db.SearchOrderBy)
}
type accessibleReposEnv struct {
org *org_model.Organization
user *user_model.User
team *org_model.Team
teamIDs []int64
keyword string
orderBy db.SearchOrderBy
}
// AccessibleReposEnv builds an AccessibleReposEnvironment for the repositories in `org`
// that are accessible to the specified user.
func AccessibleReposEnv(ctx context.Context, org *org_model.Organization, userID int64) (AccessibleReposEnvironment, error) {
var user *user_model.User
if userID > 0 {
u, err := user_model.GetUserByID(ctx, userID)
if err != nil {
return nil, err
}
user = u
}
teamIDs, err := org.GetUserTeamIDs(ctx, userID)
if err != nil {
return nil, err
}
return &accessibleReposEnv{
org: org,
user: user,
teamIDs: teamIDs,
orderBy: db.SearchOrderByRecentUpdated,
}, nil
}
// AccessibleTeamReposEnv an AccessibleReposEnvironment for the repositories in `org`
// that are accessible to the specified team.
func AccessibleTeamReposEnv(org *org_model.Organization, team *org_model.Team) AccessibleReposEnvironment {
return &accessibleReposEnv{
org: org,
team: team,
orderBy: db.SearchOrderByRecentUpdated,
}
}
func (env *accessibleReposEnv) cond() builder.Cond {
cond := builder.NewCond()
if env.team != nil {
cond = cond.And(builder.Eq{"team_repo.team_id": env.team.ID})
} else {
if env.user == nil || !env.user.IsRestricted {
cond = cond.Or(builder.Eq{
"`repository`.owner_id": env.org.ID,
"`repository`.is_private": false,
})
}
if len(env.teamIDs) > 0 {
cond = cond.Or(builder.In("team_repo.team_id", env.teamIDs))
}
}
if env.keyword != "" {
cond = cond.And(builder.Like{"`repository`.lower_name", strings.ToLower(env.keyword)})
}
return cond
}
func (env *accessibleReposEnv) CountRepos(ctx context.Context) (int64, error) {
repoCount, err := db.GetEngine(ctx).
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
Where(env.cond()).
Distinct("`repository`.id").
Count(&Repository{})
if err != nil {
return 0, fmt.Errorf("count user repositories in organization: %w", err)
}
return repoCount, nil
}
func (env *accessibleReposEnv) RepoIDs(ctx context.Context) ([]int64, error) {
var repoIDs []int64
return repoIDs, db.GetEngine(ctx).
Table("repository").
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id").
Where(env.cond()).
GroupBy("`repository`.id,`repository`." + strings.Fields(string(env.orderBy))[0]).
OrderBy(string(env.orderBy)).
Cols("`repository`.id").
Find(&repoIDs)
}
func (env *accessibleReposEnv) MirrorRepoIDs(ctx context.Context) ([]int64, error) {
repoIDs := make([]int64, 0, 10)
return repoIDs, db.GetEngine(ctx).
Table("repository").
Join("INNER", "team_repo", "`team_repo`.repo_id=`repository`.id AND `repository`.is_mirror=?", true).
Where(env.cond()).
GroupBy("`repository`.id, `repository`.updated_unix").
OrderBy(string(env.orderBy)).
Cols("`repository`.id").
Find(&repoIDs)
}
func (env *accessibleReposEnv) MirrorRepos(ctx context.Context) (RepositoryList, error) {
repoIDs, err := env.MirrorRepoIDs(ctx)
if err != nil {
return nil, fmt.Errorf("MirrorRepoIDs: %w", err)
}
repos := make([]*Repository, 0, len(repoIDs))
if len(repoIDs) == 0 {
return repos, nil
}
return repos, db.GetEngine(ctx).
In("`repository`.id", repoIDs).
Find(&repos)
}
func (env *accessibleReposEnv) AddKeyword(keyword string) {
env.keyword = keyword
}
func (env *accessibleReposEnv) SetSort(orderBy db.SearchOrderBy) {
env.orderBy = orderBy
}