+17
-13
appview/knotclient/signer.go
+17
-13
appview/knotclient/signer.go
···
106
106
return s.client.Do(req)
107
107
}
108
108
109
-
func (s *SignedClient) RepoLanguages(ownerDid, source, name, branch string) (*types.RepoLanguageResponse, error) {
109
+
func (s *SignedClient) RepoLanguages(ownerDid, repoName, ref string) (*types.RepoLanguageResponse, error) {
110
110
const (
111
111
Method = "GET"
112
112
)
113
-
endpoint := fmt.Sprintf("/repo/languages/%s", url.PathEscape(branch))
113
+
endpoint := fmt.Sprintf("/%s/%s/languages/%s", ownerDid, repoName, url.PathEscape(ref))
114
114
115
-
body, _ := json.Marshal(map[string]any{
116
-
"did": ownerDid,
117
-
"source": source,
118
-
"name": name,
119
-
})
120
-
121
-
req, err := s.newRequest(Method, endpoint, body)
115
+
req, err := s.newRequest(Method, endpoint, nil)
122
116
if err != nil {
123
117
return nil, err
124
118
}
···
128
122
return nil, err
129
123
}
130
124
131
-
var languagePercentages types.RepoLanguageResponse
132
-
if err := json.NewDecoder(resp.Body).Decode(&languagePercentages); err != nil {
133
-
log.Printf("failed to decode fork status: %s", err)
125
+
var result types.RepoLanguageResponse
126
+
if resp.StatusCode != http.StatusOK {
127
+
log.Println("failed to calculate languages", resp.Status)
128
+
return &types.RepoLanguageResponse{}, nil
129
+
}
130
+
131
+
body, err := io.ReadAll(resp.Body)
132
+
if err != nil {
134
133
return nil, err
135
134
}
136
135
137
-
return &languagePercentages, nil
136
+
err = json.Unmarshal(body, &result)
137
+
if err != nil {
138
+
return nil, err
139
+
}
140
+
141
+
return &result, nil
138
142
}
139
143
140
144
func (s *SignedClient) RepoForkAheadBehind(ownerDid, source, name, branch, hiddenRef string) (*http.Response, error) {
+12
-12
appview/pages/pages.go
+12
-12
appview/pages/pages.go
···
403
403
}
404
404
405
405
type RepoIndexParams struct {
406
-
LoggedInUser *oauth.User
407
-
RepoInfo repoinfo.RepoInfo
408
-
Active string
409
-
TagMap map[string][]string
410
-
CommitsTrunc []*object.Commit
411
-
TagsTrunc []*types.TagReference
412
-
BranchesTrunc []types.Branch
413
-
ForkInfo *types.ForkInfo
406
+
LoggedInUser *oauth.User
407
+
RepoInfo repoinfo.RepoInfo
408
+
Active string
409
+
TagMap map[string][]string
410
+
CommitsTrunc []*object.Commit
411
+
TagsTrunc []*types.TagReference
412
+
BranchesTrunc []types.Branch
413
+
ForkInfo *types.ForkInfo
414
+
HTMLReadme template.HTML
415
+
Raw bool
416
+
EmailToDidOrHandle map[string]string
417
+
Languages *types.RepoLanguageResponse
414
418
types.RepoIndexResponse
415
-
HTMLReadme template.HTML
416
-
Raw bool
417
-
EmailToDidOrHandle map[string]string
418
-
LanguagePercentages map[string]float64
419
419
}
420
420
421
421
func (p *Pages) RepoIndexPage(w io.Writer, params RepoIndexParams) error {
+13
-14
appview/state/repo.go
+13
-14
appview/state/repo.go
···
138
138
139
139
var forkInfo *types.ForkInfo
140
140
if user != nil && (repoInfo.Roles.IsOwner() || repoInfo.Roles.IsCollaborator()) {
141
-
forkInfo, err = getForkInfo(repoInfo, s, f, w, user, signedClient)
141
+
forkInfo, err = getForkInfo(repoInfo, s, f, user, signedClient)
142
142
if err != nil {
143
143
log.Printf("Failed to fetch fork information: %v", err)
144
144
return
145
145
}
146
146
}
147
147
148
-
repoLanguages, err := signedClient.RepoLanguages(user.Did, string(f.RepoAt), repoInfo.Name, f.Ref)
148
+
repoLanguages, err := signedClient.RepoLanguages(f.OwnerDid(), f.RepoName, ref)
149
149
if err != nil {
150
150
log.Printf("failed to compute language percentages: %s", err)
151
-
return
151
+
// non-fatal
152
152
}
153
153
154
154
s.pages.RepoIndexPage(w, pages.RepoIndexParams{
155
-
LoggedInUser: user,
156
-
RepoInfo: repoInfo,
157
-
TagMap: tagMap,
158
-
RepoIndexResponse: result,
159
-
CommitsTrunc: commitsTrunc,
160
-
TagsTrunc: tagsTrunc,
161
-
ForkInfo: forkInfo,
162
-
BranchesTrunc: branchesTrunc,
163
-
EmailToDidOrHandle: EmailToDidOrHandle(s, emails),
164
-
LanguagePercentages: repoLanguages.Languages,
155
+
LoggedInUser: user,
156
+
RepoInfo: repoInfo,
157
+
TagMap: tagMap,
158
+
RepoIndexResponse: result,
159
+
CommitsTrunc: commitsTrunc,
160
+
TagsTrunc: tagsTrunc,
161
+
ForkInfo: forkInfo,
162
+
BranchesTrunc: branchesTrunc,
163
+
EmailToDidOrHandle: EmailToDidOrHandle(s, emails),
164
+
Languages: repoLanguages,
165
165
})
166
166
return
167
167
}
···
170
170
repoInfo repoinfo.RepoInfo,
171
171
s *State,
172
172
f *FullyResolvedRepo,
173
-
w http.ResponseWriter,
174
173
user *oauth.User,
175
174
signedClient *knotclient.SignedClient,
176
175
) (*types.ForkInfo, error) {
+6
-1
knotserver/handler.go
+6
-1
knotserver/handler.go
···
80
80
r.Post("/add", h.AddRepoCollaborator)
81
81
})
82
82
83
+
r.Route("/languages", func(r chi.Router) {
84
+
r.With(h.VerifySignature)
85
+
r.Get("/", h.RepoLanguages)
86
+
r.Get("/{ref}", h.RepoLanguages)
87
+
})
88
+
83
89
r.Get("/", h.RepoIndex)
84
90
r.Get("/info/refs", h.InfoRefs)
85
91
r.Post("/git-upload-pack", h.UploadPack)
···
126
132
r.Route("/repo", func(r chi.Router) {
127
133
r.Use(h.VerifySignature)
128
134
r.Put("/new", h.NewRepo)
129
-
r.Get("/languages/{branch}", h.RepoLanguages)
130
135
r.Delete("/", h.RemoveRepo)
131
136
r.Route("/fork", func(r chi.Router) {
132
137
r.Post("/", h.RepoFork)
+10
-43
knotserver/routes.go
+10
-43
knotserver/routes.go
···
715
715
}
716
716
717
717
func (h *Handle) RepoLanguages(w http.ResponseWriter, r *http.Request) {
718
-
l := h.l.With("handler", "RepoForkSync")
718
+
path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r))
719
+
ref := chi.URLParam(r, "ref")
720
+
ref, _ = url.PathUnescape(ref)
719
721
720
-
data := struct {
721
-
Did string `json:"did"`
722
-
Source string `json:"source"`
723
-
Name string `json:"name,omitempty"`
724
-
}{}
722
+
l := h.l.With("handler", "RepoLanguages")
725
723
726
-
if err := json.NewDecoder(r.Body).Decode(&data); err != nil {
727
-
writeError(w, "invalid request body", http.StatusBadRequest)
728
-
return
729
-
}
730
-
731
-
did := data.Did
732
-
source := data.Source
733
-
734
-
if did == "" || source == "" {
735
-
l.Error("invalid request body, empty did or name")
736
-
w.WriteHeader(http.StatusBadRequest)
737
-
return
738
-
}
739
-
740
-
var name string
741
-
if data.Name != "" {
742
-
name = data.Name
743
-
} else {
744
-
name = filepath.Base(source)
745
-
}
746
-
747
-
branch := chi.URLParam(r, "branch")
748
-
branch, _ = url.PathUnescape(branch)
749
-
750
-
relativeRepoPath := filepath.Join(did, name)
751
-
repoPath, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, relativeRepoPath)
752
-
753
-
gr, err := git.Open(repoPath, branch)
724
+
gr, err := git.Open(path, ref)
754
725
if err != nil {
755
-
log.Println(err)
726
+
l.Error("opening repo", "error", err.Error())
756
727
notFound(w)
757
728
return
758
729
}
759
730
760
731
languageFileCount := make(map[string]int)
761
-
languagePercentage := make(map[string]float64)
762
732
763
733
err = recurseEntireTree(gr, func(absPath string) {
764
734
lang, safe := enry.GetLanguageByExtension(absPath)
···
782
752
}
783
753
}, "")
784
754
if err != nil {
785
-
log.Println(err)
755
+
l.Error("failed to recurse file tree", "error", err.Error())
786
756
writeError(w, err.Error(), http.StatusNoContent)
787
757
return
788
758
}
789
759
790
-
for path, fileCount := range languageFileCount {
791
-
percentage := float64(fileCount) / float64(len(languageFileCount)) * 100.0
792
-
languagePercentage[path] = percentage
793
-
}
760
+
resp := types.RepoLanguageResponse{Languages: languageFileCount}
794
761
795
-
w.Header().Set("Content-Type", "application/json")
796
-
json.NewEncoder(w).Encode(types.RepoLanguageResponse{Languages: languagePercentage})
762
+
writeJSON(w, resp)
763
+
return
797
764
}
798
765
799
766
func recurseEntireTree(git *git.GitRepo, callback func(absPath string), filePath string) error {