+1
appview/signer.go
+1
appview/signer.go
···
1
+
-473
appview/state/signer.go
-473
appview/state/signer.go
···
1
-
package state
2
-
3
-
import (
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
-
"strconv"
15
-
"time"
16
-
17
-
"tangled.sh/tangled.sh/core/types"
18
-
)
19
-
20
-
type SignerTransport struct {
21
-
Secret string
22
-
}
23
-
24
-
func (s SignerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
25
-
timestamp := time.Now().Format(time.RFC3339)
26
-
mac := hmac.New(sha256.New, []byte(s.Secret))
27
-
message := req.Method + req.URL.Path + timestamp
28
-
mac.Write([]byte(message))
29
-
signature := hex.EncodeToString(mac.Sum(nil))
30
-
req.Header.Set("X-Signature", signature)
31
-
req.Header.Set("X-Timestamp", timestamp)
32
-
return http.DefaultTransport.RoundTrip(req)
33
-
}
34
-
35
-
type SignedClient struct {
36
-
Secret string
37
-
Url *url.URL
38
-
client *http.Client
39
-
}
40
-
41
-
func NewSignedClient(domain, secret string, dev bool) (*SignedClient, error) {
42
-
client := &http.Client{
43
-
Timeout: 5 * time.Second,
44
-
Transport: SignerTransport{
45
-
Secret: secret,
46
-
},
47
-
}
48
-
49
-
scheme := "https"
50
-
if dev {
51
-
scheme = "http"
52
-
}
53
-
url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, domain))
54
-
if err != nil {
55
-
return nil, err
56
-
}
57
-
58
-
signedClient := &SignedClient{
59
-
Secret: secret,
60
-
client: client,
61
-
Url: url,
62
-
}
63
-
64
-
return signedClient, nil
65
-
}
66
-
67
-
func (s *SignedClient) newRequest(method, endpoint string, body []byte) (*http.Request, error) {
68
-
return http.NewRequest(method, s.Url.JoinPath(endpoint).String(), bytes.NewReader(body))
69
-
}
70
-
71
-
func (s *SignedClient) Init(did string) (*http.Response, error) {
72
-
const (
73
-
Method = "POST"
74
-
Endpoint = "/init"
75
-
)
76
-
77
-
body, _ := json.Marshal(map[string]any{
78
-
"did": did,
79
-
})
80
-
81
-
req, err := s.newRequest(Method, Endpoint, body)
82
-
if err != nil {
83
-
return nil, err
84
-
}
85
-
86
-
return s.client.Do(req)
87
-
}
88
-
89
-
func (s *SignedClient) NewRepo(did, repoName, defaultBranch string) (*http.Response, error) {
90
-
const (
91
-
Method = "PUT"
92
-
Endpoint = "/repo/new"
93
-
)
94
-
95
-
body, _ := json.Marshal(map[string]any{
96
-
"did": did,
97
-
"name": repoName,
98
-
"default_branch": defaultBranch,
99
-
})
100
-
101
-
req, err := s.newRequest(Method, Endpoint, body)
102
-
if err != nil {
103
-
return nil, err
104
-
}
105
-
106
-
return s.client.Do(req)
107
-
}
108
-
109
-
func (s *SignedClient) ForkRepo(ownerDid, source, name string) (*http.Response, error) {
110
-
const (
111
-
Method = "POST"
112
-
Endpoint = "/repo/fork"
113
-
)
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)
122
-
if err != nil {
123
-
return nil, err
124
-
}
125
-
126
-
return s.client.Do(req)
127
-
}
128
-
129
-
func (s *SignedClient) RemoveRepo(did, repoName string) (*http.Response, error) {
130
-
const (
131
-
Method = "DELETE"
132
-
Endpoint = "/repo"
133
-
)
134
-
135
-
body, _ := json.Marshal(map[string]any{
136
-
"did": did,
137
-
"name": repoName,
138
-
})
139
-
140
-
req, err := s.newRequest(Method, Endpoint, body)
141
-
if err != nil {
142
-
return nil, err
143
-
}
144
-
145
-
return s.client.Do(req)
146
-
}
147
-
148
-
func (s *SignedClient) AddMember(did string) (*http.Response, error) {
149
-
const (
150
-
Method = "PUT"
151
-
Endpoint = "/member/add"
152
-
)
153
-
154
-
body, _ := json.Marshal(map[string]any{
155
-
"did": did,
156
-
})
157
-
158
-
req, err := s.newRequest(Method, Endpoint, body)
159
-
if err != nil {
160
-
return nil, err
161
-
}
162
-
163
-
return s.client.Do(req)
164
-
}
165
-
166
-
func (s *SignedClient) SetDefaultBranch(ownerDid, repoName, branch string) (*http.Response, error) {
167
-
const (
168
-
Method = "PUT"
169
-
)
170
-
endpoint := fmt.Sprintf("/%s/%s/branches/default", ownerDid, repoName)
171
-
172
-
body, _ := json.Marshal(map[string]any{
173
-
"branch": branch,
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
-
184
-
func (s *SignedClient) AddCollaborator(ownerDid, repoName, memberDid string) (*http.Response, error) {
185
-
const (
186
-
Method = "POST"
187
-
)
188
-
endpoint := fmt.Sprintf("/%s/%s/collaborator/add", ownerDid, repoName)
189
-
190
-
body, _ := json.Marshal(map[string]any{
191
-
"did": memberDid,
192
-
})
193
-
194
-
req, err := s.newRequest(Method, endpoint, body)
195
-
if err != nil {
196
-
return nil, err
197
-
}
198
-
199
-
return s.client.Do(req)
200
-
}
201
-
202
-
func (s *SignedClient) Merge(
203
-
patch []byte,
204
-
ownerDid, targetRepo, branch, commitMessage, commitBody, authorName, authorEmail string,
205
-
) (*http.Response, error) {
206
-
const (
207
-
Method = "POST"
208
-
)
209
-
endpoint := fmt.Sprintf("/%s/%s/merge", ownerDid, targetRepo)
210
-
211
-
mr := types.MergeRequest{
212
-
Branch: branch,
213
-
CommitMessage: commitMessage,
214
-
CommitBody: commitBody,
215
-
AuthorName: authorName,
216
-
AuthorEmail: authorEmail,
217
-
Patch: string(patch),
218
-
}
219
-
220
-
body, _ := json.Marshal(mr)
221
-
222
-
req, err := s.newRequest(Method, endpoint, body)
223
-
if err != nil {
224
-
return nil, err
225
-
}
226
-
227
-
return s.client.Do(req)
228
-
}
229
-
230
-
func (s *SignedClient) MergeCheck(patch []byte, ownerDid, targetRepo, branch string) (*http.Response, error) {
231
-
const (
232
-
Method = "POST"
233
-
)
234
-
endpoint := fmt.Sprintf("/%s/%s/merge/check", ownerDid, targetRepo)
235
-
236
-
body, _ := json.Marshal(map[string]any{
237
-
"patch": string(patch),
238
-
"branch": branch,
239
-
})
240
-
241
-
req, err := s.newRequest(Method, endpoint, body)
242
-
if err != nil {
243
-
return nil, err
244
-
}
245
-
246
-
return s.client.Do(req)
247
-
}
248
-
249
-
func (s *SignedClient) NewHiddenRef(ownerDid, targetRepo, forkBranch, remoteBranch string) (*http.Response, error) {
250
-
const (
251
-
Method = "POST"
252
-
)
253
-
endpoint := fmt.Sprintf("/%s/%s/hidden-ref/%s/%s", ownerDid, targetRepo, url.PathEscape(forkBranch), url.PathEscape(remoteBranch))
254
-
255
-
req, err := s.newRequest(Method, endpoint, nil)
256
-
if err != nil {
257
-
return nil, err
258
-
}
259
-
260
-
return s.client.Do(req)
261
-
}
262
-
263
-
type UnsignedClient struct {
264
-
Url *url.URL
265
-
client *http.Client
266
-
}
267
-
268
-
func NewUnsignedClient(domain string, dev bool) (*UnsignedClient, error) {
269
-
client := &http.Client{
270
-
Timeout: 5 * time.Second,
271
-
}
272
-
273
-
scheme := "https"
274
-
if dev {
275
-
scheme = "http"
276
-
}
277
-
url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, domain))
278
-
if err != nil {
279
-
return nil, err
280
-
}
281
-
282
-
unsignedClient := &UnsignedClient{
283
-
client: client,
284
-
Url: url,
285
-
}
286
-
287
-
return unsignedClient, nil
288
-
}
289
-
290
-
func (us *UnsignedClient) newRequest(method, endpoint string, query url.Values, body []byte) (*http.Request, error) {
291
-
reqUrl := us.Url.JoinPath(endpoint)
292
-
293
-
// add query parameters
294
-
if query != nil {
295
-
reqUrl.RawQuery = query.Encode()
296
-
}
297
-
298
-
return http.NewRequest(method, reqUrl.String(), bytes.NewReader(body))
299
-
}
300
-
301
-
func (us *UnsignedClient) Index(ownerDid, repoName, ref string) (*http.Response, error) {
302
-
const (
303
-
Method = "GET"
304
-
)
305
-
306
-
endpoint := fmt.Sprintf("/%s/%s/tree/%s", ownerDid, repoName, ref)
307
-
if ref == "" {
308
-
endpoint = fmt.Sprintf("/%s/%s", ownerDid, repoName)
309
-
}
310
-
311
-
req, err := us.newRequest(Method, endpoint, nil, nil)
312
-
if err != nil {
313
-
return nil, err
314
-
}
315
-
316
-
return us.client.Do(req)
317
-
}
318
-
319
-
func (us *UnsignedClient) Log(ownerDid, repoName, ref string, page int) (*http.Response, error) {
320
-
const (
321
-
Method = "GET"
322
-
)
323
-
324
-
endpoint := fmt.Sprintf("/%s/%s/log/%s", ownerDid, repoName, url.PathEscape(ref))
325
-
326
-
query := url.Values{}
327
-
query.Add("page", strconv.Itoa(page))
328
-
query.Add("per_page", strconv.Itoa(60))
329
-
330
-
req, err := us.newRequest(Method, endpoint, query, nil)
331
-
if err != nil {
332
-
return nil, err
333
-
}
334
-
335
-
return us.client.Do(req)
336
-
}
337
-
338
-
func (us *UnsignedClient) Branches(ownerDid, repoName string) (*http.Response, error) {
339
-
const (
340
-
Method = "GET"
341
-
)
342
-
343
-
endpoint := fmt.Sprintf("/%s/%s/branches", ownerDid, repoName)
344
-
345
-
req, err := us.newRequest(Method, endpoint, nil, nil)
346
-
if err != nil {
347
-
return nil, err
348
-
}
349
-
350
-
return us.client.Do(req)
351
-
}
352
-
353
-
func (us *UnsignedClient) Tags(ownerDid, repoName string) (*http.Response, error) {
354
-
const (
355
-
Method = "GET"
356
-
)
357
-
358
-
endpoint := fmt.Sprintf("/%s/%s/tags", ownerDid, repoName)
359
-
360
-
req, err := us.newRequest(Method, endpoint, nil, nil)
361
-
if err != nil {
362
-
return nil, err
363
-
}
364
-
365
-
return us.client.Do(req)
366
-
}
367
-
368
-
func (us *UnsignedClient) Branch(ownerDid, repoName, branch string) (*http.Response, error) {
369
-
const (
370
-
Method = "GET"
371
-
)
372
-
373
-
endpoint := fmt.Sprintf("/%s/%s/branches/%s", ownerDid, repoName, url.PathEscape(branch))
374
-
375
-
req, err := us.newRequest(Method, endpoint, nil, nil)
376
-
if err != nil {
377
-
return nil, err
378
-
}
379
-
380
-
return us.client.Do(req)
381
-
}
382
-
383
-
func (us *UnsignedClient) DefaultBranch(ownerDid, repoName string) (*types.RepoDefaultBranchResponse, error) {
384
-
const (
385
-
Method = "GET"
386
-
)
387
-
388
-
endpoint := fmt.Sprintf("/%s/%s/branches/default", ownerDid, repoName)
389
-
390
-
req, err := us.newRequest(Method, endpoint, nil, nil)
391
-
if err != nil {
392
-
return nil, err
393
-
}
394
-
395
-
resp, err := us.client.Do(req)
396
-
if err != nil {
397
-
return nil, err
398
-
}
399
-
defer resp.Body.Close()
400
-
401
-
var defaultBranch types.RepoDefaultBranchResponse
402
-
if err := json.NewDecoder(resp.Body).Decode(&defaultBranch); err != nil {
403
-
return nil, err
404
-
}
405
-
406
-
return &defaultBranch, nil
407
-
}
408
-
409
-
func (us *UnsignedClient) Capabilities() (*types.Capabilities, error) {
410
-
const (
411
-
Method = "GET"
412
-
Endpoint = "/capabilities"
413
-
)
414
-
415
-
req, err := us.newRequest(Method, Endpoint, nil, nil)
416
-
if err != nil {
417
-
return nil, err
418
-
}
419
-
420
-
resp, err := us.client.Do(req)
421
-
if err != nil {
422
-
return nil, err
423
-
}
424
-
defer resp.Body.Close()
425
-
426
-
var capabilities types.Capabilities
427
-
if err := json.NewDecoder(resp.Body).Decode(&capabilities); err != nil {
428
-
return nil, err
429
-
}
430
-
431
-
return &capabilities, nil
432
-
}
433
-
434
-
func (us *UnsignedClient) Compare(ownerDid, repoName, rev1, rev2 string) (*types.RepoFormatPatchResponse, error) {
435
-
const (
436
-
Method = "GET"
437
-
)
438
-
439
-
endpoint := fmt.Sprintf("/%s/%s/compare/%s/%s", ownerDid, repoName, url.PathEscape(rev1), url.PathEscape(rev2))
440
-
441
-
req, err := us.newRequest(Method, endpoint, nil, nil)
442
-
if err != nil {
443
-
return nil, fmt.Errorf("Failed to create request.")
444
-
}
445
-
446
-
compareResp, err := us.client.Do(req)
447
-
if err != nil {
448
-
return nil, fmt.Errorf("Failed to create request.")
449
-
}
450
-
defer compareResp.Body.Close()
451
-
452
-
switch compareResp.StatusCode {
453
-
case 404:
454
-
case 400:
455
-
return nil, fmt.Errorf("Branch comparisons not supported on this knot.")
456
-
}
457
-
458
-
respBody, err := io.ReadAll(compareResp.Body)
459
-
if err != nil {
460
-
log.Println("failed to compare across branches")
461
-
return nil, fmt.Errorf("Failed to compare branches.")
462
-
}
463
-
defer compareResp.Body.Close()
464
-
465
-
var formatPatchResponse types.RepoFormatPatchResponse
466
-
err = json.Unmarshal(respBody, &formatPatchResponse)
467
-
if err != nil {
468
-
log.Println("failed to unmarshal format-patch response", err)
469
-
return nil, fmt.Errorf("failed to compare branches.")
470
-
}
471
-
472
-
return &formatPatchResponse, nil
473
-
}
+258
knotclient/signed.go
+258
knotclient/signed.go
···
1
+
package knotclient
2
+
3
+
import (
4
+
"bytes"
5
+
"crypto/hmac"
6
+
"crypto/sha256"
7
+
"encoding/hex"
8
+
"encoding/json"
9
+
"fmt"
10
+
"net/http"
11
+
"net/url"
12
+
"time"
13
+
14
+
"tangled.sh/tangled.sh/core/types"
15
+
)
16
+
17
+
type SignerTransport struct {
18
+
Secret string
19
+
}
20
+
21
+
func (s SignerTransport) RoundTrip(req *http.Request) (*http.Response, error) {
22
+
timestamp := time.Now().Format(time.RFC3339)
23
+
mac := hmac.New(sha256.New, []byte(s.Secret))
24
+
message := req.Method + req.URL.Path + timestamp
25
+
mac.Write([]byte(message))
26
+
signature := hex.EncodeToString(mac.Sum(nil))
27
+
req.Header.Set("X-Signature", signature)
28
+
req.Header.Set("X-Timestamp", timestamp)
29
+
return http.DefaultTransport.RoundTrip(req)
30
+
}
31
+
32
+
type SignedClient struct {
33
+
Secret string
34
+
Url *url.URL
35
+
client *http.Client
36
+
}
37
+
38
+
func NewSignedClient(domain, secret string, dev bool) (*SignedClient, error) {
39
+
client := &http.Client{
40
+
Timeout: 5 * time.Second,
41
+
Transport: SignerTransport{
42
+
Secret: secret,
43
+
},
44
+
}
45
+
46
+
scheme := "https"
47
+
if dev {
48
+
scheme = "http"
49
+
}
50
+
url, err := url.Parse(fmt.Sprintf("%s://%s", scheme, domain))
51
+
if err != nil {
52
+
return nil, err
53
+
}
54
+
55
+
signedClient := &SignedClient{
56
+
Secret: secret,
57
+
client: client,
58
+
Url: url,
59
+
}
60
+
61
+
return signedClient, nil
62
+
}
63
+
64
+
func (s *SignedClient) newRequest(method, endpoint string, body []byte) (*http.Request, error) {
65
+
return http.NewRequest(method, s.Url.JoinPath(endpoint).String(), bytes.NewReader(body))
66
+
}
67
+
68
+
func (s *SignedClient) Init(did string) (*http.Response, error) {
69
+
const (
70
+
Method = "POST"
71
+
Endpoint = "/init"
72
+
)
73
+
74
+
body, _ := json.Marshal(map[string]any{
75
+
"did": did,
76
+
})
77
+
78
+
req, err := s.newRequest(Method, Endpoint, body)
79
+
if err != nil {
80
+
return nil, err
81
+
}
82
+
83
+
return s.client.Do(req)
84
+
}
85
+
86
+
func (s *SignedClient) NewRepo(did, repoName, defaultBranch string) (*http.Response, error) {
87
+
const (
88
+
Method = "PUT"
89
+
Endpoint = "/repo/new"
90
+
)
91
+
92
+
body, _ := json.Marshal(map[string]any{
93
+
"did": did,
94
+
"name": repoName,
95
+
"default_branch": defaultBranch,
96
+
})
97
+
98
+
req, err := s.newRequest(Method, Endpoint, body)
99
+
if err != nil {
100
+
return nil, err
101
+
}
102
+
103
+
return s.client.Do(req)
104
+
}
105
+
106
+
func (s *SignedClient) ForkRepo(ownerDid, source, name string) (*http.Response, error) {
107
+
const (
108
+
Method = "POST"
109
+
Endpoint = "/repo/fork"
110
+
)
111
+
112
+
body, _ := json.Marshal(map[string]any{
113
+
"did": ownerDid,
114
+
"source": source,
115
+
"name": name,
116
+
})
117
+
118
+
req, err := s.newRequest(Method, Endpoint, body)
119
+
if err != nil {
120
+
return nil, err
121
+
}
122
+
123
+
return s.client.Do(req)
124
+
}
125
+
126
+
func (s *SignedClient) RemoveRepo(did, repoName string) (*http.Response, error) {
127
+
const (
128
+
Method = "DELETE"
129
+
Endpoint = "/repo"
130
+
)
131
+
132
+
body, _ := json.Marshal(map[string]any{
133
+
"did": did,
134
+
"name": repoName,
135
+
})
136
+
137
+
req, err := s.newRequest(Method, Endpoint, body)
138
+
if err != nil {
139
+
return nil, err
140
+
}
141
+
142
+
return s.client.Do(req)
143
+
}
144
+
145
+
func (s *SignedClient) AddMember(did string) (*http.Response, error) {
146
+
const (
147
+
Method = "PUT"
148
+
Endpoint = "/member/add"
149
+
)
150
+
151
+
body, _ := json.Marshal(map[string]any{
152
+
"did": did,
153
+
})
154
+
155
+
req, err := s.newRequest(Method, Endpoint, body)
156
+
if err != nil {
157
+
return nil, err
158
+
}
159
+
160
+
return s.client.Do(req)
161
+
}
162
+
163
+
func (s *SignedClient) SetDefaultBranch(ownerDid, repoName, branch string) (*http.Response, error) {
164
+
const (
165
+
Method = "PUT"
166
+
)
167
+
endpoint := fmt.Sprintf("/%s/%s/branches/default", ownerDid, repoName)
168
+
169
+
body, _ := json.Marshal(map[string]any{
170
+
"branch": branch,
171
+
})
172
+
173
+
req, err := s.newRequest(Method, endpoint, body)
174
+
if err != nil {
175
+
return nil, err
176
+
}
177
+
178
+
return s.client.Do(req)
179
+
}
180
+
181
+
func (s *SignedClient) AddCollaborator(ownerDid, repoName, memberDid string) (*http.Response, error) {
182
+
const (
183
+
Method = "POST"
184
+
)
185
+
endpoint := fmt.Sprintf("/%s/%s/collaborator/add", ownerDid, repoName)
186
+
187
+
body, _ := json.Marshal(map[string]any{
188
+
"did": memberDid,
189
+
})
190
+
191
+
req, err := s.newRequest(Method, endpoint, body)
192
+
if err != nil {
193
+
return nil, err
194
+
}
195
+
196
+
return s.client.Do(req)
197
+
}
198
+
199
+
func (s *SignedClient) Merge(
200
+
patch []byte,
201
+
ownerDid, targetRepo, branch, commitMessage, commitBody, authorName, authorEmail string,
202
+
) (*http.Response, error) {
203
+
const (
204
+
Method = "POST"
205
+
)
206
+
endpoint := fmt.Sprintf("/%s/%s/merge", ownerDid, targetRepo)
207
+
208
+
mr := types.MergeRequest{
209
+
Branch: branch,
210
+
CommitMessage: commitMessage,
211
+
CommitBody: commitBody,
212
+
AuthorName: authorName,
213
+
AuthorEmail: authorEmail,
214
+
Patch: string(patch),
215
+
}
216
+
217
+
body, _ := json.Marshal(mr)
218
+
219
+
req, err := s.newRequest(Method, endpoint, body)
220
+
if err != nil {
221
+
return nil, err
222
+
}
223
+
224
+
return s.client.Do(req)
225
+
}
226
+
227
+
func (s *SignedClient) MergeCheck(patch []byte, ownerDid, targetRepo, branch string) (*http.Response, error) {
228
+
const (
229
+
Method = "POST"
230
+
)
231
+
endpoint := fmt.Sprintf("/%s/%s/merge/check", ownerDid, targetRepo)
232
+
233
+
body, _ := json.Marshal(map[string]any{
234
+
"patch": string(patch),
235
+
"branch": branch,
236
+
})
237
+
238
+
req, err := s.newRequest(Method, endpoint, body)
239
+
if err != nil {
240
+
return nil, err
241
+
}
242
+
243
+
return s.client.Do(req)
244
+
}
245
+
246
+
func (s *SignedClient) NewHiddenRef(ownerDid, targetRepo, forkBranch, remoteBranch string) (*http.Response, error) {
247
+
const (
248
+
Method = "POST"
249
+
)
250
+
endpoint := fmt.Sprintf("/%s/%s/hidden-ref/%s/%s", ownerDid, targetRepo, url.PathEscape(forkBranch), url.PathEscape(remoteBranch))
251
+
252
+
req, err := s.newRequest(Method, endpoint, nil)
253
+
if err != nil {
254
+
return nil, err
255
+
}
256
+
257
+
return s.client.Do(req)
258
+
}
+142
knotclient/unsigned.go
+142
knotclient/unsigned.go
···
1
+
package knotclient
2
+
3
+
import (
4
+
"bytes"
5
+
"encoding/json"
6
+
"fmt"
7
+
"io"
8
+
"net/http"
9
+
"net/url"
10
+
"path"
11
+
"strconv"
12
+
13
+
"tangled.sh/tangled.sh/core/types"
14
+
)
15
+
16
+
type UnsignedClient struct {
17
+
Domain string
18
+
Dev bool
19
+
client *http.Client
20
+
}
21
+
22
+
func (u *UnsignedClient) scheme() string {
23
+
if u.Dev {
24
+
return "http"
25
+
}
26
+
27
+
return "https"
28
+
}
29
+
30
+
func (u *UnsignedClient) url() (*url.URL, error) {
31
+
return url.Parse(fmt.Sprintf("%s://%s", u.scheme(), u.Domain))
32
+
}
33
+
34
+
type KnotRequest interface {
35
+
Method() string
36
+
Path() string
37
+
Query() url.Values
38
+
Body() []byte
39
+
}
40
+
41
+
func do[T any](u *UnsignedClient, method, path string, query url.Values, body []byte) (*T, error) {
42
+
// Create a copy of the base URL to avoid modifying the original
43
+
base, err := u.url()
44
+
if err != nil {
45
+
return nil, fmt.Errorf("failed to parse URL: %w", err)
46
+
}
47
+
48
+
// add path
49
+
base = base.JoinPath(path)
50
+
51
+
// add query
52
+
if query != nil {
53
+
base.RawQuery = query.Encode()
54
+
}
55
+
56
+
// Create the request
57
+
req, err := http.NewRequest(method, base.String(), bytes.NewReader(body))
58
+
if err != nil {
59
+
return nil, err
60
+
}
61
+
62
+
resp, err := u.client.Do(req)
63
+
if err != nil {
64
+
return nil, err
65
+
}
66
+
defer resp.Body.Close()
67
+
68
+
body, err = io.ReadAll(resp.Body)
69
+
if err != nil {
70
+
return nil, err
71
+
}
72
+
73
+
var result T
74
+
err = json.Unmarshal(body, &result)
75
+
if err != nil {
76
+
return nil, err
77
+
}
78
+
79
+
return &result, nil
80
+
}
81
+
82
+
func (u *UnsignedClient) Index(ownerDid, repoName, ref string) (*types.RepoIndexResponse, error) {
83
+
method := http.MethodGet
84
+
endpoint := path.Join(ownerDid, repoName, "tree", ref)
85
+
if ref == "" {
86
+
endpoint = path.Join(ownerDid, repoName)
87
+
}
88
+
return do[types.RepoIndexResponse](u, method, endpoint, nil, nil)
89
+
}
90
+
91
+
func (u *UnsignedClient) Log(ownerDid, repoName, ref string, page int) (*types.RepoLogResponse, error) {
92
+
method := http.MethodGet
93
+
endpoint := fmt.Sprintf("/%s/%s/log/%s", ownerDid, repoName, url.PathEscape(ref))
94
+
95
+
query := url.Values{}
96
+
query.Add("page", strconv.Itoa(page))
97
+
query.Add("per_page", strconv.Itoa(60))
98
+
99
+
return do[types.RepoLogResponse](u, method, endpoint, nil, nil)
100
+
}
101
+
102
+
func (u *UnsignedClient) Branches(ownerDid, repoName string) (*types.RepoBranchesResponse, error) {
103
+
method := http.MethodGet
104
+
endpoint := fmt.Sprintf("/%s/%s/branches", ownerDid, repoName)
105
+
106
+
return do[types.RepoBranchesResponse](u, method, endpoint, nil, nil)
107
+
}
108
+
109
+
func (u *UnsignedClient) Tags(ownerDid, repoName string) (*types.RepoTagsResponse, error) {
110
+
method := http.MethodGet
111
+
endpoint := fmt.Sprintf("/%s/%s/tags", ownerDid, repoName)
112
+
113
+
return do[types.RepoTagsResponse](u, method, endpoint, nil, nil)
114
+
}
115
+
116
+
func (u *UnsignedClient) Branch(ownerDid, repoName, branch string) (*types.RepoBranchResponse, error) {
117
+
method := http.MethodGet
118
+
endpoint := fmt.Sprintf("/%s/%s/branches/%s", ownerDid, repoName, url.PathEscape(branch))
119
+
120
+
return do[types.RepoBranchResponse](u, method, endpoint, nil, nil)
121
+
}
122
+
123
+
func (u *UnsignedClient) DefaultBranch(ownerDid, repoName string) (*types.RepoBranchResponse, error) {
124
+
method := http.MethodGet
125
+
endpoint := fmt.Sprintf("/%s/%s/branches/default", ownerDid, repoName)
126
+
127
+
return do[types.RepoBranchResponse](u, method, endpoint, nil, nil)
128
+
}
129
+
130
+
func (u *UnsignedClient) Capabilities() (*types.Capabilities, error) {
131
+
method := http.MethodGet
132
+
endpoint := "capabilities"
133
+
134
+
return do[types.Capabilities](u, method, endpoint, nil, nil)
135
+
}
136
+
137
+
func (u *UnsignedClient) Compare(ownerDid, repoName, rev1, rev2 string) (*types.RepoFormatPatchResponse, error) {
138
+
method := http.MethodGet
139
+
endpoint := fmt.Sprintf("/%s/%s/compare/%s/%s", ownerDid, repoName, url.PathEscape(rev1), url.PathEscape(rev2))
140
+
141
+
return do[types.RepoFormatPatchResponse](u, method, endpoint, nil, nil)
142
+
}