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.

repo.go 10 kB

11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. // Copyright 2014 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. "fmt"
  7. "os"
  8. "path"
  9. "strings"
  10. "github.com/Unknwon/com"
  11. "code.gitea.io/git"
  12. "code.gitea.io/gitea/models"
  13. "code.gitea.io/gitea/modules/auth"
  14. "code.gitea.io/gitea/modules/base"
  15. "code.gitea.io/gitea/modules/context"
  16. "code.gitea.io/gitea/modules/log"
  17. "code.gitea.io/gitea/modules/setting"
  18. )
  19. const (
  20. tplCreate base.TplName = "repo/create"
  21. tplMigrate base.TplName = "repo/migrate"
  22. )
  23. // MustBeNotBare render when a repo is a bare git dir
  24. func MustBeNotBare(ctx *context.Context) {
  25. if ctx.Repo.Repository.IsBare {
  26. ctx.Handle(404, "MustBeNotBare", nil)
  27. }
  28. }
  29. // MustBeEditable check that repo can be edited
  30. func MustBeEditable(ctx *context.Context) {
  31. if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit {
  32. ctx.Handle(404, "", nil)
  33. return
  34. }
  35. }
  36. // MustBeAbleToUpload check that repo can be uploaded to
  37. func MustBeAbleToUpload(ctx *context.Context) {
  38. if !setting.Repository.Upload.Enabled {
  39. ctx.Handle(404, "", nil)
  40. }
  41. }
  42. func checkContextUser(ctx *context.Context, uid int64) *models.User {
  43. orgs, err := models.GetOwnedOrgsByUserIDDesc(ctx.User.ID, "updated_unix")
  44. if err != nil {
  45. ctx.Handle(500, "GetOwnedOrgsByUserIDDesc", err)
  46. return nil
  47. }
  48. ctx.Data["Orgs"] = orgs
  49. // Not equal means current user is an organization.
  50. if uid == ctx.User.ID || uid == 0 {
  51. return ctx.User
  52. }
  53. org, err := models.GetUserByID(uid)
  54. if models.IsErrUserNotExist(err) {
  55. return ctx.User
  56. }
  57. if err != nil {
  58. ctx.Handle(500, "GetUserByID", fmt.Errorf("[%d]: %v", uid, err))
  59. return nil
  60. }
  61. // Check ownership of organization.
  62. if !org.IsOrganization() || !(ctx.User.IsAdmin || org.IsOwnedBy(ctx.User.ID)) {
  63. ctx.Error(403)
  64. return nil
  65. }
  66. return org
  67. }
  68. // Create render creating repository page
  69. func Create(ctx *context.Context) {
  70. if !ctx.User.CanCreateRepo() {
  71. ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", ctx.User.MaxCreationLimit()), tplCreate, nil)
  72. }
  73. ctx.Data["Title"] = ctx.Tr("new_repo")
  74. // Give default value for template to render.
  75. ctx.Data["Gitignores"] = models.Gitignores
  76. ctx.Data["Licenses"] = models.Licenses
  77. ctx.Data["Readmes"] = models.Readmes
  78. ctx.Data["readme"] = "Default"
  79. ctx.Data["private"] = ctx.User.LastRepoVisibility
  80. ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
  81. ctxUser := checkContextUser(ctx, ctx.QueryInt64("org"))
  82. if ctx.Written() {
  83. return
  84. }
  85. ctx.Data["ContextUser"] = ctxUser
  86. ctx.HTML(200, tplCreate)
  87. }
  88. func handleCreateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form interface{}) {
  89. switch {
  90. case models.IsErrReachLimitOfRepo(err):
  91. ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form)
  92. case models.IsErrRepoAlreadyExist(err):
  93. ctx.Data["Err_RepoName"] = true
  94. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
  95. case models.IsErrNameReserved(err):
  96. ctx.Data["Err_RepoName"] = true
  97. ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
  98. case models.IsErrNamePatternNotAllowed(err):
  99. ctx.Data["Err_RepoName"] = true
  100. ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
  101. default:
  102. ctx.Handle(500, name, err)
  103. }
  104. }
  105. // CreatePost response for creating repository
  106. func CreatePost(ctx *context.Context, form auth.CreateRepoForm) {
  107. ctx.Data["Title"] = ctx.Tr("new_repo")
  108. ctx.Data["Gitignores"] = models.Gitignores
  109. ctx.Data["Licenses"] = models.Licenses
  110. ctx.Data["Readmes"] = models.Readmes
  111. ctxUser := checkContextUser(ctx, form.UID)
  112. if ctx.Written() {
  113. return
  114. }
  115. ctx.Data["ContextUser"] = ctxUser
  116. if ctx.HasError() {
  117. ctx.HTML(200, tplCreate)
  118. return
  119. }
  120. repo, err := models.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
  121. Name: form.RepoName,
  122. Description: form.Description,
  123. Gitignores: form.Gitignores,
  124. License: form.License,
  125. Readme: form.Readme,
  126. IsPrivate: form.Private || setting.Repository.ForcePrivate,
  127. AutoInit: form.AutoInit,
  128. })
  129. if err == nil {
  130. log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
  131. ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + repo.Name)
  132. return
  133. }
  134. if repo != nil {
  135. if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
  136. log.Error(4, "DeleteRepository: %v", errDelete)
  137. }
  138. }
  139. handleCreateError(ctx, ctxUser, err, "CreatePost", tplCreate, &form)
  140. }
  141. // Migrate render migration of repository page
  142. func Migrate(ctx *context.Context) {
  143. ctx.Data["Title"] = ctx.Tr("new_migrate")
  144. ctx.Data["private"] = ctx.User.LastRepoVisibility
  145. ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
  146. ctx.Data["mirror"] = ctx.Query("mirror") == "1"
  147. ctx.Data["LFSActive"] = setting.LFS.StartServer
  148. ctxUser := checkContextUser(ctx, ctx.QueryInt64("org"))
  149. if ctx.Written() {
  150. return
  151. }
  152. ctx.Data["ContextUser"] = ctxUser
  153. ctx.HTML(200, tplMigrate)
  154. }
  155. // MigratePost response for migrating from external git repository
  156. func MigratePost(ctx *context.Context, form auth.MigrateRepoForm) {
  157. ctx.Data["Title"] = ctx.Tr("new_migrate")
  158. ctxUser := checkContextUser(ctx, form.UID)
  159. if ctx.Written() {
  160. return
  161. }
  162. ctx.Data["ContextUser"] = ctxUser
  163. if ctx.HasError() {
  164. ctx.HTML(200, tplMigrate)
  165. return
  166. }
  167. remoteAddr, err := form.ParseRemoteAddr(ctx.User)
  168. if err != nil {
  169. if models.IsErrInvalidCloneAddr(err) {
  170. ctx.Data["Err_CloneAddr"] = true
  171. addrErr := err.(models.ErrInvalidCloneAddr)
  172. switch {
  173. case addrErr.IsURLError:
  174. ctx.RenderWithErr(ctx.Tr("form.url_error"), tplMigrate, &form)
  175. case addrErr.IsPermissionDenied:
  176. ctx.RenderWithErr(ctx.Tr("repo.migrate.permission_denied"), tplMigrate, &form)
  177. case addrErr.IsInvalidPath:
  178. ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), tplMigrate, &form)
  179. default:
  180. ctx.Handle(500, "Unknown error", err)
  181. }
  182. } else {
  183. ctx.Handle(500, "ParseRemoteAddr", err)
  184. }
  185. return
  186. }
  187. repo, err := models.MigrateRepository(ctx.User, ctxUser, models.MigrateRepoOptions{
  188. Name: form.RepoName,
  189. Description: form.Description,
  190. IsPrivate: form.Private || setting.Repository.ForcePrivate,
  191. IsMirror: form.Mirror,
  192. RemoteAddr: remoteAddr,
  193. })
  194. if err == nil {
  195. log.Trace("Repository migrated [%d]: %s/%s", repo.ID, ctxUser.Name, form.RepoName)
  196. ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + form.RepoName)
  197. return
  198. }
  199. if repo != nil {
  200. if errDelete := models.DeleteRepository(ctx.User, ctxUser.ID, repo.ID); errDelete != nil {
  201. log.Error(4, "DeleteRepository: %v", errDelete)
  202. }
  203. }
  204. if strings.Contains(err.Error(), "Authentication failed") ||
  205. strings.Contains(err.Error(), "could not read Username") {
  206. ctx.Data["Err_Auth"] = true
  207. ctx.RenderWithErr(ctx.Tr("form.auth_failed", models.HandleCloneUserCredentials(err.Error(), true)), tplMigrate, &form)
  208. return
  209. } else if strings.Contains(err.Error(), "fatal:") {
  210. ctx.Data["Err_CloneAddr"] = true
  211. ctx.RenderWithErr(ctx.Tr("repo.migrate.failed", models.HandleCloneUserCredentials(err.Error(), true)), tplMigrate, &form)
  212. return
  213. }
  214. handleCreateError(ctx, ctxUser, err, "MigratePost", tplMigrate, &form)
  215. }
  216. // Action response for actions to a repository
  217. func Action(ctx *context.Context) {
  218. var err error
  219. switch ctx.Params(":action") {
  220. case "watch":
  221. err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true)
  222. case "unwatch":
  223. err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, false)
  224. case "star":
  225. err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, true)
  226. case "unstar":
  227. err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, false)
  228. case "desc": // FIXME: this is not used
  229. if !ctx.Repo.IsOwner() {
  230. ctx.Error(404)
  231. return
  232. }
  233. ctx.Repo.Repository.Description = ctx.Query("desc")
  234. ctx.Repo.Repository.Website = ctx.Query("site")
  235. err = models.UpdateRepository(ctx.Repo.Repository, false)
  236. }
  237. if err != nil {
  238. ctx.Handle(500, fmt.Sprintf("Action (%s)", ctx.Params(":action")), err)
  239. return
  240. }
  241. redirectTo := ctx.Query("redirect_to")
  242. if len(redirectTo) == 0 {
  243. redirectTo = ctx.Repo.RepoLink
  244. }
  245. ctx.Redirect(redirectTo)
  246. }
  247. // Download download an archive of a repository
  248. func Download(ctx *context.Context) {
  249. var (
  250. uri = ctx.Params("*")
  251. refName string
  252. ext string
  253. archivePath string
  254. archiveType git.ArchiveType
  255. )
  256. switch {
  257. case strings.HasSuffix(uri, ".zip"):
  258. ext = ".zip"
  259. archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/zip")
  260. archiveType = git.ZIP
  261. case strings.HasSuffix(uri, ".tar.gz"):
  262. ext = ".tar.gz"
  263. archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/targz")
  264. archiveType = git.TARGZ
  265. default:
  266. log.Trace("Unknown format: %s", uri)
  267. ctx.Error(404)
  268. return
  269. }
  270. refName = strings.TrimSuffix(uri, ext)
  271. if !com.IsDir(archivePath) {
  272. if err := os.MkdirAll(archivePath, os.ModePerm); err != nil {
  273. ctx.Handle(500, "Download -> os.MkdirAll(archivePath)", err)
  274. return
  275. }
  276. }
  277. // Get corresponding commit.
  278. var (
  279. commit *git.Commit
  280. err error
  281. )
  282. gitRepo := ctx.Repo.GitRepo
  283. if gitRepo.IsBranchExist(refName) {
  284. commit, err = gitRepo.GetBranchCommit(refName)
  285. if err != nil {
  286. ctx.Handle(500, "GetBranchCommit", err)
  287. return
  288. }
  289. } else if gitRepo.IsTagExist(refName) {
  290. commit, err = gitRepo.GetTagCommit(refName)
  291. if err != nil {
  292. ctx.Handle(500, "GetTagCommit", err)
  293. return
  294. }
  295. } else if len(refName) >= 4 && len(refName) <= 40 {
  296. commit, err = gitRepo.GetCommit(refName)
  297. if err != nil {
  298. ctx.Handle(404, "GetCommit", nil)
  299. return
  300. }
  301. } else {
  302. ctx.Handle(404, "Download", nil)
  303. return
  304. }
  305. archivePath = path.Join(archivePath, base.ShortSha(commit.ID.String())+ext)
  306. if !com.IsFile(archivePath) {
  307. if err := commit.CreateArchive(archivePath, archiveType); err != nil {
  308. ctx.Handle(500, "Download -> CreateArchive "+archivePath, err)
  309. return
  310. }
  311. }
  312. ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+refName+ext)
  313. }