forked from
tangled.org/core
Monorepo for Tangled — https://tangled.org
1package knotserver
2
3import (
4 "context"
5 "fmt"
6 "net/http"
7
8 "github.com/urfave/cli/v3"
9 "tangled.org/core/api/tangled"
10 "tangled.org/core/hook"
11 "tangled.org/core/jetstream"
12 "tangled.org/core/knotserver/config"
13 "tangled.org/core/knotserver/db"
14 "tangled.org/core/log"
15 "tangled.org/core/notifier"
16 "tangled.org/core/rbac"
17)
18
19func Command() *cli.Command {
20 return &cli.Command{
21 Name: "server",
22 Usage: "run a knot server",
23 Action: Run,
24 Description: `
25 Environment variables:
26 KNOT_SERVER_LISTEN_ADDR (default: 0.0.0.0:5555)
27 KNOT_SERVER_INTERNAL_LISTEN_ADDR (default: 127.0.0.1:5444)
28 KNOT_SERVER_DB_PATH (default: knotserver.db)
29 KNOT_SERVER_HOSTNAME (required)
30 KNOT_SERVER_JETSTREAM_ENDPOINT (default: wss://jetstream1.us-west.bsky.network/subscribe)
31 KNOT_SERVER_OWNER (required)
32 KNOT_SERVER_LOG_DIDS (default: true)
33 KNOT_SERVER_DEV (default: false)
34 KNOT_REPO_SCAN_PATH (default: /home/git)
35 KNOT_REPO_README (comma-separated list)
36 KNOT_REPO_MAIN_BRANCH (default: main)
37 KNOT_GIT_USER_NAME (default: Tangled)
38 KNOT_GIT_USER_EMAIL (default: noreply@tangled.sh)
39 APPVIEW_ENDPOINT (default: https://tangled.sh)
40 `,
41 }
42}
43
44func Run(ctx context.Context, cmd *cli.Command) error {
45 logger := log.FromContext(ctx)
46 logger = log.SubLogger(logger, cmd.Name)
47 ctx = log.IntoContext(ctx, logger)
48
49 c, err := config.Load(ctx)
50 if err != nil {
51 return fmt.Errorf("failed to load config: %w", err)
52 }
53
54 err = hook.Setup(hook.Config(
55 hook.WithScanPath(c.Repo.ScanPath),
56 hook.WithInternalApi(c.Server.InternalListenAddr),
57 ))
58 if err != nil {
59 return fmt.Errorf("failed to setup hooks: %w", err)
60 }
61 logger.Info("successfully finished setting up hooks")
62
63 if c.Server.Dev {
64 logger.Info("running in dev mode, signature verification is disabled")
65 }
66
67 db, err := db.Setup(c.Server.DBPath)
68 if err != nil {
69 return fmt.Errorf("failed to load db: %w", err)
70 }
71
72 e, err := rbac.NewEnforcer(c.Server.DBPath)
73 if err != nil {
74 return fmt.Errorf("failed to setup rbac enforcer: %w", err)
75 }
76
77 e.E.EnableAutoSave(true)
78
79 jc, err := jetstream.NewJetstreamClient(c.Server.JetstreamEndpoint, "knotserver", []string{
80 tangled.PublicKeyNSID,
81 tangled.KnotMemberNSID,
82 tangled.RepoPullNSID,
83 tangled.RepoCollaboratorNSID,
84 }, nil, log.SubLogger(logger, "jetstream"), db, true, c.Server.LogDids)
85 if err != nil {
86 logger.Error("failed to setup jetstream", "error", err)
87 }
88
89 notifier := notifier.New()
90
91 mux, err := Setup(ctx, c, db, e, jc, ¬ifier)
92 if err != nil {
93 return fmt.Errorf("failed to setup server: %w", err)
94 }
95
96 imux := Internal(ctx, c, db, e, ¬ifier)
97
98 logger.Info("starting internal server", "address", c.Server.InternalListenAddr)
99 go http.ListenAndServe(c.Server.InternalListenAddr, imux)
100
101 logger.Info("starting main server", "address", c.Server.ListenAddr)
102 logger.Error("server error", "error", http.ListenAndServe(c.Server.ListenAddr, mux))
103
104 return nil
105}