Monorepo for Tangled tangled.org

wip

Changed files
+401 -473
appview
knotclient
+1
appview/signer.go
··· 1 +
-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
··· 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
··· 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 + }