mirror of
https://github.com/go-gitea/gitea.git
synced 2026-10-06 21:07:06 +02:00
Merge remote-tracking branch 'upstream/main' into limit-repo-size
This commit is contained in:
commit
83cc0337b6
633 files changed
+12185
-8618
No files matched your search
@@ -149,10 +149,7 @@ func (s *Service) UpdateTask(
|
||||
return nil, status.Errorf(codes.Internal, "load job: %v", err)
|
||||
}
|
||||
|
||||
if err := actions_service.CreateCommitStatus(ctx, task.Job); err != nil {
|
||||
log.Error("Update commit status for job %v failed: %v", task.Job.ID, err)
|
||||
// go on
|
||||
}
|
||||
actions_service.CreateCommitStatus(ctx, task.Job)
|
||||
|
||||
if req.Msg.State.Result != runnerv1.Result_RESULT_UNSPECIFIED {
|
||||
if err := actions_service.EmitJobsIfReady(task.Job.RunID); err != nil {
|
||||
|
||||
@@ -9,10 +9,12 @@ import (
|
||||
|
||||
actions_model "code.gitea.io/gitea/models/actions"
|
||||
secret_model "code.gitea.io/gitea/models/secret"
|
||||
"code.gitea.io/gitea/modules/git"
|
||||
"code.gitea.io/gitea/modules/json"
|
||||
"code.gitea.io/gitea/modules/log"
|
||||
secret_module "code.gitea.io/gitea/modules/secret"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
"code.gitea.io/gitea/services/actions"
|
||||
|
||||
runnerv1 "code.gitea.io/actions-proto-go/runner/v1"
|
||||
"google.golang.org/protobuf/types/known/structpb"
|
||||
@@ -27,6 +29,8 @@ func pickTask(ctx context.Context, runner *actions_model.ActionRunner) (*runnerv
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
actions.CreateCommitStatus(ctx, t.Job)
|
||||
|
||||
task := &runnerv1.Task{
|
||||
Id: t.ID,
|
||||
WorkflowPayload: t.Job.WorkflowPayload,
|
||||
@@ -95,7 +99,7 @@ func generateTaskContext(t *actions_model.ActionTask) *structpb.Struct {
|
||||
"head_ref": "", // string, The head_ref or source branch of the pull request in a workflow run. This property is only available when the event that triggers a workflow run is either pull_request or pull_request_target.
|
||||
"job": fmt.Sprint(t.JobID), // string, The job_id of the current job.
|
||||
"ref": t.Job.Run.Ref, // string, The fully-formed ref of the branch or tag that triggered the workflow run. For workflows triggered by push, this is the branch or tag ref that was pushed. For workflows triggered by pull_request, this is the pull request merge branch. For workflows triggered by release, this is the release tag created. For other triggers, this is the branch or tag ref that triggered the workflow run. This is only set if a branch or tag is available for the event type. The ref given is fully-formed, meaning that for branches the format is refs/heads/<branch_name>, for pull requests it is refs/pull/<pr_number>/merge, and for tags it is refs/tags/<tag_name>. For example, refs/heads/feature-branch-1.
|
||||
"ref_name": t.Job.Run.Ref, // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
|
||||
"ref_name": git.RefEndName(t.Job.Run.Ref), // string, The short ref name of the branch or tag that triggered the workflow run. This value matches the branch or tag name shown on GitHub. For example, feature-branch-1.
|
||||
"ref_protected": false, // boolean, true if branch protections are configured for the ref that triggered the workflow run.
|
||||
"ref_type": "", // string, The type of ref that triggered the workflow run. Valid values are branch or tag.
|
||||
"path": "", // string, Path on the runner to the file that sets system PATH variables from workflow commands. This file is unique to the current step and is a different file for each step in a job. For more information, see "Workflow commands for GitHub Actions."
|
||||
|
||||
+20
-32
@@ -44,6 +44,24 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
func verifyAuth(r *web.Route, authMethods []auth.Method) {
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
authMethods = append(authMethods, &auth.ReverseProxy{})
|
||||
}
|
||||
authGroup := auth.NewGroup(authMethods...)
|
||||
|
||||
r.Use(func(ctx *context.Context) {
|
||||
var err error
|
||||
ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
||||
if err != nil {
|
||||
log.Error("Failed to verify user: %v", err)
|
||||
ctx.Error(http.StatusUnauthorized, "authGroup.Verify")
|
||||
return
|
||||
}
|
||||
ctx.IsSigned = ctx.Doer != nil
|
||||
})
|
||||
}
|
||||
|
||||
// CommonRoutes provide endpoints for most package managers (except containers - see below)
|
||||
// These are mounted on `/api/packages` (not `/api/v1/packages`)
|
||||
func CommonRoutes(ctx gocontext.Context) *web.Route {
|
||||
@@ -51,27 +69,12 @@ func CommonRoutes(ctx gocontext.Context) *web.Route {
|
||||
|
||||
r.Use(context.PackageContexter(ctx))
|
||||
|
||||
authMethods := []auth.Method{
|
||||
verifyAuth(r, []auth.Method{
|
||||
&auth.OAuth2{},
|
||||
&auth.Basic{},
|
||||
&nuget.Auth{},
|
||||
&conan.Auth{},
|
||||
&chef.Auth{},
|
||||
}
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
authMethods = append(authMethods, &auth.ReverseProxy{})
|
||||
}
|
||||
|
||||
authGroup := auth.NewGroup(authMethods...)
|
||||
r.Use(func(ctx *context.Context) {
|
||||
var err error
|
||||
ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
||||
if err != nil {
|
||||
log.Error("Verify: %v", err)
|
||||
ctx.Error(http.StatusUnauthorized, "authGroup.Verify")
|
||||
return
|
||||
}
|
||||
ctx.IsSigned = ctx.Doer != nil
|
||||
})
|
||||
|
||||
r.Group("/{username}", func() {
|
||||
@@ -437,24 +440,9 @@ func ContainerRoutes(ctx gocontext.Context) *web.Route {
|
||||
|
||||
r.Use(context.PackageContexter(ctx))
|
||||
|
||||
authMethods := []auth.Method{
|
||||
verifyAuth(r, []auth.Method{
|
||||
&auth.Basic{},
|
||||
&container.Auth{},
|
||||
}
|
||||
if setting.Service.EnableReverseProxyAuth {
|
||||
authMethods = append(authMethods, &auth.ReverseProxy{})
|
||||
}
|
||||
|
||||
authGroup := auth.NewGroup(authMethods...)
|
||||
r.Use(func(ctx *context.Context) {
|
||||
var err error
|
||||
ctx.Doer, err = authGroup.Verify(ctx.Req, ctx.Resp, ctx, ctx.Session)
|
||||
if err != nil {
|
||||
log.Error("Failed to verify user: %v", err)
|
||||
ctx.Error(http.StatusUnauthorized, "Verify")
|
||||
return
|
||||
}
|
||||
ctx.IsSigned = ctx.Doer != nil
|
||||
})
|
||||
|
||||
r.Get("", container.ReqContainerAccess, container.DetermineSupport)
|
||||
|
||||
@@ -30,13 +30,10 @@ func (a *Auth) Verify(req *http.Request, w http.ResponseWriter, store auth.DataS
|
||||
if uid == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if uid == -1 {
|
||||
return user_model.NewGhostUser(), nil
|
||||
}
|
||||
|
||||
u, err := user_model.GetUserByID(req.Context(), uid)
|
||||
u, err := user_model.GetPossibleUserByID(req.Context(), uid)
|
||||
if err != nil {
|
||||
log.Error("GetUserByID: %v", err)
|
||||
log.Error("GetPossibleUserByID: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
||||
@@ -217,7 +217,7 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
|
||||
|
||||
metadata := &container_module.Metadata{
|
||||
Type: container_module.TypeOCI,
|
||||
MultiArch: make(map[string]string),
|
||||
Manifests: make([]*container_module.Manifest, 0, len(index.Manifests)),
|
||||
}
|
||||
|
||||
for _, manifest := range index.Manifests {
|
||||
@@ -233,7 +233,7 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
|
||||
}
|
||||
}
|
||||
|
||||
_, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
|
||||
pfd, err := container_model.GetContainerBlob(ctx, &container_model.BlobSearchOptions{
|
||||
OwnerID: mci.Owner.ID,
|
||||
Image: mci.Image,
|
||||
Digest: string(manifest.Digest),
|
||||
@@ -246,7 +246,18 @@ func processImageManifestIndex(mci *manifestCreationInfo, buf *packages_module.H
|
||||
return err
|
||||
}
|
||||
|
||||
metadata.MultiArch[platform] = string(manifest.Digest)
|
||||
size, err := packages_model.CalculateFileSize(ctx, &packages_model.PackageFileSearchOptions{
|
||||
VersionID: pfd.File.VersionID,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
metadata.Manifests = append(metadata.Manifests, &container_module.Manifest{
|
||||
Platform: platform,
|
||||
Digest: string(manifest.Digest),
|
||||
Size: size,
|
||||
})
|
||||
}
|
||||
|
||||
pv, err := createPackageAndVersion(ctx, mci, metadata)
|
||||
@@ -369,8 +380,8 @@ func createPackageAndVersion(ctx context.Context, mci *manifestCreationInfo, met
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
for _, digest := range metadata.MultiArch {
|
||||
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, digest); err != nil {
|
||||
for _, manifest := range metadata.Manifests {
|
||||
if _, err := packages_model.InsertProperty(ctx, packages_model.PropertyTypeVersion, pv.ID, container_module.PropertyManifestReference, manifest.Digest); err != nil {
|
||||
log.Error("Error setting package version property: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -13,6 +13,9 @@ import (
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
packages_model "code.gitea.io/gitea/models/packages"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/models/unit"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
packages_module "code.gitea.io/gitea/modules/packages"
|
||||
npm_module "code.gitea.io/gitea/modules/packages/npm"
|
||||
@@ -166,6 +169,26 @@ func UploadPackage(ctx *context.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
repo, err := repo_model.GetRepositoryByURL(ctx, npmPackage.Metadata.Repository.URL)
|
||||
if err == nil {
|
||||
canWrite := repo.OwnerID == ctx.Doer.ID
|
||||
|
||||
if !canWrite {
|
||||
perms, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
|
||||
if err != nil {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
|
||||
canWrite = perms.CanWrite(unit.TypePackages)
|
||||
}
|
||||
|
||||
if !canWrite {
|
||||
apiError(ctx, http.StatusForbidden, "no permission to upload this package")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
buf, err := packages_module.CreateHashedBufferFromReader(bytes.NewReader(npmPackage.Data), 32*1024*1024)
|
||||
if err != nil {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
@@ -217,6 +240,13 @@ func UploadPackage(ctx *context.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if repo != nil {
|
||||
if err := packages_model.SetRepositoryLink(ctx, pv.PackageID, repo.ID); err != nil {
|
||||
apiError(ctx, http.StatusInternalServerError, err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
ctx.Status(http.StatusCreated)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
package activitypub
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
@@ -18,22 +19,23 @@ import (
|
||||
|
||||
// Person function returns the Person actor for a user
|
||||
func Person(ctx *context.APIContext) {
|
||||
// swagger:operation GET /activitypub/user/{username} activitypub activitypubPerson
|
||||
// swagger:operation GET /activitypub/user-id/{user-id} activitypub activitypubPerson
|
||||
// ---
|
||||
// summary: Returns the Person actor for a user
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// - name: user-id
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// description: user ID of the user
|
||||
// type: integer
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityPub"
|
||||
|
||||
link := strings.TrimSuffix(setting.AppURL, "/") + "/api/v1/activitypub/user/" + ctx.ContextUser.Name
|
||||
// TODO: the setting.AppURL during the test doesn't follow the definition: "It always has a '/' suffix"
|
||||
link := fmt.Sprintf("%s/api/v1/activitypub/user-id/%d", strings.TrimSuffix(setting.AppURL, "/"), ctx.ContextUser.ID)
|
||||
person := ap.PersonNew(ap.IRI(link))
|
||||
|
||||
person.Name = ap.NaturalLanguageValuesNew()
|
||||
@@ -85,16 +87,16 @@ func Person(ctx *context.APIContext) {
|
||||
|
||||
// PersonInbox function handles the incoming data for a user inbox
|
||||
func PersonInbox(ctx *context.APIContext) {
|
||||
// swagger:operation POST /activitypub/user/{username}/inbox activitypub activitypubPersonInbox
|
||||
// swagger:operation POST /activitypub/user-id/{user-id}/inbox activitypub activitypubPersonInbox
|
||||
// ---
|
||||
// summary: Send to the inbox
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// - name: user-id
|
||||
// in: path
|
||||
// description: username of the user
|
||||
// type: string
|
||||
// description: user ID of the user
|
||||
// type: integer
|
||||
// required: true
|
||||
// responses:
|
||||
// "204":
|
||||
|
||||
@@ -704,10 +704,15 @@ func Routes(ctx gocontext.Context) *web.Route {
|
||||
if setting.Federation.Enabled {
|
||||
m.Get("/nodeinfo", misc.NodeInfo)
|
||||
m.Group("/activitypub", func() {
|
||||
// deprecated, remove in 1.20, use /user-id/{user-id} instead
|
||||
m.Group("/user/{username}", func() {
|
||||
m.Get("", activitypub.Person)
|
||||
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
|
||||
}, context_service.UserAssignmentAPI())
|
||||
m.Group("/user-id/{user-id}", func() {
|
||||
m.Get("", activitypub.Person)
|
||||
m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox)
|
||||
}, context_service.UserIDAssignmentAPI())
|
||||
})
|
||||
}
|
||||
m.Get("/signing-key.gpg", misc.SigningKey)
|
||||
@@ -749,6 +754,8 @@ func Routes(ctx gocontext.Context) *web.Route {
|
||||
Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken)
|
||||
m.Combo("/{id}").Delete(user.DeleteAccessToken)
|
||||
}, reqBasicAuth())
|
||||
|
||||
m.Get("/activities/feeds", user.ListUserActivityFeeds)
|
||||
}, context_service.UserAssignmentAPI())
|
||||
})
|
||||
|
||||
@@ -1026,6 +1033,14 @@ func Routes(ctx gocontext.Context) *web.Route {
|
||||
Patch(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.EditAttachmentOptions{}), repo.EditIssueAttachment).
|
||||
Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, repo.DeleteIssueAttachment)
|
||||
}, mustEnableAttachments)
|
||||
m.Combo("/dependencies").
|
||||
Get(repo.GetIssueDependencies).
|
||||
Post(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.IssueMeta{}), repo.CreateIssueDependency).
|
||||
Delete(reqToken(auth_model.AccessTokenScopeRepo), mustNotBeArchived, bind(api.IssueMeta{}), repo.RemoveIssueDependency)
|
||||
m.Combo("/blocks").
|
||||
Get(repo.GetIssueBlocks).
|
||||
Post(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueMeta{}), repo.CreateIssueBlocking).
|
||||
Delete(reqToken(auth_model.AccessTokenScopeRepo), bind(api.IssueMeta{}), repo.RemoveIssueBlocking)
|
||||
})
|
||||
}, mustEnableIssuesOrPulls)
|
||||
m.Group("/labels", func() {
|
||||
@@ -1161,7 +1176,10 @@ func Routes(ctx gocontext.Context) *web.Route {
|
||||
}, reqAdmin())
|
||||
}, reqAnyRepoReader())
|
||||
m.Get("/issue_templates", context.ReferencesGitRepo(), repo.GetIssueTemplates)
|
||||
m.Get("/issue_config", context.ReferencesGitRepo(), repo.GetIssueConfig)
|
||||
m.Get("/issue_config/validate", context.ReferencesGitRepo(), repo.ValidateIssueConfig)
|
||||
m.Get("/languages", reqRepoReader(unit.TypeCode), repo.GetLanguages)
|
||||
m.Get("/activities/feeds", repo.ListRepoActivityFeeds)
|
||||
}, repoAssignment())
|
||||
})
|
||||
|
||||
@@ -1219,6 +1237,7 @@ func Routes(ctx gocontext.Context) *web.Route {
|
||||
Patch(bind(api.EditHookOption{}), org.EditHook).
|
||||
Delete(org.DeleteHook)
|
||||
}, reqToken(auth_model.AccessTokenScopeAdminOrgHook), reqOrgOwnership(), reqWebhooksEnabled())
|
||||
m.Get("/activities/feeds", org.ListOrgActivityFeeds)
|
||||
}, orgAssignment(true))
|
||||
m.Group("/teams/{teamid}", func() {
|
||||
m.Combo("").Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeam).
|
||||
@@ -1238,6 +1257,7 @@ func Routes(ctx gocontext.Context) *web.Route {
|
||||
Delete(reqToken(auth_model.AccessTokenScopeWriteOrg), org.RemoveTeamRepository).
|
||||
Get(reqToken(auth_model.AccessTokenScopeReadOrg), org.GetTeamRepo)
|
||||
})
|
||||
m.Get("/activities/feeds", org.ListTeamActivityFeeds)
|
||||
}, orgAssignment(false, true), reqToken(""), reqTeamMembership())
|
||||
|
||||
m.Group("/admin", func() {
|
||||
|
||||
@@ -7,6 +7,7 @@ package org
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
@@ -370,3 +371,69 @@ func Delete(ctx *context.APIContext) {
|
||||
}
|
||||
ctx.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func ListOrgActivityFeeds(ctx *context.APIContext) {
|
||||
// swagger:operation GET /orgs/{org}/activities/feeds organization orgListActivityFeeds
|
||||
// ---
|
||||
// summary: List an organization's activity feeds
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: org
|
||||
// in: path
|
||||
// description: name of the org
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: date
|
||||
// in: query
|
||||
// description: the date of the activities to be found
|
||||
// type: string
|
||||
// format: date
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of results to return (1-based)
|
||||
// type: integer
|
||||
// - name: limit
|
||||
// in: query
|
||||
// description: page size of results
|
||||
// type: integer
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityFeedsList"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
includePrivate := false
|
||||
if ctx.IsSigned {
|
||||
if ctx.Doer.IsAdmin {
|
||||
includePrivate = true
|
||||
} else {
|
||||
org := organization.OrgFromUser(ctx.ContextUser)
|
||||
isMember, err := org.IsOrgMember(ctx.Doer.ID)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "IsOrgMember", err)
|
||||
return
|
||||
}
|
||||
includePrivate = isMember
|
||||
}
|
||||
}
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
opts := activities_model.GetFeedsOptions{
|
||||
RequestedUser: ctx.ContextUser,
|
||||
Actor: ctx.Doer,
|
||||
IncludePrivate: includePrivate,
|
||||
Date: ctx.FormString("date"),
|
||||
ListOptions: listOptions,
|
||||
}
|
||||
|
||||
feeds, count, err := activities_model.GetFeeds(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
|
||||
return
|
||||
}
|
||||
ctx.SetTotalCountHeader(count)
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/models"
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
@@ -135,7 +136,7 @@ func GetTeam(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
func attachTeamUnits(team *organization.Team, units []string) {
|
||||
unitTypes := unit_model.FindUnitTypes(units...)
|
||||
unitTypes, _ := unit_model.FindUnitTypes(units...)
|
||||
team.Units = make([]*organization.TeamUnit, 0, len(units))
|
||||
for _, tp := range unitTypes {
|
||||
team.Units = append(team.Units, &organization.TeamUnit{
|
||||
@@ -165,6 +166,21 @@ func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) {
|
||||
}
|
||||
}
|
||||
|
||||
func attachAdminTeamUnits(team *organization.Team) {
|
||||
team.Units = make([]*organization.TeamUnit, 0, len(unit_model.AllRepoUnitTypes))
|
||||
for _, ut := range unit_model.AllRepoUnitTypes {
|
||||
up := perm.AccessModeAdmin
|
||||
if ut == unit_model.TypeExternalTracker || ut == unit_model.TypeExternalWiki {
|
||||
up = perm.AccessModeRead
|
||||
}
|
||||
team.Units = append(team.Units, &organization.TeamUnit{
|
||||
OrgID: team.OrgID,
|
||||
Type: ut,
|
||||
AccessMode: up,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// CreateTeam api for create a team
|
||||
func CreateTeam(ctx *context.APIContext) {
|
||||
// swagger:operation POST /orgs/{org}/teams organization orgCreateTeam
|
||||
@@ -212,6 +228,8 @@ func CreateTeam(ctx *context.APIContext) {
|
||||
ctx.Error(http.StatusInternalServerError, "getTeamUnits", errors.New("units permission should not be empty"))
|
||||
return
|
||||
}
|
||||
} else {
|
||||
attachAdminTeamUnits(team)
|
||||
}
|
||||
|
||||
if err := models.NewTeam(team); err != nil {
|
||||
@@ -299,6 +317,8 @@ func EditTeam(ctx *context.APIContext) {
|
||||
} else if len(form.Units) > 0 {
|
||||
attachTeamUnits(team, form.Units)
|
||||
}
|
||||
} else {
|
||||
attachAdminTeamUnits(team)
|
||||
}
|
||||
|
||||
if err := models.UpdateTeam(team, isAuthChanged, isIncludeAllChanged); err != nil {
|
||||
@@ -792,3 +812,55 @@ func SearchTeam(ctx *context.APIContext) {
|
||||
"data": apiTeams,
|
||||
})
|
||||
}
|
||||
|
||||
func ListTeamActivityFeeds(ctx *context.APIContext) {
|
||||
// swagger:operation GET /teams/{id}/activities/feeds organization orgListTeamActivityFeeds
|
||||
// ---
|
||||
// summary: List a team's activity feeds
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: id
|
||||
// in: path
|
||||
// description: id of the team
|
||||
// type: integer
|
||||
// format: int64
|
||||
// required: true
|
||||
// - name: date
|
||||
// in: query
|
||||
// description: the date of the activities to be found
|
||||
// type: string
|
||||
// format: date
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of results to return (1-based)
|
||||
// type: integer
|
||||
// - name: limit
|
||||
// in: query
|
||||
// description: page size of results
|
||||
// type: integer
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityFeedsList"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
opts := activities_model.GetFeedsOptions{
|
||||
RequestedTeam: ctx.Org.Team,
|
||||
Actor: ctx.Doer,
|
||||
IncludePrivate: true,
|
||||
Date: ctx.FormString("date"),
|
||||
ListOptions: listOptions,
|
||||
}
|
||||
|
||||
feeds, count, err := activities_model.GetFeeds(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
|
||||
return
|
||||
}
|
||||
ctx.SetTotalCountHeader(count)
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
|
||||
}
|
||||
@@ -421,6 +421,10 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
if ruleName == "" {
|
||||
ruleName = form.BranchName //nolint
|
||||
}
|
||||
if len(ruleName) == 0 {
|
||||
ctx.Error(http.StatusBadRequest, "both rule_name and branch_name are empty", "both rule_name and branch_name are empty")
|
||||
return
|
||||
}
|
||||
|
||||
isPlainRule := !git_model.IsRuleNameSpecial(ruleName)
|
||||
var isBranchExist bool
|
||||
@@ -502,7 +506,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
|
||||
protectBranch = &git_model.ProtectedBranch{
|
||||
RepoID: ctx.Repo.Repository.ID,
|
||||
RuleName: form.RuleName,
|
||||
RuleName: ruleName,
|
||||
CanPush: form.EnablePush,
|
||||
EnableWhitelist: form.EnablePush && form.EnablePushWhitelist,
|
||||
EnableMergeWhitelist: form.EnableMergeWhitelist,
|
||||
@@ -534,7 +538,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
if isBranchExist {
|
||||
if err = pull_service.CheckPRsForBaseBranch(ctx.Repo.Repository, form.RuleName); err != nil {
|
||||
if err = pull_service.CheckPRsForBaseBranch(ctx.Repo.Repository, ruleName); err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "CheckPRsForBaseBranch", err)
|
||||
return
|
||||
}
|
||||
@@ -552,7 +556,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
}()
|
||||
}
|
||||
// FIXME: since we only need to recheck files protected rules, we could improve this
|
||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.GitRepo, form.RuleName)
|
||||
matchedBranches, err := git_model.FindAllMatchedBranches(ctx, ctx.Repo.GitRepo, ruleName)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "FindAllMatchedBranches", err)
|
||||
return
|
||||
@@ -568,7 +572,7 @@ func CreateBranchProtection(ctx *context.APIContext) {
|
||||
}
|
||||
|
||||
// Reload from db to get all whitelists
|
||||
bp, err := git_model.GetProtectedBranchRuleByName(ctx, ctx.Repo.Repository.ID, form.RuleName)
|
||||
bp, err := git_model.GetProtectedBranchRuleByName(ctx, ctx.Repo.Repository.ID, ruleName)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetProtectedBranchByID", err)
|
||||
return
|
||||
|
||||
@@ -381,7 +381,7 @@ func GetEditorconfig(ctx *context.APIContext) {
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
ec, err := ctx.Repo.GetEditorconfig(ctx.Repo.Commit)
|
||||
ec, _, err := ctx.Repo.GetEditorconfig(ctx.Repo.Commit)
|
||||
if err != nil {
|
||||
if git.IsErrNotExist(err) {
|
||||
ctx.NotFound(err)
|
||||
|
||||
@@ -0,0 +1,598 @@
|
||||
// Copyright 2016 The Gogs Authors. All rights reserved.
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package repo
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"code.gitea.io/gitea/models/db"
|
||||
issues_model "code.gitea.io/gitea/models/issues"
|
||||
access_model "code.gitea.io/gitea/models/perm/access"
|
||||
repo_model "code.gitea.io/gitea/models/repo"
|
||||
"code.gitea.io/gitea/modules/context"
|
||||
"code.gitea.io/gitea/modules/setting"
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
"code.gitea.io/gitea/modules/web"
|
||||
"code.gitea.io/gitea/services/convert"
|
||||
)
|
||||
|
||||
// GetIssueDependencies list an issue's dependencies
|
||||
func GetIssueDependencies(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/issues/{index}/dependencies issue issueListIssueDependencies
|
||||
// ---
|
||||
// summary: List an issue's dependencies, i.e all issues that block this issue.
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the issue
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of results to return (1-based)
|
||||
// type: integer
|
||||
// - name: limit
|
||||
// in: query
|
||||
// description: page size of results
|
||||
// type: integer
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/IssueList"
|
||||
|
||||
// If this issue's repository does not enable dependencies then there can be no dependencies by default
|
||||
if !ctx.Repo.Repository.IsDependenciesEnabled(ctx) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
issue, err := issues_model.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound("IsErrIssueNotExist", err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 1. We must be able to read this issue
|
||||
if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
page := ctx.FormInt("page")
|
||||
if page <= 1 {
|
||||
page = 1
|
||||
}
|
||||
limit := ctx.FormInt("limit")
|
||||
if limit == 0 {
|
||||
limit = setting.API.DefaultPagingNum
|
||||
} else if limit > setting.API.MaxResponseItems {
|
||||
limit = setting.API.MaxResponseItems
|
||||
}
|
||||
|
||||
canWrite := ctx.Repo.Permission.CanWriteIssuesOrPulls(issue.IsPull)
|
||||
|
||||
blockerIssues := make([]*issues_model.Issue, 0, limit)
|
||||
|
||||
// 2. Get the issues this issue depends on, i.e. the `<#b>`: `<issue> <- <#b>`
|
||||
blockersInfo, err := issue.BlockedByDependencies(ctx, db.ListOptions{
|
||||
Page: page,
|
||||
PageSize: limit,
|
||||
})
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "BlockedByDependencies", err)
|
||||
return
|
||||
}
|
||||
|
||||
var lastRepoID int64
|
||||
var lastPerm access_model.Permission
|
||||
for _, blocker := range blockersInfo {
|
||||
// Get the permissions for this repository
|
||||
perm := lastPerm
|
||||
if lastRepoID != blocker.Repository.ID {
|
||||
if blocker.Repository.ID == ctx.Repo.Repository.ID {
|
||||
perm = ctx.Repo.Permission
|
||||
} else {
|
||||
var err error
|
||||
perm, err = access_model.GetUserRepoPermission(ctx, &blocker.Repository, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
lastRepoID = blocker.Repository.ID
|
||||
}
|
||||
|
||||
// check permission
|
||||
if !perm.CanReadIssuesOrPulls(blocker.Issue.IsPull) {
|
||||
if !canWrite {
|
||||
hiddenBlocker := &issues_model.DependencyInfo{
|
||||
Issue: issues_model.Issue{
|
||||
Title: "HIDDEN",
|
||||
},
|
||||
}
|
||||
blocker = hiddenBlocker
|
||||
} else {
|
||||
confidentialBlocker := &issues_model.DependencyInfo{
|
||||
Issue: issues_model.Issue{
|
||||
RepoID: blocker.Issue.RepoID,
|
||||
Index: blocker.Index,
|
||||
Title: blocker.Title,
|
||||
IsClosed: blocker.IsClosed,
|
||||
IsPull: blocker.IsPull,
|
||||
},
|
||||
Repository: repo_model.Repository{
|
||||
ID: blocker.Issue.Repo.ID,
|
||||
Name: blocker.Issue.Repo.Name,
|
||||
OwnerName: blocker.Issue.Repo.OwnerName,
|
||||
},
|
||||
}
|
||||
confidentialBlocker.Issue.Repo = &confidentialBlocker.Repository
|
||||
blocker = confidentialBlocker
|
||||
}
|
||||
}
|
||||
blockerIssues = append(blockerIssues, &blocker.Issue)
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, blockerIssues))
|
||||
}
|
||||
|
||||
// CreateIssueDependency create a new issue dependencies
|
||||
func CreateIssueDependency(ctx *context.APIContext) {
|
||||
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/dependencies issue issueCreateIssueDependencies
|
||||
// ---
|
||||
// summary: Make the issue in the url depend on the issue in the form.
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the issue
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/IssueMeta"
|
||||
// responses:
|
||||
// "201":
|
||||
// "$ref": "#/responses/Issue"
|
||||
// "404":
|
||||
// description: the issue does not exist
|
||||
|
||||
// We want to make <:index> depend on <Form>, i.e. <:index> is the target
|
||||
target := getParamsIssue(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
// and <Form> represents the dependency
|
||||
form := web.GetForm(ctx).(*api.IssueMeta)
|
||||
dependency := getFormIssue(ctx, form)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
dependencyPerm := getPermissionForRepo(ctx, target.Repo)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
createIssueDependency(ctx, target, dependency, ctx.Repo.Permission, *dependencyPerm)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target))
|
||||
}
|
||||
|
||||
// RemoveIssueDependency remove an issue dependency
|
||||
func RemoveIssueDependency(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/dependencies issue issueRemoveIssueDependencies
|
||||
// ---
|
||||
// summary: Remove an issue dependency
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the issue
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/IssueMeta"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Issue"
|
||||
|
||||
// We want to make <:index> depend on <Form>, i.e. <:index> is the target
|
||||
target := getParamsIssue(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
// and <Form> represents the dependency
|
||||
form := web.GetForm(ctx).(*api.IssueMeta)
|
||||
dependency := getFormIssue(ctx, form)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
dependencyPerm := getPermissionForRepo(ctx, target.Repo)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
removeIssueDependency(ctx, target, dependency, ctx.Repo.Permission, *dependencyPerm)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, target))
|
||||
}
|
||||
|
||||
// GetIssueBlocks list issues that are blocked by this issue
|
||||
func GetIssueBlocks(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/issues/{index}/blocks issue issueListBlocks
|
||||
// ---
|
||||
// summary: List issues that are blocked by this issue
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the issue
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of results to return (1-based)
|
||||
// type: integer
|
||||
// - name: limit
|
||||
// in: query
|
||||
// description: page size of results
|
||||
// type: integer
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/IssueList"
|
||||
|
||||
// We need to list the issues that DEPEND on this issue not the other way round
|
||||
// Therefore whether dependencies are enabled or not in this repository is potentially irrelevant.
|
||||
|
||||
issue := getParamsIssue(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
if !ctx.Repo.Permission.CanReadIssuesOrPulls(issue.IsPull) {
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
page := ctx.FormInt("page")
|
||||
if page <= 1 {
|
||||
page = 1
|
||||
}
|
||||
limit := ctx.FormInt("limit")
|
||||
if limit <= 1 {
|
||||
limit = setting.API.DefaultPagingNum
|
||||
}
|
||||
|
||||
skip := (page - 1) * limit
|
||||
max := page * limit
|
||||
|
||||
deps, err := issue.BlockingDependencies(ctx)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "BlockingDependencies", err)
|
||||
return
|
||||
}
|
||||
|
||||
var lastRepoID int64
|
||||
var lastPerm access_model.Permission
|
||||
|
||||
var issues []*issues_model.Issue
|
||||
for i, depMeta := range deps {
|
||||
if i < skip || i >= max {
|
||||
continue
|
||||
}
|
||||
|
||||
// Get the permissions for this repository
|
||||
perm := lastPerm
|
||||
if lastRepoID != depMeta.Repository.ID {
|
||||
if depMeta.Repository.ID == ctx.Repo.Repository.ID {
|
||||
perm = ctx.Repo.Permission
|
||||
} else {
|
||||
var err error
|
||||
perm, err = access_model.GetUserRepoPermission(ctx, &depMeta.Repository, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.ServerError("GetUserRepoPermission", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
lastRepoID = depMeta.Repository.ID
|
||||
}
|
||||
|
||||
if !perm.CanReadIssuesOrPulls(depMeta.Issue.IsPull) {
|
||||
continue
|
||||
}
|
||||
|
||||
depMeta.Issue.Repo = &depMeta.Repository
|
||||
issues = append(issues, &depMeta.Issue)
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToAPIIssueList(ctx, issues))
|
||||
}
|
||||
|
||||
// CreateIssueBlocking block the issue given in the body by the issue in path
|
||||
func CreateIssueBlocking(ctx *context.APIContext) {
|
||||
// swagger:operation POST /repos/{owner}/{repo}/issues/{index}/blocks issue issueCreateIssueBlocking
|
||||
// ---
|
||||
// summary: Block the issue given in the body by the issue in path
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the issue
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/IssueMeta"
|
||||
// responses:
|
||||
// "201":
|
||||
// "$ref": "#/responses/Issue"
|
||||
// "404":
|
||||
// description: the issue does not exist
|
||||
|
||||
dependency := getParamsIssue(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.IssueMeta)
|
||||
target := getFormIssue(ctx, form)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
targetPerm := getPermissionForRepo(ctx, target.Repo)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
createIssueDependency(ctx, target, dependency, *targetPerm, ctx.Repo.Permission)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency))
|
||||
}
|
||||
|
||||
// RemoveIssueBlocking unblock the issue given in the body by the issue in path
|
||||
func RemoveIssueBlocking(ctx *context.APIContext) {
|
||||
// swagger:operation DELETE /repos/{owner}/{repo}/issues/{index}/blocks issue issueRemoveIssueBlocking
|
||||
// ---
|
||||
// summary: Unblock the issue given in the body by the issue in path
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: index
|
||||
// in: path
|
||||
// description: index of the issue
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: body
|
||||
// in: body
|
||||
// schema:
|
||||
// "$ref": "#/definitions/IssueMeta"
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/Issue"
|
||||
|
||||
dependency := getParamsIssue(ctx)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
form := web.GetForm(ctx).(*api.IssueMeta)
|
||||
target := getFormIssue(ctx, form)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
targetPerm := getPermissionForRepo(ctx, target.Repo)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
removeIssueDependency(ctx, target, dependency, *targetPerm, ctx.Repo.Permission)
|
||||
if ctx.Written() {
|
||||
return
|
||||
}
|
||||
|
||||
ctx.JSON(http.StatusCreated, convert.ToAPIIssue(ctx, dependency))
|
||||
}
|
||||
|
||||
func getParamsIssue(ctx *context.APIContext) *issues_model.Issue {
|
||||
issue, err := issues_model.GetIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index"))
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound("IsErrIssueNotExist", err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
issue.Repo = ctx.Repo.Repository
|
||||
return issue
|
||||
}
|
||||
|
||||
func getFormIssue(ctx *context.APIContext, form *api.IssueMeta) *issues_model.Issue {
|
||||
var repo *repo_model.Repository
|
||||
if form.Owner != ctx.Repo.Repository.OwnerName || form.Name != ctx.Repo.Repository.Name {
|
||||
if !setting.Service.AllowCrossRepositoryDependencies {
|
||||
ctx.JSON(http.StatusBadRequest, "CrossRepositoryDependencies not enabled")
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
repo, err = repo_model.GetRepositoryByOwnerAndName(ctx, form.Owner, form.Name)
|
||||
if err != nil {
|
||||
if repo_model.IsErrRepoNotExist(err) {
|
||||
ctx.NotFound("IsErrRepoNotExist", err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetRepositoryByOwnerAndName", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
repo = ctx.Repo.Repository
|
||||
}
|
||||
|
||||
issue, err := issues_model.GetIssueByIndex(repo.ID, form.Index)
|
||||
if err != nil {
|
||||
if issues_model.IsErrIssueNotExist(err) {
|
||||
ctx.NotFound("IsErrIssueNotExist", err)
|
||||
} else {
|
||||
ctx.Error(http.StatusInternalServerError, "GetIssueByIndex", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
issue.Repo = repo
|
||||
return issue
|
||||
}
|
||||
|
||||
func getPermissionForRepo(ctx *context.APIContext, repo *repo_model.Repository) *access_model.Permission {
|
||||
if repo.ID == ctx.Repo.Repository.ID {
|
||||
return &ctx.Repo.Permission
|
||||
}
|
||||
|
||||
perm, err := access_model.GetUserRepoPermission(ctx, repo, ctx.Doer)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetUserRepoPermission", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &perm
|
||||
}
|
||||
|
||||
func createIssueDependency(ctx *context.APIContext, target, dependency *issues_model.Issue, targetPerm, dependencyPerm access_model.Permission) {
|
||||
if target.Repo.IsArchived || !target.Repo.IsDependenciesEnabled(ctx) {
|
||||
// The target's repository doesn't have dependencies enabled
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !targetPerm.CanWriteIssuesOrPulls(target.IsPull) {
|
||||
// We can't write to the target
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !dependencyPerm.CanReadIssuesOrPulls(dependency.IsPull) {
|
||||
// We can't read the dependency
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
err := issues_model.CreateIssueDependency(ctx.Doer, target, dependency)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateIssueDependency", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func removeIssueDependency(ctx *context.APIContext, target, dependency *issues_model.Issue, targetPerm, dependencyPerm access_model.Permission) {
|
||||
if target.Repo.IsArchived || !target.Repo.IsDependenciesEnabled(ctx) {
|
||||
// The target's repository doesn't have dependencies enabled
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !targetPerm.CanWriteIssuesOrPulls(target.IsPull) {
|
||||
// We can't write to the target
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
if !dependencyPerm.CanReadIssuesOrPulls(dependency.IsPull) {
|
||||
// We can't read the dependency
|
||||
ctx.NotFound()
|
||||
return
|
||||
}
|
||||
|
||||
err := issues_model.RemoveIssueDependency(ctx.Doer, target, dependency, issues_model.DependencyTypeBlockedBy)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "CreateIssueDependency", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
activities_model "code.gitea.io/gitea/models/activities"
|
||||
"code.gitea.io/gitea/models/db"
|
||||
"code.gitea.io/gitea/models/organization"
|
||||
"code.gitea.io/gitea/models/perm"
|
||||
@@ -1149,3 +1150,114 @@ func GetIssueTemplates(ctx *context.APIContext) {
|
||||
|
||||
ctx.JSON(http.StatusOK, ctx.IssueTemplatesFromDefaultBranch())
|
||||
}
|
||||
|
||||
// GetIssueConfig returns the issue config for a repo
|
||||
func GetIssueConfig(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/issue_config repository repoGetIssueConfig
|
||||
// ---
|
||||
// summary: Returns the issue config for a repo
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/RepoIssueConfig"
|
||||
issueConfig, _ := ctx.IssueConfigFromDefaultBranch()
|
||||
ctx.JSON(http.StatusOK, issueConfig)
|
||||
}
|
||||
|
||||
// ValidateIssueConfig returns validation errors for the issue config
|
||||
func ValidateIssueConfig(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/issue_config/validate repository repoValidateIssueConfig
|
||||
// ---
|
||||
// summary: Returns the validation information for a issue config
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/RepoIssueConfigValidation"
|
||||
_, err := ctx.IssueConfigFromDefaultBranch()
|
||||
|
||||
if err == nil {
|
||||
ctx.JSON(http.StatusOK, api.IssueConfigValidation{Valid: true, Message: ""})
|
||||
} else {
|
||||
ctx.JSON(http.StatusOK, api.IssueConfigValidation{Valid: false, Message: err.Error()})
|
||||
}
|
||||
}
|
||||
|
||||
func ListRepoActivityFeeds(ctx *context.APIContext) {
|
||||
// swagger:operation GET /repos/{owner}/{repo}/activities/feeds repository repoListActivityFeeds
|
||||
// ---
|
||||
// summary: List a repository's activity feeds
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: owner
|
||||
// in: path
|
||||
// description: owner of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: repo
|
||||
// in: path
|
||||
// description: name of the repo
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: date
|
||||
// in: query
|
||||
// description: the date of the activities to be found
|
||||
// type: string
|
||||
// format: date
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of results to return (1-based)
|
||||
// type: integer
|
||||
// - name: limit
|
||||
// in: query
|
||||
// description: page size of results
|
||||
// type: integer
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityFeedsList"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
opts := activities_model.GetFeedsOptions{
|
||||
RequestedRepo: ctx.Repo.Repository,
|
||||
Actor: ctx.Doer,
|
||||
IncludePrivate: true,
|
||||
Date: ctx.FormString("date"),
|
||||
ListOptions: listOptions,
|
||||
}
|
||||
|
||||
feeds, count, err := activities_model.GetFeeds(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
|
||||
return
|
||||
}
|
||||
ctx.SetTotalCountHeader(count)
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// Copyright 2023 The Gitea Authors. All rights reserved.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package swagger
|
||||
|
||||
import (
|
||||
api "code.gitea.io/gitea/modules/structs"
|
||||
)
|
||||
|
||||
// ActivityFeedsList
|
||||
// swagger:response ActivityFeedsList
|
||||
type swaggerActivityFeedsList struct {
|
||||
// in:body
|
||||
Body []api.Activity `json:"body"`
|
||||
}
|
||||
@@ -41,6 +41,8 @@ type swaggerParameterBodies struct {
|
||||
CreateIssueCommentOption api.CreateIssueCommentOption
|
||||
// in:body
|
||||
EditIssueCommentOption api.EditIssueCommentOption
|
||||
// in:body
|
||||
IssueMeta api.IssueMeta
|
||||
|
||||
// in:body
|
||||
IssueLabelsOption api.IssueLabelsOption
|
||||
|
||||
@@ -386,3 +386,17 @@ type swaggerRepoCollaboratorPermission struct {
|
||||
// in:body
|
||||
Body api.RepoCollaboratorPermission `json:"body"`
|
||||
}
|
||||
|
||||
// RepoIssueConfig
|
||||
// swagger:response RepoIssueConfig
|
||||
type swaggerRepoIssueConfig struct {
|
||||
// in:body
|
||||
Body api.IssueConfig `json:"body"`
|
||||
}
|
||||
|
||||
// RepoIssueConfigValidation
|
||||
// swagger:response RepoIssueConfigValidation
|
||||
type swaggerRepoIssueConfigValidation struct {
|
||||
// in:body
|
||||
Body api.IssueConfigValidation `json:"body"`
|
||||
}
|
||||
@@ -145,3 +145,60 @@ func GetUserHeatmapData(ctx *context.APIContext) {
|
||||
}
|
||||
ctx.JSON(http.StatusOK, heatmap)
|
||||
}
|
||||
|
||||
func ListUserActivityFeeds(ctx *context.APIContext) {
|
||||
// swagger:operation GET /users/{username}/activities/feeds user userListActivityFeeds
|
||||
// ---
|
||||
// summary: List a user's activity feeds
|
||||
// produces:
|
||||
// - application/json
|
||||
// parameters:
|
||||
// - name: username
|
||||
// in: path
|
||||
// description: username of user
|
||||
// type: string
|
||||
// required: true
|
||||
// - name: only-performed-by
|
||||
// in: query
|
||||
// description: if true, only show actions performed by the requested user
|
||||
// type: boolean
|
||||
// - name: date
|
||||
// in: query
|
||||
// description: the date of the activities to be found
|
||||
// type: string
|
||||
// format: date
|
||||
// - name: page
|
||||
// in: query
|
||||
// description: page number of results to return (1-based)
|
||||
// type: integer
|
||||
// - name: limit
|
||||
// in: query
|
||||
// description: page size of results
|
||||
// type: integer
|
||||
// responses:
|
||||
// "200":
|
||||
// "$ref": "#/responses/ActivityFeedsList"
|
||||
// "404":
|
||||
// "$ref": "#/responses/notFound"
|
||||
|
||||
includePrivate := ctx.IsSigned && (ctx.Doer.IsAdmin || ctx.Doer.ID == ctx.ContextUser.ID)
|
||||
listOptions := utils.GetListOptions(ctx)
|
||||
|
||||
opts := activities_model.GetFeedsOptions{
|
||||
RequestedUser: ctx.ContextUser,
|
||||
Actor: ctx.Doer,
|
||||
IncludePrivate: includePrivate,
|
||||
OnlyPerformedBy: ctx.FormBool("only-performed-by"),
|
||||
Date: ctx.FormString("date"),
|
||||
ListOptions: listOptions,
|
||||
}
|
||||
|
||||
feeds, count, err := activities_model.GetFeeds(ctx, opts)
|
||||
if err != nil {
|
||||
ctx.Error(http.StatusInternalServerError, "GetFeeds", err)
|
||||
return
|
||||
}
|
||||
ctx.SetTotalCountHeader(count)
|
||||
|
||||
ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
|
||||
}
|
||||
Reference in new issue
Block a user