You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

content.go 6.3 kB

3 years ago
3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. // Copyright 2019 The Gitea 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 repofiles
  5. import (
  6. "code.gitea.io/gitea/routers/api/v1/utils"
  7. "fmt"
  8. "net/url"
  9. "path"
  10. "strings"
  11. "code.gitea.io/gitea/models"
  12. "code.gitea.io/gitea/modules/git"
  13. api "code.gitea.io/gitea/modules/structs"
  14. )
  15. // ContentType repo content type
  16. type ContentType string
  17. // The string representations of different content types
  18. const (
  19. // ContentTypeRegular regular content type (file)
  20. ContentTypeRegular ContentType = "file"
  21. // ContentTypeDir dir content type (dir)
  22. ContentTypeDir ContentType = "dir"
  23. // ContentLink link content type (symlink)
  24. ContentTypeLink ContentType = "symlink"
  25. // ContentTag submodule content type (submodule)
  26. ContentTypeSubmodule ContentType = "submodule"
  27. )
  28. // String gets the string of ContentType
  29. func (ct *ContentType) String() string {
  30. return string(*ct)
  31. }
  32. // GetContentsOrList gets the meta data of a file's contents (*ContentsResponse) if treePath not a tree
  33. // directory, otherwise a listing of file contents ([]*ContentsResponse). Ref can be a branch, commit or tag
  34. func GetContentsOrList(repo *models.Repository, treePath, ref string) (interface{}, error) {
  35. if repo.IsEmpty {
  36. return make([]interface{}, 0), nil
  37. }
  38. if ref == "" {
  39. ref = repo.DefaultBranch
  40. }
  41. origRef := ref
  42. // Check that the path given in opts.treePath is valid (not a git path)
  43. cleanTreePath := CleanUploadFileName(treePath)
  44. if cleanTreePath == "" && treePath != "" {
  45. return nil, models.ErrFilenameInvalid{
  46. Path: treePath,
  47. }
  48. }
  49. treePath = cleanTreePath
  50. gitRepo, err := git.OpenRepository(repo.RepoPath())
  51. if err != nil {
  52. return nil, err
  53. }
  54. defer gitRepo.Close()
  55. // Get the commit object for the ref
  56. commit, err := gitRepo.GetCommit(ref)
  57. if err != nil {
  58. return nil, err
  59. }
  60. entry, err := commit.GetTreeEntryByPath(treePath)
  61. if err != nil {
  62. return nil, err
  63. }
  64. if entry.Type() != "tree" {
  65. return GetContents(repo, treePath, origRef, false)
  66. }
  67. // We are in a directory, so we return a list of FileContentResponse objects
  68. var fileList []*api.ContentsResponse
  69. gitTree, err := commit.SubTree(treePath)
  70. if err != nil {
  71. return nil, err
  72. }
  73. entries, err := gitTree.ListEntries()
  74. if err != nil {
  75. return nil, err
  76. }
  77. for _, e := range entries {
  78. subTreePath := path.Join(treePath, e.Name())
  79. fileContentResponse, err := GetContents(repo, subTreePath, origRef, true)
  80. if err != nil {
  81. return nil, err
  82. }
  83. fileList = append(fileList, fileContentResponse)
  84. }
  85. return fileList, nil
  86. }
  87. // GetContents gets the meta data on a file's contents. Ref can be a branch, commit or tag
  88. func GetContents(repo *models.Repository, treePath, ref string, forList bool) (*api.ContentsResponse, error) {
  89. if ref == "" {
  90. ref = repo.DefaultBranch
  91. }
  92. origRef := ref
  93. // Check that the path given in opts.treePath is valid (not a git path)
  94. cleanTreePath := CleanUploadFileName(treePath)
  95. if cleanTreePath == "" && treePath != "" {
  96. return nil, models.ErrFilenameInvalid{
  97. Path: treePath,
  98. }
  99. }
  100. treePath = cleanTreePath
  101. gitRepo, err := git.OpenRepository(repo.RepoPath())
  102. if err != nil {
  103. return nil, err
  104. }
  105. defer gitRepo.Close()
  106. // Get the commit object for the ref
  107. commit, err := gitRepo.GetCommit(ref)
  108. if err != nil {
  109. return nil, err
  110. }
  111. commitID := commit.ID.String()
  112. if len(ref) >= 4 && strings.HasPrefix(commitID, ref) {
  113. ref = commit.ID.String()
  114. }
  115. entry, err := commit.GetTreeEntryByPath(treePath)
  116. if err != nil {
  117. return nil, err
  118. }
  119. refType := gitRepo.GetRefType(ref)
  120. if refType == "invalid" {
  121. return nil, fmt.Errorf("no commit found for the ref [ref: %s]", ref)
  122. }
  123. selfURL, err := url.Parse(fmt.Sprintf("%s/contents/%s?ref=%s", repo.APIURL(), treePath, origRef))
  124. if err != nil {
  125. return nil, err
  126. }
  127. selfURLString := selfURL.String()
  128. // All content types have these fields in populated
  129. contentsResponse := &api.ContentsResponse{
  130. Name: entry.Name(),
  131. Path: treePath,
  132. SHA: entry.ID.String(),
  133. Size: entry.Size(),
  134. URL: &selfURLString,
  135. Links: &api.FileLinksResponse{
  136. Self: &selfURLString,
  137. },
  138. }
  139. // Now populate the rest of the ContentsResponse based on entry type
  140. if entry.IsRegular() || entry.IsExecutable() {
  141. contentsResponse.Type = string(ContentTypeRegular)
  142. if blobResponse, err := GetBlobBySHA(repo, entry.ID.String()); err != nil {
  143. return nil, err
  144. } else if !forList {
  145. // We don't show the content if we are getting a list of FileContentResponses
  146. contentsResponse.Encoding = &blobResponse.Encoding
  147. contentsResponse.Content = &blobResponse.Content
  148. }
  149. } else if entry.IsDir() {
  150. contentsResponse.Type = string(ContentTypeDir)
  151. } else if entry.IsLink() {
  152. contentsResponse.Type = string(ContentTypeLink)
  153. // The target of a symlink file is the content of the file
  154. targetFromContent, err := entry.Blob().GetBlobContent()
  155. if err != nil {
  156. return nil, err
  157. }
  158. contentsResponse.Target = &targetFromContent
  159. } else if entry.IsSubModule() {
  160. contentsResponse.Type = string(ContentTypeSubmodule)
  161. submodule, err := commit.GetSubModule(treePath)
  162. if err != nil {
  163. return nil, err
  164. }
  165. contentsResponse.SubmoduleGitURL = &submodule.URL
  166. }
  167. // Handle links
  168. if entry.IsRegular() || entry.IsLink() {
  169. downloadURL, err := url.Parse(fmt.Sprintf("%s/raw/%s/%s/%s", repo.HTMLURL(), refType, ref, treePath))
  170. if err != nil {
  171. return nil, err
  172. }
  173. downloadURLString := downloadURL.String()
  174. contentsResponse.DownloadURL = &downloadURLString
  175. }
  176. if !entry.IsSubModule() {
  177. htmlURL, err := url.Parse(fmt.Sprintf("%s/src/%s/%s/%s", repo.HTMLURL(), refType, ref, treePath))
  178. if err != nil {
  179. return nil, err
  180. }
  181. htmlURLString := htmlURL.String()
  182. contentsResponse.HTMLURL = &htmlURLString
  183. contentsResponse.Links.HTMLURL = &htmlURLString
  184. gitURL, err := url.Parse(fmt.Sprintf("%s/git/blobs/%s", repo.APIURL(), entry.ID.String()))
  185. if err != nil {
  186. return nil, err
  187. }
  188. gitURLString := gitURL.String()
  189. contentsResponse.GitURL = &gitURLString
  190. contentsResponse.Links.GitURL = &gitURLString
  191. }
  192. // set file language and file type
  193. contentsResponse.Language = utils.JudgeLanguageBySuffix(treePath)
  194. contentsResponse.FileType = utils.JudgeFileTypeBySuffix(treePath)
  195. return contentsResponse, nil
  196. }