forked from tangled.org/core
this repo has no description
1package knotserver 2 3import ( 4 "compress/gzip" 5 "crypto/hmac" 6 "crypto/sha256" 7 "encoding/hex" 8 "encoding/json" 9 "errors" 10 "fmt" 11 "log" 12 "net/http" 13 "net/url" 14 "os" 15 "path/filepath" 16 "strconv" 17 "strings" 18 19 securejoin "github.com/cyphar/filepath-securejoin" 20 "github.com/gliderlabs/ssh" 21 "github.com/go-chi/chi/v5" 22 gogit "github.com/go-git/go-git/v5" 23 "github.com/go-git/go-git/v5/plumbing" 24 "github.com/go-git/go-git/v5/plumbing/object" 25 "tangled.sh/tangled.sh/core/knotserver/db" 26 "tangled.sh/tangled.sh/core/knotserver/git" 27 "tangled.sh/tangled.sh/core/types" 28) 29 30func (h *Handle) Index(w http.ResponseWriter, r *http.Request) { 31 w.Write([]byte("This is a knot server. More info at https://tangled.sh")) 32} 33 34func (h *Handle) Capabilities(w http.ResponseWriter, r *http.Request) { 35 w.Header().Set("Content-Type", "application/json") 36 37 capabilities := map[string]any{ 38 "pull_requests": map[string]any{ 39 "patch_submissions": true, 40 }, 41 } 42 43 jsonData, err := json.Marshal(capabilities) 44 if err != nil { 45 http.Error(w, "Failed to serialize JSON", http.StatusInternalServerError) 46 return 47 } 48 49 w.Write(jsonData) 50} 51 52func (h *Handle) RepoIndex(w http.ResponseWriter, r *http.Request) { 53 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 54 l := h.l.With("path", path, "handler", "RepoIndex") 55 ref := chi.URLParam(r, "ref") 56 ref, _ = url.PathUnescape(ref) 57 58 gr, err := git.Open(path, ref) 59 if err != nil { 60 log.Println(err) 61 if errors.Is(err, plumbing.ErrReferenceNotFound) { 62 resp := types.RepoIndexResponse{ 63 IsEmpty: true, 64 } 65 writeJSON(w, resp) 66 return 67 } else { 68 l.Error("opening repo", "error", err.Error()) 69 notFound(w) 70 return 71 } 72 } 73 74 commits, err := gr.Commits() 75 total := len(commits) 76 if err != nil { 77 writeError(w, err.Error(), http.StatusInternalServerError) 78 l.Error("fetching commits", "error", err.Error()) 79 return 80 } 81 if len(commits) > 10 { 82 commits = commits[:10] 83 } 84 85 branches, err := gr.Branches() 86 if err != nil { 87 l.Error("getting branches", "error", err.Error()) 88 writeError(w, err.Error(), http.StatusInternalServerError) 89 return 90 } 91 92 bs := []types.Branch{} 93 for _, branch := range branches { 94 b := types.Branch{} 95 b.Hash = branch.Hash().String() 96 b.Name = branch.Name().Short() 97 bs = append(bs, b) 98 } 99 100 tags, err := gr.Tags() 101 if err != nil { 102 // Non-fatal, we *should* have at least one branch to show. 103 l.Warn("getting tags", "error", err.Error()) 104 } 105 106 rtags := []*types.TagReference{} 107 for _, tag := range tags { 108 tr := types.TagReference{ 109 Tag: tag.TagObject(), 110 } 111 112 tr.Reference = types.Reference{ 113 Name: tag.Name(), 114 Hash: tag.Hash().String(), 115 } 116 117 if tag.Message() != "" { 118 tr.Message = tag.Message() 119 } 120 121 rtags = append(rtags, &tr) 122 } 123 124 var readmeContent string 125 var readmeFile string 126 for _, readme := range h.c.Repo.Readme { 127 content, _ := gr.FileContent(readme) 128 if len(content) > 0 { 129 readmeContent = string(content) 130 readmeFile = readme 131 } 132 } 133 134 files, err := gr.FileTree("") 135 if err != nil { 136 writeError(w, err.Error(), http.StatusInternalServerError) 137 l.Error("file tree", "error", err.Error()) 138 return 139 } 140 141 if ref == "" { 142 mainBranch, err := gr.FindMainBranch() 143 if err != nil { 144 writeError(w, err.Error(), http.StatusInternalServerError) 145 l.Error("finding main branch", "error", err.Error()) 146 return 147 } 148 ref = mainBranch 149 } 150 151 resp := types.RepoIndexResponse{ 152 IsEmpty: false, 153 Ref: ref, 154 Commits: commits, 155 Description: getDescription(path), 156 Readme: readmeContent, 157 ReadmeFileName: readmeFile, 158 Files: files, 159 Branches: bs, 160 Tags: rtags, 161 TotalCommits: total, 162 } 163 164 writeJSON(w, resp) 165 return 166} 167 168func (h *Handle) RepoTree(w http.ResponseWriter, r *http.Request) { 169 treePath := chi.URLParam(r, "*") 170 ref := chi.URLParam(r, "ref") 171 ref, _ = url.PathUnescape(ref) 172 173 l := h.l.With("handler", "RepoTree", "ref", ref, "treePath", treePath) 174 175 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 176 gr, err := git.Open(path, ref) 177 if err != nil { 178 notFound(w) 179 return 180 } 181 182 files, err := gr.FileTree(treePath) 183 if err != nil { 184 writeError(w, err.Error(), http.StatusInternalServerError) 185 l.Error("file tree", "error", err.Error()) 186 return 187 } 188 189 resp := types.RepoTreeResponse{ 190 Ref: ref, 191 Parent: treePath, 192 Description: getDescription(path), 193 DotDot: filepath.Dir(treePath), 194 Files: files, 195 } 196 197 writeJSON(w, resp) 198 return 199} 200 201func (h *Handle) Blob(w http.ResponseWriter, r *http.Request) { 202 treePath := chi.URLParam(r, "*") 203 ref := chi.URLParam(r, "ref") 204 ref, _ = url.PathUnescape(ref) 205 206 l := h.l.With("handler", "FileContent", "ref", ref, "treePath", treePath) 207 208 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 209 gr, err := git.Open(path, ref) 210 if err != nil { 211 notFound(w) 212 return 213 } 214 215 var isBinaryFile bool = false 216 contents, err := gr.FileContent(treePath) 217 if errors.Is(err, git.ErrBinaryFile) { 218 isBinaryFile = true 219 } else if errors.Is(err, object.ErrFileNotFound) { 220 notFound(w) 221 return 222 } else if err != nil { 223 writeError(w, err.Error(), http.StatusInternalServerError) 224 return 225 } 226 227 bytes := []byte(contents) 228 // safe := string(sanitize(bytes)) 229 sizeHint := len(bytes) 230 231 resp := types.RepoBlobResponse{ 232 Ref: ref, 233 Contents: string(bytes), 234 Path: treePath, 235 IsBinary: isBinaryFile, 236 SizeHint: uint64(sizeHint), 237 } 238 239 h.showFile(resp, w, l) 240} 241 242func (h *Handle) Archive(w http.ResponseWriter, r *http.Request) { 243 name := chi.URLParam(r, "name") 244 file := chi.URLParam(r, "file") 245 246 l := h.l.With("handler", "Archive", "name", name, "file", file) 247 248 // TODO: extend this to add more files compression (e.g.: xz) 249 if !strings.HasSuffix(file, ".tar.gz") { 250 notFound(w) 251 return 252 } 253 254 ref := strings.TrimSuffix(file, ".tar.gz") 255 256 // This allows the browser to use a proper name for the file when 257 // downloading 258 filename := fmt.Sprintf("%s-%s.tar.gz", name, ref) 259 setContentDisposition(w, filename) 260 setGZipMIME(w) 261 262 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 263 gr, err := git.Open(path, ref) 264 if err != nil { 265 notFound(w) 266 return 267 } 268 269 gw := gzip.NewWriter(w) 270 defer gw.Close() 271 272 prefix := fmt.Sprintf("%s-%s", name, ref) 273 err = gr.WriteTar(gw, prefix) 274 if err != nil { 275 // once we start writing to the body we can't report error anymore 276 // so we are only left with printing the error. 277 l.Error("writing tar file", "error", err.Error()) 278 return 279 } 280 281 err = gw.Flush() 282 if err != nil { 283 // once we start writing to the body we can't report error anymore 284 // so we are only left with printing the error. 285 l.Error("flushing?", "error", err.Error()) 286 return 287 } 288} 289 290func (h *Handle) Log(w http.ResponseWriter, r *http.Request) { 291 ref := chi.URLParam(r, "ref") 292 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 293 294 l := h.l.With("handler", "Log", "ref", ref, "path", path) 295 296 gr, err := git.Open(path, ref) 297 if err != nil { 298 notFound(w) 299 return 300 } 301 302 commits, err := gr.Commits() 303 if err != nil { 304 writeError(w, err.Error(), http.StatusInternalServerError) 305 l.Error("fetching commits", "error", err.Error()) 306 return 307 } 308 309 // Get page parameters 310 page := 1 311 pageSize := 30 312 313 if pageParam := r.URL.Query().Get("page"); pageParam != "" { 314 if p, err := strconv.Atoi(pageParam); err == nil && p > 0 { 315 page = p 316 } 317 } 318 319 if pageSizeParam := r.URL.Query().Get("per_page"); pageSizeParam != "" { 320 if ps, err := strconv.Atoi(pageSizeParam); err == nil && ps > 0 { 321 pageSize = ps 322 } 323 } 324 325 // Calculate pagination 326 start := (page - 1) * pageSize 327 end := start + pageSize 328 total := len(commits) 329 330 if start >= total { 331 commits = []*object.Commit{} 332 } else { 333 if end > total { 334 end = total 335 } 336 commits = commits[start:end] 337 } 338 339 resp := types.RepoLogResponse{ 340 Commits: commits, 341 Ref: ref, 342 Description: getDescription(path), 343 Log: true, 344 Total: total, 345 Page: page, 346 PerPage: pageSize, 347 } 348 349 writeJSON(w, resp) 350 return 351} 352 353func (h *Handle) Diff(w http.ResponseWriter, r *http.Request) { 354 ref := chi.URLParam(r, "ref") 355 ref, _ = url.PathUnescape(ref) 356 357 l := h.l.With("handler", "Diff", "ref", ref) 358 359 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 360 gr, err := git.Open(path, ref) 361 if err != nil { 362 notFound(w) 363 return 364 } 365 366 diff, err := gr.Diff() 367 if err != nil { 368 writeError(w, err.Error(), http.StatusInternalServerError) 369 l.Error("getting diff", "error", err.Error()) 370 return 371 } 372 373 resp := types.RepoCommitResponse{ 374 Ref: ref, 375 Diff: diff, 376 } 377 378 writeJSON(w, resp) 379 return 380} 381 382func (h *Handle) Tags(w http.ResponseWriter, r *http.Request) { 383 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 384 l := h.l.With("handler", "Refs") 385 386 gr, err := git.Open(path, "") 387 if err != nil { 388 notFound(w) 389 return 390 } 391 392 tags, err := gr.Tags() 393 if err != nil { 394 // Non-fatal, we *should* have at least one branch to show. 395 l.Warn("getting tags", "error", err.Error()) 396 } 397 398 rtags := []*types.TagReference{} 399 for _, tag := range tags { 400 tr := types.TagReference{ 401 Tag: tag.TagObject(), 402 } 403 404 tr.Reference = types.Reference{ 405 Name: tag.Name(), 406 Hash: tag.Hash().String(), 407 } 408 409 if tag.Message() != "" { 410 tr.Message = tag.Message() 411 } 412 413 rtags = append(rtags, &tr) 414 } 415 416 resp := types.RepoTagsResponse{ 417 Tags: rtags, 418 } 419 420 writeJSON(w, resp) 421 return 422} 423 424func (h *Handle) Branches(w http.ResponseWriter, r *http.Request) { 425 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 426 l := h.l.With("handler", "Branches") 427 428 gr, err := git.Open(path, "") 429 if err != nil { 430 notFound(w) 431 return 432 } 433 434 branches, err := gr.Branches() 435 if err != nil { 436 l.Error("getting branches", "error", err.Error()) 437 writeError(w, err.Error(), http.StatusInternalServerError) 438 return 439 } 440 441 bs := []types.Branch{} 442 for _, branch := range branches { 443 b := types.Branch{} 444 b.Hash = branch.Hash().String() 445 b.Name = branch.Name().Short() 446 bs = append(bs, b) 447 } 448 449 resp := types.RepoBranchesResponse{ 450 Branches: bs, 451 } 452 453 writeJSON(w, resp) 454 return 455} 456 457func (h *Handle) Keys(w http.ResponseWriter, r *http.Request) { 458 l := h.l.With("handler", "Keys") 459 460 switch r.Method { 461 case http.MethodGet: 462 keys, err := h.db.GetAllPublicKeys() 463 if err != nil { 464 writeError(w, err.Error(), http.StatusInternalServerError) 465 l.Error("getting public keys", "error", err.Error()) 466 return 467 } 468 469 data := make([]map[string]any, 0) 470 for _, key := range keys { 471 j := key.JSON() 472 data = append(data, j) 473 } 474 writeJSON(w, data) 475 return 476 477 case http.MethodPut: 478 pk := db.PublicKey{} 479 if err := json.NewDecoder(r.Body).Decode(&pk); err != nil { 480 writeError(w, "invalid request body", http.StatusBadRequest) 481 return 482 } 483 484 _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pk.Key)) 485 if err != nil { 486 writeError(w, "invalid pubkey", http.StatusBadRequest) 487 } 488 489 if err := h.db.AddPublicKey(pk); err != nil { 490 writeError(w, err.Error(), http.StatusInternalServerError) 491 l.Error("adding public key", "error", err.Error()) 492 return 493 } 494 495 w.WriteHeader(http.StatusNoContent) 496 return 497 } 498} 499 500func (h *Handle) NewRepo(w http.ResponseWriter, r *http.Request) { 501 l := h.l.With("handler", "NewRepo") 502 503 data := struct { 504 Did string `json:"did"` 505 Name string `json:"name"` 506 DefaultBranch string `json:"default_branch,omitempty"` 507 }{} 508 509 if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 510 writeError(w, "invalid request body", http.StatusBadRequest) 511 return 512 } 513 514 if data.DefaultBranch == "" { 515 data.DefaultBranch = h.c.Repo.MainBranch 516 } 517 518 did := data.Did 519 name := data.Name 520 defaultBranch := data.DefaultBranch 521 522 relativeRepoPath := filepath.Join(did, name) 523 repoPath, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, relativeRepoPath) 524 err := git.InitBare(repoPath, defaultBranch) 525 if err != nil { 526 l.Error("initializing bare repo", "error", err.Error()) 527 if errors.Is(err, gogit.ErrRepositoryAlreadyExists) { 528 writeError(w, "That repo already exists!", http.StatusConflict) 529 return 530 } else { 531 writeError(w, err.Error(), http.StatusInternalServerError) 532 return 533 } 534 } 535 536 // add perms for this user to access the repo 537 err = h.e.AddRepo(did, ThisServer, relativeRepoPath) 538 if err != nil { 539 l.Error("adding repo permissions", "error", err.Error()) 540 writeError(w, err.Error(), http.StatusInternalServerError) 541 return 542 } 543 544 w.WriteHeader(http.StatusNoContent) 545} 546 547func (h *Handle) RemoveRepo(w http.ResponseWriter, r *http.Request) { 548 l := h.l.With("handler", "RemoveRepo") 549 550 data := struct { 551 Did string `json:"did"` 552 Name string `json:"name"` 553 }{} 554 555 if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 556 writeError(w, "invalid request body", http.StatusBadRequest) 557 return 558 } 559 560 did := data.Did 561 name := data.Name 562 563 if did == "" || name == "" { 564 l.Error("invalid request body, empty did or name") 565 w.WriteHeader(http.StatusBadRequest) 566 return 567 } 568 569 relativeRepoPath := filepath.Join(did, name) 570 repoPath, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, relativeRepoPath) 571 err := os.RemoveAll(repoPath) 572 if err != nil { 573 l.Error("removing repo", "error", err.Error()) 574 writeError(w, err.Error(), http.StatusInternalServerError) 575 return 576 } 577 578 w.WriteHeader(http.StatusNoContent) 579 580} 581func (h *Handle) Merge(w http.ResponseWriter, r *http.Request) { 582 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 583 584 data := types.MergeRequest{} 585 586 if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 587 writeError(w, err.Error(), http.StatusBadRequest) 588 h.l.Error("git: failed to unmarshal json patch", "handler", "Merge", "error", err) 589 return 590 } 591 592 mo := &git.MergeOptions{ 593 AuthorName: data.AuthorName, 594 AuthorEmail: data.AuthorEmail, 595 CommitBody: data.CommitBody, 596 CommitMessage: data.CommitMessage, 597 } 598 599 patch := data.Patch 600 branch := data.Branch 601 gr, err := git.Open(path, branch) 602 if err != nil { 603 notFound(w) 604 return 605 } 606 if err := gr.MergeWithOptions([]byte(patch), branch, mo); err != nil { 607 var mergeErr *git.ErrMerge 608 if errors.As(err, &mergeErr) { 609 conflicts := make([]types.ConflictInfo, len(mergeErr.Conflicts)) 610 for i, conflict := range mergeErr.Conflicts { 611 conflicts[i] = types.ConflictInfo{ 612 Filename: conflict.Filename, 613 Reason: conflict.Reason, 614 } 615 } 616 response := types.MergeCheckResponse{ 617 IsConflicted: true, 618 Conflicts: conflicts, 619 Message: mergeErr.Message, 620 } 621 writeConflict(w, response) 622 h.l.Error("git: merge conflict", "handler", "Merge", "error", mergeErr) 623 } else { 624 writeError(w, err.Error(), http.StatusBadRequest) 625 h.l.Error("git: failed to merge", "handler", "Merge", "error", err.Error()) 626 } 627 return 628 } 629 630 w.WriteHeader(http.StatusOK) 631} 632 633func (h *Handle) MergeCheck(w http.ResponseWriter, r *http.Request) { 634 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 635 636 var data struct { 637 Patch string `json:"patch"` 638 Branch string `json:"branch"` 639 } 640 641 if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 642 writeError(w, err.Error(), http.StatusBadRequest) 643 h.l.Error("git: failed to unmarshal json patch", "handler", "MergeCheck", "error", err) 644 return 645 } 646 647 patch := data.Patch 648 branch := data.Branch 649 gr, err := git.Open(path, branch) 650 if err != nil { 651 notFound(w) 652 return 653 } 654 655 err = gr.MergeCheck([]byte(patch), branch) 656 if err == nil { 657 response := types.MergeCheckResponse{ 658 IsConflicted: false, 659 } 660 writeJSON(w, response) 661 return 662 } 663 664 var mergeErr *git.ErrMerge 665 if errors.As(err, &mergeErr) { 666 conflicts := make([]types.ConflictInfo, len(mergeErr.Conflicts)) 667 for i, conflict := range mergeErr.Conflicts { 668 conflicts[i] = types.ConflictInfo{ 669 Filename: conflict.Filename, 670 Reason: conflict.Reason, 671 } 672 } 673 response := types.MergeCheckResponse{ 674 IsConflicted: true, 675 Conflicts: conflicts, 676 Message: mergeErr.Message, 677 } 678 writeConflict(w, response) 679 h.l.Error("git: merge conflict", "handler", "MergeCheck", "error", mergeErr.Error()) 680 return 681 } 682 writeError(w, err.Error(), http.StatusInternalServerError) 683 h.l.Error("git: failed to check merge", "handler", "MergeCheck", "error", err.Error()) 684} 685 686func (h *Handle) AddMember(w http.ResponseWriter, r *http.Request) { 687 l := h.l.With("handler", "AddMember") 688 689 data := struct { 690 Did string `json:"did"` 691 }{} 692 693 if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 694 writeError(w, "invalid request body", http.StatusBadRequest) 695 return 696 } 697 698 did := data.Did 699 700 if err := h.db.AddDid(did); err != nil { 701 l.Error("adding did", "error", err.Error()) 702 writeError(w, err.Error(), http.StatusInternalServerError) 703 return 704 } 705 h.jc.AddDid(did) 706 707 if err := h.e.AddMember(ThisServer, did); err != nil { 708 l.Error("adding member", "error", err.Error()) 709 writeError(w, err.Error(), http.StatusInternalServerError) 710 return 711 } 712 713 if err := h.fetchAndAddKeys(r.Context(), did); err != nil { 714 l.Error("fetching and adding keys", "error", err.Error()) 715 writeError(w, err.Error(), http.StatusInternalServerError) 716 return 717 } 718 719 w.WriteHeader(http.StatusNoContent) 720} 721 722func (h *Handle) AddRepoCollaborator(w http.ResponseWriter, r *http.Request) { 723 l := h.l.With("handler", "AddRepoCollaborator") 724 725 data := struct { 726 Did string `json:"did"` 727 }{} 728 729 ownerDid := chi.URLParam(r, "did") 730 repo := chi.URLParam(r, "name") 731 732 if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 733 writeError(w, "invalid request body", http.StatusBadRequest) 734 return 735 } 736 737 if err := h.db.AddDid(data.Did); err != nil { 738 l.Error("adding did", "error", err.Error()) 739 writeError(w, err.Error(), http.StatusInternalServerError) 740 return 741 } 742 h.jc.AddDid(data.Did) 743 744 repoName, _ := securejoin.SecureJoin(ownerDid, repo) 745 if err := h.e.AddCollaborator(data.Did, ThisServer, repoName); err != nil { 746 l.Error("adding repo collaborator", "error", err.Error()) 747 writeError(w, err.Error(), http.StatusInternalServerError) 748 return 749 } 750 751 if err := h.fetchAndAddKeys(r.Context(), data.Did); err != nil { 752 l.Error("fetching and adding keys", "error", err.Error()) 753 writeError(w, err.Error(), http.StatusInternalServerError) 754 return 755 } 756 757 w.WriteHeader(http.StatusNoContent) 758} 759 760func (h *Handle) DefaultBranch(w http.ResponseWriter, r *http.Request) { 761 l := h.l.With("handler", "DefaultBranch") 762 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 763 764 gr, err := git.Open(path, "") 765 if err != nil { 766 notFound(w) 767 return 768 } 769 770 branch, err := gr.FindMainBranch() 771 if err != nil { 772 writeError(w, err.Error(), http.StatusInternalServerError) 773 l.Error("getting default branch", "error", err.Error()) 774 return 775 } 776 777 writeJSON(w, types.RepoDefaultBranchResponse{ 778 Branch: branch, 779 }) 780} 781 782func (h *Handle) SetDefaultBranch(w http.ResponseWriter, r *http.Request) { 783 l := h.l.With("handler", "SetDefaultBranch") 784 path, _ := securejoin.SecureJoin(h.c.Repo.ScanPath, didPath(r)) 785 786 data := struct { 787 Branch string `json:"branch"` 788 }{} 789 790 if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 791 writeError(w, err.Error(), http.StatusBadRequest) 792 return 793 } 794 795 gr, err := git.Open(path, "") 796 if err != nil { 797 notFound(w) 798 return 799 } 800 801 err = gr.SetDefaultBranch(data.Branch) 802 if err != nil { 803 writeError(w, err.Error(), http.StatusInternalServerError) 804 l.Error("setting default branch", "error", err.Error()) 805 return 806 } 807 808 w.WriteHeader(http.StatusNoContent) 809} 810 811func (h *Handle) Init(w http.ResponseWriter, r *http.Request) { 812 l := h.l.With("handler", "Init") 813 814 if h.knotInitialized { 815 writeError(w, "knot already initialized", http.StatusConflict) 816 return 817 } 818 819 data := struct { 820 Did string `json:"did"` 821 }{} 822 823 if err := json.NewDecoder(r.Body).Decode(&data); err != nil { 824 l.Error("failed to decode request body", "error", err.Error()) 825 writeError(w, "invalid request body", http.StatusBadRequest) 826 return 827 } 828 829 if data.Did == "" { 830 l.Error("empty DID in request", "did", data.Did) 831 writeError(w, "did is empty", http.StatusBadRequest) 832 return 833 } 834 835 if err := h.db.AddDid(data.Did); err != nil { 836 l.Error("failed to add DID", "error", err.Error()) 837 writeError(w, err.Error(), http.StatusInternalServerError) 838 return 839 } 840 h.jc.AddDid(data.Did) 841 842 if err := h.e.AddOwner(ThisServer, data.Did); err != nil { 843 l.Error("adding owner", "error", err.Error()) 844 writeError(w, err.Error(), http.StatusInternalServerError) 845 return 846 } 847 848 if err := h.fetchAndAddKeys(r.Context(), data.Did); err != nil { 849 l.Error("fetching and adding keys", "error", err.Error()) 850 writeError(w, err.Error(), http.StatusInternalServerError) 851 return 852 } 853 854 close(h.init) 855 856 mac := hmac.New(sha256.New, []byte(h.c.Server.Secret)) 857 mac.Write([]byte("ok")) 858 w.Header().Add("X-Signature", hex.EncodeToString(mac.Sum(nil))) 859 860 w.WriteHeader(http.StatusNoContent) 861} 862 863func (h *Handle) Health(w http.ResponseWriter, r *http.Request) { 864 w.Write([]byte("ok")) 865}