aboutsummaryrefslogtreecommitdiff
path: root/internal
diff options
context:
space:
mode:
authorMateusz Reszka <mateusz.reszka@outlook.com>2022-10-22 17:52:48 +0200
committerGitHub <noreply@github.com>2022-10-22 23:52:48 +0800
commit742bc36edd92229cf78608af7f950e65f71a0a01 (patch)
tree56235af3f9492d4f33b8fa79fbd51da96fde7257 /internal
parenta7299bbb8d8b363f3d828e04e060921aa0c31d93 (diff)
api: support put content (#7114)
Co-authored-by: Joe Chen <jc@unknwon.io>
Diffstat (limited to 'internal')
-rw-r--r--internal/context/api.go8
-rw-r--r--internal/route/api/v1/api.go4
-rw-r--r--internal/route/api/v1/repo/contents.go249
3 files changed, 173 insertions, 88 deletions
diff --git a/internal/context/api.go b/internal/context/api.go
index 938e207f..b3922bb6 100644
--- a/internal/context/api.go
+++ b/internal/context/api.go
@@ -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.
diff --git a/internal/route/api/v1/api.go b/internal/route/api/v1/api.go
index f1684691..0a17e657 100644
--- a/internal/route/api/v1/api.go
+++ b/internal/route/api/v1/api.go
@@ -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() {
diff --git a/internal/route/api/v1/repo/contents.go b/internal/route/api/v1/repo/contents.go
index 463bbb22..ed09e425 100644
--- a/internal/route/api/v1/repo/contents.go
+++ b/internal/route/api/v1/repo/contents.go
@@ -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,
+ },
+ )
+}