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]>
203 lines
5.8 KiB
Go
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
|
|
}
|