forked from
tangled.org/core
fork
Configure Feed
Select the types of activity you want to include in your feed.
this repo has no description
fork
Configure Feed
Select the types of activity you want to include in your feed.
1package knotclient
2
3import (
4 "bytes"
5 "crypto/hmac"
6 "crypto/sha256"
7 "encoding/hex"
8 "encoding/json"
9 "fmt"
10 "io"
11 "log"
12 "net/http"
13 "net/url"
14 "time"
15
16 "tangled.sh/tangled.sh/core/types"
17)
18
19type SignerTransport struct {
20 Secret string
21}
22
23func (s SignerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
24 timestamp := time.Now().Format(time.RFC3339)
25 mac := hmac.New(sha256.New, []byte(s.Secret))
26 message := req.Method + req.URL.Path + timestamp
27 mac.Write([]byte(message))
28 signature := hex.EncodeToString(mac.Sum(nil))
29 req.Header.Set("X-Signature", signature)
30 req.Header.Set("X-Timestamp", timestamp)
31 return http.DefaultTransport.RoundTrip(req)
32}
33
34type SignedClient struct {
35 Secret string
36 Url *url.URL
37 client *http.Client
38}
39
40func NewSignedClient(domain, secret string, dev bool) (*SignedClient, error) {
41 client := &http.Client{
42 Timeout: 5 * time.Second,
43 Transport: SignerTransport{
44 Secret: secret,
45 },
46 }
47
48 scheme := "https"
49 if dev {
50 scheme = "http"
51 }
52 url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, domain))
53 if err != nil {
54 return nil, err
55 }
56
57 signedClient := &SignedClient{
58 Secret: secret,
59 client: client,
60 Url: url,
61 }
62
63 return signedClient, nil
64}
65
66func (s *SignedClient) newRequest(method, endpoint string, body []byte) (*http.Request, error) {
67 return http.NewRequest(method, s.Url.JoinPath(endpoint).String(), bytes.NewReader(body))
68}
69
70func (s *SignedClient) Init(did string) (*http.Response, error) {
71 const (
72 Method = "POST"
73 Endpoint = "/init"
74 )
75
76 body, _ := json.Marshal(map[string]any{
77 "did": did,
78 })
79
80 req, err := s.newRequest(Method, Endpoint, body)
81 if err != nil {
82 return nil, err
83 }
84
85 return s.client.Do(req)
86}
87
88func (s *SignedClient) NewRepo(did, repoName, defaultBranch string) (*http.Response, error) {
89 const (
90 Method = "PUT"
91 Endpoint = "/repo/new"
92 )
93
94 body, _ := json.Marshal(map[string]any{
95 "did": did,
96 "name": repoName,
97 "default_branch": defaultBranch,
98 })
99
100 req, err := s.newRequest(Method, Endpoint, body)
101 if err != nil {
102 return nil, err
103 }
104
105 return s.client.Do(req)
106}
107
108func (s *SignedClient) RepoLanguages(ownerDid, repoName, ref string) (*types.RepoLanguageResponse, error) {
109 const (
110 Method = "GET"
111 )
112 endpoint := fmt.Sprintf("/%s/%s/languages/%s", ownerDid, repoName, url.PathEscape(ref))
113
114 req, err := s.newRequest(Method, endpoint, nil)
115 if err != nil {
116 return nil, err
117 }
118
119 resp, err := s.client.Do(req)
120 if err != nil {
121 return nil, err
122 }
123
124 var result types.RepoLanguageResponse
125 if resp.StatusCode != http.StatusOK {
126 log.Println("failed to calculate languages", resp.Status)
127 return &types.RepoLanguageResponse{}, nil
128 }
129
130 body, err := io.ReadAll(resp.Body)
131 if err != nil {
132 return nil, err
133 }
134
135 err = json.Unmarshal(body, &result)
136 if err != nil {
137 return nil, err
138 }
139
140 return &result, nil
141}
142
143func (s *SignedClient) RepoForkAheadBehind(ownerDid, source, name, branch, hiddenRef string) (*http.Response, error) {
144 const (
145 Method = "GET"
146 )
147 endpoint := fmt.Sprintf("/repo/fork/sync/%s", url.PathEscape(branch))
148
149 body, _ := json.Marshal(map[string]any{
150 "did": ownerDid,
151 "source": source,
152 "name": name,
153 "hiddenref": hiddenRef,
154 })
155
156 req, err := s.newRequest(Method, endpoint, body)
157 if err != nil {
158 return nil, err
159 }
160
161 return s.client.Do(req)
162}
163
164func (s *SignedClient) SyncRepoFork(ownerDid, source, name, branch string) (*http.Response, error) {
165 const (
166 Method = "POST"
167 )
168 endpoint := fmt.Sprintf("/repo/fork/sync/%s", url.PathEscape(branch))
169
170 body, _ := json.Marshal(map[string]any{
171 "did": ownerDid,
172 "source": source,
173 "name": name,
174 })
175
176 req, err := s.newRequest(Method, endpoint, body)
177 if err != nil {
178 return nil, err
179 }
180
181 return s.client.Do(req)
182}
183
184func (s *SignedClient) ForkRepo(ownerDid, source, name string) (*http.Response, error) {
185 const (
186 Method = "POST"
187 Endpoint = "/repo/fork"
188 )
189
190 body, _ := json.Marshal(map[string]any{
191 "did": ownerDid,
192 "source": source,
193 "name": name,
194 })
195
196 req, err := s.newRequest(Method, Endpoint, body)
197 if err != nil {
198 return nil, err
199 }
200
201 return s.client.Do(req)
202}
203
204func (s *SignedClient) RemoveRepo(did, repoName string) (*http.Response, error) {
205 const (
206 Method = "DELETE"
207 Endpoint = "/repo"
208 )
209
210 body, _ := json.Marshal(map[string]any{
211 "did": did,
212 "name": repoName,
213 })
214
215 req, err := s.newRequest(Method, Endpoint, body)
216 if err != nil {
217 return nil, err
218 }
219
220 return s.client.Do(req)
221}
222
223func (s *SignedClient) AddMember(did string) (*http.Response, error) {
224 const (
225 Method = "PUT"
226 Endpoint = "/member/add"
227 )
228
229 body, _ := json.Marshal(map[string]any{
230 "did": did,
231 })
232
233 req, err := s.newRequest(Method, Endpoint, body)
234 if err != nil {
235 return nil, err
236 }
237
238 return s.client.Do(req)
239}
240
241func (s *SignedClient) SetDefaultBranch(ownerDid, repoName, branch string) (*http.Response, error) {
242 const (
243 Method = "PUT"
244 )
245 endpoint := fmt.Sprintf("/%s/%s/branches/default", ownerDid, repoName)
246
247 body, _ := json.Marshal(map[string]any{
248 "branch": branch,
249 })
250
251 req, err := s.newRequest(Method, endpoint, body)
252 if err != nil {
253 return nil, err
254 }
255
256 return s.client.Do(req)
257}
258
259func (s *SignedClient) AddCollaborator(ownerDid, repoName, memberDid string) (*http.Response, error) {
260 const (
261 Method = "POST"
262 )
263 endpoint := fmt.Sprintf("/%s/%s/collaborator/add", ownerDid, repoName)
264
265 body, _ := json.Marshal(map[string]any{
266 "did": memberDid,
267 })
268
269 req, err := s.newRequest(Method, endpoint, body)
270 if err != nil {
271 return nil, err
272 }
273
274 return s.client.Do(req)
275}
276
277func (s *SignedClient) Merge(
278 patch []byte,
279 ownerDid, targetRepo, branch, commitMessage, commitBody, authorName, authorEmail string,
280) (*http.Response, error) {
281 const (
282 Method = "POST"
283 )
284 endpoint := fmt.Sprintf("/%s/%s/merge", ownerDid, targetRepo)
285
286 mr := types.MergeRequest{
287 Branch: branch,
288 CommitMessage: commitMessage,
289 CommitBody: commitBody,
290 AuthorName: authorName,
291 AuthorEmail: authorEmail,
292 Patch: string(patch),
293 }
294
295 body, _ := json.Marshal(mr)
296
297 req, err := s.newRequest(Method, endpoint, body)
298 if err != nil {
299 return nil, err
300 }
301
302 return s.client.Do(req)
303}
304
305func (s *SignedClient) MergeCheck(patch []byte, ownerDid, targetRepo, branch string) (*http.Response, error) {
306 const (
307 Method = "POST"
308 )
309 endpoint := fmt.Sprintf("/%s/%s/merge/check", ownerDid, targetRepo)
310
311 body, _ := json.Marshal(map[string]any{
312 "patch": string(patch),
313 "branch": branch,
314 })
315
316 req, err := s.newRequest(Method, endpoint, body)
317 if err != nil {
318 return nil, err
319 }
320
321 return s.client.Do(req)
322}
323
324func (s *SignedClient) NewHiddenRef(ownerDid, targetRepo, forkBranch, remoteBranch string) (*http.Response, error) {
325 const (
326 Method = "POST"
327 )
328 endpoint := fmt.Sprintf("/%s/%s/hidden-ref/%s/%s", ownerDid, targetRepo, url.PathEscape(forkBranch), url.PathEscape(remoteBranch))
329
330 req, err := s.newRequest(Method, endpoint, nil)
331 if err != nil {
332 return nil, err
333 }
334
335 return s.client.Do(req)
336}