Selaa lähdekoodia

refactor API routes and some work for #976

Unknwon 9 vuotta sitten
vanhempi
commit
56dd430a10

+ 1 - 1
README.md

@@ -5,7 +5,7 @@ Gogs - Go Git Service [![Build Status](https://travis-ci.org/gogits/gogs.svg?bra
 
 ![](public/img/gogs-large-resize.png)
 
-##### Current version: 0.7.28 Beta
+##### Current version: 0.7.29 Beta
 
 <table>
     <tr>

+ 2 - 74
cmd/web.go

@@ -29,11 +29,8 @@ import (
 	"gopkg.in/ini.v1"
 	"gopkg.in/macaron.v1"
 
-	api "github.com/gogits/go-gogs-client"
-
 	"github.com/gogits/gogs/models"
 	"github.com/gogits/gogs/modules/auth"
-	"github.com/gogits/gogs/modules/auth/apiv1"
 	"github.com/gogits/gogs/modules/avatar"
 	"github.com/gogits/gogs/modules/bindata"
 	"github.com/gogits/gogs/modules/log"
@@ -42,7 +39,7 @@ import (
 	"github.com/gogits/gogs/modules/template"
 	"github.com/gogits/gogs/routers"
 	"github.com/gogits/gogs/routers/admin"
-	"github.com/gogits/gogs/routers/api/v1"
+	apiv1 "github.com/gogits/gogs/routers/api/v1"
 	"github.com/gogits/gogs/routers/dev"
 	"github.com/gogits/gogs/routers/org"
 	"github.com/gogits/gogs/routers/repo"
@@ -186,7 +183,6 @@ func runWeb(ctx *cli.Context) {
 	ignSignInAndCsrf := middleware.Toggle(&middleware.ToggleOptions{DisableCsrf: true})
 	reqSignOut := middleware.Toggle(&middleware.ToggleOptions{SignOutRequire: true})
 
-	bind := binding.Bind
 	bindIgnErr := binding.BindIgnErr
 
 	// Routers.
@@ -197,76 +193,8 @@ func runWeb(ctx *cli.Context) {
 	m.Get("/^:type(issues|pulls)$", reqSignIn, user.Issues)
 
 	// ***** START: API *****
-	// FIXME: custom form error response
 	m.Group("/api", func() {
-		m.Group("/v1", func() {
-			// Miscellaneous
-			m.Post("/markdown", bindIgnErr(apiv1.MarkdownForm{}), v1.Markdown)
-			m.Post("/markdown/raw", v1.MarkdownRaw)
-
-			// Users
-			m.Group("/users", func() {
-				m.Get("/search", v1.SearchUsers)
-
-				m.Group("/:username", func() {
-					m.Get("", v1.GetUserInfo)
-
-					m.Group("/tokens", func() {
-						m.Combo("").Get(v1.ListAccessTokens).
-							Post(bind(v1.CreateAccessTokenForm{}), v1.CreateAccessToken)
-					}, middleware.ApiReqBasicAuth())
-				})
-			})
-
-			m.Group("/users", func() {
-				m.Group("/:username", func() {
-					m.Get("/keys", v1.ListUserPublicKeys)
-				})
-			}, middleware.ApiReqToken())
-
-			m.Group("/user", func() {
-				m.Group("/keys", func() {
-					m.Combo("").Get(v1.ListMyPublicKeys).
-						Post(bind(api.CreateKeyOption{}), v1.CreateUserPublicKey)
-					m.Combo("/:id").Get(v1.GetUserPublicKey).
-						Delete(v1.DeleteUserPublicKey)
-				})
-			}, middleware.ApiReqToken())
-
-			// Repositories
-			m.Combo("/user/repos", middleware.ApiReqToken()).Get(v1.ListMyRepos).
-				Post(bind(api.CreateRepoOption{}), v1.CreateRepo)
-			m.Post("/org/:org/repos", middleware.ApiReqToken(), bind(api.CreateRepoOption{}), v1.CreateOrgRepo)
-
-			m.Group("/repos", func() {
-				m.Get("/search", v1.SearchRepos)
-			})
-
-			m.Group("/repos", func() {
-				m.Post("/migrate", bindIgnErr(auth.MigrateRepoForm{}), v1.MigrateRepo)
-				m.Combo("/:username/:reponame").Get(v1.GetRepo).
-					Delete(v1.DeleteRepo)
-
-				m.Group("/:username/:reponame", func() {
-					m.Combo("/hooks").Get(v1.ListRepoHooks).
-						Post(bind(api.CreateHookOption{}), v1.CreateRepoHook)
-					m.Patch("/hooks/:id:int", bind(api.EditHookOption{}), v1.EditRepoHook)
-					m.Get("/raw/*", middleware.RepoRef(), v1.GetRepoRawFile)
-					m.Get("/archive/*", v1.GetRepoArchive)
-
-					m.Group("/keys", func() {
-						m.Combo("").Get(v1.ListRepoDeployKeys).
-							Post(bind(api.CreateKeyOption{}), v1.CreateRepoDeployKey)
-						m.Combo("/:id").Get(v1.GetRepoDeployKey).
-							Delete(v1.DeleteRepoDeploykey)
-					})
-				}, middleware.ApiRepoAssignment())
-			}, middleware.ApiReqToken())
-
-			m.Any("/*", func(ctx *middleware.Context) {
-				ctx.Error(404)
-			})
-		})
+		apiv1.RegisterRoutes(m)
 	}, ignSignIn)
 	// ***** END: API *****
 

+ 1 - 1
gogs.go

@@ -17,7 +17,7 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-const APP_VER = "0.7.28.1203 Beta"
+const APP_VER = "0.7.29.1204 Beta"
 
 func init() {
 	runtime.GOMAXPROCS(runtime.NumCPU())

+ 0 - 73
modules/auth/apiv1/miscellaneous.go

@@ -1,73 +0,0 @@
-// 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 apiv1
-
-import (
-	"reflect"
-
-	"github.com/go-macaron/binding"
-	"gopkg.in/macaron.v1"
-
-	"github.com/gogits/gogs/modules/auth"
-)
-
-type MarkdownForm struct {
-	Text    string
-	Mode    string
-	Context string
-}
-
-func (f *MarkdownForm) Validate(ctx *macaron.Context, errs binding.Errors) binding.Errors {
-	return validateApiReq(errs, ctx.Data, f)
-}
-
-func validateApiReq(errs binding.Errors, data map[string]interface{}, f auth.Form) binding.Errors {
-	if errs.Len() == 0 {
-		return errs
-	}
-
-	data["HasError"] = true
-
-	typ := reflect.TypeOf(f)
-	val := reflect.ValueOf(f)
-
-	if typ.Kind() == reflect.Ptr {
-		typ = typ.Elem()
-		val = val.Elem()
-	}
-
-	for i := 0; i < typ.NumField(); i++ {
-		field := typ.Field(i)
-
-		fieldName := field.Tag.Get("form")
-		// Allow ignored fields in the struct
-		if fieldName == "-" {
-			continue
-		}
-
-		if errs[0].FieldNames[0] == field.Name {
-			switch errs[0].Classification {
-			case binding.ERR_REQUIRED:
-				data["ErrorMsg"] = fieldName + " cannot be empty"
-			case binding.ERR_ALPHA_DASH:
-				data["ErrorMsg"] = fieldName + " must be valid alpha or numeric or dash(-_) characters"
-			case binding.ERR_ALPHA_DASH_DOT:
-				data["ErrorMsg"] = fieldName + " must be valid alpha or numeric or dash(-_) or dot characters"
-			case binding.ERR_MIN_SIZE:
-				data["ErrorMsg"] = fieldName + " must contain at least " + auth.GetMinSize(field) + " characters"
-			case binding.ERR_MAX_SIZE:
-				data["ErrorMsg"] = fieldName + " must contain at most " + auth.GetMaxSize(field) + " characters"
-			case binding.ERR_EMAIL:
-				data["ErrorMsg"] = fieldName + " is not a valid e-mail address"
-			case binding.ERR_URL:
-				data["ErrorMsg"] = fieldName + " is not a valid URL"
-			default:
-				data["ErrorMsg"] = "Unknown error: " + errs[0].Classification
-			}
-			return errs
-		}
-	}
-	return errs
-}

+ 0 - 19
modules/middleware/auth.go

@@ -130,22 +130,3 @@ func Toggle(options *ToggleOptions) macaron.Handler {
 		}
 	}
 }
-
-// Contexter middleware already checks token for user sign in process.
-func ApiReqToken() macaron.Handler {
-	return func(ctx *Context) {
-		if !ctx.IsSigned {
-			ctx.Error(401)
-			return
-		}
-	}
-}
-
-func ApiReqBasicAuth() macaron.Handler {
-	return func(ctx *Context) {
-		if !ctx.IsBasicAuth {
-			ctx.Error(401)
-			return
-		}
-	}
-}

+ 0 - 60
modules/middleware/repo.go

@@ -17,66 +17,6 @@ import (
 	"github.com/gogits/gogs/modules/setting"
 )
 
-func ApiRepoAssignment() macaron.Handler {
-	return func(ctx *Context) {
-		ctx.Repo = &RepoContext{}
-
-		userName := ctx.Params(":username")
-		repoName := ctx.Params(":reponame")
-
-		var (
-			owner *models.User
-			err   error
-		)
-
-		// Check if the user is the same as the repository owner.
-		if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
-			owner = ctx.User
-		} else {
-			owner, err = models.GetUserByName(userName)
-			if err != nil {
-				if models.IsErrUserNotExist(err) {
-					ctx.Error(404)
-				} else {
-					ctx.APIError(500, "GetUserByName", err)
-				}
-				return
-			}
-		}
-		ctx.Repo.Owner = owner
-
-		// Get repository.
-		repo, err := models.GetRepositoryByName(owner.Id, repoName)
-		if err != nil {
-			if models.IsErrRepoNotExist(err) {
-				ctx.Error(404)
-			} else {
-				ctx.APIError(500, "GetRepositoryByName", err)
-			}
-			return
-		} else if err = repo.GetOwner(); err != nil {
-			ctx.APIError(500, "GetOwner", err)
-			return
-		}
-
-		mode, err := models.AccessLevel(ctx.User, repo)
-		if err != nil {
-			ctx.APIError(500, "AccessLevel", err)
-			return
-		}
-
-		ctx.Repo.AccessMode = mode
-
-		// Check access.
-		if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
-			ctx.Error(404)
-			return
-		}
-
-		ctx.Repo.Repository = repo
-	}
-}
-
 // RepoRef handles repository reference name including those contain `/`.
 func RepoRef() macaron.Handler {
 	return func(ctx *Context) {

+ 185 - 0
routers/api/v1/api.go

@@ -0,0 +1,185 @@
+// 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 v1
+
+import (
+	"strings"
+
+	"github.com/go-macaron/binding"
+	"gopkg.in/macaron.v1"
+
+	api "github.com/gogits/go-gogs-client"
+
+	"github.com/gogits/gogs/models"
+	"github.com/gogits/gogs/modules/auth"
+	"github.com/gogits/gogs/modules/middleware"
+	"github.com/gogits/gogs/routers/api/v1/misc"
+	"github.com/gogits/gogs/routers/api/v1/repo"
+	"github.com/gogits/gogs/routers/api/v1/user"
+)
+
+func RepoAssignment() macaron.Handler {
+	return func(ctx *middleware.Context) {
+		ctx.Repo = &middleware.RepoContext{}
+
+		userName := ctx.Params(":username")
+		repoName := ctx.Params(":reponame")
+
+		var (
+			owner *models.User
+			err   error
+		)
+
+		// Check if the user is the same as the repository owner.
+		if ctx.IsSigned && ctx.User.LowerName == strings.ToLower(userName) {
+			owner = ctx.User
+		} else {
+			owner, err = models.GetUserByName(userName)
+			if err != nil {
+				if models.IsErrUserNotExist(err) {
+					ctx.Error(404)
+				} else {
+					ctx.APIError(500, "GetUserByName", err)
+				}
+				return
+			}
+		}
+		ctx.Repo.Owner = owner
+
+		// Get repository.
+		repo, err := models.GetRepositoryByName(owner.Id, repoName)
+		if err != nil {
+			if models.IsErrRepoNotExist(err) {
+				ctx.Error(404)
+			} else {
+				ctx.APIError(500, "GetRepositoryByName", err)
+			}
+			return
+		} else if err = repo.GetOwner(); err != nil {
+			ctx.APIError(500, "GetOwner", err)
+			return
+		}
+
+		mode, err := models.AccessLevel(ctx.User, repo)
+		if err != nil {
+			ctx.APIError(500, "AccessLevel", err)
+			return
+		}
+
+		ctx.Repo.AccessMode = mode
+
+		// Check access.
+		if ctx.Repo.AccessMode == models.ACCESS_MODE_NONE {
+			ctx.Error(404)
+			return
+		}
+
+		ctx.Repo.Repository = repo
+	}
+}
+
+// Contexter middleware already checks token for user sign in process.
+func ReqToken() macaron.Handler {
+	return func(ctx *middleware.Context) {
+		if !ctx.IsSigned {
+			ctx.Error(401)
+			return
+		}
+	}
+}
+
+func ReqBasicAuth() macaron.Handler {
+	return func(ctx *middleware.Context) {
+		if !ctx.IsBasicAuth {
+			ctx.Error(401)
+			return
+		}
+	}
+}
+
+func ReqAdmin() macaron.Handler {
+	return func(ctx *middleware.Context) {
+		if !ctx.User.IsAdmin {
+			ctx.Error(403)
+			return
+		}
+	}
+}
+
+// RegisterRoutes registers all v1 APIs routes to web application.
+// FIXME: custom form error response
+func RegisterRoutes(m *macaron.Macaron) {
+	bind := binding.Bind
+
+	m.Group("/v1", func() {
+		// Miscellaneous
+		m.Post("/markdown", bind(api.MarkdownOption{}), misc.Markdown)
+		m.Post("/markdown/raw", misc.MarkdownRaw)
+
+		// Users
+		m.Group("/users", func() {
+			m.Get("/search", user.Search)
+
+			m.Group("/:username", func() {
+				m.Get("", user.GetInfo)
+
+				m.Group("/tokens", func() {
+					m.Combo("").Get(user.ListAccessTokens).
+						Post(bind(api.CreateAccessTokenOption{}), user.CreateAccessToken)
+				}, ReqBasicAuth())
+			})
+		})
+
+		m.Group("/users", func() {
+			m.Group("/:username", func() {
+				m.Combo("/keys").Get(user.ListPublicKeys).
+					Post(ReqAdmin(), user.CreateUserPublicKey)
+			})
+		}, ReqToken())
+
+		m.Group("/user", func() {
+			m.Group("/keys", func() {
+				m.Combo("").Get(user.ListMyPublicKeys).
+					Post(bind(api.CreateKeyOption{}), user.CreatePublicKey)
+				m.Combo("/:id").Get(user.GetPublicKey).
+					Delete(user.DeletePublicKey)
+			})
+		}, ReqToken())
+
+		// Repositories
+		m.Combo("/user/repos", ReqToken()).Get(repo.ListMyRepos).
+			Post(bind(api.CreateRepoOption{}), repo.Create)
+		m.Post("/org/:org/repos", ReqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepo)
+
+		m.Group("/repos", func() {
+			m.Get("/search", repo.Search)
+		})
+
+		m.Group("/repos", func() {
+			m.Post("/migrate", bind(auth.MigrateRepoForm{}), repo.Migrate)
+			m.Combo("/:username/:reponame").Get(repo.Get).
+				Delete(repo.Delete)
+
+			m.Group("/:username/:reponame", func() {
+				m.Combo("/hooks").Get(repo.ListHooks).
+					Post(bind(api.CreateHookOption{}), repo.CreateHook)
+				m.Patch("/hooks/:id:int", bind(api.EditHookOption{}), repo.EditHook)
+				m.Get("/raw/*", middleware.RepoRef(), repo.GetRawFile)
+				m.Get("/archive/*", repo.GetArchive)
+
+				m.Group("/keys", func() {
+					m.Combo("").Get(repo.ListDeployKeys).
+						Post(bind(api.CreateKeyOption{}), repo.CreateDeployKey)
+					m.Combo("/:id").Get(repo.GetDeployKey).
+						Delete(repo.DeleteDeploykey)
+				})
+			}, RepoAssignment())
+		}, ReqToken())
+
+		m.Any("/*", func(ctx *middleware.Context) {
+			ctx.Error(404)
+		})
+	})
+}

+ 4 - 3
routers/api/v1/miscellaneous.go → routers/api/v1/misc/markdown.go

@@ -2,16 +2,17 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package v1
+package misc
 
 import (
-	"github.com/gogits/gogs/modules/auth/apiv1"
+	api "github.com/gogits/go-gogs-client"
+
 	"github.com/gogits/gogs/modules/base"
 	"github.com/gogits/gogs/modules/middleware"
 )
 
 // https://github.com/gogits/go-gogs-client/wiki/Miscellaneous#render-an-arbitrary-markdown-document
-func Markdown(ctx *middleware.Context, form apiv1.MarkdownForm) {
+func Markdown(ctx *middleware.Context, form api.MarkdownOption) {
 	if ctx.HasApiError() {
 		ctx.APIError(422, "", ctx.GetErrMsg())
 		return

+ 3 - 3
routers/api/v1/repo_file.go → routers/api/v1/repo/file.go

@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package v1
+package repo
 
 import (
 	"github.com/gogits/gogs/models"
@@ -12,7 +12,7 @@ import (
 )
 
 // https://github.com/gogits/go-gogs-client/wiki/Repositories-Contents#download-raw-content
-func GetRepoRawFile(ctx *middleware.Context) {
+func GetRawFile(ctx *middleware.Context) {
 	if !ctx.Repo.HasAccess() {
 		ctx.Error(404)
 		return
@@ -33,7 +33,7 @@ func GetRepoRawFile(ctx *middleware.Context) {
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Repositories-Contents#download-archive
-func GetRepoArchive(ctx *middleware.Context) {
+func GetArchive(ctx *middleware.Context) {
 	repoPath := models.RepoPath(ctx.Params(":username"), ctx.Params(":reponame"))
 	gitRepo, err := git.OpenRepository(repoPath)
 	if err != nil {

+ 8 - 34
routers/api/v1/repo_hooks.go → routers/api/v1/repo/hooks.go

@@ -2,11 +2,10 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package v1
+package repo
 
 import (
 	"encoding/json"
-	"fmt"
 
 	"github.com/Unknwon/com"
 
@@ -14,36 +13,11 @@ import (
 
 	"github.com/gogits/gogs/models"
 	"github.com/gogits/gogs/modules/middleware"
+	to "github.com/gogits/gogs/routers/api/v1/utils"
 )
 
-// ToApiHook converts webhook to API format.
-func ToApiHook(repoLink string, w *models.Webhook) *api.Hook {
-	config := map[string]string{
-		"url":          w.URL,
-		"content_type": w.ContentType.Name(),
-	}
-	if w.HookTaskType == models.SLACK {
-		s := w.GetSlackHook()
-		config["channel"] = s.Channel
-		config["username"] = s.Username
-		config["icon_url"] = s.IconURL
-		config["color"] = s.Color
-	}
-
-	return &api.Hook{
-		ID:      w.ID,
-		Type:    w.HookTaskType.Name(),
-		URL:     fmt.Sprintf("%s/settings/hooks/%d", repoLink, w.ID),
-		Active:  w.IsActive,
-		Config:  config,
-		Events:  w.EventsArray(),
-		Updated: w.Updated,
-		Created: w.Created,
-	}
-}
-
 // https://github.com/gogits/go-gogs-client/wiki/Repositories#list-hooks
-func ListRepoHooks(ctx *middleware.Context) {
+func ListHooks(ctx *middleware.Context) {
 	hooks, err := models.GetWebhooksByRepoID(ctx.Repo.Repository.ID)
 	if err != nil {
 		ctx.APIError(500, "GetWebhooksByRepoID", err)
@@ -52,14 +26,14 @@ func ListRepoHooks(ctx *middleware.Context) {
 
 	apiHooks := make([]*api.Hook, len(hooks))
 	for i := range hooks {
-		apiHooks[i] = ToApiHook(ctx.Repo.RepoLink, hooks[i])
+		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 CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) {
+func CreateHook(ctx *middleware.Context, form api.CreateHookOption) {
 	if !models.IsValidHookTaskType(form.Type) {
 		ctx.APIError(422, "", "Invalid hook type")
 		return
@@ -120,11 +94,11 @@ func CreateRepoHook(ctx *middleware.Context, form api.CreateHookOption) {
 		return
 	}
 
-	ctx.JSON(201, ToApiHook(ctx.Repo.RepoLink, w))
+	ctx.JSON(201, to.ApiHook(ctx.Repo.RepoLink, w))
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Repositories#edit-a-hook
-func EditRepoHook(ctx *middleware.Context, form api.EditHookOption) {
+func EditHook(ctx *middleware.Context, form api.EditHookOption) {
 	w, err := models.GetWebhookByID(ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrWebhookNotExist(err) {
@@ -187,5 +161,5 @@ func EditRepoHook(ctx *middleware.Context, form api.EditHookOption) {
 		return
 	}
 
-	ctx.JSON(200, ToApiHook(ctx.Repo.RepoLink, w))
+	ctx.JSON(200, to.ApiHook(ctx.Repo.RepoLink, w))
 }

+ 13 - 25
routers/api/v1/repo_keys.go → routers/api/v1/repo/keys.go

@@ -2,37 +2,25 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package v1
+package repo
 
 import (
 	"fmt"
 
-	"github.com/Unknwon/com"
-
 	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 ToApiDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey {
-	return &api.DeployKey{
-		ID:       key.ID,
-		Key:      key.Content,
-		URL:      apiLink + com.ToStr(key.ID),
-		Title:    key.Name,
-		Created:  key.Created,
-		ReadOnly: true, // All deploy keys are read-only.
-	}
-}
-
 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 ListRepoDeployKeys(ctx *middleware.Context) {
+func ListDeployKeys(ctx *middleware.Context) {
 	keys, err := models.ListDeployKeys(ctx.Repo.Repository.ID)
 	if err != nil {
 		ctx.Handle(500, "ListDeployKeys", err)
@@ -46,14 +34,14 @@ func ListRepoDeployKeys(ctx *middleware.Context) {
 			ctx.APIError(500, "GetContent", err)
 			return
 		}
-		apiKeys[i] = ToApiDeployKey(apiLink, keys[i])
+		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 GetRepoDeployKey(ctx *middleware.Context) {
+func GetDeployKey(ctx *middleware.Context) {
 	key, err := models.GetDeployKeyByID(ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrDeployKeyNotExist(err) {
@@ -70,10 +58,10 @@ func GetRepoDeployKey(ctx *middleware.Context) {
 	}
 
 	apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
-	ctx.JSON(200, ToApiDeployKey(apiLink, key))
+	ctx.JSON(200, to.ApiDeployKey(apiLink, key))
 }
 
-func handleCheckKeyStringError(ctx *middleware.Context, err error) {
+func HandleCheckKeyStringError(ctx *middleware.Context, err error) {
 	if models.IsErrKeyUnableVerify(err) {
 		ctx.APIError(422, "", "Unable to verify key content")
 	} else {
@@ -81,7 +69,7 @@ func handleCheckKeyStringError(ctx *middleware.Context, err error) {
 	}
 }
 
-func handleAddKeyError(ctx *middleware.Context, err error) {
+func HandleAddKeyError(ctx *middleware.Context, err error) {
 	switch {
 	case models.IsErrKeyAlreadyExist(err):
 		ctx.APIError(422, "", "Key content has been used as non-deploy key")
@@ -93,26 +81,26 @@ func handleAddKeyError(ctx *middleware.Context, err error) {
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Repositories-Deploy-Keys#add-a-new-deploy-key
-func CreateRepoDeployKey(ctx *middleware.Context, form api.CreateKeyOption) {
+func CreateDeployKey(ctx *middleware.Context, form api.CreateKeyOption) {
 	content, err := models.CheckPublicKeyString(form.Key)
 	if err != nil {
-		handleCheckKeyStringError(ctx, err)
+		HandleCheckKeyStringError(ctx, err)
 		return
 	}
 
 	key, err := models.AddDeployKey(ctx.Repo.Repository.ID, form.Title, content)
 	if err != nil {
-		handleAddKeyError(ctx, err)
+		HandleAddKeyError(ctx, err)
 		return
 	}
 
 	key.Content = content
 	apiLink := composeDeployKeysAPILink(ctx.Repo.Owner.Name + "/" + ctx.Repo.Repository.Name)
-	ctx.JSON(201, ToApiDeployKey(apiLink, key))
+	ctx.JSON(201, to.ApiDeployKey(apiLink, key))
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Repositories-Deploy-Keys#remove-a-deploy-key
-func DeleteRepoDeploykey(ctx *middleware.Context) {
+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")

+ 12 - 27
routers/api/v1/repo.go → routers/api/v1/repo/repo.go

@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package v1
+package repo
 
 import (
 	"path"
@@ -16,26 +16,11 @@ import (
 	"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"
 )
 
-// ToApiRepository converts repository to API format.
-func ToApiRepository(owner *models.User, repo *models.Repository, permission api.Permission) *api.Repository {
-	cl := repo.CloneLink()
-	return &api.Repository{
-		Id:          repo.ID,
-		Owner:       *ToApiUser(owner),
-		FullName:    owner.Name + "/" + repo.Name,
-		Private:     repo.IsPrivate,
-		Fork:        repo.IsFork,
-		HtmlUrl:     setting.AppUrl + owner.Name + "/" + repo.Name,
-		CloneUrl:    cl.HTTPS,
-		SshUrl:      cl.SSH,
-		Permissions: permission,
-	}
-}
-
 // https://github.com/gogits/go-gogs-client/wiki/Repositories#search-repositories
-func SearchRepos(ctx *middleware.Context) {
+func Search(ctx *middleware.Context) {
 	opt := models.SearchOption{
 		Keyword: path.Base(ctx.Query("q")),
 		Uid:     com.StrTo(ctx.Query("uid")).MustInt64(),
@@ -112,12 +97,12 @@ func ListMyRepos(ctx *middleware.Context) {
 
 	repos := make([]*api.Repository, numOwnRepos+len(accessibleRepos))
 	for i := range ownRepos {
-		repos[i] = ToApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true})
+		repos[i] = to.ApiRepository(ctx.User, ownRepos[i], api.Permission{true, true, true})
 	}
 	i := numOwnRepos
 
 	for repo, access := range accessibleRepos {
-		repos[i] = ToApiRepository(repo.Owner, repo, api.Permission{
+		repos[i] = to.ApiRepository(repo.Owner, repo, api.Permission{
 			Admin: access >= models.ACCESS_MODE_ADMIN,
 			Push:  access >= models.ACCESS_MODE_WRITE,
 			Pull:  true,
@@ -154,11 +139,11 @@ func createRepo(ctx *middleware.Context, owner *models.User, opt api.CreateRepoO
 		return
 	}
 
-	ctx.JSON(201, ToApiRepository(owner, repo, api.Permission{true, true, true}))
+	ctx.JSON(201, to.ApiRepository(owner, repo, api.Permission{true, true, true}))
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Repositories#create
-func CreateRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
+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")
@@ -186,7 +171,7 @@ func CreateOrgRepo(ctx *middleware.Context, opt api.CreateRepoOption) {
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Repositories#migrate
-func MigrateRepo(ctx *middleware.Context, form auth.MigrateRepoForm) {
+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.
@@ -254,7 +239,7 @@ func MigrateRepo(ctx *middleware.Context, form auth.MigrateRepoForm) {
 	}
 
 	log.Trace("Repository migrated: %s/%s", ctxUser.Name, form.RepoName)
-	ctx.JSON(201, ToApiRepository(ctxUser, repo, api.Permission{true, true, true}))
+	ctx.JSON(201, to.ApiRepository(ctxUser, repo, api.Permission{true, true, true}))
 }
 
 func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repository) {
@@ -282,17 +267,17 @@ func parseOwnerAndRepo(ctx *middleware.Context) (*models.User, *models.Repositor
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Repositories#get
-func GetRepo(ctx *middleware.Context) {
+func Get(ctx *middleware.Context) {
 	owner, repo := parseOwnerAndRepo(ctx)
 	if ctx.Written() {
 		return
 	}
 
-	ctx.JSON(200, ToApiRepository(owner, repo, api.Permission{true, true, true}))
+	ctx.JSON(200, to.ApiRepository(owner, repo, api.Permission{true, true, true}))
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Repositories#delete
-func DeleteRepo(ctx *middleware.Context) {
+func Delete(ctx *middleware.Context) {
 	owner, repo := parseOwnerAndRepo(ctx)
 	if ctx.Written() {
 		return

+ 2 - 6
routers/api/v1/user_app.go → routers/api/v1/user/app.go

@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package v1
+package user
 
 import (
 	api "github.com/gogits/go-gogs-client"
@@ -26,12 +26,8 @@ func ListAccessTokens(ctx *middleware.Context) {
 	ctx.JSON(200, &apiTokens)
 }
 
-type CreateAccessTokenForm struct {
-	Name string `json:"name" binding:"Required"`
-}
-
 // https://github.com/gogits/go-gogs-client/wiki/Users#create-a-access-token
-func CreateAccessToken(ctx *middleware.Context, form CreateAccessTokenForm) {
+func CreateAccessToken(ctx *middleware.Context, form api.CreateAccessTokenOption) {
 	t := &models.AccessToken{
 		UID:  ctx.User.Id,
 		Name: form.Name,

+ 46 - 35
routers/api/v1/user_keys.go → routers/api/v1/user/keys.go

@@ -2,33 +2,36 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package v1
+package user
 
 import (
-	"github.com/Unknwon/com"
-
 	api "github.com/gogits/go-gogs-client"
 
 	"github.com/gogits/gogs/models"
 	"github.com/gogits/gogs/modules/middleware"
 	"github.com/gogits/gogs/modules/setting"
+	"github.com/gogits/gogs/routers/api/v1/repo"
+	to "github.com/gogits/gogs/routers/api/v1/utils"
 )
 
-func ToApiPublicKey(apiLink string, key *models.PublicKey) *api.PublicKey {
-	return &api.PublicKey{
-		ID:      key.ID,
-		Key:     key.Content,
-		URL:     apiLink + com.ToStr(key.ID),
-		Title:   key.Name,
-		Created: key.Created,
+func getUserByParams(ctx *middleware.Context) *models.User {
+	user, err := models.GetUserByName(ctx.Params(":username"))
+	if err != nil {
+		if models.IsErrUserNotExist(err) {
+			ctx.Error(404)
+		} else {
+			ctx.APIError(500, "GetUserByName", err)
+		}
+		return nil
 	}
+	return user
 }
 
 func composePublicKeysAPILink() string {
 	return setting.AppUrl + "api/v1/user/keys/"
 }
 
-func listUserPublicKeys(ctx *middleware.Context, uid int64) {
+func listPublicKeys(ctx *middleware.Context, uid int64) {
 	keys, err := models.ListPublicKeys(uid)
 	if err != nil {
 		ctx.APIError(500, "ListPublicKeys", err)
@@ -38,33 +41,28 @@ func listUserPublicKeys(ctx *middleware.Context, uid int64) {
 	apiLink := composePublicKeysAPILink()
 	apiKeys := make([]*api.PublicKey, len(keys))
 	for i := range keys {
-		apiKeys[i] = ToApiPublicKey(apiLink, keys[i])
+		apiKeys[i] = to.ApiPublicKey(apiLink, keys[i])
 	}
 
 	ctx.JSON(200, &apiKeys)
 }
 
+// https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#list-your-public-keys
+func ListMyPublicKeys(ctx *middleware.Context) {
+	listPublicKeys(ctx, ctx.User.Id)
+}
+
 // https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#list-public-keys-for-a-user
-func ListUserPublicKeys(ctx *middleware.Context) {
-	user, err := models.GetUserByName(ctx.Params(":username"))
-	if err != nil {
-		if models.IsErrUserNotExist(err) {
-			ctx.Error(404)
-		} else {
-			ctx.APIError(500, "GetUserByName", err)
-		}
+func ListPublicKeys(ctx *middleware.Context) {
+	user := getUserByParams(ctx)
+	if ctx.Written() {
 		return
 	}
-	listUserPublicKeys(ctx, user.Id)
-}
-
-// https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#list-your-public-keys
-func ListMyPublicKeys(ctx *middleware.Context) {
-	listUserPublicKeys(ctx, ctx.User.Id)
+	listPublicKeys(ctx, user.Id)
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#get-a-single-public-key
-func GetUserPublicKey(ctx *middleware.Context) {
+func GetPublicKey(ctx *middleware.Context) {
 	key, err := models.GetPublicKeyByID(ctx.ParamsInt64(":id"))
 	if err != nil {
 		if models.IsErrKeyNotExist(err) {
@@ -76,28 +74,41 @@ func GetUserPublicKey(ctx *middleware.Context) {
 	}
 
 	apiLink := composePublicKeysAPILink()
-	ctx.JSON(200, ToApiPublicKey(apiLink, key))
+	ctx.JSON(200, to.ApiPublicKey(apiLink, key))
 }
 
-// https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#create-a-public-key
-func CreateUserPublicKey(ctx *middleware.Context, form api.CreateKeyOption) {
+func createUserPublicKey(ctx *middleware.Context, form api.CreateKeyOption, uid int64) {
 	content, err := models.CheckPublicKeyString(form.Key)
 	if err != nil {
-		handleCheckKeyStringError(ctx, err)
+		repo.HandleCheckKeyStringError(ctx, err)
 		return
 	}
 
-	key, err := models.AddPublicKey(ctx.User.Id, form.Title, content)
+	key, err := models.AddPublicKey(uid, form.Title, content)
 	if err != nil {
-		handleAddKeyError(ctx, err)
+		repo.HandleAddKeyError(ctx, err)
 		return
 	}
 	apiLink := composePublicKeysAPILink()
-	ctx.JSON(201, ToApiPublicKey(apiLink, key))
+	ctx.JSON(201, to.ApiPublicKey(apiLink, key))
+}
+
+// https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#create-a-public-key-for-user
+func CreateUserPublicKey(ctx *middleware.Context, form api.CreateKeyOption) {
+	user := getUserByParams(ctx)
+	if ctx.Written() {
+		return
+	}
+	createUserPublicKey(ctx, form, user.Id)
+}
+
+// https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#create-a-public-key
+func CreatePublicKey(ctx *middleware.Context, form api.CreateKeyOption) {
+	createUserPublicKey(ctx, form, ctx.User.Id)
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Users-Public-Keys#delete-a-public-key
-func DeleteUserPublicKey(ctx *middleware.Context) {
+func DeletePublicKey(ctx *middleware.Context) {
 	if err := models.DeletePublicKey(ctx.User, ctx.ParamsInt64(":id")); err != nil {
 		if models.IsErrKeyAccessDenied(err) {
 			ctx.APIError(403, "", "You do not have access to this key")

+ 3 - 14
routers/api/v1/user.go → routers/api/v1/user/user.go

@@ -2,7 +2,7 @@
 // Use of this source code is governed by a MIT-style
 // license that can be found in the LICENSE file.
 
-package v1
+package user
 
 import (
 	"github.com/Unknwon/com"
@@ -13,19 +13,8 @@ import (
 	"github.com/gogits/gogs/modules/middleware"
 )
 
-// ToApiUser converts user to API format.
-func ToApiUser(u *models.User) *api.User {
-	return &api.User{
-		ID:        u.Id,
-		UserName:  u.Name,
-		FullName:  u.FullName,
-		Email:     u.Email,
-		AvatarUrl: u.AvatarLink(),
-	}
-}
-
 // https://github.com/gogits/go-gogs-client/wiki/Users#search-users
-func SearchUsers(ctx *middleware.Context) {
+func Search(ctx *middleware.Context) {
 	opt := models.SearchOption{
 		Keyword: ctx.Query("q"),
 		Limit:   com.StrTo(ctx.Query("limit")).MustInt(),
@@ -63,7 +52,7 @@ func SearchUsers(ctx *middleware.Context) {
 }
 
 // https://github.com/gogits/go-gogs-client/wiki/Users#get-a-single-user
-func GetUserInfo(ctx *middleware.Context) {
+func GetInfo(ctx *middleware.Context) {
 	u, err := models.GetUserByName(ctx.Params(":username"))
 	if err != nil {
 		if models.IsErrUserNotExist(err) {

+ 92 - 0
routers/api/v1/utils/convert.go

@@ -0,0 +1,92 @@
+// 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 utils
+
+import (
+	"fmt"
+
+	"github.com/Unknwon/com"
+
+	api "github.com/gogits/go-gogs-client"
+
+	"github.com/gogits/gogs/models"
+	"github.com/gogits/gogs/modules/setting"
+)
+
+// ApiUser converts user to its API format.
+func ApiUser(u *models.User) *api.User {
+	return &api.User{
+		ID:        u.Id,
+		UserName:  u.Name,
+		FullName:  u.FullName,
+		Email:     u.Email,
+		AvatarUrl: u.AvatarLink(),
+	}
+}
+
+// ApiRepository converts repository to API format.
+func ApiRepository(owner *models.User, repo *models.Repository, permission api.Permission) *api.Repository {
+	cl := repo.CloneLink()
+	return &api.Repository{
+		Id:          repo.ID,
+		Owner:       *ApiUser(owner),
+		FullName:    owner.Name + "/" + repo.Name,
+		Private:     repo.IsPrivate,
+		Fork:        repo.IsFork,
+		HtmlUrl:     setting.AppUrl + owner.Name + "/" + repo.Name,
+		CloneUrl:    cl.HTTPS,
+		SshUrl:      cl.SSH,
+		Permissions: permission,
+	}
+}
+
+// ApiPublicKey converts public key to its API format.
+func ApiPublicKey(apiLink string, key *models.PublicKey) *api.PublicKey {
+	return &api.PublicKey{
+		ID:      key.ID,
+		Key:     key.Content,
+		URL:     apiLink + com.ToStr(key.ID),
+		Title:   key.Name,
+		Created: key.Created,
+	}
+}
+
+// ApiHook converts webhook to its API format.
+func ApiHook(repoLink string, w *models.Webhook) *api.Hook {
+	config := map[string]string{
+		"url":          w.URL,
+		"content_type": w.ContentType.Name(),
+	}
+	if w.HookTaskType == models.SLACK {
+		s := w.GetSlackHook()
+		config["channel"] = s.Channel
+		config["username"] = s.Username
+		config["icon_url"] = s.IconURL
+		config["color"] = s.Color
+	}
+
+	return &api.Hook{
+		ID:      w.ID,
+		Type:    w.HookTaskType.Name(),
+		URL:     fmt.Sprintf("%s/settings/hooks/%d", repoLink, w.ID),
+		Active:  w.IsActive,
+		Config:  config,
+		Events:  w.EventsArray(),
+		Updated: w.Updated,
+		Created: w.Created,
+	}
+}
+
+// ApiDeployKey converts deploy key to its API format.
+func ApiDeployKey(apiLink string, key *models.DeployKey) *api.DeployKey {
+	return &api.DeployKey{
+		ID:       key.ID,
+		Key:      key.Content,
+		URL:      apiLink + com.ToStr(key.ID),
+		Title:    key.Name,
+		Created:  key.Created,
+		ReadOnly: true, // All deploy keys are read-only.
+	}
+}

+ 1 - 1
templates/.VERSION

@@ -1 +1 @@
-0.7.28.1203 Beta
+0.7.29.1204 Beta