tangled
alpha
login
or
join now
julien.rbrt.fr
/
tangled-core
forked from
tangled.org/core
0
fork
atom
Monorepo for Tangled — https://tangled.org
0
fork
atom
overview
issues
pulls
pipelines
feat(knotserver): add list repo endpoint
julien.rbrt.fr
2 months ago
b0ebd1ae
7fe32c95
+150
3 changed files
expand all
collapse all
unified
split
api
tangled
repolistRepos.go
knotserver
xrpc
list_repos.go
xrpc.go
+46
api/tangled/repolistRepos.go
···
1
1
+
// Code generated by cmd/lexgen (see Makefile's lexgen); DO NOT EDIT.
2
2
+
3
3
+
package tangled
4
4
+
5
5
+
// schema: sh.tangled.repo.listRepos
6
6
+
7
7
+
import (
8
8
+
"context"
9
9
+
10
10
+
"github.com/bluesky-social/indigo/lex/util"
11
11
+
)
12
12
+
13
13
+
const (
14
14
+
RepoListReposNSID = "sh.tangled.repo.listRepos"
15
15
+
)
16
16
+
17
17
+
// RepoListRepos_Output is the output of a sh.tangled.repo.listRepos call.
18
18
+
type RepoListRepos_Output struct {
19
19
+
Users []*RepoListRepos_User `json:"users" cborgen:"users"`
20
20
+
}
21
21
+
22
22
+
// RepoListRepos_User is a "user" in the sh.tangled.repo.listRepos schema.
23
23
+
type RepoListRepos_User struct {
24
24
+
Did string `json:"did" cborgen:"did"`
25
25
+
Repos []*RepoListRepos_RepoEntry `json:"repos" cborgen:"repos"`
26
26
+
}
27
27
+
28
28
+
// RepoListRepos_RepoEntry is a "repoEntry" in the sh.tangled.repo.listRepos schema.
29
29
+
type RepoListRepos_RepoEntry struct {
30
30
+
Name string `json:"name" cborgen:"name"`
31
31
+
Did string `json:"did" cborgen:"did"`
32
32
+
FullPath string `json:"fullPath" cborgen:"fullPath"`
33
33
+
DefaultBranch string `json:"defaultBranch,omitempty" cborgen:"defaultBranch,omitempty"`
34
34
+
}
35
35
+
36
36
+
// RepoListRepos calls the XRPC method "sh.tangled.repo.listRepos".
37
37
+
func RepoListRepos(ctx context.Context, c util.LexClient) (*RepoListRepos_Output, error) {
38
38
+
var out RepoListRepos_Output
39
39
+
40
40
+
params := map[string]interface{}{}
41
41
+
if err := c.LexDo(ctx, util.Query, "", "sh.tangled.repo.listRepos", params, nil, &out); err != nil {
42
42
+
return nil, err
43
43
+
}
44
44
+
45
45
+
return &out, nil
46
46
+
}
+103
knotserver/xrpc/list_repos.go
···
1
1
+
package xrpc
2
2
+
3
3
+
import (
4
4
+
"net/http"
5
5
+
"os"
6
6
+
"path/filepath"
7
7
+
"strings"
8
8
+
9
9
+
securejoin "github.com/cyphar/filepath-securejoin"
10
10
+
"tangled.org/core/api/tangled"
11
11
+
"tangled.org/core/knotserver/git"
12
12
+
xrpcerr "tangled.org/core/xrpc/errors"
13
13
+
)
14
14
+
15
15
+
// ListRepos lists all users (DIDs) and their repositories by scanning the repository directory
16
16
+
func (x *Xrpc) ListRepos(w http.ResponseWriter, r *http.Request) {
17
17
+
scanPath := x.Config.Repo.ScanPath
18
18
+
19
19
+
didEntries, err := os.ReadDir(scanPath)
20
20
+
if err != nil {
21
21
+
x.Logger.Error("failed to read scan path", "error", err, "path", scanPath)
22
22
+
writeError(w, xrpcerr.GenericError(err), http.StatusInternalServerError)
23
23
+
return
24
24
+
}
25
25
+
26
26
+
var users []*tangled.RepoListRepos_User
27
27
+
28
28
+
for _, didEntry := range didEntries {
29
29
+
if !didEntry.IsDir() {
30
30
+
continue
31
31
+
}
32
32
+
33
33
+
did := didEntry.Name()
34
34
+
35
35
+
// Validate DID format (basic check)
36
36
+
if !strings.HasPrefix(did, "did:") {
37
37
+
continue
38
38
+
}
39
39
+
40
40
+
didPath, err := securejoin.SecureJoin(scanPath, did)
41
41
+
if err != nil {
42
42
+
x.Logger.Warn("failed to join path for did", "did", did, "error", err)
43
43
+
continue
44
44
+
}
45
45
+
46
46
+
// Read repositories for this DID
47
47
+
repoEntries, err := os.ReadDir(didPath)
48
48
+
if err != nil {
49
49
+
x.Logger.Warn("failed to read did directory", "did", did, "error", err)
50
50
+
continue
51
51
+
}
52
52
+
53
53
+
var repos []*tangled.RepoListRepos_RepoEntry
54
54
+
55
55
+
for _, repoEntry := range repoEntries {
56
56
+
if !repoEntry.IsDir() {
57
57
+
continue
58
58
+
}
59
59
+
60
60
+
repoName := repoEntry.Name()
61
61
+
62
62
+
// Check if it's a valid git repository
63
63
+
repoPath, err := securejoin.SecureJoin(didPath, repoName)
64
64
+
if err != nil {
65
65
+
continue
66
66
+
}
67
67
+
68
68
+
repo, err := git.PlainOpen(repoPath)
69
69
+
if err != nil {
70
70
+
// Not a valid git repository, skip
71
71
+
continue
72
72
+
}
73
73
+
74
74
+
// Get default branch
75
75
+
defaultBranch := "master"
76
76
+
branch, err := repo.FindMainBranch()
77
77
+
if err == nil {
78
78
+
defaultBranch = branch
79
79
+
}
80
80
+
81
81
+
repos = append(repos, &tangled.RepoListRepos_RepoEntry{
82
82
+
Name: repoName,
83
83
+
Did: did,
84
84
+
FullPath: filepath.Join(did, repoName),
85
85
+
DefaultBranch: defaultBranch,
86
86
+
})
87
87
+
}
88
88
+
89
89
+
// Only add user if they have repositories
90
90
+
if len(repos) > 0 {
91
91
+
users = append(users, &tangled.RepoListRepos_User{
92
92
+
Did: did,
93
93
+
Repos: repos,
94
94
+
})
95
95
+
}
96
96
+
}
97
97
+
98
98
+
response := tangled.RepoListRepos_Output{
99
99
+
Users: users,
100
100
+
}
101
101
+
102
102
+
writeJson(w, response)
103
103
+
}
+1
knotserver/xrpc/xrpc.go
···
73
73
74
74
// service query endpoints (no auth required)
75
75
r.Get("/"+tangled.OwnerNSID, x.Owner)
76
76
+
r.Get("/"+tangled.RepoListReposNSID, x.ListRepos)
76
77
77
78
return r
78
79
}