summaryrefslogtreecommitdiffstats
path: root/routers/api/v1/repo
diff options
context:
space:
mode:
authorUnknwon <u@gogs.io>2015-12-04 17:16:42 -0500
committerUnknwon <u@gogs.io>2015-12-04 17:16:42 -0500
commit56dd430a10bf5281caf648344e4660fbdc5d4dee (patch)
tree9493b1a9f77321525d62ce1ccefc4dd792391832 /routers/api/v1/repo
parente0bae9547af03e5e7c0201faaa9568d6a1cc9e1f (diff)
downloadgitea-56dd430a10bf5281caf648344e4660fbdc5d4dee.tar.gz
gitea-56dd430a10bf5281caf648344e4660fbdc5d4dee.zip
refactor API routes and some work for #976
Diffstat (limited to 'routers/api/v1/repo')
-rw-r--r--routers/api/v1/repo/file.go46
-rw-r--r--routers/api/v1/repo/hooks.go165
-rw-r--r--routers/api/v1/repo/keys.go114
-rw-r--r--routers/api/v1/repo/repo.go298
4 files changed, 623 insertions, 0 deletions
diff --git a/routers/api/v1/repo/file.go b/routers/api/v1/repo/file.go
new file mode 100644
index 0000000000..0279718bbb
--- /dev/null
+++ b/routers/api/v1/repo/file.go
@@ -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)
+}
diff --git a/routers/api/v1/repo/hooks.go b/routers/api/v1/repo/hooks.go
new file mode 100644
index 0000000000..1ec7088670
--- /dev/null
+++ b/routers/api/v1/repo/hooks.go
@@ -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))
+}
diff --git a/routers/api/v1/repo/keys.go b/routers/api/v1/repo/keys.go
new file mode 100644
index 0000000000..b0d8f9146d
--- /dev/null
+++ b/routers/api/v1/repo/keys.go
@@ -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)
+}
diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go
new file mode 100644
index 0000000000..864cb4dd17
--- /dev/null
+++ b/routers/api/v1/repo/repo.go
@@ -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)
+}