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

repo.go 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. // Copyright 2014 The Gogs Authors. All rights reserved.
  2. // Copyright 2020 The Gitea Authors. All rights reserved.
  3. // Use of this source code is governed by a MIT-style
  4. // license that can be found in the LICENSE file.
  5. package repo
  6. import (
  7. "fmt"
  8. "os"
  9. "path"
  10. "strings"
  11. "code.gitea.io/gitea/models"
  12. "code.gitea.io/gitea/modules/auth"
  13. "code.gitea.io/gitea/modules/base"
  14. "code.gitea.io/gitea/modules/context"
  15. "code.gitea.io/gitea/modules/git"
  16. "code.gitea.io/gitea/modules/log"
  17. "code.gitea.io/gitea/modules/setting"
  18. repo_service "code.gitea.io/gitea/services/repository"
  19. "github.com/unknwon/com"
  20. )
  21. const (
  22. tplCreate base.TplName = "repo/create"
  23. tplAlertDetails base.TplName = "base/alert_details"
  24. )
  25. // MustBeNotEmpty render when a repo is a empty git dir
  26. func MustBeNotEmpty(ctx *context.Context) {
  27. if ctx.Repo.Repository.IsEmpty {
  28. ctx.NotFound("MustBeNotEmpty", nil)
  29. }
  30. }
  31. // MustBeEditable check that repo can be edited
  32. func MustBeEditable(ctx *context.Context) {
  33. if !ctx.Repo.Repository.CanEnableEditor() || ctx.Repo.IsViewCommit {
  34. ctx.NotFound("", nil)
  35. return
  36. }
  37. }
  38. // MustBeAbleToUpload check that repo can be uploaded to
  39. func MustBeAbleToUpload(ctx *context.Context) {
  40. if !setting.Repository.Upload.Enabled {
  41. ctx.NotFound("", nil)
  42. }
  43. }
  44. func checkContextUser(ctx *context.Context, uid int64) *models.User {
  45. orgs, err := models.GetOrgsCanCreateRepoByUserID(ctx.User.ID)
  46. if err != nil {
  47. ctx.ServerError("GetOrgsCanCreateRepoByUserID", err)
  48. return nil
  49. }
  50. if !ctx.User.IsAdmin {
  51. orgsAvailable := []*models.User{}
  52. for i := 0; i < len(orgs); i++ {
  53. if orgs[i].CanCreateRepo() {
  54. orgsAvailable = append(orgsAvailable, orgs[i])
  55. }
  56. }
  57. ctx.Data["Orgs"] = orgsAvailable
  58. } else {
  59. ctx.Data["Orgs"] = orgs
  60. }
  61. // Not equal means current user is an organization.
  62. if uid == ctx.User.ID || uid == 0 {
  63. return ctx.User
  64. }
  65. org, err := models.GetUserByID(uid)
  66. if models.IsErrUserNotExist(err) {
  67. return ctx.User
  68. }
  69. if err != nil {
  70. ctx.ServerError("GetUserByID", fmt.Errorf("[%d]: %v", uid, err))
  71. return nil
  72. }
  73. // Check ownership of organization.
  74. if !org.IsOrganization() {
  75. ctx.Error(403)
  76. return nil
  77. }
  78. if !ctx.User.IsAdmin {
  79. canCreate, err := org.CanCreateOrgRepo(ctx.User.ID)
  80. if err != nil {
  81. ctx.ServerError("CanCreateOrgRepo", err)
  82. return nil
  83. } else if !canCreate {
  84. ctx.Error(403)
  85. return nil
  86. }
  87. } else {
  88. ctx.Data["Orgs"] = orgs
  89. }
  90. return org
  91. }
  92. func getRepoPrivate(ctx *context.Context) bool {
  93. switch strings.ToLower(setting.Repository.DefaultPrivate) {
  94. case setting.RepoCreatingLastUserVisibility:
  95. return ctx.User.LastRepoVisibility
  96. case setting.RepoCreatingPrivate:
  97. return true
  98. case setting.RepoCreatingPublic:
  99. return false
  100. default:
  101. return ctx.User.LastRepoVisibility
  102. }
  103. }
  104. // Create render creating repository page
  105. func Create(ctx *context.Context) {
  106. ctx.Data["Title"] = ctx.Tr("new_repo")
  107. // Give default value for template to render.
  108. ctx.Data["Gitignores"] = models.Gitignores
  109. ctx.Data["LabelTemplates"] = models.LabelTemplates
  110. ctx.Data["Licenses"] = models.Licenses
  111. ctx.Data["Readmes"] = models.Readmes
  112. ctx.Data["readme"] = "Default"
  113. ctx.Data["private"] = getRepoPrivate(ctx)
  114. ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
  115. ctx.Data["default_branch"] = setting.Repository.DefaultBranch
  116. ctxUser := checkContextUser(ctx, ctx.QueryInt64("org"))
  117. if ctx.Written() {
  118. return
  119. }
  120. ctx.Data["ContextUser"] = ctxUser
  121. ctx.Data["repo_template_name"] = ctx.Tr("repo.template_select")
  122. templateID := ctx.QueryInt64("template_id")
  123. if templateID > 0 {
  124. templateRepo, err := models.GetRepositoryByID(templateID)
  125. if err == nil && templateRepo.CheckUnitUser(ctxUser, models.UnitTypeCode) {
  126. ctx.Data["repo_template"] = templateID
  127. ctx.Data["repo_template_name"] = templateRepo.Name
  128. }
  129. }
  130. if !ctx.User.CanCreateRepo() {
  131. ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", ctx.User.MaxCreationLimit()), tplCreate, nil)
  132. } else {
  133. ctx.HTML(200, tplCreate)
  134. }
  135. }
  136. func handleCreateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form interface{}) {
  137. switch {
  138. case models.IsErrReachLimitOfRepo(err):
  139. ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form)
  140. case models.IsErrRepoAlreadyExist(err):
  141. ctx.Data["Err_RepoName"] = true
  142. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
  143. case models.IsErrRepoFilesAlreadyExist(err):
  144. ctx.Data["Err_RepoName"] = true
  145. switch {
  146. case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories):
  147. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"), tpl, form)
  148. case setting.Repository.AllowAdoptionOfUnadoptedRepositories:
  149. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt"), tpl, form)
  150. case setting.Repository.AllowDeleteOfUnadoptedRepositories:
  151. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.delete"), tpl, form)
  152. default:
  153. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tpl, form)
  154. }
  155. case models.IsErrNameReserved(err):
  156. ctx.Data["Err_RepoName"] = true
  157. ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
  158. case models.IsErrNamePatternNotAllowed(err):
  159. ctx.Data["Err_RepoName"] = true
  160. ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
  161. default:
  162. ctx.ServerError(name, err)
  163. }
  164. }
  165. // CreatePost response for creating repository
  166. func CreatePost(ctx *context.Context, form auth.CreateRepoForm) {
  167. ctx.Data["Title"] = ctx.Tr("new_repo")
  168. ctx.Data["Gitignores"] = models.Gitignores
  169. ctx.Data["LabelTemplates"] = models.LabelTemplates
  170. ctx.Data["Licenses"] = models.Licenses
  171. ctx.Data["Readmes"] = models.Readmes
  172. ctxUser := checkContextUser(ctx, form.UID)
  173. if ctx.Written() {
  174. return
  175. }
  176. ctx.Data["ContextUser"] = ctxUser
  177. if ctx.HasError() {
  178. ctx.HTML(200, tplCreate)
  179. return
  180. }
  181. var repo *models.Repository
  182. var err error
  183. if form.RepoTemplate > 0 {
  184. opts := models.GenerateRepoOptions{
  185. Name: form.RepoName,
  186. Description: form.Description,
  187. Private: form.Private,
  188. GitContent: form.GitContent,
  189. Topics: form.Topics,
  190. GitHooks: form.GitHooks,
  191. Webhooks: form.Webhooks,
  192. Avatar: form.Avatar,
  193. IssueLabels: form.Labels,
  194. }
  195. if !opts.IsValid() {
  196. ctx.RenderWithErr(ctx.Tr("repo.template.one_item"), tplCreate, form)
  197. return
  198. }
  199. templateRepo := getRepository(ctx, form.RepoTemplate)
  200. if ctx.Written() {
  201. return
  202. }
  203. if !templateRepo.IsTemplate {
  204. ctx.RenderWithErr(ctx.Tr("repo.template.invalid"), tplCreate, form)
  205. return
  206. }
  207. repo, err = repo_service.GenerateRepository(ctx.User, ctxUser, templateRepo, opts)
  208. if err == nil {
  209. log.Trace("Repository generated [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
  210. ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + repo.Name)
  211. return
  212. }
  213. } else {
  214. repo, err = repo_service.CreateRepository(ctx.User, ctxUser, models.CreateRepoOptions{
  215. Name: form.RepoName,
  216. Description: form.Description,
  217. Gitignores: form.Gitignores,
  218. IssueLabels: form.IssueLabels,
  219. License: form.License,
  220. Readme: form.Readme,
  221. IsPrivate: form.Private || setting.Repository.ForcePrivate,
  222. DefaultBranch: form.DefaultBranch,
  223. AutoInit: form.AutoInit,
  224. IsTemplate: form.Template,
  225. TrustModel: models.ToTrustModel(form.TrustModel),
  226. })
  227. if err == nil {
  228. log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
  229. ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + repo.Name)
  230. return
  231. }
  232. }
  233. handleCreateError(ctx, ctxUser, err, "CreatePost", tplCreate, &form)
  234. }
  235. // Action response for actions to a repository
  236. func Action(ctx *context.Context) {
  237. var err error
  238. switch ctx.Params(":action") {
  239. case "watch":
  240. err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, true)
  241. case "unwatch":
  242. err = models.WatchRepo(ctx.User.ID, ctx.Repo.Repository.ID, false)
  243. case "star":
  244. err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, true)
  245. case "unstar":
  246. err = models.StarRepo(ctx.User.ID, ctx.Repo.Repository.ID, false)
  247. case "desc": // FIXME: this is not used
  248. if !ctx.Repo.IsOwner() {
  249. ctx.Error(404)
  250. return
  251. }
  252. ctx.Repo.Repository.Description = ctx.Query("desc")
  253. ctx.Repo.Repository.Website = ctx.Query("site")
  254. err = models.UpdateRepository(ctx.Repo.Repository, false)
  255. }
  256. if err != nil {
  257. ctx.ServerError(fmt.Sprintf("Action (%s)", ctx.Params(":action")), err)
  258. return
  259. }
  260. ctx.RedirectToFirst(ctx.Query("redirect_to"), ctx.Repo.RepoLink)
  261. }
  262. // RedirectDownload return a file based on the following infos:
  263. func RedirectDownload(ctx *context.Context) {
  264. var (
  265. vTag = ctx.Params("vTag")
  266. fileName = ctx.Params("fileName")
  267. )
  268. tagNames := []string{vTag}
  269. curRepo := ctx.Repo.Repository
  270. releases, err := models.GetReleasesByRepoIDAndNames(models.DefaultDBContext(), curRepo.ID, tagNames)
  271. if err != nil {
  272. if models.IsErrAttachmentNotExist(err) {
  273. ctx.Error(404)
  274. return
  275. }
  276. ctx.ServerError("RedirectDownload", err)
  277. return
  278. }
  279. if len(releases) == 1 {
  280. release := releases[0]
  281. att, err := models.GetAttachmentByReleaseIDFileName(release.ID, fileName)
  282. if err != nil {
  283. ctx.Error(404)
  284. return
  285. }
  286. if att != nil {
  287. ctx.Redirect(att.DownloadURL())
  288. return
  289. }
  290. }
  291. ctx.Error(404)
  292. }
  293. // Download download an archive of a repository
  294. func Download(ctx *context.Context) {
  295. var (
  296. uri = ctx.Params("*")
  297. refName string
  298. ext string
  299. archivePath string
  300. archiveType git.ArchiveType
  301. )
  302. switch {
  303. case strings.HasSuffix(uri, ".zip"):
  304. ext = ".zip"
  305. archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/zip")
  306. archiveType = git.ZIP
  307. case strings.HasSuffix(uri, ".tar.gz"):
  308. ext = ".tar.gz"
  309. archivePath = path.Join(ctx.Repo.GitRepo.Path, "archives/targz")
  310. archiveType = git.TARGZ
  311. default:
  312. log.Trace("Unknown format: %s", uri)
  313. ctx.Error(404)
  314. return
  315. }
  316. refName = strings.TrimSuffix(uri, ext)
  317. if !com.IsDir(archivePath) {
  318. if err := os.MkdirAll(archivePath, os.ModePerm); err != nil {
  319. ctx.ServerError("Download -> os.MkdirAll(archivePath)", err)
  320. return
  321. }
  322. }
  323. // Get corresponding commit.
  324. var (
  325. commit *git.Commit
  326. err error
  327. )
  328. gitRepo := ctx.Repo.GitRepo
  329. if gitRepo.IsBranchExist(refName) {
  330. commit, err = gitRepo.GetBranchCommit(refName)
  331. if err != nil {
  332. ctx.ServerError("GetBranchCommit", err)
  333. return
  334. }
  335. } else if gitRepo.IsTagExist(refName) {
  336. commit, err = gitRepo.GetTagCommit(refName)
  337. if err != nil {
  338. ctx.ServerError("GetTagCommit", err)
  339. return
  340. }
  341. } else if len(refName) >= 4 && len(refName) <= 40 {
  342. commit, err = gitRepo.GetCommit(refName)
  343. if err != nil {
  344. ctx.NotFound("GetCommit", nil)
  345. return
  346. }
  347. } else {
  348. ctx.NotFound("Download", nil)
  349. return
  350. }
  351. archivePath = path.Join(archivePath, base.ShortSha(commit.ID.String())+ext)
  352. if !com.IsFile(archivePath) {
  353. if err := commit.CreateArchive(ctx.Req.Context(), archivePath, git.CreateArchiveOpts{
  354. Format: archiveType,
  355. Prefix: setting.Repository.PrefixArchiveFiles,
  356. }); err != nil {
  357. ctx.ServerError("Download -> CreateArchive "+archivePath, err)
  358. return
  359. }
  360. }
  361. ctx.ServeFile(archivePath, ctx.Repo.Repository.Name+"-"+refName+ext)
  362. }