diff --git a/server/backend/repo.go b/server/backend/repo.go index 2f72164e9e27d50f5d1e063db30d06be4d3f62b0..ddc4c934b3aff06887f8fb2a1e1cbd2a3875fd95 100644 --- a/server/backend/repo.go +++ b/server/backend/repo.go @@ -10,6 +10,7 @@ type RepositoryOptions struct { Description string ProjectName string Mirror bool + Hidden bool } // RepositoryStore is an interface for managing repositories. @@ -46,6 +47,10 @@ type RepositoryMetadata interface { SetPrivate(repo string, private bool) error // IsMirror returns whether the repository is a mirror. IsMirror(repo string) bool + // IsHidden returns whether the repository is hidden. + IsHidden(repo string) bool + // SetHidden sets whether the repository is hidden. + SetHidden(repo string, hidden bool) error } // RepositoryAccess is an interface for managing repository access. @@ -71,6 +76,8 @@ type Repository interface { IsPrivate() bool // IsMirror returns whether the repository is a mirror. IsMirror() bool + // IsHidden returns whether the repository is hidden. + IsHidden() bool // Open returns the underlying git.Repository. Open() (*git.Repository, error) } diff --git a/server/backend/sqlite/repo.go b/server/backend/sqlite/repo.go index bdd4384e714d17d6b528147c342b9cda54fccf29..9259f8bea5b063085fd8ea76c30ca3364c41de74 100644 --- a/server/backend/sqlite/repo.go +++ b/server/backend/sqlite/repo.go @@ -86,3 +86,17 @@ func (r *Repo) ProjectName() string { return name } + +// IsHidden returns whether the repository is hidden. +// +// It implements backend.Repository. +func (r *Repo) IsHidden() bool { + var hidden bool + if err := wrapTx(r.db, context.Background(), func(tx *sqlx.Tx) error { + return tx.Get(&hidden, "SELECT hidden FROM repo WHERE name = ?", r.name) + }); err != nil { + return false + } + + return hidden +} diff --git a/server/backend/sqlite/sql.go b/server/backend/sqlite/sql.go index 8158cac5ae65dda25092946025cb282bb487fc37..719c0707711133ded792eb02d6c1f807311cbbcd 100644 --- a/server/backend/sqlite/sql.go +++ b/server/backend/sqlite/sql.go @@ -37,6 +37,7 @@ var ( description TEXT NOT NULL, private BOOLEAN NOT NULL, mirror BOOLEAN NOT NULL, + hidden BOOLEAN NOT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME NOT NULL );` diff --git a/server/backend/sqlite/sqlite.go b/server/backend/sqlite/sqlite.go index 158993db2e5dd37fd8859e6b6bd657fe8ff8b0bb..2dd6bd39d7d840c6849675daf3d49dceed8e3a05 100644 --- a/server/backend/sqlite/sqlite.go +++ b/server/backend/sqlite/sqlite.go @@ -133,9 +133,9 @@ func (d *SqliteBackend) CreateRepository(name string, opts backend.RepositoryOpt } if err := wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error { - _, err := tx.Exec(`INSERT INTO repo (name, project_name, description, private, mirror, updated_at) + _, err := tx.Exec(`INSERT INTO repo (name, project_name, description, private, mirror, hidden, updated_at) VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP);`, - name, opts.ProjectName, opts.Description, opts.Private, opts.Mirror) + name, opts.ProjectName, opts.Description, opts.Private, opts.Mirror, opts.Hidden) return err }); err != nil { logger.Debug("failed to create repository in database", "err", err) @@ -324,6 +324,32 @@ func (d *SqliteBackend) IsPrivate(repo string) bool { return private } +// IsHidden returns true if the repository is hidden. +// +// It implements backend.Backend. +func (d *SqliteBackend) IsHidden(repo string) bool { + repo = utils.SanitizeRepo(repo) + var hidden bool + if err := wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error { + return tx.Get(&hidden, "SELECT hidden FROM repo WHERE name = ?", repo) + }); err != nil { + return false + } + + return hidden +} + +// SetHidden sets the hidden flag of a repository. +// +// It implements backend.Backend. +func (d *SqliteBackend) SetHidden(repo string, hidden bool) error { + repo = utils.SanitizeRepo(repo) + return wrapDbErr(wrapTx(d.db, context.Background(), func(tx *sqlx.Tx) error { + _, err := tx.Exec("UPDATE repo SET hidden = ?, updated_at = CURRENT_TIMESTAMP WHERE name = ?;", hidden, repo) + return err + })) +} + // ProjectName returns the project name of a repository. // // It implements backend.Backend. diff --git a/server/cmd/hidden.go b/server/cmd/hidden.go new file mode 100644 index 0000000000000000000000000000000000000000..7146579b36262771b56bf618857b8478932c48c9 --- /dev/null +++ b/server/cmd/hidden.go @@ -0,0 +1,37 @@ +package cmd + +import "github.com/spf13/cobra" + +func hiddenCommand() *cobra.Command { + cmd := &cobra.Command{ + Use: "hidden REPOSITORY [TRUE|FALSE]", + Short: "Hide or unhide a repository", + Args: cobra.MinimumNArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + cfg, _ := fromContext(cmd) + repo := args[0] + switch len(args) { + case 1: + if err := checkIfReadable(cmd, args); err != nil { + return err + } + + hidden := cfg.Backend.IsHidden(repo) + cmd.Println(hidden) + case 2: + if err := checkIfCollab(cmd, args); err != nil { + return err + } + + hidden := args[1] == "true" + if err := cfg.Backend.SetHidden(repo, hidden); err != nil { + return err + } + } + + return nil + }, + } + + return cmd +} diff --git a/server/cmd/repo.go b/server/cmd/repo.go index 577ab30304ed4092d0e1a035bda06c713fec42b4..a4c60d680e6444a1104d789b2d345442d4c74265 100644 --- a/server/cmd/repo.go +++ b/server/cmd/repo.go @@ -16,6 +16,7 @@ func repoCommand() *cobra.Command { createCommand(), deleteCommand(), descriptionCommand(), + hiddenCommand(), importCommand(), listCommand(), privateCommand(), diff --git a/ui/pages/selection/selection.go b/ui/pages/selection/selection.go index 97e47ff9708d67612a754dcc9a7735b03f5576a1..4e0056edad21c674e56baf4358a3edc5c177512a 100644 --- a/ui/pages/selection/selection.go +++ b/ui/pages/selection/selection.go @@ -198,6 +198,9 @@ func (s *Selection) Init() tea.Cmd { } sortedItems := make(Items, 0) for _, r := range repos { + if r.IsHidden() { + continue + } al := cfg.Backend.AccessLevelByPublicKey(r.Name(), pk) if al >= backend.ReadOnlyAccess { item, err := NewItem(r, cfg)