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