初始提交: Gitea 项目代码

This commit is contained in:
root
2026-05-30 22:47:36 +08:00
commit f288f76350
6116 changed files with 776822 additions and 0 deletions
+693
View File
@@ -0,0 +1,693 @@
// Copyright 2024 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package org
import (
"errors"
"net/http"
actions_model "gitea.dev/models/actions"
"gitea.dev/models/db"
secret_model "gitea.dev/models/secret"
api "gitea.dev/modules/structs"
"gitea.dev/modules/util"
"gitea.dev/modules/web"
"gitea.dev/routers/api/v1/shared"
"gitea.dev/routers/api/v1/utils"
actions_service "gitea.dev/services/actions"
"gitea.dev/services/context"
secret_service "gitea.dev/services/secrets"
)
// ListActionsSecrets list an organization's actions secrets
func (Action) ListActionsSecrets(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/secrets organization orgListActionsSecrets
// ---
// summary: List an organization's actions secrets
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// 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/SecretList"
// "404":
// "$ref": "#/responses/notFound"
opts := &secret_model.FindSecretsOptions{
OwnerID: ctx.Org.Organization.ID,
ListOptions: utils.GetListOptions(ctx),
}
secrets, count, err := db.FindAndCount[secret_model.Secret](ctx, opts)
if err != nil {
ctx.APIErrorInternal(err)
return
}
apiSecrets := make([]*api.Secret, len(secrets))
for k, v := range secrets {
apiSecrets[k] = &api.Secret{
Name: v.Name,
Description: v.Description,
Created: v.CreatedUnix.AsTime(),
}
}
ctx.SetLinkHeader(count, opts.PageSize)
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, apiSecrets)
}
// create or update one secret of the organization
func (Action) CreateOrUpdateSecret(ctx *context.APIContext) {
// swagger:operation PUT /orgs/{org}/actions/secrets/{secretname} organization updateOrgSecret
// ---
// summary: Create or Update a secret value in an organization
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of organization
// type: string
// required: true
// - name: secretname
// in: path
// description: name of the secret
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateOrUpdateSecretOption"
// responses:
// "201":
// description: response when creating a secret
// "204":
// description: response when updating a secret
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
opt := web.GetForm(ctx).(*api.CreateOrUpdateSecretOption)
_, created, err := secret_service.CreateOrUpdateSecret(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"), opt.Data, opt.Description)
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
ctx.APIError(http.StatusNotFound, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
if created {
ctx.Status(http.StatusCreated)
} else {
ctx.Status(http.StatusNoContent)
}
}
// DeleteSecret delete one secret of the organization
func (Action) DeleteSecret(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/actions/secrets/{secretname} organization deleteOrgSecret
// ---
// summary: Delete a secret in an organization
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of organization
// type: string
// required: true
// - name: secretname
// in: path
// description: name of the secret
// type: string
// required: true
// responses:
// "204":
// description: delete one secret of the organization
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
err := secret_service.DeleteSecretByName(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("secretname"))
if err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
ctx.APIError(http.StatusNotFound, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
ctx.Status(http.StatusNoContent)
}
// https://docs.github.com/en/rest/actions/self-hosted-runners?apiVersion=2022-11-28#create-a-registration-token-for-an-organization
// CreateRegistrationToken returns the token to register org runners
func (Action) CreateRegistrationToken(ctx *context.APIContext) {
// swagger:operation POST /orgs/{org}/actions/runners/registration-token organization orgCreateRunnerRegistrationToken
// ---
// summary: Get an organization's actions runner registration token
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/RegistrationToken"
shared.GetRegistrationToken(ctx, ctx.Org.Organization.ID, 0)
}
// ListVariables list org-level variables
func (Action) ListVariables(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/variables organization getOrgVariablesList
// ---
// summary: Get an org-level variables list
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// 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/VariableList"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
listOptions := utils.GetListOptions(ctx)
vars, count, err := db.FindAndCount[actions_model.ActionVariable](ctx, &actions_model.FindVariablesOpts{
OwnerID: ctx.Org.Organization.ID,
ListOptions: listOptions,
})
if err != nil {
ctx.APIErrorInternal(err)
return
}
variables := make([]*api.ActionVariable, len(vars))
for i, v := range vars {
variables[i] = &api.ActionVariable{
OwnerID: v.OwnerID,
RepoID: v.RepoID,
Name: v.Name,
Data: v.Data,
Description: v.Description,
}
}
ctx.SetLinkHeader(count, listOptions.PageSize)
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, variables)
}
// GetVariable get an org-level variable
func (Action) GetVariable(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/variables/{variablename} organization getOrgVariable
// ---
// summary: Get an org-level variable
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: variablename
// in: path
// description: name of the variable
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/ActionVariable"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{
OwnerID: ctx.Org.Organization.ID,
Name: ctx.PathParam("variablename"),
})
if err != nil {
if errors.Is(err, util.ErrNotExist) {
ctx.APIError(http.StatusNotFound, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
variable := &api.ActionVariable{
OwnerID: v.OwnerID,
RepoID: v.RepoID,
Name: v.Name,
Data: v.Data,
Description: v.Description,
}
ctx.JSON(http.StatusOK, variable)
}
// DeleteVariable delete an org-level variable
func (Action) DeleteVariable(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/actions/variables/{variablename} organization deleteOrgVariable
// ---
// summary: Delete an org-level variable
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: variablename
// in: path
// description: name of the variable
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/ActionVariable"
// "201":
// description: response when deleting a variable
// "204":
// description: response when deleting a variable
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
if err := actions_service.DeleteVariableByName(ctx, ctx.Org.Organization.ID, 0, ctx.PathParam("variablename")); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
ctx.APIError(http.StatusBadRequest, err)
} else if errors.Is(err, util.ErrNotExist) {
ctx.APIError(http.StatusNotFound, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
ctx.Status(http.StatusNoContent)
}
// CreateVariable create an org-level variable
func (Action) CreateVariable(ctx *context.APIContext) {
// swagger:operation POST /orgs/{org}/actions/variables/{variablename} organization createOrgVariable
// ---
// summary: Create an org-level variable
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: variablename
// in: path
// description: name of the variable
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateVariableOption"
// responses:
// "201":
// description: successfully created the org-level variable
// "400":
// "$ref": "#/responses/error"
// "409":
// description: variable name already exists.
// "500":
// "$ref": "#/responses/error"
opt := web.GetForm(ctx).(*api.CreateVariableOption)
ownerID := ctx.Org.Organization.ID
variableName := ctx.PathParam("variablename")
v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{
OwnerID: ownerID,
Name: variableName,
})
if err != nil && !errors.Is(err, util.ErrNotExist) {
ctx.APIErrorInternal(err)
return
}
if v != nil && v.ID > 0 {
ctx.APIError(http.StatusConflict, util.NewAlreadyExistErrorf("variable name %s already exists", variableName))
return
}
if _, err := actions_service.CreateVariable(ctx, ownerID, 0, variableName, opt.Value, opt.Description); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
ctx.APIError(http.StatusBadRequest, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
ctx.Status(http.StatusCreated)
}
// UpdateVariable update an org-level variable
func (Action) UpdateVariable(ctx *context.APIContext) {
// swagger:operation PUT /orgs/{org}/actions/variables/{variablename} organization updateOrgVariable
// ---
// summary: Update an org-level variable
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: variablename
// in: path
// description: name of the variable
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/UpdateVariableOption"
// responses:
// "201":
// description: response when updating an org-level variable
// "204":
// description: response when updating an org-level variable
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
opt := web.GetForm(ctx).(*api.UpdateVariableOption)
v, err := actions_service.GetVariable(ctx, actions_model.FindVariablesOpts{
OwnerID: ctx.Org.Organization.ID,
Name: ctx.PathParam("variablename"),
})
if err != nil {
if errors.Is(err, util.ErrNotExist) {
ctx.APIError(http.StatusNotFound, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
if opt.Name == "" {
opt.Name = ctx.PathParam("variablename")
}
v.Name = opt.Name
v.Data = opt.Value
v.Description = opt.Description
if _, err := actions_service.UpdateVariableNameData(ctx, v); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
ctx.APIError(http.StatusBadRequest, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
ctx.Status(http.StatusNoContent)
}
// ListRunners get org-level runners
func (Action) ListRunners(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/runners organization getOrgRunners
// ---
// summary: Get org-level runners
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: disabled
// in: query
// description: filter by disabled status (true or false)
// type: boolean
// required: false
// responses:
// "200":
// "$ref": "#/responses/RunnerList"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.ListRunners(ctx, ctx.Org.Organization.ID, 0)
}
// GetRunner get an org-level runner
func (Action) GetRunner(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/runners/{runner_id} organization getOrgRunner
// ---
// summary: Get an org-level runner
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: runner_id
// in: path
// description: id of the runner
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/Runner"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.GetRunner(ctx, ctx.Org.Organization.ID, 0, ctx.PathParamInt64("runner_id"))
}
// DeleteRunner delete an org-level runner
func (Action) DeleteRunner(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/actions/runners/{runner_id} organization deleteOrgRunner
// ---
// summary: Delete an org-level runner
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: runner_id
// in: path
// description: id of the runner
// type: string
// required: true
// responses:
// "204":
// description: runner has been deleted
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.DeleteRunner(ctx, ctx.Org.Organization.ID, 0, ctx.PathParamInt64("runner_id"))
}
// UpdateRunner update an org-level runner
func (Action) UpdateRunner(ctx *context.APIContext) {
// swagger:operation PATCH /orgs/{org}/actions/runners/{runner_id} organization updateOrgRunner
// ---
// summary: Update an org-level runner
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: runner_id
// in: path
// description: id of the runner
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/EditActionRunnerOption"
// responses:
// "200":
// "$ref": "#/responses/Runner"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
shared.UpdateRunner(ctx, ctx.Org.Organization.ID, 0, ctx.PathParamInt64("runner_id"))
}
func (Action) ListWorkflowJobs(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/jobs organization getOrgWorkflowJobs
// ---
// summary: Get org-level workflow jobs
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: status
// in: query
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - 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/WorkflowJobsList"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.ListJobs(ctx, ctx.Org.Organization.ID, 0, 0, nil)
}
func (Action) ListWorkflowRuns(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/actions/runs organization getOrgWorkflowRuns
// ---
// summary: Get org-level workflow runs
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: event
// in: query
// description: workflow event name
// type: string
// required: false
// - name: branch
// in: query
// description: workflow branch
// type: string
// required: false
// - name: status
// in: query
// description: workflow status (pending, queued, in_progress, failure, success, skipped)
// type: string
// required: false
// - name: actor
// in: query
// description: triggered by user
// type: string
// required: false
// - name: head_sha
// in: query
// description: triggering sha of the workflow run
// type: string
// required: false
// - 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/WorkflowRunsList"
// "400":
// "$ref": "#/responses/error"
// "404":
// "$ref": "#/responses/notFound"
shared.ListRuns(ctx, ctx.Org.Organization.ID, 0)
}
var _ actions_service.API = new(Action)
// Action implements actions_service.API
type Action struct{}
// NewAction creates a new Action service
func NewAction() actions_service.API {
return Action{}
}
+80
View File
@@ -0,0 +1,80 @@
// Copyright 2023 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package org
import (
"encoding/base64"
"net/http"
api "gitea.dev/modules/structs"
"gitea.dev/modules/web"
"gitea.dev/services/context"
user_service "gitea.dev/services/user"
)
// UpdateAvatarupdates the Avatar of an Organisation
func UpdateAvatar(ctx *context.APIContext) {
// swagger:operation POST /orgs/{org}/avatar organization orgUpdateAvatar
// ---
// summary: Update Avatar
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/UpdateUserAvatarOption"
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
form := web.GetForm(ctx).(*api.UpdateUserAvatarOption)
content, err := base64.StdEncoding.DecodeString(form.Image)
if err != nil {
ctx.APIError(http.StatusBadRequest, err)
return
}
err = user_service.UploadAvatar(ctx, ctx.Org.Organization.AsUser(), content)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
// DeleteAvatar deletes the Avatar of an Organisation
func DeleteAvatar(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/avatar organization orgDeleteAvatar
// ---
// summary: Delete Avatar
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
err := user_service.DeleteAvatar(ctx, ctx.Org.Organization.AsUser())
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
+116
View File
@@ -0,0 +1,116 @@
// Copyright 2024 The Gitea Authors.
// SPDX-License-Identifier: MIT
package org
import (
"gitea.dev/routers/api/v1/shared"
"gitea.dev/services/context"
)
func ListBlocks(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/blocks organization organizationListBlocks
// ---
// summary: List users blocked by the organization
// parameters:
// - name: org
// in: path
// description: name of the organization
// 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
// produces:
// - application/json
// responses:
// "200":
// "$ref": "#/responses/UserList"
shared.ListBlocks(ctx, ctx.Org.Organization.AsUser())
}
func CheckUserBlock(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/blocks/{username} organization organizationCheckUserBlock
// ---
// summary: Check if a user is blocked by the organization
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: username
// in: path
// description: username of the user to check
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
shared.CheckUserBlock(ctx, ctx.Org.Organization.AsUser())
}
func BlockUser(ctx *context.APIContext) {
// swagger:operation PUT /orgs/{org}/blocks/{username} organization organizationBlockUser
// ---
// summary: Block a user
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: username
// in: path
// description: username of the user to block
// type: string
// required: true
// - name: note
// in: query
// description: optional note for the block
// type: string
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
shared.BlockUser(ctx, ctx.Org.Organization.AsUser())
}
func UnblockUser(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/blocks/{username} organization organizationUnblockUser
// ---
// summary: Unblock a user
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: username
// in: path
// description: username of the user to unblock
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
shared.UnblockUser(ctx, ctx.Doer, ctx.Org.Organization.AsUser())
}
+189
View File
@@ -0,0 +1,189 @@
// Copyright 2016 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package org
import (
"net/http"
api "gitea.dev/modules/structs"
"gitea.dev/modules/web"
"gitea.dev/routers/api/v1/utils"
"gitea.dev/services/context"
webhook_service "gitea.dev/services/webhook"
)
// ListHooks list an organization's webhooks
func ListHooks(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/hooks organization orgListHooks
// ---
// summary: List an organization's webhooks
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// 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/HookList"
// "404":
// "$ref": "#/responses/notFound"
utils.ListOwnerHooks(
ctx,
ctx.ContextUser,
)
}
// GetHook get an organization's hook by id
func GetHook(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/hooks/{id} organization orgGetHook
// ---
// summary: Get a hook
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: id
// in: path
// description: id of the hook to get
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/Hook"
// "404":
// "$ref": "#/responses/notFound"
hook, err := utils.GetOwnerHook(ctx, ctx.ContextUser.ID, ctx.PathParamInt64("id"))
if err != nil {
return
}
apiHook, err := webhook_service.ToHook(ctx.ContextUser.HomeLink(), hook)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiHook)
}
// CreateHook create a hook for an organization
func CreateHook(ctx *context.APIContext) {
// swagger:operation POST /orgs/{org}/hooks organization orgCreateHook
// ---
// summary: Create a hook
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/CreateHookOption"
// responses:
// "201":
// "$ref": "#/responses/Hook"
// "404":
// "$ref": "#/responses/notFound"
utils.AddOwnerHook(
ctx,
ctx.ContextUser,
web.GetForm(ctx).(*api.CreateHookOption),
)
}
// EditHook modify a hook of an organization
func EditHook(ctx *context.APIContext) {
// swagger:operation PATCH /orgs/{org}/hooks/{id} organization orgEditHook
// ---
// summary: Update a hook
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: id
// in: path
// description: id of the hook to update
// type: integer
// format: int64
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/EditHookOption"
// responses:
// "200":
// "$ref": "#/responses/Hook"
// "404":
// "$ref": "#/responses/notFound"
utils.EditOwnerHook(
ctx,
ctx.ContextUser,
web.GetForm(ctx).(*api.EditHookOption),
ctx.PathParamInt64("id"),
)
}
// DeleteHook delete a hook of an organization
func DeleteHook(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/hooks/{id} organization orgDeleteHook
// ---
// summary: Delete a hook
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: id
// in: path
// description: id of the hook to delete
// type: integer
// format: int64
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
utils.DeleteOwnerHook(
ctx,
ctx.ContextUser,
ctx.PathParamInt64("id"),
)
}
+258
View File
@@ -0,0 +1,258 @@
// Copyright 2020 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package org
import (
"net/http"
"strconv"
"strings"
issues_model "gitea.dev/models/issues"
"gitea.dev/modules/label"
api "gitea.dev/modules/structs"
"gitea.dev/modules/web"
"gitea.dev/routers/api/v1/utils"
"gitea.dev/services/context"
"gitea.dev/services/convert"
)
// ListLabels list all the labels of an organization
func ListLabels(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/labels organization orgListLabels
// ---
// summary: List an organization's labels
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// 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/LabelList"
// "404":
// "$ref": "#/responses/notFound"
labels, err := issues_model.GetLabelsByOrgID(ctx, ctx.Org.Organization.ID, ctx.FormString("sort"), utils.GetListOptions(ctx))
if err != nil {
ctx.APIErrorInternal(err)
return
}
count, err := issues_model.CountLabelsByOrgID(ctx, ctx.Org.Organization.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToLabelList(labels, nil, ctx.Org.Organization.AsUser()))
}
// CreateLabel create a label for a repository
func CreateLabel(ctx *context.APIContext) {
// swagger:operation POST /orgs/{org}/labels organization orgCreateLabel
// ---
// summary: Create a label for an organization
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateLabelOption"
// responses:
// "201":
// "$ref": "#/responses/Label"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.CreateLabelOption)
form.Color = strings.Trim(form.Color, " ")
color, err := label.NormalizeColor(form.Color)
if err != nil {
ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
form.Color = color
label := &issues_model.Label{
Name: form.Name,
Exclusive: form.Exclusive,
Color: form.Color,
OrgID: ctx.Org.Organization.ID,
Description: form.Description,
}
if err := issues_model.NewLabel(ctx, label); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, convert.ToLabel(label, nil, ctx.Org.Organization.AsUser()))
}
// GetLabel get label by organization and label id
func GetLabel(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/labels/{id} organization orgGetLabel
// ---
// summary: Get a single label
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: id
// in: path
// description: id of the label to get
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/Label"
// "404":
// "$ref": "#/responses/notFound"
var (
label *issues_model.Label
err error
)
strID := ctx.PathParam("id")
if intID, err2 := strconv.ParseInt(strID, 10, 64); err2 != nil {
label, err = issues_model.GetLabelInOrgByName(ctx, ctx.Org.Organization.ID, strID)
} else {
label, err = issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, intID)
}
if err != nil {
if issues_model.IsErrOrgLabelNotExist(err) {
ctx.APIErrorNotFound()
} else {
ctx.APIErrorInternal(err)
}
return
}
ctx.JSON(http.StatusOK, convert.ToLabel(label, nil, ctx.Org.Organization.AsUser()))
}
// EditLabel modify a label for an Organization
func EditLabel(ctx *context.APIContext) {
// swagger:operation PATCH /orgs/{org}/labels/{id} organization orgEditLabel
// ---
// summary: Update a label
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: id
// in: path
// description: id of the label to edit
// type: integer
// format: int64
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/EditLabelOption"
// responses:
// "200":
// "$ref": "#/responses/Label"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.EditLabelOption)
l, err := issues_model.GetLabelInOrgByID(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64("id"))
if err != nil {
if issues_model.IsErrOrgLabelNotExist(err) {
ctx.APIErrorNotFound()
} else {
ctx.APIErrorInternal(err)
}
return
}
if form.Name != nil {
l.Name = *form.Name
}
if form.Exclusive != nil {
l.Exclusive = *form.Exclusive
}
if form.Color != nil {
color, err := label.NormalizeColor(*form.Color)
if err != nil {
ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
l.Color = color
}
if form.Description != nil {
l.Description = *form.Description
}
l.SetArchived(form.IsArchived != nil && *form.IsArchived)
if err := issues_model.UpdateLabel(ctx, l); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToLabel(l, nil, ctx.Org.Organization.AsUser()))
}
// DeleteLabel delete a label for an organization
func DeleteLabel(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/labels/{id} organization orgDeleteLabel
// ---
// summary: Delete a label
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: id
// in: path
// description: id of the label to delete
// type: integer
// format: int64
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
if err := issues_model.DeleteLabel(ctx, ctx.Org.Organization.ID, ctx.PathParamInt64("id")); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
+346
View File
@@ -0,0 +1,346 @@
// Copyright 2017 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package org
import (
"net/http"
"net/url"
"gitea.dev/models/organization"
user_model "gitea.dev/models/user"
"gitea.dev/modules/setting"
api "gitea.dev/modules/structs"
"gitea.dev/routers/api/v1/user"
"gitea.dev/routers/api/v1/utils"
"gitea.dev/services/context"
"gitea.dev/services/convert"
org_service "gitea.dev/services/org"
)
// listMembers list an organization's members
func listMembers(ctx *context.APIContext, isMember bool) {
listOptions := utils.GetListOptions(ctx)
opts := &organization.FindOrgMembersOpts{
Doer: ctx.Doer,
IsDoerMember: isMember,
OrgID: ctx.Org.Organization.ID,
ListOptions: listOptions,
}
count, err := organization.CountOrgMembers(ctx, opts)
if err != nil {
ctx.APIErrorInternal(err)
return
}
members, _, err := organization.FindOrgMembers(ctx, opts)
if err != nil {
ctx.APIErrorInternal(err)
return
}
apiMembers := make([]*api.User, len(members))
for i, member := range members {
apiMembers[i] = convert.ToUser(ctx, member, ctx.Doer)
}
ctx.SetLinkHeader(count, listOptions.PageSize)
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, apiMembers)
}
// ListMembers list an organization's members
func ListMembers(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/members organization orgListMembers
// ---
// summary: List an organization's members
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// 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/UserList"
// "404":
// "$ref": "#/responses/notFound"
var (
isMember bool
err error
)
if ctx.Doer != nil {
isMember, err = ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
}
listMembers(ctx, isMember)
}
// ListPublicMembers list an organization's public members
func ListPublicMembers(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/public_members organization orgListPublicMembers
// ---
// summary: List an organization's public members
// parameters:
// - name: org
// in: path
// description: name of the organization
// 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
// produces:
// - application/json
// responses:
// "200":
// "$ref": "#/responses/UserList"
// "404":
// "$ref": "#/responses/notFound"
listMembers(ctx, false)
}
// IsMember check if a user is a member of an organization
func IsMember(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/members/{username} organization orgIsMember
// ---
// summary: Check if a user is a member of an organization
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: username
// in: path
// description: username of the user to check for an organization membership
// type: string
// required: true
// responses:
// "204":
// description: user is a member
// "303":
// description: redirection to /orgs/{org}/public_members/{username}
// "404":
// description: user is not a member
userToCheck := user.GetContextUserByPathParam(ctx)
if ctx.Written() {
return
}
if ctx.Doer != nil {
userIsMember, err := ctx.Org.Organization.IsOrgMember(ctx, ctx.Doer.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
} else if userIsMember || ctx.Doer.IsAdmin {
userToCheckIsMember, err := ctx.Org.Organization.IsOrgMember(ctx, userToCheck.ID)
if err != nil {
ctx.APIErrorInternal(err)
} else if userToCheckIsMember {
ctx.Status(http.StatusNoContent)
} else {
ctx.APIErrorNotFound()
}
return
} else if ctx.Doer.ID == userToCheck.ID {
ctx.APIErrorNotFound()
return
}
}
redirectURL := setting.AppSubURL + "/api/v1/orgs/" + url.PathEscape(ctx.Org.Organization.Name) + "/public_members/" + url.PathEscape(userToCheck.Name)
ctx.Redirect(redirectURL)
}
// IsPublicMember check if a user is a public member of an organization
func IsPublicMember(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/public_members/{username} organization orgIsPublicMember
// ---
// summary: Check if a user is a public member of an organization
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: username
// in: path
// description: username of the user to check for a public organization membership
// type: string
// required: true
// responses:
// "204":
// description: user is a public member
// "404":
// description: user is not a public member
userToCheck := user.GetContextUserByPathParam(ctx)
if ctx.Written() {
return
}
is, err := organization.IsPublicMembership(ctx, ctx.Org.Organization.ID, userToCheck.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
if is {
ctx.Status(http.StatusNoContent)
} else {
ctx.APIErrorNotFound()
}
}
func checkCanChangeOrgUserStatus(ctx *context.APIContext, targetUser *user_model.User) {
// allow user themselves to change their status, and allow admins to change any user
if targetUser.ID == ctx.Doer.ID || ctx.Doer.IsAdmin {
return
}
// allow org owners to change status of members
isOwner, err := ctx.Org.Organization.IsOwnedBy(ctx, ctx.Doer.ID)
if err != nil {
ctx.APIError(http.StatusInternalServerError, err)
} else if !isOwner {
ctx.APIError(http.StatusForbidden, "Cannot change member visibility")
}
}
// PublicizeMember make a member's membership public
func PublicizeMember(ctx *context.APIContext) {
// swagger:operation PUT /orgs/{org}/public_members/{username} organization orgPublicizeMember
// ---
// summary: Publicize a user's membership
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: username
// in: path
// description: username of the user whose membership is to be publicized
// type: string
// required: true
// responses:
// "204":
// description: membership publicized
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
userToPublicize := user.GetContextUserByPathParam(ctx)
if ctx.Written() {
return
}
checkCanChangeOrgUserStatus(ctx, userToPublicize)
if ctx.Written() {
return
}
err := organization.ChangeOrgUserStatus(ctx, ctx.Org.Organization.ID, userToPublicize.ID, true)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
// ConcealMember make a member's membership not public
func ConcealMember(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/public_members/{username} organization orgConcealMember
// ---
// summary: Conceal a user's membership
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: username
// in: path
// description: username of the user whose membership is to be concealed
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
userToConceal := user.GetContextUserByPathParam(ctx)
if ctx.Written() {
return
}
checkCanChangeOrgUserStatus(ctx, userToConceal)
if ctx.Written() {
return
}
err := organization.ChangeOrgUserStatus(ctx, ctx.Org.Organization.ID, userToConceal.ID, false)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
// DeleteMember remove a member from an organization
func DeleteMember(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/members/{username} organization orgDeleteMember
// ---
// summary: Remove a member from an organization
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: username
// in: path
// description: username of the user to remove from the organization
// type: string
// required: true
// responses:
// "204":
// description: member removed
// "404":
// "$ref": "#/responses/notFound"
member := user.GetContextUserByPathParam(ctx)
if ctx.Written() {
return
}
if err := org_service.RemoveOrgUser(ctx, ctx.Org.Organization, member); err != nil {
ctx.APIErrorInternal(err)
}
ctx.Status(http.StatusNoContent)
}
+578
View File
@@ -0,0 +1,578 @@
// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2018 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package org
import (
gocontext "context"
"errors"
"fmt"
"net/http"
activities_model "gitea.dev/models/activities"
"gitea.dev/models/db"
"gitea.dev/models/organization"
"gitea.dev/models/perm"
repo_model "gitea.dev/models/repo"
system_model "gitea.dev/models/system"
user_model "gitea.dev/models/user"
"gitea.dev/modules/graceful"
"gitea.dev/modules/log"
"gitea.dev/modules/optional"
api "gitea.dev/modules/structs"
"gitea.dev/modules/util"
"gitea.dev/modules/web"
"gitea.dev/routers/api/v1/user"
"gitea.dev/routers/api/v1/utils"
"gitea.dev/services/context"
"gitea.dev/services/convert"
feed_service "gitea.dev/services/feed"
"gitea.dev/services/org"
repo_service "gitea.dev/services/repository"
user_service "gitea.dev/services/user"
)
func listUserOrgs(ctx *context.APIContext, u *user_model.User) {
listOptions := utils.GetListOptions(ctx)
opts := organization.FindOrgOptions{
ListOptions: listOptions,
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)
return
}
apiOrgs := make([]*api.Organization, len(orgs))
for i := range orgs {
apiOrgs[i] = convert.ToOrganization(ctx, orgs[i])
}
ctx.SetLinkHeader(maxResults, listOptions.PageSize)
ctx.SetTotalCountHeader(maxResults)
ctx.JSON(http.StatusOK, &apiOrgs)
}
// ListMyOrgs list all my orgs
func ListMyOrgs(ctx *context.APIContext) {
// swagger:operation GET /user/orgs organization orgListCurrentUserOrgs
// ---
// summary: List the current user's organizations
// produces:
// - application/json
// parameters:
// - 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/OrganizationList"
// "404":
// "$ref": "#/responses/notFound"
listUserOrgs(ctx, ctx.Doer)
}
// ListUserOrgs list user's orgs
func ListUserOrgs(ctx *context.APIContext) {
// swagger:operation GET /users/{username}/orgs organization orgListUserOrgs
// ---
// summary: List a user's organizations
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of the user whose organizations are to be listed
// 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/OrganizationList"
// "404":
// "$ref": "#/responses/notFound"
listUserOrgs(ctx, ctx.ContextUser)
}
// GetUserOrgsPermissions get user permissions in organization
func GetUserOrgsPermissions(ctx *context.APIContext) {
// swagger:operation GET /users/{username}/orgs/{org}/permissions organization orgGetUserPermissions
// ---
// summary: Get user permissions in organization
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of the user whose permissions are to be obtained
// type: string
// required: true
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/OrganizationPermissions"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
var o *user_model.User
if o = user.GetUserByPathParam(ctx, "org"); o == nil {
return
}
op := api.OrganizationPermissions{}
if !organization.HasOrgOrUserVisible(ctx, o, ctx.Doer) {
ctx.APIErrorNotFound("HasOrgOrUserVisible", nil)
return
}
org := organization.OrgFromUser(o)
authorizeLevel, err := org.GetOrgUserMaxAuthorizeLevel(ctx, ctx.ContextUser.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
if authorizeLevel > perm.AccessModeNone {
op.CanRead = true
}
if authorizeLevel > perm.AccessModeRead {
op.CanWrite = true
}
if authorizeLevel > perm.AccessModeWrite {
op.IsAdmin = true
}
if authorizeLevel > perm.AccessModeAdmin {
op.IsOwner = true
}
op.CanCreateRepository, err = org.CanCreateOrgRepo(ctx, ctx.ContextUser.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, op)
}
// GetAll return list of all public organizations
func GetAll(ctx *context.APIContext) {
// swagger:operation Get /orgs organization orgGetAll
// ---
// summary: Get list of organizations
// produces:
// - application/json
// parameters:
// - 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/OrganizationList"
vMode := []api.VisibleType{api.VisibleTypePublic}
if ctx.IsSigned {
vMode = append(vMode, api.VisibleTypeLimited)
if ctx.Doer.IsAdmin {
vMode = append(vMode, api.VisibleTypePrivate)
}
}
listOptions := utils.GetListOptions(ctx)
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
}
orgs := make([]*api.Organization, len(publicOrgs))
for i := range publicOrgs {
orgs[i] = convert.ToOrganization(ctx, organization.OrgFromUser(publicOrgs[i]))
}
ctx.SetLinkHeader(maxResults, listOptions.PageSize)
ctx.SetTotalCountHeader(maxResults)
ctx.JSON(http.StatusOK, &orgs)
}
// Create api for create organization
func Create(ctx *context.APIContext) {
// swagger:operation POST /orgs organization orgCreate
// ---
// summary: Create an organization
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: organization
// in: body
// required: true
// schema: { "$ref": "#/definitions/CreateOrgOption" }
// responses:
// "201":
// "$ref": "#/responses/Organization"
// "403":
// "$ref": "#/responses/forbidden"
// "422":
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.CreateOrgOption)
if !ctx.Doer.CanCreateOrganization() {
ctx.APIError(http.StatusForbidden, nil)
return
}
visibility := api.VisibleTypePublic
if form.Visibility != "" {
visibility = api.VisibilityModes[string(form.Visibility)]
}
org := &organization.Organization{
Name: form.UserName,
FullName: form.FullName,
Email: form.Email,
Description: form.Description,
Website: form.Website,
Location: form.Location,
IsActive: true,
Type: user_model.UserTypeOrganization,
Visibility: visibility,
RepoAdminChangeTeamAccess: form.RepoAdminChangeTeamAccess,
}
if err := organization.CreateOrganization(ctx, org, ctx.Doer); err != nil {
if user_model.IsErrUserAlreadyExist(err) ||
db.IsErrNameReserved(err) ||
db.IsErrNameCharsNotAllowed(err) ||
db.IsErrNamePatternNotAllowed(err) {
ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
ctx.JSON(http.StatusCreated, convert.ToOrganization(ctx, org))
}
// Get get an organization
func Get(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org} organization orgGet
// ---
// summary: Get an organization
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization to get
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/Organization"
// "404":
// "$ref": "#/responses/notFound"
if !organization.HasOrgOrUserVisible(ctx, ctx.Org.Organization.AsUser(), ctx.Doer) {
ctx.APIErrorNotFound("HasOrgOrUserVisible", nil)
return
}
org := convert.ToOrganization(ctx, ctx.Org.Organization)
// Don't show Mail, when User is not logged in
if ctx.Doer == nil {
org.Email = ""
}
ctx.JSON(http.StatusOK, org)
}
func Rename(ctx *context.APIContext) {
// swagger:operation POST /orgs/{org}/rename organization renameOrg
// ---
// summary: Rename an organization
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: existing org name
// type: string
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/RenameOrgOption"
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "422":
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.RenameOrgOption)
orgUser := ctx.Org.Organization.AsUser()
if err := user_service.RenameUser(ctx, orgUser, form.NewName, ctx.Doer); err != nil {
if user_model.IsErrUserAlreadyExist(err) || db.IsErrNameReserved(err) || db.IsErrNamePatternNotAllowed(err) || db.IsErrNameCharsNotAllowed(err) {
ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
ctx.Status(http.StatusNoContent)
}
// Edit change an organization's information
func Edit(ctx *context.APIContext) {
// swagger:operation PATCH /orgs/{org} organization orgEdit
// ---
// summary: Edit an organization
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization to edit
// type: string
// required: true
// - name: body
// in: body
// required: true
// schema:
// "$ref": "#/definitions/EditOrgOption"
// responses:
// "200":
// "$ref": "#/responses/Organization"
// "404":
// "$ref": "#/responses/notFound"
form := web.GetForm(ctx).(*api.EditOrgOption)
if err := org.UpdateOrgEmailAddress(ctx, ctx.Org.Organization, form.Email); err != nil {
if errors.Is(err, util.ErrInvalidArgument) {
ctx.APIError(http.StatusUnprocessableEntity, err)
return
}
ctx.APIErrorInternal(err)
return
}
opts := &user_service.UpdateOptions{
FullName: optional.FromPtr(form.FullName),
Description: optional.FromPtr(form.Description),
Website: optional.FromPtr(form.Website),
Location: optional.FromPtr(form.Location),
Visibility: optional.FromMapLookup(api.VisibilityModes, string(optional.FromPtr(form.Visibility).Value())),
RepoAdminChangeTeamAccess: optional.FromPtr(form.RepoAdminChangeTeamAccess),
}
if err := user_service.UpdateUser(ctx, ctx.Org.Organization.AsUser(), opts); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToOrganization(ctx, ctx.Org.Organization))
}
// Delete an organization
func Delete(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org} organization orgDelete
// ---
// summary: Delete an organization
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: organization that is to be deleted
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
if err := org.DeleteOrganization(ctx, ctx.Org.Organization, false); err != nil {
ctx.APIErrorInternal(err)
return
}
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, ctx.Doer.ID)
if err != nil {
ctx.APIErrorInternal(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,
}
opts.ApplyPublicOnly(ctx.PublicOnly)
feeds, count, err := feed_service.GetFeeds(ctx, opts)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
}
func deleteOrgReposBackground(ctx gocontext.Context, org *organization.Organization, repoIDs []int64, doer *user_model.User) {
defer func() {
if r := recover(); r != nil {
log.Error("panic during org repo deletion: %v, stack: %v", r, log.Stack(2))
}
}()
for _, repoID := range repoIDs {
repo, err := repo_model.GetRepositoryByID(ctx, repoID)
if err != nil {
desc := fmt.Sprintf("Failed to get repository ID %d in org %s: %v", repoID, org.Name, err)
_ = system_model.CreateNotice(ctx, system_model.NoticeRepository, desc)
log.Error("GetRepositoryByID failed: %v", desc)
continue
}
if err := repo_service.DeleteRepository(ctx, doer, repo, true); err != nil {
desc := fmt.Sprintf("Failed to delete repository %s (ID: %d) in org %s: %v", repo.Name, repo.ID, org.Name, err)
_ = system_model.CreateNotice(ctx, system_model.NoticeRepository, desc)
log.Error("DeleteRepository failed: %v", desc)
continue
}
log.Info("Successfully deleted repository %s (ID: %d) in org %s", repo.Name, repo.ID, org.Name)
}
log.Info("Completed deletion of repositories in org %s", org.Name)
}
func DeleteOrgRepos(ctx *context.APIContext) {
// swagger:operation DELETE /orgs/{org}/repos organization orgDeleteRepos
// ---
// summary: Delete all repositories in an organization
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// responses:
// "202":
// "$ref": "#/responses/empty"
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// Intentionally it only loads repository IDs to avoid loading too much data into memory
// There is no need to do pagination here as the number of repositories is expected to be manageable
repoIDs, err := repo_model.GetOrgRepositoryIDs(ctx, ctx.Org.Organization.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
}
if len(repoIDs) == 0 {
ctx.Status(http.StatusNoContent)
return
}
// Start deletion (slow) in background with detached context, so it can continue even if the request is canceled
go deleteOrgReposBackground(graceful.GetManager().ShutdownContext(), ctx.Org.Organization, repoIDs, ctx.Doer)
ctx.Status(http.StatusAccepted)
}
+888
View File
@@ -0,0 +1,888 @@
// Copyright 2016 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package org
import (
"errors"
"net/http"
activities_model "gitea.dev/models/activities"
"gitea.dev/models/organization"
"gitea.dev/models/perm"
access_model "gitea.dev/models/perm/access"
repo_model "gitea.dev/models/repo"
unit_model "gitea.dev/models/unit"
user_model "gitea.dev/models/user"
"gitea.dev/modules/log"
api "gitea.dev/modules/structs"
"gitea.dev/modules/web"
"gitea.dev/routers/api/v1/user"
"gitea.dev/routers/api/v1/utils"
"gitea.dev/services/context"
"gitea.dev/services/convert"
feed_service "gitea.dev/services/feed"
org_service "gitea.dev/services/org"
repo_service "gitea.dev/services/repository"
)
// ListTeams list all the teams of an organization
func ListTeams(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/teams organization orgListTeams
// ---
// summary: List an organization's teams
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// 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/TeamList"
// "404":
// "$ref": "#/responses/notFound"
listOptions := utils.GetListOptions(ctx)
teams, count, err := organization.SearchTeam(ctx, &organization.SearchTeamOptions{
ListOptions: listOptions,
OrgID: ctx.Org.Organization.ID,
})
if err != nil {
ctx.APIErrorInternal(err)
return
}
apiTeams, err := convert.ToTeams(ctx, teams, false)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.SetLinkHeader(count, listOptions.PageSize)
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, apiTeams)
}
// ListUserTeams list all the teams a user belongs to
func ListUserTeams(ctx *context.APIContext) {
// swagger:operation GET /user/teams user userListTeams
// ---
// summary: List all the teams a user belongs to
// produces:
// - application/json
// parameters:
// - 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/TeamList"
listOptions := utils.GetListOptions(ctx)
teams, count, err := organization.SearchTeam(ctx, &organization.SearchTeamOptions{
ListOptions: listOptions,
UserID: ctx.Doer.ID,
})
if err != nil {
ctx.APIErrorInternal(err)
return
}
apiTeams, err := convert.ToTeams(ctx, teams, true)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.SetLinkHeader(count, listOptions.PageSize)
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, apiTeams)
}
// GetTeam api for get a team
func GetTeam(ctx *context.APIContext) {
// swagger:operation GET /teams/{id} organization orgGetTeam
// ---
// summary: Get a team
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the team to get
// type: integer
// format: int64
// required: true
// responses:
// "200":
// "$ref": "#/responses/Team"
// "404":
// "$ref": "#/responses/notFound"
apiTeam, err := convert.ToTeam(ctx, ctx.Org.Team, true)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiTeam)
}
func attachTeamUnits(team *organization.Team, defaultAccessMode perm.AccessMode, units []string) {
unitTypes, _ := unit_model.FindUnitTypes(units...)
team.Units = make([]*organization.TeamUnit, 0, len(units))
for _, tp := range unitTypes {
team.Units = append(team.Units, &organization.TeamUnit{
OrgID: team.OrgID,
Type: tp,
AccessMode: defaultAccessMode,
})
}
}
func attachTeamUnitsMap(team *organization.Team, unitsMap map[string]string) {
team.Units = make([]*organization.TeamUnit, 0, len(unitsMap))
for unitKey, p := range unitsMap {
team.Units = append(team.Units, &organization.TeamUnit{
OrgID: team.OrgID,
Type: unit_model.TypeFromKey(unitKey),
AccessMode: perm.ParseAccessMode(p),
})
}
}
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
// ---
// summary: Create a team
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/CreateTeamOption"
// responses:
// "201":
// "$ref": "#/responses/Team"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/validationError"
form := web.GetForm(ctx).(*api.CreateTeamOption)
teamPermission := perm.ParseAccessMode(string(form.Permission), perm.AccessModeNone, perm.AccessModeAdmin)
team := &organization.Team{
OrgID: ctx.Org.Organization.ID,
Name: form.Name,
Description: form.Description,
IncludesAllRepositories: form.IncludesAllRepositories,
CanCreateOrgRepo: form.CanCreateOrgRepo,
AccessMode: teamPermission,
}
if team.AccessMode < perm.AccessModeAdmin {
if len(form.UnitsMap) > 0 {
attachTeamUnitsMap(team, form.UnitsMap)
} else if len(form.Units) > 0 {
unitPerm := perm.ParseAccessMode(string(form.Permission), perm.AccessModeRead, perm.AccessModeWrite)
attachTeamUnits(team, unitPerm, form.Units)
} else {
ctx.APIErrorInternal(errors.New("units permission should not be empty"))
return
}
} else {
attachAdminTeamUnits(team)
}
if err := org_service.NewTeam(ctx, team); err != nil {
if organization.IsErrTeamAlreadyExist(err) {
ctx.APIError(http.StatusUnprocessableEntity, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
apiTeam, err := convert.ToTeam(ctx, team, true)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusCreated, apiTeam)
}
// EditTeam api for edit a team
func EditTeam(ctx *context.APIContext) {
// swagger:operation PATCH /teams/{id} organization orgEditTeam
// ---
// summary: Edit a team
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the team to edit
// type: integer
// required: true
// - name: body
// in: body
// schema:
// "$ref": "#/definitions/EditTeamOption"
// responses:
// "200":
// "$ref": "#/responses/Team"
// "404":
// "$ref": "#/responses/notFound"
form := web.GetForm(ctx).(*api.EditTeamOption)
team := ctx.Org.Team
if err := team.LoadUnits(ctx); err != nil {
ctx.APIErrorInternal(err)
return
}
if form.CanCreateOrgRepo != nil {
team.CanCreateOrgRepo = team.IsOwnerTeam() || *form.CanCreateOrgRepo
}
if len(form.Name) > 0 {
team.Name = form.Name
}
if form.Description != nil {
team.Description = *form.Description
}
isAuthChanged := false
isIncludeAllChanged := false
if !team.IsOwnerTeam() && len(form.Permission) != 0 {
teamPermission := perm.ParseAccessMode(string(form.Permission), perm.AccessModeNone, perm.AccessModeAdmin)
if team.AccessMode != teamPermission {
isAuthChanged = true
team.AccessMode = teamPermission
}
if form.IncludesAllRepositories != nil {
isIncludeAllChanged = true
team.IncludesAllRepositories = *form.IncludesAllRepositories
}
}
if team.AccessMode < perm.AccessModeAdmin {
if len(form.UnitsMap) > 0 {
attachTeamUnitsMap(team, form.UnitsMap)
} else if len(form.Units) > 0 {
unitPerm := perm.ParseAccessMode(string(form.Permission), perm.AccessModeRead, perm.AccessModeWrite)
attachTeamUnits(team, unitPerm, form.Units)
}
} else {
attachAdminTeamUnits(team)
}
if err := org_service.UpdateTeam(ctx, team, isAuthChanged, isIncludeAllChanged); err != nil {
ctx.APIErrorInternal(err)
return
}
apiTeam, err := convert.ToTeam(ctx, team)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, apiTeam)
}
// DeleteTeam api for delete a team
func DeleteTeam(ctx *context.APIContext) {
// swagger:operation DELETE /teams/{id} organization orgDeleteTeam
// ---
// summary: Delete a team
// parameters:
// - name: id
// in: path
// description: id of the team to delete
// type: integer
// format: int64
// required: true
// responses:
// "204":
// description: team deleted
// "404":
// "$ref": "#/responses/notFound"
if err := org_service.DeleteTeam(ctx, ctx.Org.Team); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
// GetTeamMembers api for get a team's members
func GetTeamMembers(ctx *context.APIContext) {
// swagger:operation GET /teams/{id}/members organization orgListTeamMembers
// ---
// summary: List a team's members
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the team
// type: integer
// format: int64
// 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/UserList"
// "404":
// "$ref": "#/responses/notFound"
isMember, err := organization.IsOrganizationMember(ctx, ctx.Org.Team.OrgID, ctx.Doer.ID)
if err != nil {
ctx.APIErrorInternal(err)
return
} else if !isMember && !ctx.Doer.IsAdmin {
ctx.APIErrorNotFound()
return
}
listOptions := utils.GetListOptions(ctx)
teamMembers, err := organization.GetTeamMembers(ctx, &organization.SearchMembersOptions{
ListOptions: listOptions,
TeamID: ctx.Org.Team.ID,
})
if err != nil {
ctx.APIErrorInternal(err)
return
}
members := make([]*api.User, len(teamMembers))
for i, member := range teamMembers {
members[i] = convert.ToUser(ctx, member, ctx.Doer)
}
ctx.SetLinkHeader(int64(ctx.Org.Team.NumMembers), listOptions.PageSize)
ctx.SetTotalCountHeader(int64(ctx.Org.Team.NumMembers))
ctx.JSON(http.StatusOK, members)
}
// GetTeamMember api for get a particular member of team
func GetTeamMember(ctx *context.APIContext) {
// swagger:operation GET /teams/{id}/members/{username} organization orgListTeamMember
// ---
// summary: List a particular member of team
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the team
// type: integer
// format: int64
// required: true
// - name: username
// in: path
// description: username of the user whose data is to be listed
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/User"
// "404":
// "$ref": "#/responses/notFound"
u := user.GetContextUserByPathParam(ctx)
if ctx.Written() {
return
}
teamID := ctx.PathParamInt64("teamid")
isTeamMember, err := organization.IsUserInTeams(ctx, u.ID, []int64{teamID})
if err != nil {
ctx.APIErrorInternal(err)
return
} else if !isTeamMember {
ctx.APIErrorNotFound()
return
}
ctx.JSON(http.StatusOK, convert.ToUser(ctx, u, ctx.Doer))
}
// AddTeamMember api for add a member to a team
func AddTeamMember(ctx *context.APIContext) {
// swagger:operation PUT /teams/{id}/members/{username} organization orgAddTeamMember
// ---
// summary: Add a team member
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the team
// type: integer
// format: int64
// required: true
// - name: username
// in: path
// description: username of the user to add to a team
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
u := user.GetContextUserByPathParam(ctx)
if ctx.Written() {
return
}
if err := org_service.AddTeamMember(ctx, ctx.Org.Team, u); err != nil {
if errors.Is(err, user_model.ErrBlockedUser) {
ctx.APIError(http.StatusForbidden, err)
} else {
ctx.APIErrorInternal(err)
}
return
}
ctx.Status(http.StatusNoContent)
}
// RemoveTeamMember api for remove one member from a team
func RemoveTeamMember(ctx *context.APIContext) {
// swagger:operation DELETE /teams/{id}/members/{username} organization orgRemoveTeamMember
// ---
// summary: Remove a team member
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the team
// type: integer
// format: int64
// required: true
// - name: username
// in: path
// description: username of the user to remove from a team
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "404":
// "$ref": "#/responses/notFound"
u := user.GetContextUserByPathParam(ctx)
if ctx.Written() {
return
}
if err := org_service.RemoveTeamMember(ctx, ctx.Org.Team, u); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
// GetTeamRepos api for get a team's repos
func GetTeamRepos(ctx *context.APIContext) {
// swagger:operation GET /teams/{id}/repos organization orgListTeamRepos
// ---
// summary: List a team's repos
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the team
// type: integer
// format: int64
// 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/RepositoryList"
// "404":
// "$ref": "#/responses/notFound"
team := ctx.Org.Team
listOptions := utils.GetListOptions(ctx)
teamRepos, err := repo_model.GetTeamRepositories(ctx, &repo_model.SearchTeamRepoOptions{
ListOptions: listOptions,
TeamID: team.ID,
})
if err != nil {
ctx.APIErrorInternal(err)
return
}
repos := make([]*api.Repository, len(teamRepos))
for i, repo := range teamRepos {
permission, err := access_model.GetDoerRepoPermission(ctx, repo, ctx.Doer)
if err != nil {
ctx.APIErrorInternal(err)
return
}
repos[i] = convert.ToRepo(ctx, repo, permission)
}
ctx.SetLinkHeader(int64(team.NumRepos), listOptions.PageSize)
ctx.SetTotalCountHeader(int64(team.NumRepos))
ctx.JSON(http.StatusOK, repos)
}
// GetTeamRepo api for get a particular repo of team
func GetTeamRepo(ctx *context.APIContext) {
// swagger:operation GET /teams/{id}/repos/{org}/{repo} organization orgListTeamRepo
// ---
// summary: List a particular repo of team
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the team
// type: integer
// format: int64
// required: true
// - name: org
// in: path
// description: organization that owns the repo to list
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo to list
// type: string
// required: true
// responses:
// "200":
// "$ref": "#/responses/Repository"
// "404":
// "$ref": "#/responses/notFound"
repo := getRepositoryByParams(ctx)
if ctx.Written() {
return
}
if !organization.HasTeamRepo(ctx, ctx.Org.Team.OrgID, ctx.Org.Team.ID, repo.ID) {
ctx.APIErrorNotFound()
return
}
permission, err := access_model.GetDoerRepoPermission(ctx, repo, ctx.Doer)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.JSON(http.StatusOK, convert.ToRepo(ctx, repo, permission))
}
// getRepositoryByParams get repository by a team's organization ID and repo name
func getRepositoryByParams(ctx *context.APIContext) *repo_model.Repository {
repo, err := repo_model.GetRepositoryByName(ctx, ctx.Org.Team.OrgID, ctx.PathParam("reponame"))
if err != nil {
if repo_model.IsErrRepoNotExist(err) {
ctx.APIErrorNotFound()
} else {
ctx.APIErrorInternal(err)
}
return nil
}
return repo
}
// AddTeamRepository api for adding a repository to a team
func AddTeamRepository(ctx *context.APIContext) {
// swagger:operation PUT /teams/{id}/repos/{org}/{repo} organization orgAddTeamRepository
// ---
// summary: Add a repository to a team
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the team
// type: integer
// format: int64
// required: true
// - name: org
// in: path
// description: organization that owns the repo to add
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo to add
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
repo := getRepositoryByParams(ctx)
if ctx.Written() {
return
}
if access, err := access_model.AccessLevel(ctx, ctx.Doer, repo); err != nil {
ctx.APIErrorInternal(err)
return
} else if access < perm.AccessModeAdmin {
ctx.APIError(http.StatusForbidden, "Must have admin-level access to the repository")
return
}
if err := repo_service.TeamAddRepository(ctx, ctx.Org.Team, repo); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
// RemoveTeamRepository api for removing a repository from a team
func RemoveTeamRepository(ctx *context.APIContext) {
// swagger:operation DELETE /teams/{id}/repos/{org}/{repo} organization orgRemoveTeamRepository
// ---
// summary: Remove a repository from a team
// description: This does not delete the repository, it only removes the
// repository from the team.
// produces:
// - application/json
// parameters:
// - name: id
// in: path
// description: id of the team
// type: integer
// format: int64
// required: true
// - name: org
// in: path
// description: organization that owns the repo to remove
// type: string
// required: true
// - name: repo
// in: path
// description: name of the repo to remove
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
repo := getRepositoryByParams(ctx)
if ctx.Written() {
return
}
if access, err := access_model.AccessLevel(ctx, ctx.Doer, repo); err != nil {
ctx.APIErrorInternal(err)
return
} else if access < perm.AccessModeAdmin {
ctx.APIError(http.StatusForbidden, "Must have admin-level access to the repository")
return
}
if err := repo_service.RemoveRepositoryFromTeam(ctx, ctx.Org.Team, repo.ID); err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.Status(http.StatusNoContent)
}
// SearchTeam api for searching teams
func SearchTeam(ctx *context.APIContext) {
// swagger:operation GET /orgs/{org}/teams/search organization teamSearch
// ---
// summary: Search for teams within an organization
// produces:
// - application/json
// parameters:
// - name: org
// in: path
// description: name of the organization
// type: string
// required: true
// - name: q
// in: query
// description: keywords to search
// type: string
// - name: include_desc
// in: query
// description: include search within team description (defaults to true)
// type: boolean
// - 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":
// description: "SearchResults of a successful search"
// schema:
// type: object
// properties:
// ok:
// type: boolean
// data:
// type: array
// items:
// "$ref": "#/definitions/Team"
// "404":
// "$ref": "#/responses/notFound"
listOptions := utils.GetListOptions(ctx)
opts := &organization.SearchTeamOptions{
Keyword: ctx.FormTrim("q"),
OrgID: ctx.Org.Organization.ID,
IncludeDesc: ctx.FormString("include_desc") == "" || ctx.FormBool("include_desc"),
ListOptions: listOptions,
}
// Only admin is allowed to search for all teams
if !ctx.Doer.IsAdmin {
opts.UserID = ctx.Doer.ID
}
teams, maxResults, err := organization.SearchTeam(ctx, opts)
if err != nil {
log.Error("SearchTeam failed: %v", err)
ctx.JSON(http.StatusInternalServerError, map[string]any{
"ok": false,
"error": "SearchTeam internal failure",
})
return
}
apiTeams, err := convert.ToTeams(ctx, teams, false)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.SetLinkHeader(maxResults, listOptions.PageSize)
ctx.SetTotalCountHeader(maxResults)
ctx.JSON(http.StatusOK, map[string]any{
"ok": true,
"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 := feed_service.GetFeeds(ctx, opts)
if err != nil {
ctx.APIErrorInternal(err)
return
}
ctx.SetLinkHeader(count, listOptions.PageSize)
ctx.SetTotalCountHeader(count)
ctx.JSON(http.StatusOK, convert.ToActivities(ctx, feeds, ctx.Doer))
}