diff --git a/internal/models/gist.go b/internal/models/gist.go index db8d570..98cb27d 100644 --- a/internal/models/gist.go +++ b/internal/models/gist.go @@ -93,6 +93,15 @@ func GetAllGistsFromUser(fromUser string, currentUserId uint, offset int, sort s return gists, err } +func GetAllGistsRows() ([]*Gist, error) { + var gists []*Gist + err := db.Table("gists"). + Preload("User"). + Find(&gists).Error + + return gists, err +} + func (gist *Gist) Create() error { // avoids foreign key constraint error because the default value in the struct is 0 return db.Omit("forked_id").Create(&gist).Error diff --git a/internal/web/admin.go b/internal/web/admin.go index e4db488..c637511 100644 --- a/internal/web/admin.go +++ b/internal/web/admin.go @@ -2,11 +2,20 @@ package web import ( "github.com/labstack/echo/v4" + "github.com/rs/zerolog/log" "opengist/internal/config" "opengist/internal/git" "opengist/internal/models" + "os" + "path/filepath" "runtime" "strconv" + "strings" +) + +var ( + syncReposFromFS = false + syncReposFromDB = false ) func adminIndex(ctx echo.Context) error { @@ -39,6 +48,8 @@ func adminIndex(ctx echo.Context) error { } setData(ctx, "countKeys", countKeys) + setData(ctx, "syncReposFromFS", syncReposFromFS) + setData(ctx, "syncReposFromDB", syncReposFromDB) return html(ctx, "admin_index.html") } @@ -109,5 +120,65 @@ func adminGistDelete(ctx echo.Context) error { addFlash(ctx, "Gist has been deleted", "success") return redirect(ctx, "/admin/gists") - +} + +func adminSyncReposFromFS(ctx echo.Context) error { + addFlash(ctx, "Syncing repositories from filesystem...", "success") + go func() { + if syncReposFromFS { + return + } + syncReposFromFS = true + + gists, err := models.GetAllGistsRows() + if err != nil { + log.Error().Err(err).Msg("Cannot get gists") + syncReposFromFS = false + return + } + for _, gist := range gists { + // if repository does not exist, delete gist from database + if _, err := os.Stat(git.RepositoryPath(gist.User.Username, gist.Uuid)); err != nil && !os.IsExist(err) { + if err2 := gist.Delete(); err2 != nil { + log.Error().Err(err2).Msg("Cannot delete gist") + syncReposFromFS = false + return + } + } + } + syncReposFromFS = false + }() + return redirect(ctx, "/admin") +} + +func adminSyncReposFromDB(ctx echo.Context) error { + addFlash(ctx, "Syncing repositories from database...", "success") + go func() { + if syncReposFromDB { + return + } + syncReposFromDB = true + entries, err := filepath.Glob(filepath.Join(config.GetHomeDir(), "repos", "*", "*")) + if err != nil { + log.Error().Err(err).Msg("Cannot read repos directories") + syncReposFromDB = false + return + } + + for _, e := range entries { + path := strings.Split(e, string(os.PathSeparator)) + gist, _ := models.GetGist(path[len(path)-2], path[len(path)-1]) + + if gist.ID == 0 { + if err := git.DeleteRepository(path[len(path)-2], path[len(path)-1]); err != nil { + log.Error().Err(err).Msg("Cannot delete repository") + syncReposFromDB = false + return + } + } + } + syncReposFromDB = false + return + }() + return redirect(ctx, "/admin") } diff --git a/internal/web/run.go b/internal/web/run.go index bc00dc8..3342832 100644 --- a/internal/web/run.go +++ b/internal/web/run.go @@ -140,6 +140,8 @@ func Start() { g2.POST("/users/:user/delete", adminUserDelete) g2.GET("/gists", adminGists) g2.POST("/gists/:gist/delete", adminGistDelete) + g2.POST("/sync-fs", adminSyncReposFromFS) + g2.POST("/sync-db", adminSyncReposFromDB) } g1.GET("/all", allGists) diff --git a/templates/pages/admin_index.html b/templates/pages/admin_index.html index 92184a7..03fe60d 100644 --- a/templates/pages/admin_index.html +++ b/templates/pages/admin_index.html @@ -49,6 +49,28 @@ + +
+
+
+ Actions +
+
+
+ {{ .csrfHtml }} + +
+
+ {{ .csrfHtml }} + +
+
+
+
{{ template "admin_footer" .}}