1package cmd
2
3import (
4 "context"
5 "errors"
6 "fmt"
7 "io/fs"
8 "os"
9
10 "github.com/charmbracelet/soft-serve/pkg/access"
11 "github.com/charmbracelet/soft-serve/pkg/backend"
12 "github.com/charmbracelet/soft-serve/pkg/config"
13 "github.com/charmbracelet/soft-serve/pkg/db"
14 "github.com/charmbracelet/soft-serve/pkg/hooks"
15 "github.com/charmbracelet/soft-serve/pkg/proto"
16 "github.com/charmbracelet/soft-serve/pkg/sshutils"
17 "github.com/charmbracelet/soft-serve/pkg/store"
18 "github.com/charmbracelet/soft-serve/pkg/store/database"
19 "github.com/spf13/cobra"
20)
21
22// InitBackendContext initializes the backend context.
23// When a public-key is provided via the "SOFT_SERVE_PUBLIC_KEY" environment
24// variable, it will be used to try to find the corresponding user in the
25// database and set the user in the context.
26func InitBackendContext(cmd *cobra.Command, _ []string) error {
27 ctx := cmd.Context()
28 cfg := config.FromContext(ctx)
29 if _, err := os.Stat(cfg.DataPath); errors.Is(err, fs.ErrNotExist) {
30 if err := os.MkdirAll(cfg.DataPath, os.ModePerm); err != nil {
31 return fmt.Errorf("create data directory: %w", err)
32 }
33 }
34 dbx, err := db.Open(ctx, cfg.DB.Driver, cfg.DB.DataSource)
35 if err != nil {
36 return fmt.Errorf("open database: %w", err)
37 }
38
39 ctx = db.WithContext(ctx, dbx)
40 dbstore := database.New(ctx, dbx)
41 ctx = store.WithContext(ctx, dbstore)
42 be := backend.New(ctx, cfg, dbx)
43 ctx = backend.WithContext(ctx, be)
44
45 // Store user in context if public key is provided
46 // via environment variable.
47 if ak, ok := os.LookupEnv("SOFT_SERVE_PUBLIC_KEY"); ok {
48 pk, _, err := sshutils.ParseAuthorizedKey(ak)
49 if err == nil && pk != nil {
50 user, err := be.UserByPublicKey(ctx, pk)
51 if err == nil && user != nil {
52 ctx = proto.WithUserContext(ctx, user)
53 }
54 }
55 }
56
57 cmd.SetContext(ctx)
58
59 return nil
60}
61
62// CloseDBContext closes the database context.
63func CloseDBContext(cmd *cobra.Command, _ []string) error {
64 ctx := cmd.Context()
65 dbx := db.FromContext(ctx)
66 if dbx != nil {
67 if err := dbx.Close(); err != nil {
68 return fmt.Errorf("close database: %w", err)
69 }
70 }
71
72 return nil
73}
74
75// InitializeHooks initializes the hooks.
76func InitializeHooks(ctx context.Context, cfg *config.Config, be *backend.Backend) error {
77 repos, err := be.Repositories(ctx)
78 if err != nil {
79 return err
80 }
81
82 for _, repo := range repos {
83 if err := hooks.GenerateHooks(ctx, cfg, repo.Name()); err != nil {
84 return err
85 }
86 }
87
88 return nil
89}
90
91// CheckUserHasAccess checks if the user in context has access to the repository.
92// If there is no user in context, it will skip the check and return true.
93// It won't skip this check if the "strict" flag is set to true.
94func CheckUserHasAccess(cmd *cobra.Command, repo string, level access.AccessLevel) bool {
95 ctx := cmd.Context()
96 isStrict := cmd.Flag("strict").Value.String() == "true"
97 if _, ok := os.LookupEnv("SOFT_SERVE_PUBLIC_KEY"); !ok && isStrict {
98 return false
99 }
100
101 user := proto.UserFromContext(ctx)
102 be := backend.FromContext(ctx)
103 al := be.AccessLevelForUser(ctx, repo, user)
104 return al >= level
105}