this repo has no description
1package knotclient
2
3import (
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
17type SignerTransport struct {
18 Secret string
19}
20
21func (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
32type SignedClient struct {
33 Secret string
34 Url *url.URL
35 client *http.Client
36}
37
38func 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
64func (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
68func (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
86func (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
106func (s *SignedClient) RepoForkAheadBehind(ownerDid, source, name, branch, hiddenRef string) (*http.Response, error) {
107 const (
108 Method = "GET"
109 )
110 endpoint := fmt.Sprintf("/repo/fork/sync/%s", url.PathEscape(branch))
111
112 body, _ := json.Marshal(map[string]any{
113 "did": ownerDid,
114 "source": source,
115 "name": name,
116 "hiddenref": hiddenRef,
117 })
118
119 req, err := s.newRequest(Method, endpoint, body)
120 if err != nil {
121 return nil, err
122 }
123
124 return s.client.Do(req)
125}
126
127func (s *SignedClient) SyncRepoFork(ownerDid, source, name, branch string) (*http.Response, error) {
128 const (
129 Method = "POST"
130 )
131 endpoint := fmt.Sprintf("/repo/fork/sync/%s", url.PathEscape(branch))
132
133 body, _ := json.Marshal(map[string]any{
134 "did": ownerDid,
135 "source": source,
136 "name": name,
137 })
138
139 req, err := s.newRequest(Method, endpoint, body)
140 if err != nil {
141 return nil, err
142 }
143
144 return s.client.Do(req)
145}
146
147func (s *SignedClient) ForkRepo(ownerDid, source, name string) (*http.Response, error) {
148 const (
149 Method = "POST"
150 Endpoint = "/repo/fork"
151 )
152
153 body, _ := json.Marshal(map[string]any{
154 "did": ownerDid,
155 "source": source,
156 "name": name,
157 })
158
159 req, err := s.newRequest(Method, Endpoint, body)
160 if err != nil {
161 return nil, err
162 }
163
164 return s.client.Do(req)
165}
166
167func (s *SignedClient) RemoveRepo(did, repoName string) (*http.Response, error) {
168 const (
169 Method = "DELETE"
170 Endpoint = "/repo"
171 )
172
173 body, _ := json.Marshal(map[string]any{
174 "did": did,
175 "name": repoName,
176 })
177
178 req, err := s.newRequest(Method, Endpoint, body)
179 if err != nil {
180 return nil, err
181 }
182
183 return s.client.Do(req)
184}
185
186func (s *SignedClient) AddMember(did string) (*http.Response, error) {
187 const (
188 Method = "PUT"
189 Endpoint = "/member/add"
190 )
191
192 body, _ := json.Marshal(map[string]any{
193 "did": did,
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) SetDefaultBranch(ownerDid, repoName, branch string) (*http.Response, error) {
205 const (
206 Method = "PUT"
207 )
208 endpoint := fmt.Sprintf("/%s/%s/branches/default", ownerDid, repoName)
209
210 body, _ := json.Marshal(map[string]any{
211 "branch": branch,
212 })
213
214 req, err := s.newRequest(Method, endpoint, body)
215 if err != nil {
216 return nil, err
217 }
218
219 return s.client.Do(req)
220}
221
222func (s *SignedClient) AddCollaborator(ownerDid, repoName, memberDid string) (*http.Response, error) {
223 const (
224 Method = "POST"
225 )
226 endpoint := fmt.Sprintf("/%s/%s/collaborator/add", ownerDid, repoName)
227
228 body, _ := json.Marshal(map[string]any{
229 "did": memberDid,
230 })
231
232 req, err := s.newRequest(Method, endpoint, body)
233 if err != nil {
234 return nil, err
235 }
236
237 return s.client.Do(req)
238}
239
240func (s *SignedClient) Merge(
241 patch []byte,
242 ownerDid, targetRepo, branch, commitMessage, commitBody, authorName, authorEmail string,
243) (*http.Response, error) {
244 const (
245 Method = "POST"
246 )
247 endpoint := fmt.Sprintf("/%s/%s/merge", ownerDid, targetRepo)
248
249 mr := types.MergeRequest{
250 Branch: branch,
251 CommitMessage: commitMessage,
252 CommitBody: commitBody,
253 AuthorName: authorName,
254 AuthorEmail: authorEmail,
255 Patch: string(patch),
256 }
257
258 body, _ := json.Marshal(mr)
259
260 req, err := s.newRequest(Method, endpoint, body)
261 if err != nil {
262 return nil, err
263 }
264
265 return s.client.Do(req)
266}
267
268func (s *SignedClient) MergeCheck(patch []byte, ownerDid, targetRepo, branch string) (*http.Response, error) {
269 const (
270 Method = "POST"
271 )
272 endpoint := fmt.Sprintf("/%s/%s/merge/check", ownerDid, targetRepo)
273
274 body, _ := json.Marshal(map[string]any{
275 "patch": string(patch),
276 "branch": branch,
277 })
278
279 req, err := s.newRequest(Method, endpoint, body)
280 if err != nil {
281 return nil, err
282 }
283
284 return s.client.Do(req)
285}
286
287func (s *SignedClient) NewHiddenRef(ownerDid, targetRepo, forkBranch, remoteBranch string) (*http.Response, error) {
288 const (
289 Method = "POST"
290 )
291 endpoint := fmt.Sprintf("/%s/%s/hidden-ref/%s/%s", ownerDid, targetRepo, url.PathEscape(forkBranch), url.PathEscape(remoteBranch))
292
293 req, err := s.newRequest(Method, endpoint, nil)
294 if err != nil {
295 return nil, err
296 }
297
298 return s.client.Do(req)
299}