mirror of https://github.com/gogs/gogs.git
parent
a7299bbb8d
commit
742bc36edd
|
@ -8,6 +8,7 @@ All notable changes to Gogs are documented in this file.
|
|||
|
||||
- Support using personal access token in the password field. [#3866](https://github.com/gogs/gogs/issues/3866)
|
||||
- An unlisted option is added when create or migrate a repository. Unlisted repositories are public but not being listed for users without direct access in the UI. [#5733](https://github.com/gogs/gogs/issues/5733)
|
||||
- New API endpoint `PUT /repos/:owner/:repo/contents/:path` for creating and update repository contents. [#5967](https://github.com/gogs/gogs/issues/5967)
|
||||
- New configuration option `[git.timeout] DIFF` for customizing operation timeout of `git diff`. [#6315](https://github.com/gogs/gogs/issues/6315)
|
||||
- New configuration option `[server] SSH_SERVER_MACS` for setting list of accepted MACs for connections to builtin SSH server. [#6434](https://github.com/gogs/gogs/issues/6434)
|
||||
- Support specifying custom schema for PostgreSQL. [#6695](https://github.com/gogs/gogs/pull/6695)
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/unknwon/paginater"
|
||||
"gopkg.in/macaron.v1"
|
||||
log "unknwon.dev/clog/v2"
|
||||
|
@ -49,8 +50,11 @@ func (c *APIContext) ErrorStatus(status int, err error) {
|
|||
|
||||
// Error renders the 500 response.
|
||||
func (c *APIContext) Error(err error, msg string) {
|
||||
log.ErrorDepth(5, "%s: %v", msg, err)
|
||||
c.ErrorStatus(http.StatusInternalServerError, err)
|
||||
log.ErrorDepth(4, "%s: %v", msg, err)
|
||||
c.ErrorStatus(
|
||||
http.StatusInternalServerError,
|
||||
errors.New("Something went wrong, please check the server logs for more information."),
|
||||
)
|
||||
}
|
||||
|
||||
// Errorf renders the 500 response with formatted message.
|
||||
|
|
|
@ -273,7 +273,9 @@ func RegisterRoutes(m *macaron.Macaron) {
|
|||
m.Get("/raw/*", context.RepoRef(), repo.GetRawFile)
|
||||
m.Group("/contents", func() {
|
||||
m.Get("", repo.GetContents)
|
||||
m.Get("/*", repo.GetContents)
|
||||
m.Combo("/*").
|
||||
Get(repo.GetContents).
|
||||
Put(bind(repo.PutContentsRequest{}), repo.PutContents)
|
||||
})
|
||||
m.Get("/archive/*", repo.GetArchive)
|
||||
m.Group("/git", func() {
|
||||
|
|
|
@ -7,17 +7,103 @@ package repo
|
|||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
"github.com/gogs/git-module"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"gogs.io/gogs/internal/context"
|
||||
"gogs.io/gogs/internal/db"
|
||||
"gogs.io/gogs/internal/gitutil"
|
||||
"gogs.io/gogs/internal/repoutil"
|
||||
)
|
||||
|
||||
type links struct {
|
||||
Git string `json:"git"`
|
||||
Self string `json:"self"`
|
||||
HTML string `json:"html"`
|
||||
}
|
||||
|
||||
type repoContent struct {
|
||||
Type string `json:"type"`
|
||||
Target string `json:"target,omitempty"`
|
||||
SubmoduleGitURL string `json:"submodule_git_url,omitempty"`
|
||||
Encoding string `json:"encoding,omitempty"`
|
||||
Size int64 `json:"size"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Content string `json:"content,omitempty"`
|
||||
Sha string `json:"sha"`
|
||||
URL string `json:"url"`
|
||||
GitURL string `json:"git_url"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
DownloadURL string `json:"download_url"`
|
||||
Links links `json:"_links"`
|
||||
}
|
||||
|
||||
func toRepoContent(c *context.APIContext, ref, subpath string, commit *git.Commit, entry *git.TreeEntry) (*repoContent, error) {
|
||||
repoURL := fmt.Sprintf("%s/repos/%s/%s", c.BaseURL, c.Params(":username"), c.Params(":reponame"))
|
||||
selfURL := fmt.Sprintf("%s/contents/%s", repoURL, subpath)
|
||||
htmlURL := fmt.Sprintf("%s/src/%s/%s", repoutil.HTMLURL(c.Repo.Owner.Name, c.Repo.Repository.Name), ref, entry.Name())
|
||||
downloadURL := fmt.Sprintf("%s/raw/%s/%s", repoutil.HTMLURL(c.Repo.Owner.Name, c.Repo.Repository.Name), ref, entry.Name())
|
||||
|
||||
content := &repoContent{
|
||||
Size: entry.Size(),
|
||||
Name: entry.Name(),
|
||||
Path: subpath,
|
||||
Sha: entry.ID().String(),
|
||||
URL: selfURL,
|
||||
HTMLURL: htmlURL,
|
||||
DownloadURL: downloadURL,
|
||||
Links: links{
|
||||
Self: selfURL,
|
||||
HTML: htmlURL,
|
||||
},
|
||||
}
|
||||
|
||||
switch {
|
||||
case entry.IsBlob(), entry.IsExec():
|
||||
content.Type = "file"
|
||||
p, err := entry.Blob().Bytes()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get blob content")
|
||||
}
|
||||
content.Encoding = "base64"
|
||||
content.Content = base64.StdEncoding.EncodeToString(p)
|
||||
content.GitURL = fmt.Sprintf("%s/git/blobs/%s", repoURL, entry.ID().String())
|
||||
|
||||
case entry.IsTree():
|
||||
content.Type = "dir"
|
||||
content.GitURL = fmt.Sprintf("%s/git/trees/%s", repoURL, entry.ID().String())
|
||||
|
||||
case entry.IsSymlink():
|
||||
content.Type = "symlink"
|
||||
p, err := entry.Blob().Bytes()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get blob content")
|
||||
}
|
||||
content.Target = string(p)
|
||||
|
||||
case entry.IsCommit():
|
||||
content.Type = "submodule"
|
||||
mod, err := commit.Submodule(subpath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get submodule")
|
||||
}
|
||||
content.SubmoduleGitURL = mod.URL
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
content.Links.Git = content.GitURL
|
||||
return content, nil
|
||||
}
|
||||
|
||||
func GetContents(c *context.APIContext) {
|
||||
gitRepo, err := git.Open(c.Repo.Repository.RepoPath())
|
||||
repoPath := repoutil.RepositoryPath(c.Params(":username"), c.Params(":reponame"))
|
||||
gitRepo, err := git.Open(repoPath)
|
||||
if err != nil {
|
||||
c.Error(err, "open repository")
|
||||
return
|
||||
|
@ -41,90 +127,8 @@ func GetContents(c *context.APIContext) {
|
|||
return
|
||||
}
|
||||
|
||||
type links struct {
|
||||
Git string `json:"git"`
|
||||
Self string `json:"self"`
|
||||
HTML string `json:"html"`
|
||||
}
|
||||
type repoContent struct {
|
||||
Type string `json:"type"`
|
||||
Target string `json:"target,omitempty"`
|
||||
SubmoduleGitURL string `json:"submodule_git_url,omitempty"`
|
||||
Encoding string `json:"encoding,omitempty"`
|
||||
Size int64 `json:"size"`
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Content string `json:"content,omitempty"`
|
||||
Sha string `json:"sha"`
|
||||
URL string `json:"url"`
|
||||
GitURL string `json:"git_url"`
|
||||
HTMLURL string `json:"html_url"`
|
||||
DownloadURL string `json:"download_url"`
|
||||
Links links `json:"_links"`
|
||||
}
|
||||
|
||||
toRepoContent := func(subpath string, entry *git.TreeEntry) (*repoContent, error) {
|
||||
repoURL := fmt.Sprintf("%s/repos/%s/%s", c.BaseURL, c.Params(":username"), c.Params(":reponame"))
|
||||
selfURL := fmt.Sprintf("%s/contents/%s", repoURL, subpath)
|
||||
htmlURL := fmt.Sprintf("%s/src/%s/%s", c.Repo.Repository.HTMLURL(), ref, entry.Name())
|
||||
downloadURL := fmt.Sprintf("%s/raw/%s/%s", c.Repo.Repository.HTMLURL(), ref, entry.Name())
|
||||
|
||||
content := &repoContent{
|
||||
Size: entry.Size(),
|
||||
Name: entry.Name(),
|
||||
Path: subpath,
|
||||
Sha: entry.ID().String(),
|
||||
URL: selfURL,
|
||||
HTMLURL: htmlURL,
|
||||
DownloadURL: downloadURL,
|
||||
Links: links{
|
||||
Self: selfURL,
|
||||
HTML: htmlURL,
|
||||
},
|
||||
}
|
||||
|
||||
switch {
|
||||
case entry.IsBlob(), entry.IsExec():
|
||||
content.Type = "file"
|
||||
p, err := entry.Blob().Bytes()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get blob content")
|
||||
}
|
||||
content.Encoding = "base64"
|
||||
content.Content = base64.StdEncoding.EncodeToString(p)
|
||||
content.GitURL = fmt.Sprintf("%s/git/blobs/%s", repoURL, entry.ID().String())
|
||||
|
||||
case entry.IsTree():
|
||||
content.Type = "dir"
|
||||
content.GitURL = fmt.Sprintf("%s/git/trees/%s", repoURL, entry.ID().String())
|
||||
|
||||
case entry.IsSymlink():
|
||||
content.Type = "symlink"
|
||||
p, err := entry.Blob().Bytes()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get blob content")
|
||||
}
|
||||
content.Target = string(p)
|
||||
|
||||
case entry.IsCommit():
|
||||
content.Type = "submodule"
|
||||
mod, err := commit.Submodule(subpath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "get submodule")
|
||||
}
|
||||
content.SubmoduleGitURL = mod.URL
|
||||
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
content.Links.Git = content.GitURL
|
||||
|
||||
return content, nil
|
||||
}
|
||||
|
||||
if !entry.IsTree() {
|
||||
content, err := toRepoContent(treePath, entry)
|
||||
content, err := toRepoContent(c, ref, treePath, commit, entry)
|
||||
if err != nil {
|
||||
c.Errorf(err, "convert %q to repoContent", treePath)
|
||||
return
|
||||
|
@ -155,7 +159,7 @@ func GetContents(c *context.APIContext) {
|
|||
contents := make([]*repoContent, 0, len(entries))
|
||||
for _, entry := range entries {
|
||||
subpath := path.Join(treePath, entry.Name())
|
||||
content, err := toRepoContent(subpath, entry)
|
||||
content, err := toRepoContent(c, ref, subpath, commit, entry)
|
||||
if err != nil {
|
||||
c.Errorf(err, "convert %q to repoContent", subpath)
|
||||
return
|
||||
|
@ -165,3 +169,78 @@ func GetContents(c *context.APIContext) {
|
|||
}
|
||||
c.JSONSuccess(contents)
|
||||
}
|
||||
|
||||
// PutContentsRequest is the API message for creating or updating a file.
|
||||
type PutContentsRequest struct {
|
||||
Message string `json:"message" binding:"Required"`
|
||||
Content string `json:"content" binding:"Required"`
|
||||
Branch string `json:"branch"`
|
||||
}
|
||||
|
||||
// PUT /repos/:username/:reponame/contents/*
|
||||
func PutContents(c *context.APIContext, r PutContentsRequest) {
|
||||
content, err := base64.StdEncoding.DecodeString(r.Content)
|
||||
if err != nil {
|
||||
c.Error(err, "decoding base64")
|
||||
return
|
||||
}
|
||||
|
||||
if r.Branch == "" {
|
||||
r.Branch = c.Repo.Repository.DefaultBranch
|
||||
}
|
||||
treePath := c.Params("*")
|
||||
err = c.Repo.Repository.UpdateRepoFile(
|
||||
c.User,
|
||||
db.UpdateRepoFileOptions{
|
||||
OldBranch: c.Repo.Repository.DefaultBranch,
|
||||
NewBranch: r.Branch,
|
||||
OldTreeName: treePath,
|
||||
NewTreeName: treePath,
|
||||
Message: r.Message,
|
||||
Content: string(content),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
c.Error(err, "updating repository file")
|
||||
return
|
||||
}
|
||||
|
||||
repoPath := repoutil.RepositoryPath(c.Params(":username"), c.Params(":reponame"))
|
||||
gitRepo, err := git.Open(repoPath)
|
||||
if err != nil {
|
||||
c.Error(err, "open repository")
|
||||
return
|
||||
}
|
||||
|
||||
commit, err := gitRepo.CatFileCommit(r.Branch)
|
||||
if err != nil {
|
||||
c.Error(err, "get file commit")
|
||||
return
|
||||
}
|
||||
|
||||
entry, err := commit.TreeEntry(treePath)
|
||||
if err != nil {
|
||||
c.Error(err, "get tree entry")
|
||||
return
|
||||
}
|
||||
|
||||
apiContent, err := toRepoContent(c, r.Branch, treePath, commit, entry)
|
||||
if err != nil {
|
||||
c.Error(err, "convert to *repoContent")
|
||||
return
|
||||
}
|
||||
|
||||
apiCommit, err := gitCommitToAPICommit(commit, c)
|
||||
if err != nil {
|
||||
c.Error(err, "convert to *api.Commit")
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(
|
||||
http.StatusCreated,
|
||||
map[string]any{
|
||||
"content": apiContent,
|
||||
"commit": apiCommit,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue