2020-09-09 21:29:10 +03:00
// Copyright 2014 The Gogs Authors. All rights reserved.
// Copyright 2020 The Gitea Authors. All rights reserved.
2022-11-27 21:20:29 +03:00
// SPDX-License-Identifier: MIT
2020-09-09 21:29:10 +03:00
package repo
import (
2020-12-21 17:39:41 +03:00
"net/http"
2021-11-16 21:18:25 +03:00
"net/url"
2020-09-09 21:29:10 +03:00
"strings"
"code.gitea.io/gitea/models"
2023-05-11 11:25:46 +03:00
admin_model "code.gitea.io/gitea/models/admin"
2021-11-24 12:49:20 +03:00
"code.gitea.io/gitea/models/db"
2021-12-12 18:48:20 +03:00
repo_model "code.gitea.io/gitea/models/repo"
2021-11-24 12:49:20 +03:00
user_model "code.gitea.io/gitea/models/user"
2020-09-09 21:29:10 +03:00
"code.gitea.io/gitea/modules/base"
2021-04-09 01:25:57 +03:00
"code.gitea.io/gitea/modules/lfs"
2021-03-16 00:52:11 +03:00
"code.gitea.io/gitea/modules/log"
2020-09-09 21:29:10 +03:00
"code.gitea.io/gitea/modules/setting"
"code.gitea.io/gitea/modules/structs"
"code.gitea.io/gitea/modules/util"
2021-01-26 18:36:53 +03:00
"code.gitea.io/gitea/modules/web"
2024-02-27 10:12:22 +03:00
"code.gitea.io/gitea/services/context"
2021-04-06 22:44:05 +03:00
"code.gitea.io/gitea/services/forms"
2021-11-16 18:25:33 +03:00
"code.gitea.io/gitea/services/migrations"
2021-11-18 09:47:57 +03:00
"code.gitea.io/gitea/services/task"
2020-09-09 21:29:10 +03:00
)
const (
tplMigrate base . TplName = "repo/migrate/migrate"
)
// Migrate render migration of repository page
func Migrate ( ctx * context . Context ) {
2020-12-21 17:39:41 +03:00
if setting . Repository . DisableMigrations {
ctx . Error ( http . StatusForbidden , "Migrate: the site administrator has disabled migrations" )
return
}
2021-07-29 04:42:15 +03:00
serviceType := structs . GitServiceType ( ctx . FormInt ( "service_type" ) )
2021-03-07 22:21:09 +03:00
setMigrationContextData ( ctx , serviceType )
2020-09-09 21:29:10 +03:00
if serviceType == 0 {
2021-08-11 03:31:13 +03:00
ctx . Data [ "Org" ] = ctx . FormString ( "org" )
ctx . Data [ "Mirror" ] = ctx . FormString ( "mirror" )
2020-10-24 02:01:58 +03:00
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tplMigrate )
2020-09-09 21:29:10 +03:00
return
}
ctx . Data [ "private" ] = getRepoPrivate ( ctx )
2021-08-11 03:31:13 +03:00
ctx . Data [ "mirror" ] = ctx . FormString ( "mirror" ) == "1"
ctx . Data [ "lfs" ] = ctx . FormString ( "lfs" ) == "1"
ctx . Data [ "wiki" ] = ctx . FormString ( "wiki" ) == "1"
ctx . Data [ "milestones" ] = ctx . FormString ( "milestones" ) == "1"
ctx . Data [ "labels" ] = ctx . FormString ( "labels" ) == "1"
ctx . Data [ "issues" ] = ctx . FormString ( "issues" ) == "1"
ctx . Data [ "pull_requests" ] = ctx . FormString ( "pull_requests" ) == "1"
ctx . Data [ "releases" ] = ctx . FormString ( "releases" ) == "1"
2020-09-09 21:29:10 +03:00
2021-07-29 04:42:15 +03:00
ctxUser := checkContextUser ( ctx , ctx . FormInt64 ( "org" ) )
2020-09-09 21:29:10 +03:00
if ctx . Written ( ) {
return
}
ctx . Data [ "ContextUser" ] = ctxUser
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , base . TplName ( "repo/migrate/" + serviceType . Name ( ) ) )
2020-09-09 21:29:10 +03:00
}
2021-11-24 12:49:20 +03:00
func handleMigrateError ( ctx * context . Context , owner * user_model . User , err error , name string , tpl base . TplName , form * forms . MigrateRepoForm ) {
2020-12-21 17:39:41 +03:00
if setting . Repository . DisableMigrations {
ctx . Error ( http . StatusForbidden , "MigrateError: the site administrator has disabled migrations" )
return
}
2020-09-09 21:29:10 +03:00
switch {
case migrations . IsRateLimitError ( err ) :
ctx . RenderWithErr ( ctx . Tr ( "form.visit_rate_limit" ) , tpl , form )
case migrations . IsTwoFactorAuthError ( err ) :
ctx . RenderWithErr ( ctx . Tr ( "form.2fa_auth_required" ) , tpl , form )
2021-12-12 18:48:20 +03:00
case repo_model . IsErrReachLimitOfRepo ( err ) :
2022-01-02 05:38:07 +03:00
maxCreationLimit := owner . MaxCreationLimit ( )
2022-01-02 06:33:57 +03:00
msg := ctx . TrN ( maxCreationLimit , "repo.form.reach_limit_of_creation_1" , "repo.form.reach_limit_of_creation_n" , maxCreationLimit )
2021-12-31 11:43:03 +03:00
ctx . RenderWithErr ( msg , tpl , form )
2021-12-12 18:48:20 +03:00
case repo_model . IsErrRepoAlreadyExist ( err ) :
2020-09-09 21:29:10 +03:00
ctx . Data [ "Err_RepoName" ] = true
ctx . RenderWithErr ( ctx . Tr ( "form.repo_name_been_taken" ) , tpl , form )
2021-12-12 18:48:20 +03:00
case repo_model . IsErrRepoFilesAlreadyExist ( err ) :
2020-09-25 07:09:23 +03:00
ctx . Data [ "Err_RepoName" ] = true
switch {
case ctx . IsUserSiteAdmin ( ) || ( setting . Repository . AllowAdoptionOfUnadoptedRepositories && setting . Repository . AllowDeleteOfUnadoptedRepositories ) :
ctx . RenderWithErr ( ctx . Tr ( "form.repository_files_already_exist.adopt_or_delete" ) , tpl , form )
case setting . Repository . AllowAdoptionOfUnadoptedRepositories :
ctx . RenderWithErr ( ctx . Tr ( "form.repository_files_already_exist.adopt" ) , tpl , form )
case setting . Repository . AllowDeleteOfUnadoptedRepositories :
ctx . RenderWithErr ( ctx . Tr ( "form.repository_files_already_exist.delete" ) , tpl , form )
default :
ctx . RenderWithErr ( ctx . Tr ( "form.repository_files_already_exist" ) , tpl , form )
}
2021-11-24 12:49:20 +03:00
case db . IsErrNameReserved ( err ) :
2020-09-09 21:29:10 +03:00
ctx . Data [ "Err_RepoName" ] = true
2021-11-24 12:49:20 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.form.name_reserved" , err . ( db . ErrNameReserved ) . Name ) , tpl , form )
case db . IsErrNamePatternNotAllowed ( err ) :
2020-09-09 21:29:10 +03:00
ctx . Data [ "Err_RepoName" ] = true
2021-11-24 12:49:20 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.form.name_pattern_not_allowed" , err . ( db . ErrNamePatternNotAllowed ) . Pattern ) , tpl , form )
2020-09-09 21:29:10 +03:00
default :
2022-03-31 05:25:40 +03:00
err = util . SanitizeErrorCredentialURLs ( err )
2020-09-09 21:29:10 +03:00
if strings . Contains ( err . Error ( ) , "Authentication failed" ) ||
strings . Contains ( err . Error ( ) , "Bad credentials" ) ||
strings . Contains ( err . Error ( ) , "could not read Username" ) {
ctx . Data [ "Err_Auth" ] = true
ctx . RenderWithErr ( ctx . Tr ( "form.auth_failed" , err . Error ( ) ) , tpl , form )
} else if strings . Contains ( err . Error ( ) , "fatal:" ) {
ctx . Data [ "Err_CloneAddr" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.migrate.failed" , err . Error ( ) ) , tpl , form )
} else {
ctx . ServerError ( name , err )
}
}
}
2021-04-09 01:25:57 +03:00
func handleMigrateRemoteAddrError ( ctx * context . Context , err error , tpl base . TplName , form * forms . MigrateRepoForm ) {
if models . IsErrInvalidCloneAddr ( err ) {
addrErr := err . ( * models . ErrInvalidCloneAddr )
switch {
case addrErr . IsProtocolInvalid :
ctx . RenderWithErr ( ctx . Tr ( "repo.mirror_address_protocol_invalid" ) , tpl , form )
case addrErr . IsURLError :
2022-06-12 08:43:27 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.url_error" , addrErr . Host ) , tpl , form )
2021-04-09 01:25:57 +03:00
case addrErr . IsPermissionDenied :
if addrErr . LocalPath {
ctx . RenderWithErr ( ctx . Tr ( "repo.migrate.permission_denied" ) , tpl , form )
} else {
2021-11-20 12:34:05 +03:00
ctx . RenderWithErr ( ctx . Tr ( "repo.migrate.permission_denied_blocked" ) , tpl , form )
2021-04-09 01:25:57 +03:00
}
case addrErr . IsInvalidPath :
ctx . RenderWithErr ( ctx . Tr ( "repo.migrate.invalid_local_path" ) , tpl , form )
default :
log . Error ( "Error whilst updating url: %v" , err )
2022-06-12 08:43:27 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.url_error" , "unknown" ) , tpl , form )
2021-04-09 01:25:57 +03:00
}
} else {
log . Error ( "Error whilst updating url: %v" , err )
2022-06-12 08:43:27 +03:00
ctx . RenderWithErr ( ctx . Tr ( "form.url_error" , "unknown" ) , tpl , form )
2021-04-09 01:25:57 +03:00
}
}
2020-09-09 21:29:10 +03:00
// MigratePost response for migrating from external git repository
2021-01-26 18:36:53 +03:00
func MigratePost ( ctx * context . Context ) {
2021-04-06 22:44:05 +03:00
form := web . GetForm ( ctx ) . ( * forms . MigrateRepoForm )
2020-12-21 17:39:41 +03:00
if setting . Repository . DisableMigrations {
ctx . Error ( http . StatusForbidden , "MigratePost: the site administrator has disabled migrations" )
return
}
2021-09-07 18:49:36 +03:00
if form . Mirror && setting . Mirror . DisableNewPull {
ctx . Error ( http . StatusBadRequest , "MigratePost: the site administrator has disabled creation of new mirrors" )
return
}
2020-09-09 21:29:10 +03:00
2021-09-07 18:49:36 +03:00
setMigrationContextData ( ctx , form . Service )
2020-09-23 23:25:46 +03:00
2020-09-09 21:29:10 +03:00
ctxUser := checkContextUser ( ctx , form . UID )
if ctx . Written ( ) {
return
}
ctx . Data [ "ContextUser" ] = ctxUser
2021-09-07 18:49:36 +03:00
tpl := base . TplName ( "repo/migrate/" + form . Service . Name ( ) )
2021-03-07 22:21:09 +03:00
2020-09-09 21:29:10 +03:00
if ctx . HasError ( ) {
2021-04-05 18:30:52 +03:00
ctx . HTML ( http . StatusOK , tpl )
2020-09-09 21:29:10 +03:00
return
}
2021-04-06 22:44:05 +03:00
remoteAddr , err := forms . ParseRemoteAddr ( form . CloneAddr , form . AuthUsername , form . AuthPassword )
2021-03-16 00:52:11 +03:00
if err == nil {
2022-03-22 10:03:22 +03:00
err = migrations . IsMigrateURLAllowed ( remoteAddr , ctx . Doer )
2021-03-16 00:52:11 +03:00
}
2020-09-09 21:29:10 +03:00
if err != nil {
2021-04-09 01:25:57 +03:00
ctx . Data [ "Err_CloneAddr" ] = true
handleMigrateRemoteAddrError ( ctx , err , tpl , form )
2020-09-09 21:29:10 +03:00
return
}
2021-04-09 01:25:57 +03:00
form . LFS = form . LFS && setting . LFS . StartServer
if form . LFS && len ( form . LFSEndpoint ) > 0 {
ep := lfs . DetermineEndpoint ( "" , form . LFSEndpoint )
if ep == nil {
ctx . Data [ "Err_LFSEndpoint" ] = true
ctx . RenderWithErr ( ctx . Tr ( "repo.migrate.invalid_lfs_endpoint" ) , tpl , & form )
return
}
2022-03-22 10:03:22 +03:00
err = migrations . IsMigrateURLAllowed ( ep . String ( ) , ctx . Doer )
2021-04-09 01:25:57 +03:00
if err != nil {
ctx . Data [ "Err_LFSEndpoint" ] = true
handleMigrateRemoteAddrError ( ctx , err , tpl , form )
return
}
}
2022-01-20 20:46:10 +03:00
opts := migrations . MigrateOptions {
2020-09-09 21:29:10 +03:00
OriginalURL : form . CloneAddr ,
2021-09-07 18:49:36 +03:00
GitServiceType : form . Service ,
2020-09-09 21:29:10 +03:00
CloneAddr : remoteAddr ,
RepoName : form . RepoName ,
Description : form . Description ,
Private : form . Private || setting . Repository . ForcePrivate ,
2021-09-07 18:49:36 +03:00
Mirror : form . Mirror ,
2021-04-09 01:25:57 +03:00
LFS : form . LFS ,
LFSEndpoint : form . LFSEndpoint ,
2020-09-09 21:29:10 +03:00
AuthUsername : form . AuthUsername ,
AuthPassword : form . AuthPassword ,
AuthToken : form . AuthToken ,
Wiki : form . Wiki ,
Issues : form . Issues ,
Milestones : form . Milestones ,
Labels : form . Labels ,
Comments : form . Issues || form . PullRequests ,
PullRequests : form . PullRequests ,
Releases : form . Releases ,
}
if opts . Mirror {
opts . Issues = false
opts . Milestones = false
opts . Labels = false
opts . Comments = false
opts . PullRequests = false
opts . Releases = false
}
2023-09-16 17:39:12 +03:00
err = repo_model . CheckCreateRepository ( ctx , ctx . Doer , ctxUser , opts . RepoName , false )
2020-09-09 21:29:10 +03:00
if err != nil {
2021-01-26 18:36:53 +03:00
handleMigrateError ( ctx , ctxUser , err , "MigratePost" , tpl , form )
2020-09-09 21:29:10 +03:00
return
}
2023-09-16 17:39:12 +03:00
err = task . MigrateRepository ( ctx , ctx . Doer , ctxUser , opts )
2020-09-09 21:29:10 +03:00
if err == nil {
2021-11-16 21:18:25 +03:00
ctx . Redirect ( ctxUser . HomeLink ( ) + "/" + url . PathEscape ( opts . RepoName ) )
2020-09-09 21:29:10 +03:00
return
}
2021-01-26 18:36:53 +03:00
handleMigrateError ( ctx , ctxUser , err , "MigratePost" , tpl , form )
2020-09-09 21:29:10 +03:00
}
2021-03-07 22:21:09 +03:00
func setMigrationContextData ( ctx * context . Context , serviceType structs . GitServiceType ) {
ctx . Data [ "Title" ] = ctx . Tr ( "new_migrate" )
ctx . Data [ "LFSActive" ] = setting . LFS . StartServer
ctx . Data [ "IsForcedPrivate" ] = setting . Repository . ForcePrivate
2021-09-07 18:49:36 +03:00
ctx . Data [ "DisableNewPullMirrors" ] = setting . Mirror . DisableNewPull
2021-03-07 22:21:09 +03:00
// Plain git should be first
ctx . Data [ "Services" ] = append ( [ ] structs . GitServiceType { structs . PlainGitService } , structs . SupportedFullGitService ... )
ctx . Data [ "service" ] = serviceType
}
2023-05-11 11:25:46 +03:00
2023-08-04 05:21:32 +03:00
func MigrateRetryPost ( ctx * context . Context ) {
2023-09-16 17:39:12 +03:00
if err := task . RetryMigrateTask ( ctx , ctx . Repo . Repository . ID ) ; err != nil {
2023-08-04 05:21:32 +03:00
log . Error ( "Retry task failed: %v" , err )
ctx . ServerError ( "task.RetryMigrateTask" , err )
return
}
ctx . JSONOK ( )
}
2023-05-11 11:25:46 +03:00
func MigrateCancelPost ( ctx * context . Context ) {
2023-09-16 17:39:12 +03:00
migratingTask , err := admin_model . GetMigratingTask ( ctx , ctx . Repo . Repository . ID )
2023-05-11 11:25:46 +03:00
if err != nil {
log . Error ( "GetMigratingTask: %v" , err )
ctx . Redirect ( ctx . Repo . Repository . Link ( ) )
return
}
if migratingTask . Status == structs . TaskStatusRunning {
taskUpdate := & admin_model . Task { ID : migratingTask . ID , Status : structs . TaskStatusFailed , Message : "canceled" }
2023-09-16 17:39:12 +03:00
if err = taskUpdate . UpdateCols ( ctx , "status" , "message" ) ; err != nil {
2023-05-11 11:25:46 +03:00
ctx . ServerError ( "task.UpdateCols" , err )
return
}
}
ctx . Redirect ( ctx . Repo . Repository . Link ( ) )
}