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.

migrate.go 7.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  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. "net/http"
  8. "strings"
  9. "code.gitea.io/gitea/models"
  10. "code.gitea.io/gitea/modules/base"
  11. "code.gitea.io/gitea/modules/context"
  12. auth "code.gitea.io/gitea/modules/forms"
  13. "code.gitea.io/gitea/modules/log"
  14. "code.gitea.io/gitea/modules/migrations"
  15. "code.gitea.io/gitea/modules/setting"
  16. "code.gitea.io/gitea/modules/structs"
  17. "code.gitea.io/gitea/modules/task"
  18. "code.gitea.io/gitea/modules/util"
  19. "code.gitea.io/gitea/modules/web"
  20. )
  21. const (
  22. tplMigrate base.TplName = "repo/migrate/migrate"
  23. )
  24. // Migrate render migration of repository page
  25. func Migrate(ctx *context.Context) {
  26. if setting.Repository.DisableMigrations {
  27. ctx.Error(http.StatusForbidden, "Migrate: the site administrator has disabled migrations")
  28. return
  29. }
  30. serviceType := structs.GitServiceType(ctx.QueryInt("service_type"))
  31. setMigrationContextData(ctx, serviceType)
  32. if serviceType == 0 {
  33. ctx.Data["Org"] = ctx.Query("org")
  34. ctx.Data["Mirror"] = ctx.Query("mirror")
  35. ctx.HTML(http.StatusOK, tplMigrate)
  36. return
  37. }
  38. ctx.Data["private"] = getRepoPrivate(ctx)
  39. ctx.Data["mirror"] = ctx.Query("mirror") == "1"
  40. ctx.Data["wiki"] = ctx.Query("wiki") == "1"
  41. ctx.Data["milestones"] = ctx.Query("milestones") == "1"
  42. ctx.Data["labels"] = ctx.Query("labels") == "1"
  43. ctx.Data["issues"] = ctx.Query("issues") == "1"
  44. ctx.Data["pull_requests"] = ctx.Query("pull_requests") == "1"
  45. ctx.Data["releases"] = ctx.Query("releases") == "1"
  46. ctxUser := checkContextUser(ctx, ctx.QueryInt64("org"))
  47. if ctx.Written() {
  48. return
  49. }
  50. ctx.Data["ContextUser"] = ctxUser
  51. ctx.HTML(http.StatusOK, base.TplName("repo/migrate/"+serviceType.Name()))
  52. }
  53. func handleMigrateError(ctx *context.Context, owner *models.User, err error, name string, tpl base.TplName, form *auth.MigrateRepoForm) {
  54. if setting.Repository.DisableMigrations {
  55. ctx.Error(http.StatusForbidden, "MigrateError: the site administrator has disabled migrations")
  56. return
  57. }
  58. switch {
  59. case migrations.IsRateLimitError(err):
  60. ctx.RenderWithErr(ctx.Tr("form.visit_rate_limit"), tpl, form)
  61. case migrations.IsTwoFactorAuthError(err):
  62. ctx.RenderWithErr(ctx.Tr("form.2fa_auth_required"), tpl, form)
  63. case models.IsErrReachLimitOfRepo(err):
  64. ctx.RenderWithErr(ctx.Tr("repo.form.reach_limit_of_creation", owner.MaxCreationLimit()), tpl, form)
  65. case models.IsErrRepoAlreadyExist(err):
  66. ctx.Data["Err_RepoName"] = true
  67. ctx.RenderWithErr(ctx.Tr("form.repo_name_been_taken"), tpl, form)
  68. case models.IsErrRepoFilesAlreadyExist(err):
  69. ctx.Data["Err_RepoName"] = true
  70. switch {
  71. case ctx.IsUserSiteAdmin() || (setting.Repository.AllowAdoptionOfUnadoptedRepositories && setting.Repository.AllowDeleteOfUnadoptedRepositories):
  72. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt_or_delete"), tpl, form)
  73. case setting.Repository.AllowAdoptionOfUnadoptedRepositories:
  74. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.adopt"), tpl, form)
  75. case setting.Repository.AllowDeleteOfUnadoptedRepositories:
  76. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist.delete"), tpl, form)
  77. default:
  78. ctx.RenderWithErr(ctx.Tr("form.repository_files_already_exist"), tpl, form)
  79. }
  80. case models.IsErrNameReserved(err):
  81. ctx.Data["Err_RepoName"] = true
  82. ctx.RenderWithErr(ctx.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
  83. case models.IsErrNamePatternNotAllowed(err):
  84. ctx.Data["Err_RepoName"] = true
  85. ctx.RenderWithErr(ctx.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
  86. default:
  87. remoteAddr, _ := auth.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword)
  88. err = util.URLSanitizedError(err, remoteAddr)
  89. if strings.Contains(err.Error(), "Authentication failed") ||
  90. strings.Contains(err.Error(), "Bad credentials") ||
  91. strings.Contains(err.Error(), "could not read Username") {
  92. ctx.Data["Err_Auth"] = true
  93. ctx.RenderWithErr(ctx.Tr("form.auth_failed", err.Error()), tpl, form)
  94. } else if strings.Contains(err.Error(), "fatal:") {
  95. ctx.Data["Err_CloneAddr"] = true
  96. ctx.RenderWithErr(ctx.Tr("repo.migrate.failed", err.Error()), tpl, form)
  97. } else {
  98. ctx.ServerError(name, err)
  99. }
  100. }
  101. }
  102. // MigratePost response for migrating from external git repository
  103. func MigratePost(ctx *context.Context) {
  104. form := web.GetForm(ctx).(*auth.MigrateRepoForm)
  105. if setting.Repository.DisableMigrations {
  106. ctx.Error(http.StatusForbidden, "MigratePost: the site administrator has disabled migrations")
  107. return
  108. }
  109. serviceType := structs.GitServiceType(form.Service)
  110. setMigrationContextData(ctx, serviceType)
  111. ctxUser := checkContextUser(ctx, form.UID)
  112. if ctx.Written() {
  113. return
  114. }
  115. ctx.Data["ContextUser"] = ctxUser
  116. tpl := base.TplName("repo/migrate/" + serviceType.Name())
  117. if ctx.HasError() {
  118. ctx.HTML(http.StatusOK, tpl)
  119. return
  120. }
  121. remoteAddr, err := auth.ParseRemoteAddr(form.CloneAddr, form.AuthUsername, form.AuthPassword)
  122. if err == nil {
  123. err = migrations.IsMigrateURLAllowed(remoteAddr, ctx.User)
  124. }
  125. if err != nil {
  126. if models.IsErrInvalidCloneAddr(err) {
  127. ctx.Data["Err_CloneAddr"] = true
  128. addrErr := err.(*models.ErrInvalidCloneAddr)
  129. switch {
  130. case addrErr.IsProtocolInvalid:
  131. ctx.RenderWithErr(ctx.Tr("repo.mirror_address_protocol_invalid"), tpl, &form)
  132. case addrErr.IsURLError:
  133. ctx.RenderWithErr(ctx.Tr("form.url_error"), tpl, &form)
  134. case addrErr.IsPermissionDenied:
  135. if addrErr.LocalPath {
  136. ctx.RenderWithErr(ctx.Tr("repo.migrate.permission_denied"), tpl, &form)
  137. } else if len(addrErr.PrivateNet) == 0 {
  138. ctx.RenderWithErr(ctx.Tr("repo.migrate.permission_denied_blocked"), tpl, &form)
  139. } else {
  140. ctx.RenderWithErr(ctx.Tr("repo.migrate.permission_denied_private_ip"), tpl, &form)
  141. }
  142. case addrErr.IsInvalidPath:
  143. ctx.RenderWithErr(ctx.Tr("repo.migrate.invalid_local_path"), tpl, &form)
  144. default:
  145. log.Error("Error whilst updating url: %v", err)
  146. ctx.RenderWithErr(ctx.Tr("form.url_error"), tpl, &form)
  147. }
  148. } else {
  149. log.Error("Error whilst updating url: %v", err)
  150. ctx.RenderWithErr(ctx.Tr("form.url_error"), tpl, &form)
  151. }
  152. return
  153. }
  154. var opts = migrations.MigrateOptions{
  155. OriginalURL: form.CloneAddr,
  156. GitServiceType: serviceType,
  157. CloneAddr: remoteAddr,
  158. RepoName: form.RepoName,
  159. Description: form.Description,
  160. Private: form.Private || setting.Repository.ForcePrivate,
  161. Mirror: form.Mirror && !setting.Repository.DisableMirrors,
  162. AuthUsername: form.AuthUsername,
  163. AuthPassword: form.AuthPassword,
  164. AuthToken: form.AuthToken,
  165. Wiki: form.Wiki,
  166. Issues: form.Issues,
  167. Milestones: form.Milestones,
  168. Labels: form.Labels,
  169. Comments: form.Issues || form.PullRequests,
  170. PullRequests: form.PullRequests,
  171. Releases: form.Releases,
  172. }
  173. if opts.Mirror {
  174. opts.Issues = false
  175. opts.Milestones = false
  176. opts.Labels = false
  177. opts.Comments = false
  178. opts.PullRequests = false
  179. opts.Releases = false
  180. }
  181. err = models.CheckCreateRepository(ctx.User, ctxUser, opts.RepoName, false)
  182. if err != nil {
  183. handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, form)
  184. return
  185. }
  186. err = task.MigrateRepository(ctx.User, ctxUser, opts)
  187. if err == nil {
  188. ctx.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + opts.RepoName)
  189. return
  190. }
  191. handleMigrateError(ctx, ctxUser, err, "MigratePost", tpl, form)
  192. }
  193. func setMigrationContextData(ctx *context.Context, serviceType structs.GitServiceType) {
  194. ctx.Data["Title"] = ctx.Tr("new_migrate")
  195. ctx.Data["LFSActive"] = setting.LFS.StartServer
  196. ctx.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
  197. ctx.Data["DisableMirrors"] = setting.Repository.DisableMirrors
  198. // Plain git should be first
  199. ctx.Data["Services"] = append([]structs.GitServiceType{structs.PlainGitService}, structs.SupportedFullGitService...)
  200. ctx.Data["service"] = serviceType
  201. }