* Move restore repo to internal router and invoke from command to avoid open the same db file or queues files
* Follow @zeripath's review
* set no timeout for resotre repo private request
* make restore repo cancelable
package cmd
import (
- "context"
- "strings"
+ "errors"
+ "net/http"
"code.gitea.io/gitea/modules/log"
- "code.gitea.io/gitea/modules/migrations"
- "code.gitea.io/gitea/modules/migrations/base"
+ "code.gitea.io/gitea/modules/private"
"code.gitea.io/gitea/modules/setting"
- "code.gitea.io/gitea/modules/storage"
- pull_service "code.gitea.io/gitea/services/pull"
"github.com/urfave/cli"
)
}
func runRestoreRepository(ctx *cli.Context) error {
- if err := initDB(); err != nil {
- return err
- }
-
- log.Trace("AppPath: %s", setting.AppPath)
- log.Trace("AppWorkPath: %s", setting.AppWorkPath)
- log.Trace("Custom path: %s", setting.CustomPath)
- log.Trace("Log path: %s", setting.LogRootPath)
- setting.InitDBConfig()
-
- if err := storage.Init(); err != nil {
- return err
- }
-
- if err := pull_service.Init(); err != nil {
- return err
- }
-
- var opts = base.MigrateOptions{
- RepoName: ctx.String("repo_name"),
- }
-
- if len(ctx.String("units")) == 0 {
- opts.Wiki = true
- opts.Issues = true
- opts.Milestones = true
- opts.Labels = true
- opts.Releases = true
- opts.Comments = true
- opts.PullRequests = true
- opts.ReleaseAssets = true
- } else {
- units := strings.Split(ctx.String("units"), ",")
- for _, unit := range units {
- switch strings.ToLower(unit) {
- case "wiki":
- opts.Wiki = true
- case "issues":
- opts.Issues = true
- case "milestones":
- opts.Milestones = true
- case "labels":
- opts.Labels = true
- case "releases":
- opts.Releases = true
- case "release_assets":
- opts.ReleaseAssets = true
- case "comments":
- opts.Comments = true
- case "pull_requests":
- opts.PullRequests = true
- }
- }
- }
+ setting.NewContext()
- if err := migrations.RestoreRepository(
- context.Background(),
+ statusCode, errStr := private.RestoreRepo(
ctx.String("repo_dir"),
ctx.String("owner_name"),
ctx.String("repo_name"),
- ); err != nil {
- log.Fatal("Failed to restore repository: %v", err)
- return err
+ ctx.StringSlice("units"),
+ )
+ if statusCode == http.StatusOK {
+ return nil
}
- return nil
+ log.Fatal("Failed to restore repository: %v", errStr)
+ return errors.New(errStr)
}
"os"
"path/filepath"
"strconv"
+ "strings"
"time"
"code.gitea.io/gitea/models"
return nil
}
+func updateOptionsUnits(opts *base.MigrateOptions, units []string) {
+ if len(units) == 0 {
+ opts.Wiki = true
+ opts.Issues = true
+ opts.Milestones = true
+ opts.Labels = true
+ opts.Releases = true
+ opts.Comments = true
+ opts.PullRequests = true
+ opts.ReleaseAssets = true
+ } else {
+ for _, unit := range units {
+ switch strings.ToLower(unit) {
+ case "wiki":
+ opts.Wiki = true
+ case "issues":
+ opts.Issues = true
+ case "milestones":
+ opts.Milestones = true
+ case "labels":
+ opts.Labels = true
+ case "releases":
+ opts.Releases = true
+ case "release_assets":
+ opts.ReleaseAssets = true
+ case "comments":
+ opts.Comments = true
+ case "pull_requests":
+ opts.PullRequests = true
+ }
+ }
+ }
+}
+
// RestoreRepository restore a repository from the disk directory
-func RestoreRepository(ctx context.Context, baseDir string, ownerName, repoName string) error {
+func RestoreRepository(ctx context.Context, baseDir string, ownerName, repoName string, units []string) error {
doer, err := models.GetAdminUser()
if err != nil {
return err
}
tp, _ := strconv.Atoi(opts["service_type"])
- if err = migrateRepository(downloader, uploader, base.MigrateOptions{
- Wiki: true,
- Issues: true,
- Milestones: true,
- Labels: true,
- Releases: true,
- Comments: true,
- PullRequests: true,
- ReleaseAssets: true,
+ var migrateOpts = base.MigrateOptions{
GitServiceType: structs.GitServiceType(tp),
- }); err != nil {
+ }
+ updateOptionsUnits(&migrateOpts, units)
+
+ if err = migrateRepository(downloader, uploader, migrateOpts); err != nil {
if err1 := uploader.Rollback(); err1 != nil {
log.Error("rollback failed: %v", err1)
}
--- /dev/null
+// Copyright 2020 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package private
+
+import (
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "time"
+
+ "code.gitea.io/gitea/modules/setting"
+ jsoniter "github.com/json-iterator/go"
+)
+
+// RestoreParams structure holds a data for restore repository
+type RestoreParams struct {
+ RepoDir string
+ OwnerName string
+ RepoName string
+ Units []string
+}
+
+// RestoreRepo calls the internal RestoreRepo function
+func RestoreRepo(repoDir, ownerName, repoName string, units []string) (int, string) {
+ reqURL := setting.LocalURL + "api/internal/restore_repo"
+
+ req := newInternalRequest(reqURL, "POST")
+ req.SetTimeout(3*time.Second, 0) // since the request will spend much time, don't timeout
+ req = req.Header("Content-Type", "application/json")
+ json := jsoniter.ConfigCompatibleWithStandardLibrary
+ jsonBytes, _ := json.Marshal(RestoreParams{
+ RepoDir: repoDir,
+ OwnerName: ownerName,
+ RepoName: repoName,
+ Units: units,
+ })
+ req.Body(jsonBytes)
+ resp, err := req.Response()
+ if err != nil {
+ return http.StatusInternalServerError, fmt.Sprintf("Unable to contact gitea: %v, could you confirm it's running?", err.Error())
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ var ret = struct {
+ Err string `json:"err"`
+ }{}
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return http.StatusInternalServerError, fmt.Sprintf("Response body error: %v", err.Error())
+ }
+ if err := json.Unmarshal(body, &ret); err != nil {
+ return http.StatusInternalServerError, fmt.Sprintf("Response body Unmarshal error: %v", err.Error())
+ }
+ }
+
+ return http.StatusOK, fmt.Sprintf("Restore repo %s/%s successfully", ownerName, repoName)
+}
r.Post("/manager/add-logger", bind(private.LoggerOptions{}), AddLogger)
r.Post("/manager/remove-logger/{group}/{name}", RemoveLogger)
r.Post("/mail/send", SendEmail)
+ r.Post("/restore_repo", RestoreRepo)
return r
}
--- /dev/null
+// Copyright 2021 The Gitea Authors. All rights reserved.
+// Use of this source code is governed by a MIT-style
+// license that can be found in the LICENSE file.
+
+package private
+
+import (
+ "io/ioutil"
+
+ myCtx "code.gitea.io/gitea/modules/context"
+ "code.gitea.io/gitea/modules/migrations"
+ jsoniter "github.com/json-iterator/go"
+)
+
+// RestoreRepo restore a repository from data
+func RestoreRepo(ctx *myCtx.PrivateContext) {
+ json := jsoniter.ConfigCompatibleWithStandardLibrary
+ bs, err := ioutil.ReadAll(ctx.Req.Body)
+ if err != nil {
+ ctx.JSON(500, map[string]string{
+ "err": err.Error(),
+ })
+ return
+ }
+ var params = struct {
+ RepoDir string
+ OwnerName string
+ RepoName string
+ Units []string
+ }{}
+ if err = json.Unmarshal(bs, ¶ms); err != nil {
+ ctx.JSON(500, map[string]string{
+ "err": err.Error(),
+ })
+ return
+ }
+
+ if err := migrations.RestoreRepository(
+ ctx.Req.Context(),
+ params.RepoDir,
+ params.OwnerName,
+ params.RepoName,
+ params.Units,
+ ); err != nil {
+ ctx.JSON(500, map[string]string{
+ "err": err.Error(),
+ })
+ } else {
+ ctx.Status(200)
+ }
+}