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