Skip to content

Commit

Permalink
refactor: use db module and abstract backend logic
Browse files Browse the repository at this point in the history
  • Loading branch information
aymanbagabas committed Jul 12, 2023
1 parent e06f1e2 commit 33b8de7
Show file tree
Hide file tree
Showing 67 changed files with 1,627 additions and 2,118 deletions.
30 changes: 15 additions & 15 deletions cmd/soft/hook.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import (

"github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/server/backend"
"github.com/charmbracelet/soft-serve/server/backend/sqlite"
"github.com/charmbracelet/soft-serve/server/config"
"github.com/charmbracelet/soft-serve/server/db"
"github.com/charmbracelet/soft-serve/server/hooks"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -49,15 +49,16 @@ var (
logger.SetOutput(f)
ctx = log.WithContext(ctx, logger)
cmd.SetContext(ctx)

// Set up the backend
// TODO: support other backends
sb, err := sqlite.NewSqliteBackend(ctx)
db, err := db.Open(cfg.DB.Driver, cfg.DB.DataSource)
if err != nil {
return fmt.Errorf("failed to create sqlite backend: %w", err)
return fmt.Errorf("open database: %w", err)
}

cfg = cfg.WithBackend(sb)
// Set up the backend
// TODO: support other backends
sb := backend.New(ctx, cfg, db)
ctx = backend.WithContext(ctx, sb)
cmd.SetContext(ctx)

return nil
},
Expand All @@ -69,8 +70,7 @@ var (

hooksRunE = func(cmd *cobra.Command, args []string) error {
ctx := cmd.Context()
cfg := config.FromContext(ctx)
hks := cfg.Backend.(backend.Hooks)
hks := backend.FromContext(ctx)

// This is set in the server before invoking git-receive-pack/git-upload-pack
repoName := os.Getenv("SOFT_SERVE_REPO_NAME")
Expand All @@ -83,7 +83,7 @@ var (
customHookPath := filepath.Join(filepath.Dir(configPath), "hooks", cmdName)

var buf bytes.Buffer
opts := make([]backend.HookArg, 0)
opts := make([]hooks.HookArg, 0)

switch cmdName {
case hooks.PreReceiveHook, hooks.PostReceiveHook:
Expand All @@ -94,7 +94,7 @@ var (
if len(fields) != 3 {
return fmt.Errorf("invalid hook input: %s", scanner.Text())
}
opts = append(opts, backend.HookArg{
opts = append(opts, hooks.HookArg{
OldSha: fields[0],
NewSha: fields[1],
RefName: fields[2],
Expand All @@ -103,22 +103,22 @@ var (

switch cmdName {
case hooks.PreReceiveHook:
hks.PreReceive(stdout, stderr, repoName, opts)
hks.PreReceive(ctx, stdout, stderr, repoName, opts)
case hooks.PostReceiveHook:
hks.PostReceive(stdout, stderr, repoName, opts)
hks.PostReceive(ctx, stdout, stderr, repoName, opts)
}
case hooks.UpdateHook:
if len(args) != 3 {
return fmt.Errorf("invalid update hook input: %s", args)
}

hks.Update(stdout, stderr, repoName, backend.HookArg{
hks.Update(ctx, stdout, stderr, repoName, hooks.HookArg{
OldSha: args[0],
NewSha: args[1],
RefName: args[2],
})
case hooks.PostUpdateHook:
hks.PostUpdate(stdout, stderr, repoName, args...)
hks.PostUpdate(ctx, stdout, stderr, repoName, args...)
}

// Custom hooks
Expand Down
40 changes: 21 additions & 19 deletions cmd/soft/migrate_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ import (
"github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/git"
"github.com/charmbracelet/soft-serve/server/backend"
"github.com/charmbracelet/soft-serve/server/backend/sqlite"
"github.com/charmbracelet/soft-serve/server/config"
"github.com/charmbracelet/soft-serve/server/db"
"github.com/charmbracelet/soft-serve/server/sshutils"
"github.com/charmbracelet/soft-serve/server/store"
"github.com/charmbracelet/soft-serve/server/utils"
gitm "github.com/gogs/git-module"
"github.com/spf13/cobra"
Expand All @@ -39,15 +41,15 @@ var (
bindAddr := os.Getenv("SOFT_SERVE_BIND_ADDRESS")
cfg := config.DefaultConfig()
ctx = config.WithContext(ctx, cfg)
sb, err := sqlite.NewSqliteBackend(ctx)
db, err := db.Open(cfg.DB.Driver, cfg.DB.DataSource)
if err != nil {
return fmt.Errorf("failed to create sqlite backend: %w", err)
return fmt.Errorf("open database: %w", err)
}

// FIXME: Admin user gets created when the database is created.
sb.DeleteUser("admin") // nolint: errcheck
sb := backend.New(ctx, cfg, db)

cfg = cfg.WithBackend(sb)
// FIXME: Admin user gets created when the database is created.
sb.DeleteUser(ctx, "admin") // nolint: errcheck

// Set SSH listen address
logger.Info("Setting SSH listen address...")
Expand Down Expand Up @@ -127,12 +129,12 @@ var (

// Set server settings
logger.Info("Setting server settings...")
if cfg.Backend.SetAllowKeyless(ocfg.AllowKeyless) != nil {
if sb.SetAllowKeyless(ctx, ocfg.AllowKeyless) != nil {
fmt.Fprintf(os.Stderr, "failed to set allow keyless\n")
}
anon := backend.ParseAccessLevel(ocfg.AnonAccess)
anon := store.ParseAccessLevel(ocfg.AnonAccess)
if anon >= 0 {
if err := sb.SetAnonAccess(anon); err != nil {
if err := sb.SetAnonAccess(ctx, anon); err != nil {
fmt.Fprintf(os.Stderr, "failed to set anon access: %s\n", err)
}
}
Expand Down Expand Up @@ -169,7 +171,7 @@ var (
return fmt.Errorf("failed to copy repo: %w", err)
}

if _, err := sb.CreateRepository(dir.Name(), backend.RepositoryOptions{}); err != nil {
if _, err := sb.CreateRepository(ctx, dir.Name(), store.RepositoryOptions{}); err != nil {
fmt.Fprintf(os.Stderr, "failed to create repository: %s\n", err)
}
}
Expand Down Expand Up @@ -231,7 +233,7 @@ var (
}

// Create `.soft-serve` repository and add readme
if _, err := sb.CreateRepository(".soft-serve", backend.RepositoryOptions{
if _, err := sb.CreateRepository(ctx, ".soft-serve", store.RepositoryOptions{
ProjectName: "Home",
Description: "Soft Serve home repository",
Hidden: true,
Expand All @@ -252,20 +254,20 @@ var (
r.Private = false
}

if err := sb.SetProjectName(repo, name); err != nil {
if err := sb.SetProjectName(ctx, repo, name); err != nil {
logger.Errorf("failed to set repo name to %s: %s", repo, err)
}

if err := sb.SetDescription(repo, r.Note); err != nil {
if err := sb.SetDescription(ctx, repo, r.Note); err != nil {
logger.Errorf("failed to set repo description to %s: %s", repo, err)
}

if err := sb.SetPrivate(repo, r.Private); err != nil {
if err := sb.SetPrivate(ctx, repo, r.Private); err != nil {
logger.Errorf("failed to set repo private to %s: %s", repo, err)
}

for _, collab := range r.Collabs {
if err := sb.AddCollaborator(repo, collab); err != nil {
if err := sb.AddCollaborator(ctx, repo, collab); err != nil {
logger.Errorf("failed to add repo collab to %s: %s", repo, err)
}
}
Expand All @@ -276,11 +278,11 @@ var (
for _, user := range ocfg.Users {
keys := make(map[string]ssh.PublicKey)
for _, key := range user.PublicKeys {
pk, _, err := backend.ParseAuthorizedKey(key)
pk, _, err := sshutils.ParseAuthorizedKey(key)
if err != nil {
continue
}
ak := backend.MarshalAuthorizedKey(pk)
ak := sshutils.MarshalAuthorizedKey(pk)
keys[ak] = pk
}

Expand All @@ -292,15 +294,15 @@ var (
username := strings.ToLower(user.Name)
username = strings.ReplaceAll(username, " ", "-")
logger.Infof("Creating user %q", username)
if _, err := sb.CreateUser(username, backend.UserOptions{
if _, err := sb.CreateUser(ctx, username, store.UserOptions{
Admin: user.Admin,
PublicKeys: pubkeys,
}); err != nil {
logger.Errorf("failed to create user: %s", err)
}

for _, repo := range user.CollabRepos {
if err := sb.AddCollaborator(repo, username); err != nil {
if err := sb.AddCollaborator(ctx, repo, username); err != nil {
logger.Errorf("failed to add user collab to %s: %s\n", repo, err)
}
}
Expand Down
26 changes: 26 additions & 0 deletions cmd/soft/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,15 @@ import (

"github.com/charmbracelet/soft-serve/server"
"github.com/charmbracelet/soft-serve/server/config"
"github.com/charmbracelet/soft-serve/server/db"
"github.com/charmbracelet/soft-serve/server/db/migrate"
"github.com/spf13/cobra"
)

var (
autoMigrate bool
rollback bool

serveCmd = &cobra.Command{
Use: "serve",
Short: "Start the server",
Expand Down Expand Up @@ -44,6 +49,21 @@ var (
os.MkdirAll(logPath, os.ModePerm) // nolint: errcheck
}

db, err := db.Open(cfg.DB.Driver, cfg.DB.DataSource)
if err != nil {
return fmt.Errorf("open database: %w", err)
}

if rollback {
if err := migrate.Rollback(ctx, db); err != nil {
return fmt.Errorf("rollback error: %w", err)
}
} else if autoMigrate {
if err := migrate.Migrate(ctx, db); err != nil {
return fmt.Errorf("migration error: %w", err)
}
}

s, err := server.NewServer(ctx)
if err != nil {
return fmt.Errorf("start server: %w", err)
Expand Down Expand Up @@ -71,3 +91,9 @@ var (
},
}
)

func init() {
serveCmd.Flags().BoolVarP(&autoMigrate, "auto-migrate", "", false, "automatically run database migrations")
serveCmd.Flags().BoolVarP(&rollback, "rollback", "", false, "rollback the last database migration")
rootCmd.AddCommand(serveCmd)
}
50 changes: 0 additions & 50 deletions server/backend/access.go

This file was deleted.

64 changes: 29 additions & 35 deletions server/backend/backend.go
Original file line number Diff line number Diff line change
@@ -1,47 +1,41 @@
package backend

import (
"bytes"
"context"

"github.com/charmbracelet/ssh"
gossh "golang.org/x/crypto/ssh"
"github.com/charmbracelet/log"
"github.com/charmbracelet/soft-serve/server/config"
"github.com/charmbracelet/soft-serve/server/db"
"github.com/charmbracelet/soft-serve/server/store"
"github.com/charmbracelet/soft-serve/server/store/database"
)

// Backend is an interface that handles repositories management and any
// non-Git related operations.
type Backend interface {
SettingsBackend
RepositoryStore
RepositoryMetadata
RepositoryAccess
UserStore
UserAccess
Hooks

// WithContext returns a copy Backend with the given context.
WithContext(ctx context.Context) Backend
}

// ParseAuthorizedKey parses an authorized key string into a public key.
func ParseAuthorizedKey(ak string) (gossh.PublicKey, string, error) {
pk, c, _, _, err := gossh.ParseAuthorizedKey([]byte(ak))
return pk, c, err
// Backend is the Soft Serve backend that handles users, repositories, and
// server settings management and operations.
type Backend struct {
ctx context.Context
cfg *config.Config
db *db.DB
store store.Store
logger *log.Logger
cache *cache
}

// MarshalAuthorizedKey marshals a public key into an authorized key string.
//
// This is the inverse of ParseAuthorizedKey.
// This function is a copy of ssh.MarshalAuthorizedKey, but without the trailing newline.
// It returns an empty string if pk is nil.
func MarshalAuthorizedKey(pk gossh.PublicKey) string {
if pk == nil {
return ""
// New returns a new Soft Serve backend.
func New(ctx context.Context, cfg *config.Config, db *db.DB) *Backend {
dbstore := database.New(ctx, db)
logger := log.FromContext(ctx).WithPrefix("backend")
b := &Backend{
ctx: ctx,
cfg: cfg,
db: db,
store: dbstore,
logger: logger,
}
return string(bytes.TrimSuffix(gossh.MarshalAuthorizedKey(pk), []byte("\n")))
}

// KeysEqual returns whether the two public keys are equal.
func KeysEqual(a, b gossh.PublicKey) bool {
return ssh.KeysEqual(a, b)
// TODO: implement a proper caching interface
cache := newCache(b, 1000)
b.cache = cache

return b
}
Loading

0 comments on commit 33b8de7

Please sign in to comment.