mirror of
https://github.com/go-gitea/gitea.git
synced 2026-05-28 02:38:44 +09:00
backport #37118 This PR closes remaining `public-only` token gaps in the API by making the restriction apply consistently across repository, organization, activity, notification, and authenticated `/api/v1/user/...` routes. Previously, `public-only` tokens were still able to: - receive private results from some list/search/self endpoints, - access repository data through ID-based lookups, - and reach several authenticated self routes that should remain unavailable for public-only access. This change treats `public-only` as a cross-cutting visibility boundary: - list/search endpoints now filter private resources consistently, - repository lookups enforce the same restriction even when addressed indirectly, - and self routes that inherently expose or mutate private account state now reject `public-only` tokens. --- Generated by a coding agent with Codex 5.2 Co-authored-by: silverwind <me@silverwind.io> Co-authored-by: Claude (Opus 4.7) <noreply@anthropic.com> Co-authored-by: Nicolas <bircni@icloud.com>
This commit is contained in:
+81
-58
@@ -212,6 +212,11 @@ func repoAssignment() func(ctx *context.APIContext) {
|
||||
ctx.APIErrorNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.TokenCanAccessRepo(repo) {
|
||||
ctx.APIErrorNotFound()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,51 +254,66 @@ func checkTokenPublicOnly() func(ctx *context.APIContext) {
|
||||
return
|
||||
}
|
||||
|
||||
// public Only permission check
|
||||
switch {
|
||||
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryRepository):
|
||||
if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public repos")
|
||||
return
|
||||
}
|
||||
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryIssue):
|
||||
if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public issues")
|
||||
return
|
||||
}
|
||||
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryOrganization):
|
||||
if ctx.Org.Organization != nil && ctx.Org.Organization.Visibility != api.VisibleTypePublic {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public orgs")
|
||||
return
|
||||
}
|
||||
if ctx.ContextUser != nil && ctx.ContextUser.IsOrganization() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public orgs")
|
||||
return
|
||||
}
|
||||
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryUser):
|
||||
if ctx.ContextUser != nil && ctx.ContextUser.IsTokenAccessAllowed() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public users")
|
||||
return
|
||||
}
|
||||
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryActivityPub):
|
||||
if ctx.ContextUser != nil && ctx.ContextUser.IsTokenAccessAllowed() && ctx.ContextUser.Visibility != api.VisibleTypePublic {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public activitypub")
|
||||
return
|
||||
}
|
||||
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryNotification):
|
||||
if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public notifications")
|
||||
return
|
||||
}
|
||||
case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryPackage):
|
||||
if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public packages")
|
||||
return
|
||||
for _, category := range requiredScopeCategories {
|
||||
switch category {
|
||||
case auth_model.AccessTokenScopeCategoryRepository:
|
||||
if !ctx.TokenCanAccessRepo(ctx.Repo.Repository) {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public repos")
|
||||
return
|
||||
}
|
||||
case auth_model.AccessTokenScopeCategoryIssue:
|
||||
if !ctx.TokenCanAccessRepo(ctx.Repo.Repository) {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public issues")
|
||||
return
|
||||
}
|
||||
case auth_model.AccessTokenScopeCategoryOrganization:
|
||||
orgPrivate := ctx.Org.Organization != nil && !ctx.Org.Organization.Visibility.IsPublic()
|
||||
userOrgPrivate := ctx.ContextUser != nil && ctx.ContextUser.IsOrganization() && !ctx.ContextUser.Visibility.IsPublic()
|
||||
if orgPrivate || userOrgPrivate {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public orgs")
|
||||
return
|
||||
}
|
||||
case auth_model.AccessTokenScopeCategoryUser:
|
||||
if ctx.ContextUser != nil && ctx.ContextUser.IsTokenAccessAllowed() && !ctx.ContextUser.Visibility.IsPublic() {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public users")
|
||||
return
|
||||
}
|
||||
case auth_model.AccessTokenScopeCategoryActivityPub:
|
||||
if ctx.ContextUser != nil && ctx.ContextUser.IsTokenAccessAllowed() && !ctx.ContextUser.Visibility.IsPublic() {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public activitypub")
|
||||
return
|
||||
}
|
||||
case auth_model.AccessTokenScopeCategoryNotification:
|
||||
if !ctx.TokenCanAccessRepo(ctx.Repo.Repository) {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public notifications")
|
||||
return
|
||||
}
|
||||
case auth_model.AccessTokenScopeCategoryPackage:
|
||||
if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() {
|
||||
ctx.APIError(http.StatusForbidden, "token scope is limited to public packages")
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func rejectPublicOnly() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
if !ctx.PublicOnly {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.APIError(http.StatusForbidden, "this endpoint is not available for public-only tokens")
|
||||
}
|
||||
}
|
||||
|
||||
func contextAuthenticatedUser() func(ctx *context.APIContext) {
|
||||
return func(ctx *context.APIContext) {
|
||||
ctx.ContextUser = ctx.Doer
|
||||
}
|
||||
}
|
||||
|
||||
// if a token is being used for auth, we check that it contains the required scope
|
||||
// if a token is not being used, reqToken will enforce other sign in methods
|
||||
func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeCategory) func(ctx *context.APIContext) {
|
||||
@@ -958,6 +978,8 @@ func Routes() *web.Router {
|
||||
})
|
||||
|
||||
// Notifications (requires 'notifications' scope)
|
||||
// The notifications API is not available for public-only tokens because a user's notifications mix
|
||||
// public and private repository events in the same mailbox.
|
||||
m.Group("/notifications", func() {
|
||||
m.Combo("").
|
||||
Get(reqToken(), notify.ListNotifications).
|
||||
@@ -966,7 +988,7 @@ func Routes() *web.Router {
|
||||
m.Combo("/threads/{id}").
|
||||
Get(reqToken(), notify.GetThread).
|
||||
Patch(reqToken(), notify.ReadThread)
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryNotification))
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryNotification), rejectPublicOnly())
|
||||
|
||||
// Users (requires user scope)
|
||||
m.Group("/users", func() {
|
||||
@@ -1014,8 +1036,9 @@ func Routes() *web.Router {
|
||||
m.Group("/settings", func() {
|
||||
m.Get("", user.GetUserSettings)
|
||||
m.Patch("", bind(api.UserSettingsOptions{}), user.UpdateUserSettings)
|
||||
}, reqToken())
|
||||
m.Combo("/emails").
|
||||
}, rejectPublicOnly())
|
||||
// Email addresses are always private account data.
|
||||
m.Combo("/emails", rejectPublicOnly()).
|
||||
Get(user.ListEmails).
|
||||
Post(bind(api.CreateEmailOption{}), user.AddEmail).
|
||||
Delete(bind(api.DeleteEmailOption{}), user.DeleteEmail)
|
||||
@@ -1047,7 +1070,7 @@ func Routes() *web.Router {
|
||||
|
||||
m.Get("/runs", reqToken(), user.ListWorkflowRuns)
|
||||
m.Get("/jobs", reqToken(), user.ListWorkflowJobs)
|
||||
})
|
||||
}, rejectPublicOnly())
|
||||
|
||||
m.Get("/followers", user.ListMyFollowers)
|
||||
m.Group("/following", func() {
|
||||
@@ -1065,7 +1088,7 @@ func Routes() *web.Router {
|
||||
Post(bind(api.CreateKeyOption{}), user.CreatePublicKey)
|
||||
m.Combo("/{id}").Get(user.GetPublicKey).
|
||||
Delete(user.DeletePublicKey)
|
||||
})
|
||||
}, rejectPublicOnly())
|
||||
|
||||
// (admin:application scope)
|
||||
m.Group("/applications", func() {
|
||||
@@ -1076,7 +1099,7 @@ func Routes() *web.Router {
|
||||
Delete(user.DeleteOauth2Application).
|
||||
Patch(bind(api.CreateOAuth2ApplicationOptions{}), user.UpdateOauth2Application).
|
||||
Get(user.GetOauth2Application)
|
||||
})
|
||||
}, rejectPublicOnly())
|
||||
|
||||
// (admin:gpg_key scope)
|
||||
m.Group("/gpg_keys", func() {
|
||||
@@ -1084,13 +1107,13 @@ func Routes() *web.Router {
|
||||
Post(bind(api.CreateGPGKeyOption{}), user.CreateGPGKey)
|
||||
m.Combo("/{id}").Get(user.GetGPGKey).
|
||||
Delete(user.DeleteGPGKey)
|
||||
})
|
||||
m.Get("/gpg_key_token", user.GetVerificationToken)
|
||||
m.Post("/gpg_key_verify", bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey)
|
||||
}, rejectPublicOnly())
|
||||
m.Get("/gpg_key_token", rejectPublicOnly(), user.GetVerificationToken)
|
||||
m.Post("/gpg_key_verify", rejectPublicOnly(), bind(api.VerifyGPGKeyOption{}), user.VerifyUserGPGKey)
|
||||
|
||||
// (repo scope)
|
||||
m.Combo("/repos", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(user.ListMyRepos).
|
||||
Post(bind(api.CreateRepoOption{}), repo.Create)
|
||||
Post(rejectPublicOnly(), bind(api.CreateRepoOption{}), repo.Create)
|
||||
|
||||
// (repo scope)
|
||||
m.Group("/starred", func() {
|
||||
@@ -1101,22 +1124,22 @@ func Routes() *web.Router {
|
||||
m.Delete("", user.Unstar)
|
||||
}, repoAssignment(), checkTokenPublicOnly())
|
||||
}, reqStarsEnabled(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository))
|
||||
m.Get("/times", repo.ListMyTrackedTimes)
|
||||
m.Get("/stopwatches", repo.GetStopwatches)
|
||||
m.Get("/times", rejectPublicOnly(), repo.ListMyTrackedTimes)
|
||||
m.Get("/stopwatches", rejectPublicOnly(), repo.GetStopwatches)
|
||||
m.Get("/subscriptions", user.GetMyWatchedRepos)
|
||||
m.Get("/teams", org.ListUserTeams)
|
||||
m.Get("/teams", rejectPublicOnly(), org.ListUserTeams)
|
||||
m.Group("/hooks", func() {
|
||||
m.Combo("").Get(user.ListHooks).
|
||||
Post(bind(api.CreateHookOption{}), user.CreateHook)
|
||||
m.Combo("/{id}").Get(user.GetHook).
|
||||
Patch(bind(api.EditHookOption{}), user.EditHook).
|
||||
Delete(user.DeleteHook)
|
||||
}, reqWebhooksEnabled())
|
||||
}, reqWebhooksEnabled(), rejectPublicOnly())
|
||||
|
||||
m.Group("/avatar", func() {
|
||||
m.Post("", bind(api.UpdateUserAvatarOption{}), user.UpdateAvatar)
|
||||
m.Delete("", user.DeleteAvatar)
|
||||
})
|
||||
}, rejectPublicOnly())
|
||||
|
||||
m.Group("/blocks", func() {
|
||||
m.Get("", user.ListBlocks)
|
||||
@@ -1125,8 +1148,8 @@ func Routes() *web.Router {
|
||||
m.Put("", user.BlockUser)
|
||||
m.Delete("", user.UnblockUser)
|
||||
}, context.UserAssignmentAPI(), checkTokenPublicOnly())
|
||||
})
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken())
|
||||
}, rejectPublicOnly())
|
||||
}, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken(), contextAuthenticatedUser(), checkTokenPublicOnly())
|
||||
|
||||
// Repositories (requires repo scope, org scope)
|
||||
m.Post("/org/{org}/repos",
|
||||
@@ -1597,7 +1620,7 @@ func Routes() *web.Router {
|
||||
}, reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly())
|
||||
|
||||
// Organizations
|
||||
m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs)
|
||||
m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), checkTokenPublicOnly(), org.ListMyOrgs)
|
||||
m.Group("/users/{username}/orgs", func() {
|
||||
m.Get("", reqToken(), org.ListUserOrgs)
|
||||
m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions)
|
||||
|
||||
@@ -33,6 +33,7 @@ func listUserOrgs(ctx *context.APIContext, u *user_model.User) {
|
||||
UserID: u.ID,
|
||||
IncludeVisibility: organization.DoerViewOtherVisibility(ctx.Doer, u),
|
||||
}
|
||||
opts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
orgs, maxResults, err := db.FindAndCount[organization.Organization](ctx, opts)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
@@ -192,7 +193,7 @@ func GetAll(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/OrganizationList"
|
||||
|
||||
vMode := []api.VisibleType{api.VisibleTypePublic}
|
||||
if ctx.IsSigned && !ctx.PublicOnly {
|
||||
if ctx.IsSigned {
|
||||
vMode = append(vMode, api.VisibleTypeLimited)
|
||||
if ctx.Doer.IsAdmin {
|
||||
vMode = append(vMode, api.VisibleTypePrivate)
|
||||
@@ -201,13 +202,16 @@ func GetAll(ctx *context.APIContext) {
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
publicOrgs, maxResults, err := user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||
searchOpts := user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
ListOptions: listOptions,
|
||||
Types: []user_model.UserType{user_model.UserTypeOrganization},
|
||||
OrderBy: db.SearchOrderByAlphabetically,
|
||||
Visible: vMode,
|
||||
})
|
||||
}
|
||||
searchOpts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
|
||||
publicOrgs, maxResults, err := user_model.SearchUsers(ctx, searchOpts)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
@@ -487,6 +491,7 @@ func ListOrgActivityFeeds(ctx *context.APIContext) {
|
||||
Date: ctx.FormString("date"),
|
||||
ListOptions: listOptions,
|
||||
}
|
||||
opts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
|
||||
feeds, count, err := feed_service.GetFeeds(ctx, opts)
|
||||
if err != nil {
|
||||
|
||||
@@ -47,9 +47,10 @@ func buildSearchIssuesRepoIDs(ctx *context.APIContext) (repoIDs []int64, allPubl
|
||||
Actor: ctx.Doer,
|
||||
}
|
||||
if ctx.IsSigned {
|
||||
opts.Private = !ctx.PublicOnly
|
||||
opts.Private = true
|
||||
opts.AllLimited = true
|
||||
}
|
||||
opts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
if ctx.FormString("owner") != "" {
|
||||
owner, err := user_model.GetUserByName(ctx, ctx.FormString("owner"))
|
||||
if err != nil {
|
||||
|
||||
@@ -131,9 +131,6 @@ func Search(ctx *context.APIContext) {
|
||||
// "$ref": "#/responses/validationError"
|
||||
|
||||
private := ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private"))
|
||||
if ctx.PublicOnly {
|
||||
private = false
|
||||
}
|
||||
|
||||
opts := repo_model.SearchRepoOptions{
|
||||
ListOptions: utils.GetListOptions(ctx),
|
||||
@@ -149,6 +146,7 @@ func Search(ctx *context.APIContext) {
|
||||
StarredByID: ctx.FormInt64("starredBy"),
|
||||
IncludeDescription: ctx.FormBool("includeDesc"),
|
||||
}
|
||||
opts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
|
||||
if ctx.FormString("template") != "" {
|
||||
opts.Template = optional.Some(ctx.FormBool("template"))
|
||||
@@ -567,6 +565,10 @@ func GetByID(ctx *context.APIContext) {
|
||||
}
|
||||
return
|
||||
}
|
||||
if !ctx.TokenCanAccessRepo(repo) {
|
||||
ctx.APIErrorNotFound()
|
||||
return
|
||||
}
|
||||
|
||||
permission, err := access_model.GetDoerRepoPermission(ctx, repo, ctx.Doer)
|
||||
if err != nil {
|
||||
@@ -1254,6 +1256,7 @@ func ListRepoActivityFeeds(ctx *context.APIContext) {
|
||||
Date: ctx.FormString("date"),
|
||||
ListOptions: listOptions,
|
||||
}
|
||||
opts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
|
||||
feeds, count, err := feed_service.GetFeeds(ctx, opts)
|
||||
if err != nil {
|
||||
|
||||
@@ -19,12 +19,15 @@ import (
|
||||
func listUserRepos(ctx *context.APIContext, u *user_model.User, private bool) {
|
||||
opts := utils.GetListOptions(ctx)
|
||||
|
||||
repos, count, err := repo_model.GetUserRepositories(ctx, repo_model.SearchRepoOptions{
|
||||
searchOpts := repo_model.SearchRepoOptions{
|
||||
Actor: u,
|
||||
Private: private,
|
||||
ListOptions: opts,
|
||||
OrderBy: "id ASC",
|
||||
})
|
||||
}
|
||||
searchOpts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
|
||||
repos, count, err := repo_model.GetUserRepositories(ctx, searchOpts)
|
||||
if err != nil {
|
||||
ctx.APIErrorInternal(err)
|
||||
return
|
||||
@@ -79,8 +82,7 @@ func ListUserRepos(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
private := ctx.IsSigned
|
||||
listUserRepos(ctx, ctx.ContextUser, private)
|
||||
listUserRepos(ctx, ctx.ContextUser, ctx.IsSigned)
|
||||
}
|
||||
|
||||
// ListMyRepos - list the repositories you own or have access to.
|
||||
@@ -110,6 +112,7 @@ func ListMyRepos(ctx *context.APIContext) {
|
||||
Private: ctx.IsSigned,
|
||||
IncludeDescription: true,
|
||||
}
|
||||
opts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
|
||||
repos, count, err := repo_model.SearchRepository(ctx, opts)
|
||||
if err != nil {
|
||||
|
||||
@@ -20,11 +20,14 @@ import (
|
||||
// getStarredRepos returns the repos that the user with the specified userID has
|
||||
// starred
|
||||
func getStarredRepos(ctx *context.APIContext, user *user_model.User, private bool) ([]*api.Repository, error) {
|
||||
starredRepos, err := repo_model.GetStarredRepos(ctx, &repo_model.StarredReposOptions{
|
||||
opts := &repo_model.StarredReposOptions{
|
||||
ListOptions: utils.GetListOptions(ctx),
|
||||
StarrerID: user.ID,
|
||||
IncludePrivate: private,
|
||||
})
|
||||
}
|
||||
opts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
|
||||
starredRepos, err := repo_model.GetStarredRepos(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import (
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
user_model "code.gitea.io/gitea/models/user"
|
||||
"code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/routers/api/v1/utils"
|
||||
"code.gitea.io/gitea/services/context"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
@@ -69,19 +68,16 @@ func Search(ctx *context.APIContext) {
|
||||
maxResults = 1
|
||||
users = []*user_model.User{user_model.NewActionsUser()}
|
||||
default:
|
||||
var visible []structs.VisibleType
|
||||
if ctx.PublicOnly {
|
||||
visible = []structs.VisibleType{structs.VisibleTypePublic}
|
||||
}
|
||||
users, maxResults, err = user_model.SearchUsers(ctx, user_model.SearchUserOptions{
|
||||
opts := user_model.SearchUserOptions{
|
||||
Actor: ctx.Doer,
|
||||
Keyword: ctx.FormTrim("q"),
|
||||
UID: uid,
|
||||
Types: []user_model.UserType{user_model.UserTypeIndividual},
|
||||
SearchByEmail: true,
|
||||
Visible: visible,
|
||||
ListOptions: listOptions,
|
||||
})
|
||||
}
|
||||
opts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
users, maxResults, err = user_model.SearchUsers(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.JSON(http.StatusInternalServerError, map[string]any{
|
||||
"ok": false,
|
||||
@@ -214,6 +210,7 @@ func ListUserActivityFeeds(ctx *context.APIContext) {
|
||||
Date: ctx.FormString("date"),
|
||||
ListOptions: listOptions,
|
||||
}
|
||||
opts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
|
||||
feeds, count, err := feed_service.GetFeeds(ctx, opts)
|
||||
if err != nil {
|
||||
|
||||
@@ -18,11 +18,14 @@ import (
|
||||
|
||||
// getWatchedRepos returns the repos that the user with the specified userID is watching
|
||||
func getWatchedRepos(ctx *context.APIContext, user *user_model.User, private bool) ([]*api.Repository, int64, error) {
|
||||
watchedRepos, total, err := repo_model.GetWatchedRepos(ctx, &repo_model.WatchedReposOptions{
|
||||
opts := &repo_model.WatchedReposOptions{
|
||||
ListOptions: utils.GetListOptions(ctx),
|
||||
WatcherID: user.ID,
|
||||
IncludePrivate: private,
|
||||
})
|
||||
}
|
||||
opts.ApplyPublicOnly(ctx.PublicOnly)
|
||||
|
||||
watchedRepos, total, err := repo_model.GetWatchedRepos(ctx, opts)
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user