Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add 2 new admin actions #191

Merged
merged 3 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Add new admin actions
  • Loading branch information
thomiceli committed Jan 2, 2024
commit 1a9b38fec0a8bea19f51db7875a51c013703ae18
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 @@ -553,7 +553,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 @@ -817,7 +817,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