refactor API routes and some work for #976
parent
e0bae9547a
commit
56dd430a10
@ -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,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)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
@ -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 @@
|
|||||||
0.7.28.1203 Beta
|
0.7.29.1204 Beta
|
Loading…
Reference in new issue