1
0
Fork 0
forked from forgejo/forgejo

refactor API routes and some work for #976

This commit is contained in:
Unknwon 2015-12-04 17:16:42 -05:00
parent e0bae9547a
commit 56dd430a10
17 changed files with 374 additions and 377 deletions

View file

@ -0,0 +1,46 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/git"
"github.com/gogits/gogs/modules/middleware"
"github.com/gogits/gogs/routers/repo"
)
// https://github.com/gogits/go-gogs-client/wiki/Repositories-Contents#download-raw-content
func GetRawFile(ctx *middleware.Context) {
if !ctx.Repo.HasAccess() {
ctx.Error(404)
return
}
blob, err := ctx.Repo.Commit.GetBlobByPath(ctx.Repo.TreeName)
if err != nil {
if err == git.ErrNotExist {
ctx.Error(404)
} else {
ctx.APIError(500, "GetBlobByPath", err)
}
return
}
if err = repo.ServeBlob(ctx, blob); err != nil {
ctx.APIError(500, "ServeBlob", err)
}
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories-Contents#download-archive
func GetArchive(ctx *middleware.Context) {
repoPath := models.RepoPath(ctx.Params(":username"), ctx.Params(":reponame"))
gitRepo, err := git.OpenRepository(repoPath)
if err != nil {
ctx.APIError(500, "OpenRepository", err)
return
}
ctx.Repo.GitRepo = gitRepo
repo.Download(ctx)
}

View file

@ -0,0 +1,165 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
"encoding/json"
"github.com/Unknwon/com"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/middleware"
to "github.com/gogits/gogs/routers/api/v1/utils"
)
// https://github.com/gogits/go-gogs-client/wiki/Repositories#list-hooks
func ListHooks(ctx *middleware.Context) {
hooks, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID)
if err != nil {
ctx.APIError(500, "GetWebhooksByRepoID", err)
return
}
apiHooks := make([]*api.Hook, len(hooks))
for i := range hooks {
apiHooks[i] = to.ApiHook(ctx.Repo.RepoLink, hooks[i])
}
ctx.JSON(200, &apiHooks)
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories#create-a-hook
func CreateHook(ctx *middleware.Context, form api.CreateHookOption) {
if !models.IsValidHookTaskType(form.Type) {
ctx.APIError(422, "", "Invalid hook type")
return
}
for _, name := range []string{"url", "content_type"} {
if _, ok := form.Config[name]; !ok {
ctx.APIError(422, "", "Missing config option: "+name)
return
}
}
if !models.IsValidHookContentType(form.Config["content_type"]) {
ctx.APIError(422, "", "Invalid content type")
return
}
if len(form.Events) == 0 {
form.Events = []string{"push"}
}
w := &models.Webhook{
RepoID: ctx.Repo.Repository.ID,
URL: form.Config["url"],
ContentType: models.ToHookContentType(form.Config["content_type"]),
Secret: form.Config["secret"],
HookEvent: &models.HookEvent{
ChooseEvents: true,
HookEvents: models.HookEvents{
Create: com.IsSliceContainsStr(form.Events, string(models.HOOK_EVENT_CREATE)),
Push: com.IsSliceContainsStr(form.Events, string(models.HOOK_EVENT_PUSH)),
},
},
IsActive: form.Active,
HookTaskType: models.ToHookTaskType(form.Type),
}
if w.HookTaskType == models.SLACK {
channel, ok := form.Config["channel"]
if !ok {
ctx.APIError(422, "", "Missing config option: channel")
return
}
meta, err := json.Marshal(&models.SlackMeta{
Channel: channel,
Username: form.Config["username"],
IconURL: form.Config["icon_url"],
Color: form.Config["color"],
})
if err != nil {
ctx.APIError(500, "slack: JSON marshal failed", err)
return
}
w.Meta = string(meta)
}
if err := w.UpdateEvent(); err != nil {
ctx.APIError(500, "UpdateEvent", err)
return
} else if err := models.CreateWebhook(w); err != nil {
ctx.APIError(500, "CreateWebhook", err)
return
}
ctx.JSON(201, to.ApiHook(ctx.Repo.RepoLink, w))
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories#edit-a-hook
func EditHook(ctx *middleware.Context, form api.EditHookOption) {
w, err := models.GetWebhookByID(ctx.ParamsInt64(":id"))
if err != nil {
if models.IsErrWebhookNotExist(err) {
ctx.Error(404)
} else {
ctx.APIError(500, "GetWebhookById", err)
}
return
}
if form.Config != nil {
if url, ok := form.Config["url"]; ok {
w.URL = url
}
if ct, ok := form.Config["content_type"]; ok {
if !models.IsValidHookContentType(ct) {
ctx.APIError(422, "", "Invalid content type")
return
}
w.ContentType = models.ToHookContentType(ct)
}
if w.HookTaskType == models.SLACK {
if channel, ok := form.Config["channel"]; ok {
meta, err := json.Marshal(&models.SlackMeta{
Channel: channel,
Username: form.Config["username"],
IconURL: form.Config["icon_url"],
Color: form.Config["color"],
})
if err != nil {
ctx.APIError(500, "slack: JSON marshal failed", err)
return
}
w.Meta = string(meta)
}
}
}
// Update events
if len(form.Events) == 0 {
form.Events = []string{"push"}
}
w.PushOnly = false
w.SendEverything = false
w.ChooseEvents = true
w.Create = com.IsSliceContainsStr(form.Events, string(models.HOOK_EVENT_CREATE))
w.Push = com.IsSliceContainsStr(form.Events, string(models.HOOK_EVENT_PUSH))
if err = w.UpdateEvent(); err != nil {
ctx.APIError(500, "UpdateEvent", err)
return
}
if form.Active != nil {
w.IsActive = *form.Active
}
if err := models.UpdateWebhook(w); err != nil {
ctx.APIError(500, "UpdateWebhook", err)
return
}
ctx.JSON(200, to.ApiHook(ctx.Repo.RepoLink, w))
}

114
routers/api/v1/repo/keys.go Normal file
View file

@ -0,0 +1,114 @@
// Copyright 2015 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
"fmt"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/middleware"
"github.com/gogits/gogs/modules/setting"
to "github.com/gogits/gogs/routers/api/v1/utils"
)
func composeDeployKeysAPILink(repoPath string) string {
return setting.AppUrl + "api/v1/repos/" + repoPath + "/keys/"
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories-Deploy-Keys#list-deploy-keys
func ListDeployKeys(ctx *middleware.Context) {
keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID)
if err != nil {
ctx.Handle(500, "ListDeployKeys", err)
return
}
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
apiKeys := make([]*api.DeployKey, len(keys))
for i := range keys {
if err = keys[i].GetContent(); err != nil {
ctx.APIError(500, "GetContent", err)
return
}
apiKeys[i] = to.ApiDeployKey(apiLink, keys[i])
}
ctx.JSON(200, &apiKeys)
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories-Deploy-Keys#get-a-deploy-key
func GetDeployKey(ctx *middleware.Context) {
key, err := models.GetDeployKeyByID(ctx.ParamsInt64(":id"))
if err != nil {
if models.IsErrDeployKeyNotExist(err) {
ctx.Error(404)
} else {
ctx.Handle(500, "GetDeployKeyByID", err)
}
return
}
if err = key.GetContent(); err != nil {
ctx.APIError(500, "GetContent", err)
return
}
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
ctx.JSON(200, to.ApiDeployKey(apiLink, key))
}
func HandleCheckKeyStringError(ctx *middleware.Context, err error) {
if models.IsErrKeyUnableVerify(err) {
ctx.APIError(422, "", "Unable to verify key content")
} else {
ctx.APIError(422, "", fmt.Errorf("Invalid key content: %v", err))
}
}
func HandleAddKeyError(ctx *middleware.Context, err error) {
switch {
case models.IsErrKeyAlreadyExist(err):
ctx.APIError(422, "", "Key content has been used as non-deploy key")
case models.IsErrKeyNameAlreadyUsed(err):
ctx.APIError(422, "", "Key title has been used")
default:
ctx.APIError(500, "AddKey", err)
}
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories-Deploy-Keys#add-a-new-deploy-key
func CreateDeployKey(ctx *middleware.Context, form api.CreateKeyOption) {
content, err := models.CheckPublicKeyString(form.Key)
if err != nil {
HandleCheckKeyStringError(ctx, err)
return
}
key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content)
if err != nil {
HandleAddKeyError(ctx, err)
return
}
key.Content = content
apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
ctx.JSON(201, to.ApiDeployKey(apiLink, key))
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories-Deploy-Keys#remove-a-deploy-key
func DeleteDeploykey(ctx *middleware.Context) {
if err := models.DeleteDeployKey(ctx.User, ctx.ParamsInt64(":id")); err != nil {
if models.IsErrKeyAccessDenied(err) {
ctx.APIError(403, "", "You do not have access to this key")
} else {
ctx.APIError(500, "DeleteDeployKey", err)
}
return
}
ctx.Status(204)
}

298
routers/api/v1/repo/repo.go Normal file
View file

@ -0,0 +1,298 @@
// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
package repo
import (
"path"
"github.com/Unknwon/com"
api "github.com/gogits/go-gogs-client"
"github.com/gogits/gogs/models"
"github.com/gogits/gogs/modules/auth"
"github.com/gogits/gogs/modules/log"
"github.com/gogits/gogs/modules/middleware"
"github.com/gogits/gogs/modules/setting"
to "github.com/gogits/gogs/routers/api/v1/utils"
)
// https://github.com/gogits/go-gogs-client/wiki/Repositories#search-repositories
func Search(ctx *middleware.Context) {
opt := models.SearchOption{
Keyword: path.Base(ctx.Query("q")),
Uid: com.StrTo(ctx.Query("uid")).MustInt64(),
Limit: com.StrTo(ctx.Query("limit")).MustInt(),
}
if opt.Limit == 0 {
opt.Limit = 10
}
// Check visibility.
if ctx.IsSigned && opt.Uid > 0 {
if ctx.User.Id == opt.Uid {
opt.Private = true
} else {
u, err := models.GetUserByID(opt.Uid)
if err != nil {
ctx.JSON(500, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
return
}
if u.IsOrganization() && u.IsOwnedBy(ctx.User.Id) {
opt.Private = true
}
// FIXME: how about collaborators?
}
}
repos, err := models.SearchRepositoryByName(opt)
if err != nil {
ctx.JSON(500, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
return
}
results := make([]*api.Repository, len(repos))
for i := range repos {
if err = repos[i].GetOwner(); err != nil {
ctx.JSON(500, map[string]interface{}{
"ok": false,
"error": err.Error(),
})
return
}
results[i] = &api.Repository{
Id: repos[i].ID,
FullName: path.Join(repos[i].Owner.Name, repos[i].Name),
}
}
ctx.JSON(200, map[string]interface{}{
"ok": true,
"data": results,
})
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories#list-your-repositories
func ListMyRepos(ctx *middleware.Context) {
ownRepos, err := models.GetRepositories(ctx.User.Id, true)
if err != nil {
ctx.APIError(500, "GetRepositories", err)
return
}
numOwnRepos := len(ownRepos)
accessibleRepos, err := ctx.User.GetRepositoryAccesses()
if err != nil {
ctx.APIError(500, "GetRepositoryAccesses", err)
return
}
repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos))
for i := range ownRepos {
repos[i] = to.ApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true})
}
i := numOwnRepos
for repo, access := range accessibleRepos {
repos[i] = to.ApiRepository(repo.Owner, repo, api.Permission{
Admin: access >= models.ACCESS_MODE_ADMIN,
Push: access >= models.ACCESS_MODE_WRITE,
Pull: true,
})
i++
}
ctx.JSON(200, &repos)
}
func createRepo(ctx *middleware.Context, owner *models.User, opt api.CreateRepoOption) {
repo, err := models.CreateRepository(owner, models.CreateRepoOptions{
Name: opt.Name,
Description: opt.Description,
Gitignores: opt.Gitignores,
License: opt.License,
Readme: opt.Readme,
IsPrivate: opt.Private,
AutoInit: opt.AutoInit,
})
if err != nil {
if models.IsErrRepoAlreadyExist(err) ||
models.IsErrNameReserved(err) ||
models.IsErrNamePatternNotAllowed(err) {
ctx.APIError(422, "", err)
} else {
if repo != nil {
if err = models.DeleteRepository(ctx.User.Id, repo.ID); err != nil {
log.Error(4, "DeleteRepository: %v", err)
}
}
ctx.APIError(500, "CreateRepository", err)
}
return
}
ctx.JSON(201, to.ApiRepository(owner, repo, api.Permission{true, true, true}))
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories#create
func Create(ctx *middleware.Context, opt api.CreateRepoOption) {
// Shouldn't reach this condition, but just in case.
if ctx.User.IsOrganization() {
ctx.APIError(422, "", "not allowed creating repository for organization")
return
}
createRepo(ctx, ctx.User, opt)
}
func CreateOrgRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
org, err := models.GetOrgByName(ctx.Params(":org"))
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.APIError(422, "", err)
} else {
ctx.APIError(500, "GetOrgByName", err)
}
return
}
if !org.IsOwnedBy(ctx.User.Id) {
ctx.APIError(403, "", "Given user is not owner of organization.")
return
}
createRepo(ctx, org, opt)
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories#migrate
func Migrate(ctx *middleware.Context, form auth.MigrateRepoForm) {
ctxUser := ctx.User
// Not equal means context user is an organization,
// or is another user/organization if current user is admin.
if form.Uid != ctxUser.Id {
org, err := models.GetUserByID(form.Uid)
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.APIError(422, "", err)
} else {
ctx.APIError(500, "GetUserByID", err)
}
return
}
ctxUser = org
}
if ctx.HasError() {
ctx.APIError(422, "", ctx.GetErrMsg())
return
}
if ctxUser.IsOrganization() && !ctx.User.IsAdmin {
// Check ownership of organization.
if !ctxUser.IsOwnedBy(ctx.User.Id) {
ctx.APIError(403, "", "Given user is not owner of organization.")
return
}
}
remoteAddr, err := form.ParseRemoteAddr(ctx.User)
if err != nil {
if models.IsErrInvalidCloneAddr(err) {
addrErr := err.(models.ErrInvalidCloneAddr)
switch {
case addrErr.IsURLError:
ctx.APIError(422, "", err)
case addrErr.IsPermissionDenied:
ctx.APIError(422, "", "You are not allowed to import local repositories.")
case addrErr.IsInvalidPath:
ctx.APIError(422, "", "Invalid local path, it does not exist or not a directory.")
default:
ctx.APIError(500, "ParseRemoteAddr", "Unknown error type (ErrInvalidCloneAddr): "+err.Error())
}
} else {
ctx.APIError(500, "ParseRemoteAddr", err)
}
return
}
repo, err := models.MigrateRepository(ctxUser, models.MigrateRepoOptions{
Name: form.RepoName,
Description: form.Description,
IsPrivate: form.Private || setting.Repository.ForcePrivate,
IsMirror: form.Mirror,
RemoteAddr: remoteAddr,
})
if err != nil {
if repo != nil {
if errDelete := models.DeleteRepository(ctxUser.Id, repo.ID); errDelete != nil {
log.Error(4, "DeleteRepository: %v", errDelete)
}
}
ctx.APIError(500, "MigrateRepository", err)
return
}
log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
ctx.JSON(201, to.ApiRepository(ctxUser, repo, api.Permission{true, true, true}))
}
func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) {
owner, err := models.GetUserByName(ctx.Params(":username"))
if err != nil {
if models.IsErrUserNotExist(err) {
ctx.APIError(422, "", err)
} else {
ctx.APIError(500, "GetUserByName", err)
}
return nil, nil
}
repo, err := models.GetRepositoryByName(owner.Id, ctx.Params(":reponame"))
if err != nil {
if models.IsErrRepoNotExist(err) {
ctx.Error(404)
} else {
ctx.APIError(500, "GetRepositoryByName", err)
}
return nil, nil
}
return owner, repo
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories#get
func Get(ctx *middleware.Context) {
owner, repo := parseOwnerAndRepo(ctx)
if ctx.Written() {
return
}
ctx.JSON(200, to.ApiRepository(owner, repo, api.Permission{true, true, true}))
}
// https://github.com/gogits/go-gogs-client/wiki/Repositories#delete
func Delete(ctx *middleware.Context) {
owner, repo := parseOwnerAndRepo(ctx)
if ctx.Written() {
return
}
if owner.IsOrganization() && !owner.IsOwnedBy(ctx.User.Id) {
ctx.APIError(403, "", "Given user is not owner of organization.")
return
}
if err := models.DeleteRepository(owner.Id, repo.ID); err != nil {
ctx.APIError(500, "DeleteRepository", err)
return
}
log.Trace("Repository deleted: %s/%s", owner.Name, repo.Name)
ctx.Status(204)
}