Monorepo for Tangled
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

hacking on UI

Signed-off-by: oppiliappan <me@oppi.li>

+875 -616
+358 -381
api/tangled/cbor_gen.go
··· 2460 2460 2461 2461 return nil 2462 2462 } 2463 + func (t *GraphVouch) MarshalCBOR(w io.Writer) error { 2464 + if t == nil { 2465 + _, err := w.Write(cbg.CborNull) 2466 + return err 2467 + } 2468 + 2469 + cw := cbg.NewCborWriter(w) 2470 + fieldCount := 4 2471 + 2472 + if t.Reason == nil { 2473 + fieldCount-- 2474 + } 2475 + 2476 + if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { 2477 + return err 2478 + } 2479 + 2480 + // t.Kind (string) (string) 2481 + if len("kind") > 1000000 { 2482 + return xerrors.Errorf("Value in field \"kind\" was too long") 2483 + } 2484 + 2485 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("kind"))); err != nil { 2486 + return err 2487 + } 2488 + if _, err := cw.WriteString(string("kind")); err != nil { 2489 + return err 2490 + } 2491 + 2492 + if len(t.Kind) > 1000000 { 2493 + return xerrors.Errorf("Value in field t.Kind was too long") 2494 + } 2495 + 2496 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.Kind))); err != nil { 2497 + return err 2498 + } 2499 + if _, err := cw.WriteString(string(t.Kind)); err != nil { 2500 + return err 2501 + } 2502 + 2503 + // t.LexiconTypeID (string) (string) 2504 + if len("$type") > 1000000 { 2505 + return xerrors.Errorf("Value in field \"$type\" was too long") 2506 + } 2507 + 2508 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { 2509 + return err 2510 + } 2511 + if _, err := cw.WriteString(string("$type")); err != nil { 2512 + return err 2513 + } 2514 + 2515 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.graph.vouch"))); err != nil { 2516 + return err 2517 + } 2518 + if _, err := cw.WriteString(string("sh.tangled.graph.vouch")); err != nil { 2519 + return err 2520 + } 2521 + 2522 + // t.Reason (string) (string) 2523 + if t.Reason != nil { 2524 + 2525 + if len("reason") > 1000000 { 2526 + return xerrors.Errorf("Value in field \"reason\" was too long") 2527 + } 2528 + 2529 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("reason"))); err != nil { 2530 + return err 2531 + } 2532 + if _, err := cw.WriteString(string("reason")); err != nil { 2533 + return err 2534 + } 2535 + 2536 + if t.Reason == nil { 2537 + if _, err := cw.Write(cbg.CborNull); err != nil { 2538 + return err 2539 + } 2540 + } else { 2541 + if len(*t.Reason) > 1000000 { 2542 + return xerrors.Errorf("Value in field t.Reason was too long") 2543 + } 2544 + 2545 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Reason))); err != nil { 2546 + return err 2547 + } 2548 + if _, err := cw.WriteString(string(*t.Reason)); err != nil { 2549 + return err 2550 + } 2551 + } 2552 + } 2553 + 2554 + // t.CreatedAt (string) (string) 2555 + if len("createdAt") > 1000000 { 2556 + return xerrors.Errorf("Value in field \"createdAt\" was too long") 2557 + } 2558 + 2559 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { 2560 + return err 2561 + } 2562 + if _, err := cw.WriteString(string("createdAt")); err != nil { 2563 + return err 2564 + } 2565 + 2566 + if len(t.CreatedAt) > 1000000 { 2567 + return xerrors.Errorf("Value in field t.CreatedAt was too long") 2568 + } 2569 + 2570 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.CreatedAt))); err != nil { 2571 + return err 2572 + } 2573 + if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 2574 + return err 2575 + } 2576 + return nil 2577 + } 2578 + 2579 + func (t *GraphVouch) UnmarshalCBOR(r io.Reader) (err error) { 2580 + *t = GraphVouch{} 2581 + 2582 + cr := cbg.NewCborReader(r) 2583 + 2584 + maj, extra, err := cr.ReadHeader() 2585 + if err != nil { 2586 + return err 2587 + } 2588 + defer func() { 2589 + if err == io.EOF { 2590 + err = io.ErrUnexpectedEOF 2591 + } 2592 + }() 2593 + 2594 + if maj != cbg.MajMap { 2595 + return fmt.Errorf("cbor input should be of type map") 2596 + } 2597 + 2598 + if extra > cbg.MaxLength { 2599 + return fmt.Errorf("GraphVouch: map struct too large (%d)", extra) 2600 + } 2601 + 2602 + n := extra 2603 + 2604 + nameBuf := make([]byte, 9) 2605 + for i := uint64(0); i < n; i++ { 2606 + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 2607 + if err != nil { 2608 + return err 2609 + } 2610 + 2611 + if !ok { 2612 + // Field doesn't exist on this type, so ignore it 2613 + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { 2614 + return err 2615 + } 2616 + continue 2617 + } 2618 + 2619 + switch string(nameBuf[:nameLen]) { 2620 + // t.Kind (string) (string) 2621 + case "kind": 2622 + 2623 + { 2624 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 2625 + if err != nil { 2626 + return err 2627 + } 2628 + 2629 + t.Kind = string(sval) 2630 + } 2631 + // t.LexiconTypeID (string) (string) 2632 + case "$type": 2633 + 2634 + { 2635 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 2636 + if err != nil { 2637 + return err 2638 + } 2639 + 2640 + t.LexiconTypeID = string(sval) 2641 + } 2642 + // t.Reason (string) (string) 2643 + case "reason": 2644 + 2645 + { 2646 + b, err := cr.ReadByte() 2647 + if err != nil { 2648 + return err 2649 + } 2650 + if b != cbg.CborNull[0] { 2651 + if err := cr.UnreadByte(); err != nil { 2652 + return err 2653 + } 2654 + 2655 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 2656 + if err != nil { 2657 + return err 2658 + } 2659 + 2660 + t.Reason = (*string)(&sval) 2661 + } 2662 + } 2663 + // t.CreatedAt (string) (string) 2664 + case "createdAt": 2665 + 2666 + { 2667 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 2668 + if err != nil { 2669 + return err 2670 + } 2671 + 2672 + t.CreatedAt = string(sval) 2673 + } 2674 + 2675 + default: 2676 + // Field doesn't exist on this type, so ignore it 2677 + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { 2678 + return err 2679 + } 2680 + } 2681 + } 2682 + 2683 + return nil 2684 + } 2463 2685 func (t *Knot) MarshalCBOR(w io.Writer) error { 2464 2686 if t == nil { 2465 2687 _, err := w.Write(cbg.CborNull) ··· 9520 9742 9521 9743 return nil 9522 9744 } 9745 + func (t *RepoPull_Round) MarshalCBOR(w io.Writer) error { 9746 + if t == nil { 9747 + _, err := w.Write(cbg.CborNull) 9748 + return err 9749 + } 9750 + 9751 + cw := cbg.NewCborWriter(w) 9752 + 9753 + if _, err := cw.Write([]byte{162}); err != nil { 9754 + return err 9755 + } 9756 + 9757 + // t.CreatedAt (string) (string) 9758 + if len("createdAt") > 1000000 { 9759 + return xerrors.Errorf("Value in field \"createdAt\" was too long") 9760 + } 9761 + 9762 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { 9763 + return err 9764 + } 9765 + if _, err := cw.WriteString(string("createdAt")); err != nil { 9766 + return err 9767 + } 9768 + 9769 + if len(t.CreatedAt) > 1000000 { 9770 + return xerrors.Errorf("Value in field t.CreatedAt was too long") 9771 + } 9772 + 9773 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.CreatedAt))); err != nil { 9774 + return err 9775 + } 9776 + if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 9777 + return err 9778 + } 9779 + 9780 + // t.PatchBlob (util.LexBlob) (struct) 9781 + if len("patchBlob") > 1000000 { 9782 + return xerrors.Errorf("Value in field \"patchBlob\" was too long") 9783 + } 9784 + 9785 + if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("patchBlob"))); err != nil { 9786 + return err 9787 + } 9788 + if _, err := cw.WriteString(string("patchBlob")); err != nil { 9789 + return err 9790 + } 9791 + 9792 + if err := t.PatchBlob.MarshalCBOR(cw); err != nil { 9793 + return err 9794 + } 9795 + return nil 9796 + } 9797 + 9798 + func (t *RepoPull_Round) UnmarshalCBOR(r io.Reader) (err error) { 9799 + *t = RepoPull_Round{} 9800 + 9801 + cr := cbg.NewCborReader(r) 9802 + 9803 + maj, extra, err := cr.ReadHeader() 9804 + if err != nil { 9805 + return err 9806 + } 9807 + defer func() { 9808 + if err == io.EOF { 9809 + err = io.ErrUnexpectedEOF 9810 + } 9811 + }() 9812 + 9813 + if maj != cbg.MajMap { 9814 + return fmt.Errorf("cbor input should be of type map") 9815 + } 9816 + 9817 + if extra > cbg.MaxLength { 9818 + return fmt.Errorf("RepoPull_Round: map struct too large (%d)", extra) 9819 + } 9820 + 9821 + n := extra 9822 + 9823 + nameBuf := make([]byte, 9) 9824 + for i := uint64(0); i < n; i++ { 9825 + nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 9826 + if err != nil { 9827 + return err 9828 + } 9829 + 9830 + if !ok { 9831 + // Field doesn't exist on this type, so ignore it 9832 + if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { 9833 + return err 9834 + } 9835 + continue 9836 + } 9837 + 9838 + switch string(nameBuf[:nameLen]) { 9839 + // t.CreatedAt (string) (string) 9840 + case "createdAt": 9841 + 9842 + { 9843 + sval, err := cbg.ReadStringWithMax(cr, 1000000) 9844 + if err != nil { 9845 + return err 9846 + } 9847 + 9848 + t.CreatedAt = string(sval) 9849 + } 9850 + // t.PatchBlob (util.LexBlob) (struct) 9851 + case "patchBlob": 9852 + 9853 + { 9854 + 9855 + b, err := cr.ReadByte() 9856 + if err != nil { 9857 + return err 9858 + } 9859 + if b != cbg.CborNull[0] { 9860 + if err := cr.UnreadByte(); err != nil { 9861 + return err 9862 + } 9863 + t.PatchBlob = new(util.LexBlob) 9864 + if err := t.PatchBlob.UnmarshalCBOR(cr); err != nil { 9865 + return xerrors.Errorf("unmarshaling t.PatchBlob pointer: %w", err) 9866 + } 9867 + } 9868 + 9869 + } 9870 + 9871 + default: 9872 + // Field doesn't exist on this type, so ignore it 9873 + if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { 9874 + return err 9875 + } 9876 + } 9877 + } 9878 + 9879 + return nil 9880 + } 9523 9881 func (t *RepoPull_Source) MarshalCBOR(w io.Writer) error { 9524 9882 if t == nil { 9525 9883 _, err := w.Write(cbg.CborNull) ··· 9723 10081 9724 10082 t.RepoDid = (*string)(&sval) 9725 10083 } 9726 - } 9727 - 9728 - default: 9729 - // Field doesn't exist on this type, so ignore it 9730 - if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { 9731 - return err 9732 - } 9733 - } 9734 - } 9735 - 9736 - return nil 9737 - } 9738 - func (t *RepoPull_Round) MarshalCBOR(w io.Writer) error { 9739 - if t == nil { 9740 - _, err := w.Write(cbg.CborNull) 9741 - return err 9742 - } 9743 - 9744 - cw := cbg.NewCborWriter(w) 9745 - 9746 - if _, err := cw.Write([]byte{162}); err != nil { 9747 - return err 9748 - } 9749 - 9750 - // t.CreatedAt (string) (string) 9751 - if len("createdAt") > 1000000 { 9752 - return xerrors.Errorf("Value in field \"createdAt\" was too long") 9753 - } 9754 - 9755 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { 9756 - return err 9757 - } 9758 - if _, err := cw.WriteString(string("createdAt")); err != nil { 9759 - return err 9760 - } 9761 - 9762 - if len(t.CreatedAt) > 1000000 { 9763 - return xerrors.Errorf("Value in field t.CreatedAt was too long") 9764 - } 9765 - 9766 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.CreatedAt))); err != nil { 9767 - return err 9768 - } 9769 - if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 9770 - return err 9771 - } 9772 - 9773 - // t.PatchBlob (util.LexBlob) (struct) 9774 - if len("patchBlob") > 1000000 { 9775 - return xerrors.Errorf("Value in field \"patchBlob\" was too long") 9776 - } 9777 - 9778 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("patchBlob"))); err != nil { 9779 - return err 9780 - } 9781 - if _, err := cw.WriteString(string("patchBlob")); err != nil { 9782 - return err 9783 - } 9784 - 9785 - if err := t.PatchBlob.MarshalCBOR(cw); err != nil { 9786 - return err 9787 - } 9788 - return nil 9789 - } 9790 - 9791 - func (t *RepoPull_Round) UnmarshalCBOR(r io.Reader) (err error) { 9792 - *t = RepoPull_Round{} 9793 - 9794 - cr := cbg.NewCborReader(r) 9795 - 9796 - maj, extra, err := cr.ReadHeader() 9797 - if err != nil { 9798 - return err 9799 - } 9800 - defer func() { 9801 - if err == io.EOF { 9802 - err = io.ErrUnexpectedEOF 9803 - } 9804 - }() 9805 - 9806 - if maj != cbg.MajMap { 9807 - return fmt.Errorf("cbor input should be of type map") 9808 - } 9809 - 9810 - if extra > cbg.MaxLength { 9811 - return fmt.Errorf("RepoPull_Round: map struct too large (%d)", extra) 9812 - } 9813 - 9814 - n := extra 9815 - 9816 - nameBuf := make([]byte, 9) 9817 - for i := uint64(0); i < n; i++ { 9818 - nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 9819 - if err != nil { 9820 - return err 9821 - } 9822 - 9823 - if !ok { 9824 - // Field doesn't exist on this type, so ignore it 9825 - if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { 9826 - return err 9827 - } 9828 - continue 9829 - } 9830 - 9831 - switch string(nameBuf[:nameLen]) { 9832 - // t.CreatedAt (string) (string) 9833 - case "createdAt": 9834 - 9835 - { 9836 - sval, err := cbg.ReadStringWithMax(cr, 1000000) 9837 - if err != nil { 9838 - return err 9839 - } 9840 - 9841 - t.CreatedAt = string(sval) 9842 - } 9843 - // t.PatchBlob (util.LexBlob) (struct) 9844 - case "patchBlob": 9845 - 9846 - { 9847 - 9848 - b, err := cr.ReadByte() 9849 - if err != nil { 9850 - return err 9851 - } 9852 - if b != cbg.CborNull[0] { 9853 - if err := cr.UnreadByte(); err != nil { 9854 - return err 9855 - } 9856 - t.PatchBlob = new(util.LexBlob) 9857 - if err := t.PatchBlob.UnmarshalCBOR(cr); err != nil { 9858 - return xerrors.Errorf("unmarshaling t.PatchBlob pointer: %w", err) 9859 - } 9860 - } 9861 - 9862 10084 } 9863 10085 9864 10086 default: ··· 10810 11032 10811 11033 return nil 10812 11034 } 10813 - func (t *GraphVouch) MarshalCBOR(w io.Writer) error { 10814 - if t == nil { 10815 - _, err := w.Write(cbg.CborNull) 10816 - return err 10817 - } 10818 - 10819 - cw := cbg.NewCborWriter(w) 10820 - fieldCount := 4 10821 - 10822 - if t.Kind == nil { 10823 - fieldCount-- 10824 - } 10825 - 10826 - if t.Reason == nil { 10827 - fieldCount-- 10828 - } 10829 - 10830 - if _, err := cw.Write(cbg.CborEncodeMajorType(cbg.MajMap, uint64(fieldCount))); err != nil { 10831 - return err 10832 - } 10833 - 10834 - // t.Kind (string) (string) 10835 - if t.Kind != nil { 10836 - 10837 - if len("kind") > 1000000 { 10838 - return xerrors.Errorf("Value in field \"kind\" was too long") 10839 - } 10840 - 10841 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("kind"))); err != nil { 10842 - return err 10843 - } 10844 - if _, err := cw.WriteString(string("kind")); err != nil { 10845 - return err 10846 - } 10847 - 10848 - if t.Kind == nil { 10849 - if _, err := cw.Write(cbg.CborNull); err != nil { 10850 - return err 10851 - } 10852 - } else { 10853 - if len(*t.Kind) > 1000000 { 10854 - return xerrors.Errorf("Value in field t.Kind was too long") 10855 - } 10856 - 10857 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Kind))); err != nil { 10858 - return err 10859 - } 10860 - if _, err := cw.WriteString(string(*t.Kind)); err != nil { 10861 - return err 10862 - } 10863 - } 10864 - } 10865 - 10866 - // t.LexiconTypeID (string) (string) 10867 - if len("$type") > 1000000 { 10868 - return xerrors.Errorf("Value in field \"$type\" was too long") 10869 - } 10870 - 10871 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("$type"))); err != nil { 10872 - return err 10873 - } 10874 - if _, err := cw.WriteString(string("$type")); err != nil { 10875 - return err 10876 - } 10877 - 10878 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("sh.tangled.graph.vouch"))); err != nil { 10879 - return err 10880 - } 10881 - if _, err := cw.WriteString(string("sh.tangled.graph.vouch")); err != nil { 10882 - return err 10883 - } 10884 - 10885 - // t.Reason (string) (string) 10886 - if t.Reason != nil { 10887 - 10888 - if len("reason") > 1000000 { 10889 - return xerrors.Errorf("Value in field \"reason\" was too long") 10890 - } 10891 - 10892 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("reason"))); err != nil { 10893 - return err 10894 - } 10895 - if _, err := cw.WriteString(string("reason")); err != nil { 10896 - return err 10897 - } 10898 - 10899 - if t.Reason == nil { 10900 - if _, err := cw.Write(cbg.CborNull); err != nil { 10901 - return err 10902 - } 10903 - } else { 10904 - if len(*t.Reason) > 1000000 { 10905 - return xerrors.Errorf("Value in field t.Reason was too long") 10906 - } 10907 - 10908 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(*t.Reason))); err != nil { 10909 - return err 10910 - } 10911 - if _, err := cw.WriteString(string(*t.Reason)); err != nil { 10912 - return err 10913 - } 10914 - } 10915 - } 10916 - 10917 - // t.CreatedAt (string) (string) 10918 - if len("createdAt") > 1000000 { 10919 - return xerrors.Errorf("Value in field \"createdAt\" was too long") 10920 - } 10921 - 10922 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len("createdAt"))); err != nil { 10923 - return err 10924 - } 10925 - if _, err := cw.WriteString(string("createdAt")); err != nil { 10926 - return err 10927 - } 10928 - 10929 - if len(t.CreatedAt) > 1000000 { 10930 - return xerrors.Errorf("Value in field t.CreatedAt was too long") 10931 - } 10932 - 10933 - if err := cw.WriteMajorTypeHeader(cbg.MajTextString, uint64(len(t.CreatedAt))); err != nil { 10934 - return err 10935 - } 10936 - if _, err := cw.WriteString(string(t.CreatedAt)); err != nil { 10937 - return err 10938 - } 10939 - return nil 10940 - } 10941 - 10942 - func (t *GraphVouch) UnmarshalCBOR(r io.Reader) (err error) { 10943 - *t = GraphVouch{} 10944 - 10945 - cr := cbg.NewCborReader(r) 10946 - 10947 - maj, extra, err := cr.ReadHeader() 10948 - if err != nil { 10949 - return err 10950 - } 10951 - defer func() { 10952 - if err == io.EOF { 10953 - err = io.ErrUnexpectedEOF 10954 - } 10955 - }() 10956 - 10957 - if maj != cbg.MajMap { 10958 - return fmt.Errorf("cbor input should be of type map") 10959 - } 10960 - 10961 - if extra > cbg.MaxLength { 10962 - return fmt.Errorf("GraphVouch: map struct too large (%d)", extra) 10963 - } 10964 - 10965 - n := extra 10966 - 10967 - nameBuf := make([]byte, 9) 10968 - for i := uint64(0); i < n; i++ { 10969 - nameLen, ok, err := cbg.ReadFullStringIntoBuf(cr, nameBuf, 1000000) 10970 - if err != nil { 10971 - return err 10972 - } 10973 - 10974 - if !ok { 10975 - // Field doesn't exist on this type, so ignore it 10976 - if err := cbg.ScanForLinks(cr, func(cid.Cid) {}); err != nil { 10977 - return err 10978 - } 10979 - continue 10980 - } 10981 - 10982 - switch string(nameBuf[:nameLen]) { 10983 - // t.Kind (string) (string) 10984 - case "kind": 10985 - 10986 - { 10987 - b, err := cr.ReadByte() 10988 - if err != nil { 10989 - return err 10990 - } 10991 - if b != cbg.CborNull[0] { 10992 - if err := cr.UnreadByte(); err != nil { 10993 - return err 10994 - } 10995 - 10996 - sval, err := cbg.ReadStringWithMax(cr, 1000000) 10997 - if err != nil { 10998 - return err 10999 - } 11000 - 11001 - t.Kind = (*string)(&sval) 11002 - } 11003 - } 11004 - // t.LexiconTypeID (string) (string) 11005 - case "$type": 11006 - 11007 - { 11008 - sval, err := cbg.ReadStringWithMax(cr, 1000000) 11009 - if err != nil { 11010 - return err 11011 - } 11012 - 11013 - t.LexiconTypeID = string(sval) 11014 - } 11015 - // t.Reason (string) (string) 11016 - case "reason": 11017 - 11018 - { 11019 - b, err := cr.ReadByte() 11020 - if err != nil { 11021 - return err 11022 - } 11023 - if b != cbg.CborNull[0] { 11024 - if err := cr.UnreadByte(); err != nil { 11025 - return err 11026 - } 11027 - 11028 - sval, err := cbg.ReadStringWithMax(cr, 1000000) 11029 - if err != nil { 11030 - return err 11031 - } 11032 - 11033 - t.Reason = (*string)(&sval) 11034 - } 11035 - } 11036 - // t.CreatedAt (string) (string) 11037 - case "createdAt": 11038 - 11039 - { 11040 - sval, err := cbg.ReadStringWithMax(cr, 1000000) 11041 - if err != nil { 11042 - return err 11043 - } 11044 - 11045 - t.CreatedAt = string(sval) 11046 - } 11047 - 11048 - default: 11049 - // Field doesn't exist on this type, so ignore it 11050 - if err := cbg.ScanForLinks(r, func(cid.Cid) {}); err != nil { 11051 - return err 11052 - } 11053 - } 11054 - } 11055 - 11056 - return nil 11057 - }
+1 -1
api/tangled/graphvouch.go
··· 20 20 LexiconTypeID string `json:"$type,const=sh.tangled.graph.vouch" cborgen:"$type,const=sh.tangled.graph.vouch"` 21 21 CreatedAt string `json:"createdAt" cborgen:"createdAt"` 22 22 // kind: Whether this user is being vouched for or denounced 23 - Kind *string `json:"kind,omitempty" cborgen:"kind,omitempty"` 23 + Kind string `json:"kind" cborgen:"kind"` 24 24 // reason: The reason for this vouch/denouncement 25 25 Reason *string `json:"reason,omitempty" cborgen:"reason,omitempty"` 26 26 }
+1
appview/db/db.go
··· 95 95 create table if not exists vouches ( 96 96 did text not null, 97 97 subject_did text not null, 98 + cid text not null, 98 99 kind text not null default 'vouch', 99 100 reason text, 100 101 created_at text not null default (strftime('%Y-%m-%dT%H:%M:%SZ', 'now')),
+38 -106
appview/db/vouch.go
··· 7 7 "strings" 8 8 "time" 9 9 10 + "github.com/ipfs/go-cid" 10 11 "tangled.org/core/appview/models" 11 12 "tangled.org/core/orm" 12 13 ) 13 14 14 15 func AddVouch(e Execer, vouch *models.Vouch) error { 15 - query := `insert or replace into vouches (did, subject_did, kind, reason) values (?, ?, ?, ?)` 16 - _, err := e.Exec(query, vouch.Did, vouch.SubjectDid, vouch.Kind, vouch.Reason) 16 + query := `insert or replace into vouches (did, subject_did, cid, kind, reason) values (?, ?, ?, ?, ?)` 17 + _, err := e.Exec(query, vouch.Did, vouch.SubjectDid, vouch.Cid.String(), vouch.Kind, vouch.Reason) 17 18 return err 18 19 } 19 20 ··· 49 50 } 50 51 51 52 query := fmt.Sprintf( 52 - `select did, subject_did, kind, reason, created_at 53 + `select did, subject_did, cid, kind, reason, created_at 53 54 from vouches 54 55 %s 55 56 order by created_at desc ··· 65 66 for rows.Next() { 66 67 var vouch models.Vouch 67 68 var createdAt string 69 + var cidStr string 68 70 var reason sql.NullString 69 71 err := rows.Scan( 70 72 &vouch.Did, 71 73 &vouch.SubjectDid, 74 + &cidStr, 72 75 &vouch.Kind, 73 76 &reason, 74 77 &createdAt, ··· 76 79 if err != nil { 77 80 return nil, err 78 81 } 82 + 83 + vouch.Cid, err = cid.Parse(cidStr) 84 + if err != nil { 85 + log.Println("unable to parse CID:", err) 86 + continue 87 + } 88 + 79 89 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 80 90 if err != nil { 81 91 log.Println("unable to determine created at time") ··· 101 111 return err 102 112 } 103 113 104 - func GetVouchStats(e Execer, did string) (models.VouchStats, error) { 105 - var vouches, denounces int64 106 - err := e.QueryRow( 107 - `SELECT 108 - COUNT(CASE WHEN kind = 'vouch' THEN 1 END) AS vouches, 109 - COUNT(CASE WHEN kind = 'denounce' THEN 1 END) AS denounces 110 - FROM vouches 111 - WHERE subject_did = ?`, did).Scan(&vouches, &denounces) 112 - if err != nil { 113 - return models.VouchStats{}, err 114 - } 115 - return models.VouchStats{ 116 - Vouches: vouches, 117 - Denounces: denounces, 118 - }, nil 119 - } 120 - 121 114 func GetVouchStatsBatch(e Execer, dids []string) (map[string]models.VouchStats, error) { 122 115 if len(dids) == 0 { 123 116 return nil, nil ··· 181 174 return GetVouches(e, 0, orm.FilterEq("subject_did", did)) 182 175 } 183 176 184 - // GetNetworkVouchesForSubject returns vouches for subjectDid from people that viewerDid follows or vouches for 185 177 func GetNetworkVouchesForSubject(e Execer, viewerDid, subjectDid string, limit int) ([]models.Vouch, error) { 186 178 query := ` 187 - select distinct v.did, v.subject_did, v.kind, v.reason, v.created_at 179 + select distinct v.did, v.subject_did, v.cid, v.kind, v.reason, v.created_at 188 180 from vouches v 189 181 where v.subject_did = ? 190 - and v.kind = 'vouch' 191 182 and v.did in ( 192 - select subject_did from follows where user_did = ? 193 - union 194 183 select subject_did from vouches where did = ? and kind = 'vouch' 195 184 ) 196 185 order by v.created_at desc ··· 212 201 for rows.Next() { 213 202 var vouch models.Vouch 214 203 var createdAt string 204 + var cidStr string 215 205 var reason sql.NullString 216 206 err := rows.Scan( 217 207 &vouch.Did, 218 208 &vouch.SubjectDid, 209 + &cidStr, 219 210 &vouch.Kind, 220 211 &reason, 221 212 &createdAt, ··· 223 214 if err != nil { 224 215 return nil, err 225 216 } 217 + 218 + vouch.Cid, err = cid.Parse(cidStr) 219 + if err != nil { 220 + log.Println("unable to parse CID:", err) 221 + continue 222 + } 223 + 226 224 createdAtTime, err := time.Parse(time.RFC3339, createdAt) 227 225 if err != nil { 228 226 log.Println("unable to determine created at time") ··· 238 236 return vouches, nil 239 237 } 240 238 241 - // GetNetworkDenouncesForSubject returns denounces for subjectDid from people that viewerDid follows or vouches for 242 - func GetNetworkDenouncesForSubject(e Execer, viewerDid, subjectDid string, limit int) ([]models.Vouch, error) { 243 - query := ` 244 - select distinct v.did, v.subject_did, v.kind, v.reason, v.created_at 245 - from vouches v 246 - where v.subject_did = ? 247 - and v.kind = 'denounce' 248 - and v.did in ( 249 - select subject_did from follows where user_did = ? 250 - union 251 - select subject_did from vouches where did = ? and kind = 'vouch' 252 - ) 253 - order by v.created_at desc 254 - ` 255 - args := []any{subjectDid, viewerDid, viewerDid} 256 - 257 - if limit > 0 { 258 - query += " limit ?" 259 - args = append(args, limit) 239 + func GetVouchRelationship(e Execer, viewerDid, subjectDid string) (*models.VouchRelationship, error) { 240 + relationship := &models.VouchRelationship{ 241 + ViewerDid: viewerDid, 242 + SubjectDid: subjectDid, 243 + NetworkVouches: []models.Vouch{}, 260 244 } 261 245 262 - rows, err := e.Query(query, args...) 263 - if err != nil { 246 + // Get direct vouch from viewer to subject (if exists) 247 + directVouch, err := GetVouch(e, viewerDid, subjectDid) 248 + if err == nil { 249 + relationship.NetworkVouches = append(relationship.NetworkVouches, *directVouch) 250 + } else if err != sql.ErrNoRows { 264 251 return nil, err 265 252 } 266 - defer rows.Close() 267 253 268 - var vouches []models.Vouch 269 - for rows.Next() { 270 - var vouch models.Vouch 271 - var createdAt string 272 - var reason sql.NullString 273 - err := rows.Scan( 274 - &vouch.Did, 275 - &vouch.SubjectDid, 276 - &vouch.Kind, 277 - &reason, 278 - &createdAt, 279 - ) 280 - if err != nil { 281 - return nil, err 282 - } 283 - createdAtTime, err := time.Parse(time.RFC3339, createdAt) 284 - if err != nil { 285 - log.Println("unable to determine created at time") 286 - vouch.CreatedAt = time.Now() 287 - } else { 288 - vouch.CreatedAt = createdAtTime 289 - } 290 - if reason.Valid { 291 - vouch.Reason = &reason.String 292 - } 293 - vouches = append(vouches, vouch) 254 + networkVouches, err := GetNetworkVouchesForSubject(e, viewerDid, subjectDid, 0) 255 + if err != nil { 256 + return nil, err 294 257 } 295 - return vouches, nil 296 - } 297 258 298 - // CountNetworkVouchesForSubject returns count of vouches for subjectDid from viewerDid's network 299 - func CountNetworkVouchesForSubject(e Execer, viewerDid, subjectDid string) (int64, error) { 300 - var count int64 301 - err := e.QueryRow(` 302 - select count(distinct v.did) 303 - from vouches v 304 - where v.subject_did = ? 305 - and v.kind = 'vouch' 306 - and v.did in ( 307 - select subject_did from follows where user_did = ? 308 - union 309 - select subject_did from vouches where did = ? and kind = 'vouch' 310 - ) 311 - `, subjectDid, viewerDid, viewerDid).Scan(&count) 312 - return count, err 313 - } 259 + relationship.NetworkVouches = append(relationship.NetworkVouches, networkVouches...) 314 260 315 - // CountNetworkDenouncesForSubject returns count of denounces for subjectDid from viewerDid's network 316 - func CountNetworkDenouncesForSubject(e Execer, viewerDid, subjectDid string) (int64, error) { 317 - var count int64 318 - err := e.QueryRow(` 319 - select count(distinct v.did) 320 - from vouches v 321 - where v.subject_did = ? 322 - and v.kind = 'denounce' 323 - and v.did in ( 324 - select subject_did from follows where user_did = ? 325 - union 326 - select subject_did from vouches where did = ? and kind = 'vouch' 327 - ) 328 - `, subjectDid, viewerDid, viewerDid).Scan(&count) 329 - return count, err 261 + return relationship, nil 330 262 }
+10 -6
appview/ingester.go
··· 211 211 212 212 l := i.Logger.With("handler", "ingestVouch") 213 213 l = l.With("nsid", e.Commit.Collection) 214 + l.Info("ingesting vouch") 214 215 215 216 switch e.Commit.Operation { 216 217 case jmodels.CommitOperationCreate, jmodels.CommitOperationUpdate: ··· 245 246 return err 246 247 } 247 248 248 - kind := "vouch" 249 - if record.Kind != nil { 250 - kind = *record.Kind 249 + kind, err := models.ParseVouchKind(record.Kind) 250 + if err != nil { 251 + l.Error("invalid kind", "kind", kind) 252 + return fmt.Errorf("invalid kind: %s", kind) 251 253 } 252 254 253 - if kind != "vouch" && kind != "denounce" { 254 - l.Error("invalid kind", "kind", kind) 255 - return fmt.Errorf("invalid kind: %s", kind) 255 + recordCid, err := cid.Parse(e.Commit.CID) 256 + if err != nil { 257 + l.Error("invalid cid", "err", err, "cid", e.Commit.CID) 258 + return fmt.Errorf("invalid cid: %w", err) 256 259 } 257 260 258 261 err = db.AddVouch(i.Db, &models.Vouch{ 259 262 Did: did, 260 263 SubjectDid: subjectDID, 264 + Cid: recordCid, 261 265 Kind: kind, 262 266 Reason: record.Reason, 263 267 })
+102 -2
appview/models/vouch.go
··· 1 1 package models 2 2 3 - import "time" 3 + import ( 4 + "fmt" 5 + "time" 6 + 7 + "github.com/ipfs/go-cid" 8 + ) 9 + 10 + type VouchKind string 11 + 12 + const ( 13 + VouchKindVouch VouchKind = "vouch" 14 + VouchKindDenounce VouchKind = "denounce" 15 + ) 16 + 17 + func ParseVouchKind(v string) (VouchKind, error) { 18 + switch v { 19 + case "vouch": 20 + return VouchKindVouch, nil 21 + case "denounce": 22 + return VouchKindVouch, nil 23 + default: 24 + return VouchKindVouch, fmt.Errorf("invalid vouch kind: %s", v) 25 + } 26 + } 4 27 5 28 type Vouch struct { 6 29 Did string 7 30 SubjectDid string 8 - Kind string 31 + Cid cid.Cid 32 + Kind VouchKind 9 33 Reason *string 10 34 CreatedAt time.Time 11 35 } 12 36 37 + func (v Vouch) IsVouch() bool { 38 + return v.Kind == VouchKindVouch 39 + } 40 + 41 + func (v Vouch) IsDenounce() bool { 42 + return v.Kind == VouchKindDenounce 43 + } 44 + 13 45 type VouchStats struct { 14 46 Vouches int64 15 47 Denounces int64 16 48 } 49 + 50 + type VouchRelationship struct { 51 + ViewerDid string 52 + SubjectDid string 53 + 54 + NetworkVouches []Vouch 55 + } 56 + 57 + func (vr *VouchRelationship) IsDirectVouch() bool { 58 + for _, v := range vr.NetworkVouches { 59 + if v.Did == vr.ViewerDid && v.SubjectDid == vr.SubjectDid && v.Kind == VouchKindVouch { 60 + return true 61 + } 62 + } 63 + return false 64 + } 65 + 66 + func (vr *VouchRelationship) IsDirectDenounce() bool { 67 + for _, v := range vr.NetworkVouches { 68 + if v.Did == vr.ViewerDid && v.SubjectDid == vr.SubjectDid && v.Kind == VouchKindDenounce { 69 + return true 70 + } 71 + } 72 + return false 73 + } 74 + 75 + func (vr *VouchRelationship) IndirectVouches() []Vouch { 76 + var indirectVouches []Vouch 77 + for _, v := range vr.NetworkVouches { 78 + if v.Did != vr.ViewerDid { 79 + indirectVouches = append(indirectVouches, v) 80 + } 81 + } 82 + return indirectVouches 83 + } 84 + 85 + func (vr *VouchRelationship) IsEmpty() bool { 86 + return len(vr.NetworkVouches) == 0 87 + } 88 + 89 + func (vr *VouchRelationship) GetDirectVouch() *Vouch { 90 + for _, v := range vr.NetworkVouches { 91 + if v.Did == vr.ViewerDid && v.SubjectDid == vr.SubjectDid { 92 + return &v 93 + } 94 + } 95 + return nil 96 + } 97 + 98 + func (vr *VouchRelationship) VouchStrength() int { 99 + count := 0 100 + for _, v := range vr.NetworkVouches { 101 + if v.Did != vr.ViewerDid && v.Kind == VouchKindVouch { 102 + count++ 103 + } 104 + } 105 + return count 106 + } 107 + 108 + func (vr *VouchRelationship) DenounceStrength() int { 109 + count := 0 110 + for _, v := range vr.NetworkVouches { 111 + if v.Did != vr.ViewerDid && v.Kind == VouchKindDenounce { 112 + count++ 113 + } 114 + } 115 + return count 116 + }
+19 -18
appview/oauth/scopes.go
··· 3 3 var TangledScopes = []string{ 4 4 "atproto", 5 5 6 + "repo:sh.tangled.actor.profile", 7 + "repo:sh.tangled.feed.reaction", 8 + "repo:sh.tangled.feed.star", 9 + "repo:sh.tangled.graph.follow", 10 + "repo:sh.tangled.graph.vouch", 11 + "repo:sh.tangled.knot", 12 + "repo:sh.tangled.knot.member", 13 + "repo:sh.tangled.label.definition", 14 + "repo:sh.tangled.label.op", 6 15 "repo:sh.tangled.publicKey", 7 16 "repo:sh.tangled.repo", 8 - "repo:sh.tangled.repo.pull", 9 - "repo:sh.tangled.repo.pull.comment", 10 17 "repo:sh.tangled.repo.artifact", 18 + "repo:sh.tangled.repo.collaborator", 11 19 "repo:sh.tangled.repo.issue", 12 20 "repo:sh.tangled.repo.issue.comment", 13 - "repo:sh.tangled.repo.collaborator", 14 - "repo:sh.tangled.knot", 15 - "repo:sh.tangled.knot.member", 21 + "repo:sh.tangled.repo.pull", 22 + "repo:sh.tangled.repo.pull.comment", 16 23 "repo:sh.tangled.spindle", 17 24 "repo:sh.tangled.spindle.member", 18 - "repo:sh.tangled.graph.follow", 19 - "repo:sh.tangled.feed.star", 20 - "repo:sh.tangled.feed.reaction", 21 - "repo:sh.tangled.label.definition", 22 - "repo:sh.tangled.label.op", 23 25 "repo:sh.tangled.string", 24 - "repo:sh.tangled.actor.profile", 25 26 26 27 "blob:*/*", 27 28 29 + "rpc:sh.tangled.pipeline.cancelPipeline?aud=*", 30 + "rpc:sh.tangled.repo.addSecret?aud=*", 28 31 "rpc:sh.tangled.repo.create?aud=*", 29 32 "rpc:sh.tangled.repo.delete?aud=*", 30 - "rpc:sh.tangled.repo.merge?aud=*", 31 - "rpc:sh.tangled.repo.hiddenRef?aud=*", 32 33 "rpc:sh.tangled.repo.deleteBranch?aud=*", 33 - "rpc:sh.tangled.repo.setDefaultBranch?aud=*", 34 + "rpc:sh.tangled.repo.forkStatus?aud=*", 34 35 "rpc:sh.tangled.repo.forkSync?aud=*", 35 - "rpc:sh.tangled.repo.forkStatus?aud=*", 36 + "rpc:sh.tangled.repo.hiddenRef?aud=*", 37 + "rpc:sh.tangled.repo.listSecrets?aud=*", 38 + "rpc:sh.tangled.repo.merge?aud=*", 36 39 "rpc:sh.tangled.repo.mergeCheck?aud=*", 37 - "rpc:sh.tangled.pipeline.cancelPipeline?aud=*", 38 - "rpc:sh.tangled.repo.addSecret?aud=*", 39 40 "rpc:sh.tangled.repo.removeSecret?aud=*", 40 - "rpc:sh.tangled.repo.listSecrets?aud=*", 41 + "rpc:sh.tangled.repo.setDefaultBranch?aud=*", 41 42 }
+8 -7
appview/pages/pages.go
··· 604 604 } 605 605 606 606 type ProfileCard struct { 607 - UserDid string 608 - HasProfile bool 609 - FollowStatus models.FollowStatus 610 - Punchcard *models.Punchcard 611 - Profile *models.Profile 612 - Stats ProfileStats 613 - Active string 607 + UserDid string 608 + HasProfile bool 609 + FollowStatus models.FollowStatus 610 + VouchRelationship *models.VouchRelationship 611 + Punchcard *models.Punchcard 612 + Profile *models.Profile 613 + Stats ProfileStats 614 + Active string 614 615 } 615 616 616 617 type ProfileStats struct {
+16 -12
appview/pages/templates/user/fragments/profileCard.html
··· 87 87 </div> 88 88 {{ end }} 89 89 90 - <div class="flex mt-2 items-center gap-2"> 90 + <div class="flex my-2 items-center gap-2"> 91 91 {{ if ne .FollowStatus.String "IsSelf" }} 92 92 {{ template "user/fragments/follow" . }} 93 93 {{ else }} ··· 108 108 </a> 109 109 </div> 110 110 111 + {{ if ne .FollowStatus.String "IsSelf" }} 112 + {{ template "user/fragments/vouch" . }} 113 + {{ end }} 114 + 111 115 </div> 112 116 <div id="update-profile" class="text-red-400 dark:text-red-500"></div> 113 117 </div> ··· 115 119 {{ end }} 116 120 117 121 {{ define "followerFollowing" }} 118 - {{ $root := index . 0 }} 119 - {{ $userIdent := index . 1 }} 120 - {{ with $root }} 121 - <div class="flex items-center gap-2 my-2 overflow-hidden text-ellipsis whitespace-nowrap max-w-full text-sm"> 122 - <span class="flex-shrink-0">{{ i "users" "size-4" }}</span> 123 - <span id="followers" data-followers-did="{{ .UserDid }}"><a href="/{{ $userIdent }}?tab=followers">{{ 124 - .Stats.FollowersCount }} followers</a></span> 125 - <span class="select-none after:content-['·']"></span> 126 - <span id="following"><a href="/{{ $userIdent }}?tab=following">{{ .Stats.FollowingCount }} following</a></span> 127 - </div> 122 + {{ $root := index . 0 }} 123 + {{ $userIdent := index . 1 }} 124 + {{ with $root }} 125 + <div class="flex items-center gap-2 my-2 overflow-hidden text-ellipsis whitespace-nowrap max-w-full text-sm"> 126 + <span class="flex-shrink-0">{{ i "users" "size-4" }}</span> 127 + <span id="followers" data-followers-did="{{ .UserDid }}"><a href="/{{ $userIdent }}?tab=followers">{{ 128 + .Stats.FollowersCount }} followers</a></span> 129 + <span class="select-none after:content-['·']"></span> 130 + <span id="following"><a href="/{{ $userIdent }}?tab=following">{{ .Stats.FollowingCount }} following</a></span> 131 + </div> 132 + {{ end }} 128 133 {{ end }} 129 - {{ end }}
+197
appview/pages/templates/user/fragments/vouch.html
··· 1 + {{ define "user/fragments/vouch" }} 2 + {{ $userIdent := resolve .UserDid }} 3 + {{ $isVouched := false }} 4 + {{ $isDenounced := false }} 5 + {{ $isUndecided := false }} 6 + {{ if .VouchRelationship }} 7 + {{ if .VouchRelationship.IsDirectVouch }} 8 + {{ $isVouched = true }} 9 + {{ else if .VouchRelationship.IsDirectDenounce }} 10 + {{ $isDenounced = true }} 11 + {{ else }} 12 + {{ $isUndecided = true }} 13 + {{ end }} 14 + {{ else }} 15 + {{ $isUndecided = true }} 16 + {{ end }} 17 + <div class="relative"> 18 + <button 19 + id="vouch-btn-{{ normalizeForHtmlId .UserDid }}" 20 + type="button" 21 + popovertarget="vouch-modal-{{ normalizeForHtmlId .UserDid }}" 22 + popovertargetaction="toggle" 23 + class="{{ if $isVouched }}btn-create{{else if $isDenounced}}btn-cancel{{else}}btn{{end}} w-full flex gap-2 items-center justify-center"> 24 + {{ if $isVouched }} 25 + {{ i "shield-check" "size-4" }} vouched 26 + {{ else if $isDenounced }} 27 + {{ i "shield-off" "size-4" }} denounced 28 + {{ else }} 29 + {{ i "shield-question-mark" "size-4" }} vouch 30 + {{ end }} 31 + </button> 32 + 33 + <div 34 + id="vouch-modal-{{ normalizeForHtmlId .UserDid }}" 35 + popover 36 + class="bg-white w-[95%] md:w-96 dark:bg-gray-800 p-4 rounded border border-gray-200 dark:border-gray-700 drop-shadow dark:text-white backdrop:bg-gray-400/50 dark:backdrop:bg-gray-800/50 space-y-2"> 37 + 38 + <form 39 + hx-post="/vouch" 40 + hx-swap="none" 41 + class="flex flex-col gap-4 group"> 42 + 43 + <h3 class="text-lg font-semibold">Vouch for {{ $userIdent }}</h3> 44 + {{ with .VouchRelationship }} 45 + {{ with .GetDirectVouch }} 46 + <p class="text-gray-700 dark:text-gray-300"> 47 + You {{if $isVouched}}vouched{{else}}denounced{{end}} {{ $userIdent }} {{ relTimeFmt .CreatedAt }}. 48 + You can change your descision below. 49 + </p> 50 + {{ end }} 51 + {{ end }} 52 + 53 + {{ if .VouchRelationship }} 54 + {{ if .VouchRelationship.IndirectVouches }} 55 + <h3 class="text-md font-semibold">Vouches from your network</h3> 56 + {{ template "networkVouches" . }} 57 + {{ end }} 58 + {{ end }} 59 + 60 + <input type="hidden" name="subject" value="{{ .UserDid }}" /> 61 + 62 + {{ $labelClass := "grid grid-cols-[auto_1fr_auto] items-center gap-2 rounded p-2 py- ring-1 ring-gray-200 dark:ring-gray-700 cursor-pointer" }} 63 + 64 + <div class="grid grid-cols-3 gap-2"> 65 + <input 66 + id="vouch-{{ normalizeForHtmlId .UserDid }}" 67 + type="radio" 68 + name="kind" 69 + value="vouch" 70 + {{ if $isVouched }}checked{{ end }} 71 + class="peer/vouch hidden appearance-none" /> 72 + <input 73 + id="denounce-{{ normalizeForHtmlId .UserDid }}" 74 + type="radio" 75 + name="kind" 76 + value="denounce" 77 + {{ if $isDenounced }}checked{{ end }} 78 + class="peer/denounce hidden appearance-none" /> 79 + <input 80 + id="none-{{ normalizeForHtmlId .UserDid }}" 81 + type="radio" 82 + name="kind" 83 + value="none" 84 + {{ if $isUndecided }}checked{{ end }} 85 + class="peer/none hidden appearance-none" /> 86 + 87 + <label 88 + for="vouch-{{ normalizeForHtmlId .UserDid }}" 89 + class=" 90 + {{ $labelClass }} 91 + hover:bg-gray-100 peer-checked/vouch:bg-green-200 peer-checked/vouch:text-green-800 peer-checked/vouch:ring-green-400 92 + dark:hover:bg-white/5 dark:peer-checked/vouch:bg-green-700 dark:peer-checked/vouch:text-green-200 dark:peer-checked/vouch:ring-green-600"> 93 + {{ i "shield-check" "size-5" }} 94 + <span class="text-sm font-medium">Vouch</span> 95 + </label> 96 + <label 97 + for="denounce-{{ normalizeForHtmlId .UserDid }}" 98 + class=" 99 + {{ $labelClass }} 100 + hover:bg-gray-100 peer-checked/denounce:bg-red-200 peer-checked/denounce:text-red-800 peer-checked/denounce:ring-red-400 101 + dark:hover:bg-white/5 dark:peer-checked/denounce:bg-red-700 dark:peer-checked/denounce:text-red-200 dark:peer-checked/denounce:ring-red-600"> 102 + {{ i "shield-ban" "size-5" }} 103 + <span class="text-sm font-medium">Denounce</span> 104 + </label> 105 + <label 106 + for="none-{{ normalizeForHtmlId .UserDid }}" 107 + class=" 108 + {{ $labelClass }} 109 + hover:bg-gray-100 peer-checked/none:bg-gray-200 peer-checked/none:text-gray-800 peer-checked/none:ring-gray-400 110 + dark:hover:bg-white/5 dark:peer-checked/none:bg-gray-700 dark:peer-checked/none:text-gray-200 dark:peer-checked/none:ring-gray-600"> 111 + {{ i "shield-off" "size-5" }} 112 + <span class="text-sm font-medium">None</span> 113 + </label> 114 + 115 + <textarea 116 + id="reason-{{ normalizeForHtmlId .UserDid }}" 117 + name="reason" 118 + rows="3" 119 + maxlength="300" 120 + placeholder="Why are you vouching for or denouncing this user?" 121 + class="w-full resize-none col-span-3 peer-checked/none:hidden"> 122 + {{- with .VouchRelationship -}} 123 + {{- with .GetDirectVouch -}} 124 + {{- .Reason -}} 125 + {{- end -}} 126 + {{- end -}} 127 + </textarea> 128 + </div> 129 + 130 + <div class="flex gap-2"> 131 + <button 132 + type="button" 133 + popovertarget="vouch-modal-{{ normalizeForHtmlId .UserDid }}" 134 + popovertargetaction="hide" 135 + class="btn flex-1 flex items-center justify-center gap-2"> 136 + {{ i "x" "size-4" }} 137 + cancel 138 + </button> 139 + <button 140 + type="submit" 141 + class="btn-create flex-1 flex items-center justify-center gap-2 text-white group"> 142 + {{ i "check" "size-4 inline group-[.htmx-request]:hidden" }} 143 + {{ i "loader-circle" "size-4 animate-spin hidden group-[.htmx-request]:inline" }} 144 + {{ if and .VouchRelationship .VouchRelationship.GetDirectVouch }} update {{ else }} save {{end}} 145 + </button> 146 + </div> 147 + </form> 148 + 149 + <div id="error" class="w-full error"></div> 150 + </div> 151 + </div> 152 + {{ end }} 153 + 154 + {{ define "networkVouches" }} 155 + {{ $style := "flex flex-col border divide-y rounded" }} 156 + {{ $indirectVouches := .VouchRelationship.IndirectVouches }} 157 + 158 + {{ $bg := "bg-green-200 dark:bg-green-700" }} 159 + {{ $fg := "text-green-800 dark:text-green-200" }} 160 + {{ $border := "border-green-400 dark:border-green-600" }} 161 + <div class="{{ $style }} {{ $bg }} {{ $fg }} {{ $border }}"> 162 + {{ range .VouchRelationship.IndirectVouches }} 163 + {{ if .IsVouch }} 164 + {{ template "networkVouchItem" . }} 165 + {{ end }} 166 + {{ end }} 167 + </div> 168 + 169 + {{ $bg = "bg-red-200 dark:bg-red-700" }} 170 + {{ $fg = "text-red-800 dark:text-red-200" }} 171 + {{ $border = "border-red-400 dark:border-red-600" }} 172 + <div class="{{ $style }} {{ $bg }} {{ $fg }} {{ $border }}"> 173 + {{ range .VouchRelationship.IndirectVouches }} 174 + {{ if .IsDenounce }} 175 + {{ template "networkVouchItem" . }} 176 + {{ end }} 177 + {{ end }} 178 + </div> 179 + {{ end }} 180 + 181 + {{ define "networkVouchItem" }} 182 + <div class="flex items-center gap-2 p-2"> 183 + <div class="flex-shrink-0 h-fit relative"> 184 + {{ template "user/fragments/picLink" (list .Did "size-8") }} 185 + </div> 186 + <div class="flex-1 min-w-0"> 187 + <div class="text-sm flex items-center gap-1 group-target/comment:bg-yellow-200/30 group-target/comment:dark:bg-yellow-600/30"> 188 + {{ resolve .Did }} 189 + <span class="before:content-['·']"></span> 190 + {{ template "repo/fragments/shortTime" .CreatedAt }} 191 + </div> 192 + <div class="line-clamp-1"> 193 + {{ .Reason | description }} 194 + </div> 195 + </div> 196 + </div> 197 + {{ end }}
+12 -5
appview/state/profile.go
··· 91 91 92 92 loggedInUser := s.oauth.GetMultiAccountUser(r) 93 93 followStatus := models.IsNotFollowing 94 + var vouchRelationship *models.VouchRelationship 95 + 94 96 if loggedInUser != nil { 95 97 followStatus = db.GetFollowStatus(s.db, loggedInUser.Did, did) 98 + vouchRelationship, err = db.GetVouchRelationship(s.db, loggedInUser.Did, did) 96 99 } 97 100 98 101 showPunchcard := s.shouldShowPunchcard(did, loggedInUser.Did) ··· 113 116 } 114 117 115 118 return &pages.ProfileCard{ 116 - UserDid: did, 117 - HasProfile: hasProfile, 118 - Profile: profile, 119 - FollowStatus: followStatus, 119 + UserDid: did, 120 + HasProfile: hasProfile, 121 + Profile: profile, 122 + FollowStatus: followStatus, 123 + VouchRelationship: vouchRelationship, 120 124 Stats: pages.ProfileStats{ 121 125 RepoCount: repoCount, 122 126 StringCount: stringCount, ··· 174 178 l.Error("failed to create timeline", "err", err) 175 179 } 176 180 177 - s.pages.ProfileOverview(w, pages.ProfileOverviewParams{ 181 + err = s.pages.ProfileOverview(w, pages.ProfileOverviewParams{ 178 182 LoggedInUser: s.oauth.GetMultiAccountUser(r), 179 183 Card: profile, 180 184 Repos: pinnedRepos, 181 185 CollaboratingRepos: pinnedCollaboratingRepos, 182 186 ProfileTimeline: timeline, 183 187 }) 188 + if err != nil { 189 + l.Error("failed to render template", "err", err) 190 + } 184 191 } 185 192 186 193 func (s *State) shouldShowPunchcard(targetDid, requesterDid string) bool {
-1
appview/state/router.go
··· 180 180 181 181 r.With(middleware.AuthMiddleware(s.oauth)).Route("/vouch", func(r chi.Router) { 182 182 r.Post("/", s.Vouch) 183 - r.Delete("/", s.Vouch) 184 183 }) 185 184 186 185 r.With(middleware.AuthMiddleware(s.oauth)).Route("/star", func(r chi.Router) {
+10 -9
appview/state/state.go
··· 115 115 config.Jetstream.Endpoint, 116 116 "appview", 117 117 []string{ 118 + tangled.ActorProfileNSID, 119 + tangled.FeedStarNSID, 118 120 tangled.GraphFollowNSID, 119 - tangled.FeedStarNSID, 121 + tangled.GraphVouchNSID, 122 + tangled.KnotMemberNSID, 123 + tangled.KnotNSID, 124 + tangled.LabelDefinitionNSID, 125 + tangled.LabelOpNSID, 120 126 tangled.PublicKeyNSID, 121 127 tangled.RepoArtifactNSID, 122 - tangled.ActorProfileNSID, 123 - tangled.KnotMemberNSID, 128 + tangled.RepoIssueCommentNSID, 129 + tangled.RepoIssueNSID, 130 + tangled.RepoPullNSID, 124 131 tangled.SpindleMemberNSID, 125 132 tangled.SpindleNSID, 126 - tangled.KnotNSID, 127 133 tangled.StringNSID, 128 - tangled.RepoPullNSID, 129 - tangled.RepoIssueNSID, 130 - tangled.RepoIssueCommentNSID, 131 - tangled.LabelDefinitionNSID, 132 - tangled.LabelOpNSID, 133 134 }, 134 135 nil, 135 136 tlog.SubLogger(logger, "jetstream"),
+90 -67
appview/state/vouch.go
··· 1 1 package state 2 2 3 3 import ( 4 - "encoding/json" 5 4 "net/http" 6 5 "time" 7 6 8 7 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 8 lexutil "github.com/bluesky-social/indigo/lex/util" 9 + "github.com/ipfs/go-cid" 10 10 "tangled.org/core/api/tangled" 11 11 "tangled.org/core/appview/db" 12 12 "tangled.org/core/appview/models" 13 + "tangled.org/core/log" 13 14 ) 14 15 15 16 func (s *State) Vouch(w http.ResponseWriter, r *http.Request) { 16 - l := s.logger.With("handler", "Vouch") 17 + l := log.SubLogger(s.logger, "vouch") 18 + l = s.logger.With("handler", "Vouch") 17 19 currentUser := s.oauth.GetMultiAccountUser(r) 18 20 19 - subject := r.URL.Query().Get("subject") 21 + var subject string 22 + 23 + subject = r.FormValue("subject") 24 + 20 25 if subject == "" { 21 26 l.Warn("invalid form: missing subject") 22 - http.Error(w, "missing subject", http.StatusBadRequest) 23 - return 24 - } 25 - 26 - kind := r.URL.Query().Get("kind") 27 - if kind == "" { 28 - kind = "vouch" 29 - } 30 - if kind != "vouch" && kind != "denounce" { 31 - l.Warn("invalid kind", "kind", kind) 32 - http.Error(w, "invalid kind", http.StatusBadRequest) 27 + s.pages.Notice(w, "error", "Missing subject user.") 33 28 return 34 29 } 35 - 36 - reason := r.URL.Query().Get("reason") 37 30 38 31 subjectIdent, err := s.idResolver.ResolveIdent(r.Context(), subject) 39 32 if err != nil { 40 33 l.Error("failed to vouch, invalid did", "subject", subject, "err", err) 41 - http.Error(w, "invalid subject", http.StatusBadRequest) 34 + s.pages.Notice(w, "error", "Could not find that user.") 42 35 return 43 36 } 44 37 45 38 if currentUser.Active.Did == subjectIdent.DID.String() { 46 39 l.Warn("cant vouch or denounce yourself") 47 - http.Error(w, "cannot vouch yourself", http.StatusBadRequest) 40 + s.pages.Notice(w, "error", "You cannot vouch for yourself.") 48 41 return 49 42 } 50 43 51 44 client, err := s.oauth.AuthorizedClient(r) 52 45 if err != nil { 53 46 l.Error("failed to authorize client", "err", err) 54 - http.Error(w, "unauthorized", http.StatusUnauthorized) 47 + s.pages.Notice(w, "error", "Authentication required.") 55 48 return 56 49 } 57 50 58 - switch r.Method { 59 - case http.MethodPost: 60 - createdAt := time.Now().Format(time.RFC3339) 61 - subjectDid := subjectIdent.DID.String() 62 - 63 - var reasonPtr *string 64 - if reason != "" { 65 - reasonPtr = &reason 66 - } 67 - 68 - resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 69 - Collection: tangled.GraphVouchNSID, 70 - Repo: currentUser.Active.Did, 71 - Rkey: subjectDid, 72 - Record: &lexutil.LexiconTypeDecoder{ 73 - Val: &tangled.GraphVouch{ 74 - Kind: &kind, 75 - Reason: reasonPtr, 76 - CreatedAt: createdAt, 77 - }}, 78 - }) 79 - if err != nil { 80 - l.Error("failed to create atproto record", "err", err) 81 - http.Error(w, "failed to create vouch", http.StatusInternalServerError) 82 - return 83 - } 84 - 85 - l.Info("created atproto record", "uri", resp.Uri, "kind", kind) 86 - 87 - vouch := &models.Vouch{ 88 - Did: currentUser.Active.Did, 89 - SubjectDid: subjectDid, 90 - Kind: kind, 91 - Reason: reasonPtr, 92 - } 93 - 94 - err = db.AddVouch(s.db, vouch) 95 - if err != nil { 96 - l.Error("failed to add vouch to db", "err", err) 97 - http.Error(w, "failed to add vouch", http.StatusInternalServerError) 98 - return 99 - } 100 - 51 + if err := r.ParseForm(); err != nil { 52 + l.Warn("failed to parse form", "err", err) 53 + s.pages.Notice(w, "error", "Invalid form data.") 101 54 return 55 + } 102 56 103 - case http.MethodDelete: 104 - subjectDid := subjectIdent.DID.String() 57 + subjectDid := subjectIdent.DID.String() 105 58 59 + // handle "none" by deleting any existing vouch 60 + if r.FormValue("kind") == "none" { 106 61 _, err := db.GetVouch(s.db, currentUser.Active.Did, subjectDid) 107 62 if err != nil { 108 - l.Error("failed to get vouch relationship", "err", err) 109 - http.Error(w, "vouch not found", http.StatusNotFound) 63 + l.Info("no existing vouch to delete") 64 + s.pages.HxRefresh(w) 110 65 return 111 66 } 112 67 ··· 118 73 119 74 if err != nil { 120 75 l.Error("failed to delete vouch record", "err", err) 121 - http.Error(w, "failed to delete vouch", http.StatusInternalServerError) 76 + s.pages.Notice(w, "error", "Failed to delete vouch record.") 122 77 return 123 78 } 124 79 ··· 127 82 l.Warn("failed to delete vouch from DB", "err", err) 128 83 } 129 84 85 + l.Info("deleted vouch record") 86 + s.pages.HxRefresh(w) 130 87 return 131 88 } 89 + 90 + kind, err := models.ParseVouchKind(r.FormValue("kind")) 91 + if err != nil { 92 + l.Warn("failed to parse vouch kind", "err", err) 93 + s.pages.Notice(w, "error", "Invalid action type.") 94 + return 95 + } 96 + 97 + reason := r.FormValue("reason") 98 + createdAt := time.Now().Format(time.RFC3339) 99 + 100 + var reasonPtr *string 101 + if reason != "" { 102 + reasonPtr = &reason 103 + } 104 + 105 + var swapCid *string 106 + existingVouch, err := db.GetVouch(s.db, currentUser.Active.Did, subjectDid) 107 + if err == nil { 108 + cidStr := existingVouch.Cid.String() 109 + swapCid = &cidStr 110 + } 111 + 112 + resp, err := comatproto.RepoPutRecord(r.Context(), client, &comatproto.RepoPutRecord_Input{ 113 + Collection: tangled.GraphVouchNSID, 114 + Repo: currentUser.Active.Did, 115 + Rkey: subjectDid, 116 + SwapRecord: swapCid, 117 + Record: &lexutil.LexiconTypeDecoder{ 118 + Val: &tangled.GraphVouch{ 119 + Kind: string(kind), 120 + Reason: reasonPtr, 121 + CreatedAt: createdAt, 122 + }}, 123 + }) 124 + if err != nil { 125 + l.Error("failed to create atproto record", "err", err) 126 + s.pages.Notice(w, "error", "Failed to create vouch record.") 127 + return 128 + } 129 + 130 + l.Info("created atproto record", "uri", resp.Uri, "kind", kind) 131 + 132 + newCid, err := cid.Parse(resp.Cid) 133 + if err != nil { 134 + l.Error("failed to parse returned cid", "err", err) 135 + s.pages.Notice(w, "error", "Failed to save vouch.") 136 + return 137 + } 138 + 139 + vouch := &models.Vouch{ 140 + Did: currentUser.Active.Did, 141 + SubjectDid: subjectDid, 142 + Cid: newCid, 143 + Kind: kind, 144 + Reason: reasonPtr, 145 + } 146 + 147 + err = db.AddVouch(s.db, vouch) 148 + if err != nil { 149 + l.Error("failed to add vouch to db", "err", err) 150 + s.pages.Notice(w, "error", "Failed to save vouch.") 151 + return 152 + } 153 + 154 + s.pages.HxRefresh(w) 132 155 }
+9
input.css
··· 147 147 disabled:before:bg-green-400 dark:disabled:before:bg-green-600; 148 148 } 149 149 150 + .btn-cancel { 151 + @apply btn text-white 152 + before:bg-red-600 hover:before:bg-red-700 153 + dark:before:bg-red-700 dark:hover:before:bg-red-800 154 + before:border before:border-red-700 hover:before:border-red-800 155 + focus-visible:before:outline-red-500 156 + disabled:before:bg-red-400 dark:disabled:before:bg-red-600; 157 + } 158 + 150 159 .prose { 151 160 overflow-wrap: anywhere; 152 161 }
+4 -1
lexicons/graph/vouch.json
··· 10 10 "record": { 11 11 "type": "object", 12 12 "required": [ 13 + "kind", 13 14 "createdAt" 14 15 ], 15 16 "properties": { ··· 21 22 }, 22 23 "reason": { 23 24 "type": "string", 24 - "description": "The reason for this vouch/denouncement" 25 + "description": "The reason for this vouch/denouncement", 26 + "maxGraphemes": 256, 27 + "maxLength": 2560 25 28 }, 26 29 "createdAt": { 27 30 "type": "string",