contents.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. // Copyright 2020 The Gogs Authors. All rights reserved.
  2. // Use of this source code is governed by a MIT-style
  3. // license that can be found in the LICENSE file.
  4. package repo
  5. import (
  6. "encoding/base64"
  7. "fmt"
  8. "net/http"
  9. "path"
  10. "github.com/gogs/git-module"
  11. "github.com/pkg/errors"
  12. "gogs.io/gogs/internal/context"
  13. "gogs.io/gogs/internal/database"
  14. "gogs.io/gogs/internal/gitutil"
  15. "gogs.io/gogs/internal/repoutil"
  16. )
  17. type links struct {
  18. Git string `json:"git"`
  19. Self string `json:"self"`
  20. HTML string `json:"html"`
  21. }
  22. type repoContent struct {
  23. Type string `json:"type"`
  24. Target string `json:"target,omitempty"`
  25. SubmoduleGitURL string `json:"submodule_git_url,omitempty"`
  26. Encoding string `json:"encoding,omitempty"`
  27. Size int64 `json:"size"`
  28. Name string `json:"name"`
  29. Path string `json:"path"`
  30. Content string `json:"content,omitempty"`
  31. Sha string `json:"sha"`
  32. URL string `json:"url"`
  33. GitURL string `json:"git_url"`
  34. HTMLURL string `json:"html_url"`
  35. DownloadURL string `json:"download_url"`
  36. Links links `json:"_links"`
  37. }
  38. func toRepoContent(c *context.APIContext, ref, subpath string, commit *git.Commit, entry *git.TreeEntry) (*repoContent, error) {
  39. repoURL := fmt.Sprintf("%s/repos/%s/%s", c.BaseURL, c.Params(":username"), c.Params(":reponame"))
  40. selfURL := fmt.Sprintf("%s/contents/%s", repoURL, subpath)
  41. htmlURL := fmt.Sprintf("%s/src/%s/%s", repoutil.HTMLURL(c.Repo.Owner.Name, c.Repo.Repository.Name), ref, entry.Name())
  42. downloadURL := fmt.Sprintf("%s/raw/%s/%s", repoutil.HTMLURL(c.Repo.Owner.Name, c.Repo.Repository.Name), ref, entry.Name())
  43. content := &repoContent{
  44. Size: entry.Size(),
  45. Name: entry.Name(),
  46. Path: subpath,
  47. Sha: entry.ID().String(),
  48. URL: selfURL,
  49. HTMLURL: htmlURL,
  50. DownloadURL: downloadURL,
  51. Links: links{
  52. Self: selfURL,
  53. HTML: htmlURL,
  54. },
  55. }
  56. switch {
  57. case entry.IsBlob(), entry.IsExec():
  58. content.Type = "file"
  59. p, err := entry.Blob().Bytes()
  60. if err != nil {
  61. return nil, errors.Wrap(err, "get blob content")
  62. }
  63. content.Encoding = "base64"
  64. content.Content = base64.StdEncoding.EncodeToString(p)
  65. content.GitURL = fmt.Sprintf("%s/git/blobs/%s", repoURL, entry.ID().String())
  66. case entry.IsTree():
  67. content.Type = "dir"
  68. content.GitURL = fmt.Sprintf("%s/git/trees/%s", repoURL, entry.ID().String())
  69. case entry.IsSymlink():
  70. content.Type = "symlink"
  71. p, err := entry.Blob().Bytes()
  72. if err != nil {
  73. return nil, errors.Wrap(err, "get blob content")
  74. }
  75. content.Target = string(p)
  76. case entry.IsCommit():
  77. content.Type = "submodule"
  78. mod, err := commit.Submodule(subpath)
  79. if err != nil {
  80. return nil, errors.Wrap(err, "get submodule")
  81. }
  82. content.SubmoduleGitURL = mod.URL
  83. default:
  84. panic("unreachable")
  85. }
  86. content.Links.Git = content.GitURL
  87. return content, nil
  88. }
  89. func GetContents(c *context.APIContext) {
  90. repoPath := repoutil.RepositoryPath(c.Params(":username"), c.Params(":reponame"))
  91. gitRepo, err := git.Open(repoPath)
  92. if err != nil {
  93. c.Error(err, "open repository")
  94. return
  95. }
  96. ref := c.Query("ref")
  97. if ref == "" {
  98. ref = c.Repo.Repository.DefaultBranch
  99. }
  100. commit, err := gitRepo.CatFileCommit(ref)
  101. if err != nil {
  102. c.NotFoundOrError(gitutil.NewError(err), "get commit")
  103. return
  104. }
  105. treePath := c.Params("*")
  106. entry, err := commit.TreeEntry(treePath)
  107. if err != nil {
  108. c.NotFoundOrError(gitutil.NewError(err), "get tree entry")
  109. return
  110. }
  111. if !entry.IsTree() {
  112. content, err := toRepoContent(c, ref, treePath, commit, entry)
  113. if err != nil {
  114. c.Errorf(err, "convert %q to repoContent", treePath)
  115. return
  116. }
  117. c.JSONSuccess(content)
  118. return
  119. }
  120. // The entry is a directory
  121. dir, err := gitRepo.LsTree(entry.ID().String())
  122. if err != nil {
  123. c.NotFoundOrError(gitutil.NewError(err), "get tree")
  124. return
  125. }
  126. entries, err := dir.Entries()
  127. if err != nil {
  128. c.NotFoundOrError(gitutil.NewError(err), "list entries")
  129. return
  130. }
  131. if len(entries) == 0 {
  132. c.JSONSuccess([]string{})
  133. return
  134. }
  135. contents := make([]*repoContent, 0, len(entries))
  136. for _, entry := range entries {
  137. subpath := path.Join(treePath, entry.Name())
  138. content, err := toRepoContent(c, ref, subpath, commit, entry)
  139. if err != nil {
  140. c.Errorf(err, "convert %q to repoContent", subpath)
  141. return
  142. }
  143. contents = append(contents, content)
  144. }
  145. c.JSONSuccess(contents)
  146. }
  147. // PutContentsRequest is the API message for creating or updating a file.
  148. type PutContentsRequest struct {
  149. Message string `json:"message" binding:"Required"`
  150. Content string `json:"content" binding:"Required"`
  151. Branch string `json:"branch"`
  152. }
  153. // PUT /repos/:username/:reponame/contents/*
  154. func PutContents(c *context.APIContext, r PutContentsRequest) {
  155. content, err := base64.StdEncoding.DecodeString(r.Content)
  156. if err != nil {
  157. c.Error(err, "decoding base64")
  158. return
  159. }
  160. if r.Branch == "" {
  161. r.Branch = c.Repo.Repository.DefaultBranch
  162. }
  163. treePath := c.Params("*")
  164. err = c.Repo.Repository.UpdateRepoFile(
  165. c.User,
  166. database.UpdateRepoFileOptions{
  167. OldBranch: c.Repo.Repository.DefaultBranch,
  168. NewBranch: r.Branch,
  169. OldTreeName: treePath,
  170. NewTreeName: treePath,
  171. Message: r.Message,
  172. Content: string(content),
  173. },
  174. )
  175. if err != nil {
  176. c.Error(err, "updating repository file")
  177. return
  178. }
  179. repoPath := repoutil.RepositoryPath(c.Params(":username"), c.Params(":reponame"))
  180. gitRepo, err := git.Open(repoPath)
  181. if err != nil {
  182. c.Error(err, "open repository")
  183. return
  184. }
  185. commit, err := gitRepo.CatFileCommit(r.Branch)
  186. if err != nil {
  187. c.Error(err, "get file commit")
  188. return
  189. }
  190. entry, err := commit.TreeEntry(treePath)
  191. if err != nil {
  192. c.Error(err, "get tree entry")
  193. return
  194. }
  195. apiContent, err := toRepoContent(c, r.Branch, treePath, commit, entry)
  196. if err != nil {
  197. c.Error(err, "convert to *repoContent")
  198. return
  199. }
  200. apiCommit, err := gitCommitToAPICommit(commit, c)
  201. if err != nil {
  202. c.Error(err, "convert to *api.Commit")
  203. return
  204. }
  205. c.JSON(
  206. http.StatusCreated,
  207. map[string]any{
  208. "content": apiContent,
  209. "commit": apiCommit,
  210. },
  211. )
  212. }