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 8.6KB

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