Skip to content

Commit

Permalink
Add 2 new admin actions (thomiceli#191)
Browse files Browse the repository at this point in the history
* Synchronize all gists previews
* Reset Git server hooks for all repositories
  • Loading branch information
thomiceli committed Jan 4, 2024
1 parent 97707f7 commit f52310a
Show file tree
Hide file tree
Showing 10 changed files with 213 additions and 86 deletions.
156 changes: 156 additions & 0 deletions internal/actions/actions.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package actions

import (
"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/config"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/git"
"os"
"path/filepath"
"strings"
"sync"
)

type ActionStatus struct {
Running bool
}

const (
SyncReposFromFS = iota
SyncReposFromDB = iota
GitGcRepos = iota
SyncGistPreviews = iota
ResetHooks = iota
)

var (
mutex sync.Mutex
actions = make(map[int]ActionStatus)
)

func updateActionStatus(actionType int, running bool) {
actions[actionType] = ActionStatus{
Running: running,
}
}

func IsRunning(actionType int) bool {
mutex.Lock()
defer mutex.Unlock()
return actions[actionType].Running
}

func Run(actionType int) {
mutex.Lock()

if actions[actionType].Running {
mutex.Unlock()
return
}

updateActionStatus(actionType, true)
mutex.Unlock()

defer func() {
mutex.Lock()
updateActionStatus(actionType, false)
mutex.Unlock()
}()

var functionToRun func()
switch actionType {
case SyncReposFromFS:
functionToRun = syncReposFromFS
case SyncReposFromDB:
functionToRun = syncReposFromDB
case GitGcRepos:
functionToRun = gitGcRepos
case SyncGistPreviews:
functionToRun = syncGistPreviews
case ResetHooks:
functionToRun = resetHooks
default:
panic("unhandled default case")
}

functionToRun()
}

func syncReposFromFS() {
log.Info().Msg("Syncing repositories from filesystem...")
gists, err := db.GetAllGistsRows()
if err != nil {
log.Error().Err(err).Msg("Cannot get gists")
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).Msgf("Cannot delete gist %d", gist.ID)
return
}
}
}
}

func syncReposFromDB() {
log.Info().Msg("Syncing repositories from database...")
entries, err := filepath.Glob(filepath.Join(config.GetHomeDir(), "repos", "*", "*"))
if err != nil {
log.Error().Err(err).Msg("Cannot read repos directories")
return
}

for _, e := range entries {
path := strings.Split(e, string(os.PathSeparator))
gist, _ := db.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).Msgf("Cannot delete repository %s/%s", path[len(path)-2], path[len(path)-1])
return
}
}
}
}

func gitGcRepos() {
log.Info().Msg("Garbage collecting all repositories...")
if err := git.GcRepos(); err != nil {
log.Error().Err(err).Msg("Error garbage collecting repositories")
}
}

func syncGistPreviews() {
log.Info().Msg("Syncing all Gist previews...")

gists, err := db.GetAllGistsRows()
if err != nil {
log.Error().Err(err).Msg("Cannot get gists")
return
}
for _, gist := range gists {
if err = gist.UpdatePreviewAndCount(false); err != nil {
log.Error().Err(err).Msgf("Cannot update preview and count for gist %d", gist.ID)
return
}
}
}

func resetHooks() {
log.Info().Msg("Resetting Git server hooks for all repositories...")
entries, err := filepath.Glob(filepath.Join(config.GetHomeDir(), "repos", "*", "*"))
if err != nil {
log.Error().Err(err).Msg("Cannot read repos directories")
return
}

for _, e := range entries {
path := strings.Split(e, string(os.PathSeparator))
if err := git.CreateDotGitFiles(path[len(path)-2], path[len(path)-1]); err != nil {
log.Error().Err(err).Msgf("Cannot reset hooks for repository %s/%s", path[len(path)-2], path[len(path)-1])
return
}
}
}
11 changes: 9 additions & 2 deletions internal/db/gist.go
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,10 @@ func (gist *Gist) Update() error {
return db.Omit("forked_id").Save(&gist).Error
}

func (gist *Gist) UpdateNoTimestamps() error {
return db.Omit("forked_id", "updated_at").Save(&gist).Error
}

func (gist *Gist) Delete() error {
err := gist.DeleteRepository()
if err != nil {
Expand Down Expand Up @@ -419,7 +423,7 @@ func (gist *Gist) RPC(service string) ([]byte, error) {
return git.RPC(gist.User.Username, gist.Uuid, service)
}

func (gist *Gist) UpdatePreviewAndCount() error {
func (gist *Gist) UpdatePreviewAndCount(withTimestampUpdate bool) error {
filesStr, err := git.GetFilesOfRepository(gist.User.Username, gist.Uuid, "HEAD")
if err != nil {
return err
Expand All @@ -445,7 +449,10 @@ func (gist *Gist) UpdatePreviewAndCount() error {
gist.PreviewFilename = file.Filename
}

return gist.Update()
if withTimestampUpdate {
return gist.Update()
}
return gist.UpdateNoTimestamps()
}

func (gist *Gist) VisibilityStr() string {
Expand Down
10 changes: 6 additions & 4 deletions internal/git/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ func InitRepository(user string, gist string) error {
return err
}

return createDotGitFiles(repositoryPath)
return CreateDotGitFiles(user, gist)
}

func InitRepositoryViaInit(user string, gist string, ctx echo.Context) error {
Expand Down Expand Up @@ -371,7 +371,7 @@ func ForkClone(userSrc string, gistSrc string, userDst string, gistDst string) e
return err
}

return createDotGitFiles(repositoryPathDst)
return CreateDotGitFiles(userDst, gistDst)
}

func SetFileContent(gistTmpId string, filename string, content string) error {
Expand Down Expand Up @@ -525,7 +525,9 @@ func GetGitVersion() (string, error) {
return versionFields[2], nil
}

func createDotGitFiles(repositoryPath string) error {
func CreateDotGitFiles(user string, gist string) error {
repositoryPath := RepositoryPath(user, gist)

f1, err := os.OpenFile(filepath.Join(repositoryPath, "git-daemon-export-ok"), os.O_RDONLY|os.O_CREATE, 0644)
if err != nil {
return err
Expand All @@ -540,7 +542,7 @@ func createDotGitFiles(repositoryPath string) error {
}

func createDotGitHookFile(repositoryPath string, hook string, content string) error {
preReceiveDst, err := os.OpenFile(filepath.Join(repositoryPath, "hooks", hook), os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0744)
preReceiveDst, err := os.OpenFile(filepath.Join(repositoryPath, "hooks", hook), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0744)
if err != nil {
return err
}
Expand Down
4 changes: 3 additions & 1 deletion internal/i18n/locales/en-US.yml
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,9 @@ admin.stats: Stats
admin.actions: Actions
admin.actions.sync-fs: Synchronize gists from filesystem
admin.actions.sync-db: Synchronize gists from database
admin.actions.git-gc: Garbage collect git repositories
admin.actions.git-gc: Garbage collect all git repositories
admin.actions.sync-previews: Synchronize all gists previews
admin.actions.reset-hooks: Reset Git server hooks for all repositories
admin.id: ID
admin.user: User
admin.delete: Delete
Expand Down
2 changes: 1 addition & 1 deletion internal/ssh/git_ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ func runGitCommand(ch ssh.Channel, gitCmd string, key string, ip string) error {
// updatedAt is updated only if serviceType is receive-pack
if verb == "receive-pack" {
_ = gist.SetLastActiveNow()
_ = gist.UpdatePreviewAndCount()
_ = gist.UpdatePreviewAndCount(false)
}

return nil
Expand Down
96 changes: 21 additions & 75 deletions internal/web/admin.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,12 @@ package web

import (
"github.com/labstack/echo/v4"
"github.com/rs/zerolog/log"
"github.com/thomiceli/opengist/internal/actions"
"github.com/thomiceli/opengist/internal/config"
"github.com/thomiceli/opengist/internal/db"
"github.com/thomiceli/opengist/internal/git"
"os"
"path/filepath"
"runtime"
"strconv"
"strings"
)

var (
syncReposFromFS = false
syncReposFromDB = false
gitGcRepos = false
)

func adminIndex(ctx echo.Context) error {
Expand Down Expand Up @@ -50,9 +41,11 @@ func adminIndex(ctx echo.Context) error {
}
setData(ctx, "countKeys", countKeys)

setData(ctx, "syncReposFromFS", syncReposFromFS)
setData(ctx, "syncReposFromDB", syncReposFromDB)
setData(ctx, "gitGcRepos", gitGcRepos)
setData(ctx, "syncReposFromFS", actions.IsRunning(actions.SyncReposFromFS))
setData(ctx, "syncReposFromDB", actions.IsRunning(actions.SyncReposFromDB))
setData(ctx, "gitGcRepos", actions.IsRunning(actions.GitGcRepos))
setData(ctx, "syncGistPreviews", actions.IsRunning(actions.SyncGistPreviews))
setData(ctx, "resetHooks", actions.IsRunning(actions.ResetHooks))
return html(ctx, "admin_index.html")
}

Expand Down Expand Up @@ -129,78 +122,31 @@ func adminGistDelete(ctx echo.Context) error {

func adminSyncReposFromFS(ctx echo.Context) error {
addFlash(ctx, "Syncing repositories from filesystem...", "success")
go func() {
if syncReposFromFS {
return
}
syncReposFromFS = true

gists, err := db.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
}()
go actions.Run(actions.SyncReposFromFS)
return redirect(ctx, "/admin-panel")
}

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, _ := db.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
}()
go actions.Run(actions.SyncReposFromDB)
return redirect(ctx, "/admin-panel")
}

func adminGcRepos(ctx echo.Context) error {
addFlash(ctx, "Garbage collecting repositories...", "success")
go func() {
if gitGcRepos {
return
}
gitGcRepos = true
if err := git.GcRepos(); err != nil {
log.Error().Err(err).Msg("Error garbage collecting repositories")
gitGcRepos = false
return
}
gitGcRepos = false
}()
go actions.Run(actions.GitGcRepos)
return redirect(ctx, "/admin-panel")
}

func adminSyncGistPreviews(ctx echo.Context) error {
addFlash(ctx, "Syncing Gist previews...", "success")
go actions.Run(actions.SyncGistPreviews)
return redirect(ctx, "/admin-panel")
}

func adminResetHooks(ctx echo.Context) error {
addFlash(ctx, "Resetting Git server hooks for all repositories...", "success")
go actions.Run(actions.ResetHooks)
return redirect(ctx, "/admin-panel")
}

Expand Down
4 changes: 2 additions & 2 deletions internal/web/gist.go
Original file line number Diff line number Diff line change
Expand Up @@ -542,7 +542,7 @@ func toggleVisibility(ctx echo.Context) error {
gist := getData(ctx, "gist").(*db.Gist)

gist.Private = (gist.Private + 1) % 3
if err := gist.Update(); err != nil {
if err := gist.UpdateNoTimestamps(); err != nil {
return errorRes(500, "Error updating this gist", err)
}

Expand Down Expand Up @@ -806,7 +806,7 @@ func checkbox(ctx echo.Context) error {
return errorRes(500, "Error adding and committing files", err)
}

if err = gist.UpdatePreviewAndCount(); err != nil {
if err = gist.UpdatePreviewAndCount(true); err != nil {
return errorRes(500, "Error updating the gist", err)
}

Expand Down
Loading

0 comments on commit f52310a

Please sign in to comment.