1
0
Эх сурвалжийг харах

routes/api/srcgraph: initial support for General Protocol

unknwon 5 жил өмнө
parent
commit
e2629d7ea6

+ 3 - 0
cmd/web.go

@@ -39,6 +39,7 @@ import (
 	"github.com/gogs/gogs/pkg/template"
 	"github.com/gogs/gogs/routes"
 	"github.com/gogs/gogs/routes/admin"
+	"github.com/gogs/gogs/routes/api/srcgraph"
 	apiv1 "github.com/gogs/gogs/routes/api/v1"
 	"github.com/gogs/gogs/routes/dev"
 	"github.com/gogs/gogs/routes/org"
@@ -670,6 +671,8 @@ func runWeb(c *cli.Context) error {
 				c.RequireBasicAuth(setting.Prometheus.BasicAuthUsername, setting.Prometheus.BasicAuthPassword)
 			}, promhttp.Handler())
 		}
+
+		m.Get("/srcgraph/*", srcgraph.NewHandler())
 	})
 
 	// robots.txt

+ 170 - 0
routes/api/srcgraph/general_protocol.go

@@ -0,0 +1,170 @@
+// Copyright 2019 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 srcgraph
+
+import (
+	"github.com/Unknwon/com"
+	"net/http"
+	"time"
+
+	adapter "github.com/sourcegraph/external-service-adapter"
+	log "gopkg.in/clog.v1"
+
+	"github.com/gogs/gogs/models"
+	"github.com/gogs/gogs/models/errors"
+	"github.com/gogs/gogs/pkg/setting"
+)
+
+func NewHandler() http.HandlerFunc {
+	h := adapter.NewHandler(externalService{}, adapter.Options{
+		URL:             setting.AppURL,
+		PathPrefix:      "/-/srcgraph",
+		MaxPageLen:      100000, // Current version returns all repositories at once, does not matter
+		TokenAsUsername: true,
+	})
+	return h.ServeHTTP
+}
+
+type externalService struct{}
+
+func (es externalService) ListRepos(ai adapter.AuthInfo, params adapter.Params) ([]*adapter.Repo, adapter.Page, error) {
+	return es.listUserRepos("", ai, params)
+}
+
+func (es externalService) ListUserRepos(user string, ai adapter.AuthInfo, params adapter.Params) ([]*adapter.Repo, adapter.Page, error) {
+	return es.listUserRepos(user, ai, params)
+}
+
+func toRepo(r *models.Repository) *adapter.Repo {
+	var parent *adapter.Repo
+	if r.IsFork {
+		parent = toRepo(r.BaseRepo)
+	}
+
+	cl := r.CloneLink()
+	return &adapter.Repo{
+		ID:          com.ToStr(r.ID),
+		Name:        r.Name,
+		Slug:        r.Name,
+		FullName:    r.FullName(),
+		SCM:         "git",
+		Description: r.Description,
+		IsPrivate:   r.IsPrivate,
+		Parent:      parent,
+		Links: []adapter.Link{
+			{adapter.CloneSSH, cl.SSH},
+			{adapter.CloneHTTP, cl.HTTPS},
+		},
+	}
+}
+
+func (es externalService) listUserRepos(username string, ai adapter.AuthInfo, params adapter.Params) ([]*adapter.Repo, adapter.Page, error) {
+	authUser, err := userFromAuthInfo(ai)
+	if err != nil {
+		if errors.IsUserNotExist(err) {
+			return nil, adapter.Page{}, errors.New("403 Forbidden")
+		}
+		log.Error(2, "Failed to get user from auth info: %v", err)
+		return nil, adapter.Page{}, errors.New("500 Internal Server Error")
+	}
+
+	// Fall back to authenticated user
+	if username == "" {
+		username = authUser.Name
+	}
+
+	user, err := models.GetUserByName(username)
+	if err != nil {
+		if errors.IsUserNotExist(err) {
+			return nil, adapter.Page{}, errors.New("404 Not Found")
+		}
+		log.Error(2, "Failed to get user by username %q: %v", username, err)
+		return nil, adapter.Page{}, errors.New("500 Internal Server Error")
+	}
+
+	// Only list public repositories if user requests someone else's repository list,
+	// or an organization isn't a member of.
+	var ownRepos []*models.Repository
+	if user.IsOrganization() {
+		ownRepos, _, err = user.GetUserRepositories(authUser.ID, params.Page, user.NumRepos)
+	} else {
+		ownRepos, err = models.GetUserRepositories(&models.UserRepoOptions{
+			UserID:   user.ID,
+			Private:  authUser.ID == user.ID,
+			Page:     params.Page,
+			PageSize: user.NumRepos,
+		})
+	}
+	if err != nil {
+		log.Error(2, "Failed to get repositories of user %q: %v", username, err)
+		return nil, adapter.Page{}, errors.New("500 Internal Server Error")
+	}
+
+	if err = models.RepositoryList(ownRepos).LoadAttributes(); err != nil {
+		log.Error(2, "Failed to load attributes of repositories: %v", err)
+		return nil, adapter.Page{}, errors.New("500 Internal Server Error")
+	}
+
+	// Early return for querying other user's repositories
+	if authUser.ID != user.ID {
+		repos := make([]*adapter.Repo, len(ownRepos))
+		for i := range ownRepos {
+			repos[i] = toRepo(ownRepos[i])
+		}
+		return repos, adapter.Page{Last: 1}, nil
+	}
+
+	accessibleRepos, err := user.GetRepositoryAccesses()
+	if err != nil {
+		log.Error(2, "Failed to get accessible repositories of user %q: %v", username, err)
+		return nil, adapter.Page{}, errors.New("500 Internal Server Error")
+	}
+
+	numOwnRepos := len(ownRepos)
+	repos := make([]*adapter.Repo, numOwnRepos+len(accessibleRepos))
+	for i := range ownRepos {
+		repos[i] = toRepo(ownRepos[i])
+	}
+
+	i := numOwnRepos
+	for repo := range accessibleRepos {
+		repos[i] = toRepo(repo)
+		i++
+	}
+
+	return repos, adapter.Page{Last: 1}, nil
+}
+
+func userFromAuthInfo(ai adapter.AuthInfo) (*models.User, error) {
+	u, err := models.UserLogin(ai.Username, ai.Password, -1)
+	if err != nil && !errors.IsUserNotExist(err) {
+		return nil, err
+	}
+
+	if u != nil {
+		if u.IsEnabledTwoFactor() {
+			return nil, errors.New(
+				"User with two-factor authentication enabled cannot perform HTTP/HTTPS operations via plain username and password." +
+					" Please create and use personal access token on user settings page.")
+		}
+		return u, nil
+	}
+
+	t, err := models.GetAccessTokenBySHA(ai.Username)
+	if err != nil {
+		if models.IsErrAccessTokenEmpty(err) || models.IsErrAccessTokenNotExist(err) {
+			return nil, errors.UserNotExist{}
+		}
+		return nil, err
+	}
+	t.Updated = time.Now()
+
+	u, err = models.GetUserByID(t.UID)
+	if err != nil {
+		return nil, err
+	}
+
+	return u, models.UpdateAccessToken(t)
+}