~bigbes/sr-ht-dolt

ref: 8ba716c343fd6c99a38fe208cb07268c08ac2f18 sr-ht-dolt/web/adapters.go -rw-r--r-- 5.2 KiB
8ba716c3 — Eugene Blikh instconf: take the origin and required-key helpers from ecore 9 days ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
package web

import (
	"context"
	"time"

	"sourcecraft.dev/bigbes/sr-ht-core/auth"
	"sourcecraft.dev/bigbes/sr-ht-core/client"

	"sourcecraft.dev/bigbes/sr-ht-dolt/authn"
	"sourcecraft.dev/bigbes/sr-ht-dolt/browse"
	"sourcecraft.dev/bigbes/sr-ht-dolt/core"
	"sourcecraft.dev/bigbes/sr-ht-dolt/db"
)

// This file holds the production adapters that satisfy the small interfaces in
// deps.go over the real db, browse and core-go layers. Tests do not use these;
// they inject their own fakes. Each adapter is a zero-value struct with a
// compile-time interface assertion so a signature drift in a committed package
// breaks the build here rather than at Phase-3 wiring time.

// DBAdapter satisfies RepoStore over the request-scoped db.Store. It holds no
// connection: every method builds a Store from the *sql.DB the core-go database
// middleware installed in ctx (db.FromContext), so one value serves all
// requests and leaks nothing.
type DBAdapter struct{}

var _ RepoStore = DBAdapter{}

func (DBAdapter) CreateRepo(ctx context.Context, r *core.Repo) (*core.Repo, error) {
	return db.FromContext(ctx).CreateRepo(ctx, r)
}

func (DBAdapter) GetRepoByOwnerAndName(ctx context.Context, owner, name string) (*core.Repo, error) {
	return db.FromContext(ctx).GetRepoByOwnerAndName(ctx, owner, name)
}

func (DBAdapter) ListReposByOwner(ctx context.Context, owner string, viewer *core.Caller) ([]*core.Repo, error) {
	return db.FromContext(ctx).ListReposByOwner(ctx, owner, viewer)
}

func (DBAdapter) ListReposForDashboard(ctx context.Context, userID int) ([]*core.Repo, error) {
	return db.FromContext(ctx).ListReposForDashboard(ctx, userID)
}

func (DBAdapter) UpdateRepo(ctx context.Context, id int, description string, visibility core.Visibility) error {
	return db.FromContext(ctx).UpdateRepo(ctx, id, description, visibility)
}

func (DBAdapter) DeleteRepo(ctx context.Context, id int) error {
	return db.FromContext(ctx).DeleteRepo(ctx, id)
}

func (DBAdapter) EffectiveAccess(ctx context.Context, userID, repoID int) (*core.AccessMode, error) {
	return db.FromContext(ctx).EffectiveAccess(ctx, userID, repoID)
}

func (DBAdapter) ListACL(ctx context.Context, repoID int) ([]*db.ACLEntry, error) {
	return db.FromContext(ctx).ListACL(ctx, repoID)
}

func (DBAdapter) UpsertACL(ctx context.Context, repoID, userID int, mode core.AccessMode) error {
	return db.FromContext(ctx).UpsertACL(ctx, repoID, userID, mode)
}

func (DBAdapter) DeleteACL(ctx context.Context, repoID, userID int) error {
	return db.FromContext(ctx).DeleteACL(ctx, repoID, userID)
}

func (DBAdapter) InsertKey(ctx context.Context, userID int, kid string, pubkey []byte, comment string) (*db.DoltKey, error) {
	return db.FromContext(ctx).InsertKey(ctx, userID, kid, pubkey, comment)
}

func (DBAdapter) ListKeysByUser(ctx context.Context, userID int) ([]*db.DoltKey, error) {
	return db.FromContext(ctx).ListKeysByUser(ctx, userID)
}

func (DBAdapter) DeleteKey(ctx context.Context, id, userID int) error {
	return db.FromContext(ctx).DeleteKey(ctx, id, userID)
}

// BrowseAdapter satisfies BrowseOpener over browse.Open. The returned *browse.DB
// already implements every BrowseSession method plus Close.
type BrowseAdapter struct{}

var _ BrowseOpener = BrowseAdapter{}

func (BrowseAdapter) Open(ctx context.Context, diskPath string) (BrowseSession, error) {
	dbh, err := browse.Open(ctx, diskPath)
	if err != nil {
		return nil, err
	}
	return dbh, nil
}

// gitDescribeTimeout bounds the git.sr.ht GraphQL lookup. It must stay well
// under dolt-git-hook's 5s client timeout: the lookup runs inside the hook's
// POST to /internal/repos, so a slow git.sr.ht API must not push the whole
// request past what the hook will wait for.
const gitDescribeTimeout = 3 * time.Second

// GitDescriptionResolver satisfies GitDescriber over git.sr.ht's GraphQL API,
// queried with internal auth in the owner's name — the same service-to-service
// network-key trust dolt-git-hook uses to reach us, pointed the other way.
type GitDescriptionResolver struct{}

var _ GitDescriber = GitDescriptionResolver{}

func (GitDescriptionResolver) Description(ctx context.Context, owner, name string) (string, bool) {
	ctx, cancel := context.WithTimeout(ctx, gitDescribeTimeout)
	defer cancel()

	var resp struct {
		Me struct {
			Repository *struct {
				Description *string `json:"description"`
			} `json:"repository"`
		} `json:"me"`
	}
	err := client.Do(ctx, owner, "git.sr.ht", client.GraphQLQuery{
		Query:     `query($name: String!) { me { repository(name: $name) { description } } }`,
		Variables: map[string]any{"name": name},
	}, &resp)
	if err != nil || resp.Me.Repository == nil {
		return "", false
	}
	if resp.Me.Repository.Description == nil {
		return "", true
	}
	return *resp.Me.Repository.Description, true
}

// MetaUserResolver satisfies UserResolver via core-go's auth.LookupUser, which
// mirrors the meta.sr.ht profile into the local user table and yields the
// account's UserID. Used to resolve an ACL grantee by username.
type MetaUserResolver struct{}

var _ UserResolver = MetaUserResolver{}

func (MetaUserResolver) LookupUser(ctx context.Context, username string) (*core.Caller, error) {
	var ac auth.AuthContext
	if err := auth.LookupUser(ctx, username, &ac); err != nil {
		return nil, err
	}
	return authn.AsCoreCaller(&ac), nil
}