mirror of
https://github.com/versia-pub/versia-go.git
synced 2026-03-12 20:19:15 +01:00
refactor!: add missing fields and docs
This commit is contained in:
parent
61891d891a
commit
6e59386f60
73 changed files with 726 additions and 580 deletions
|
|
@ -1,51 +0,0 @@
|
|||
package lysand
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type Follow struct {
|
||||
Entity
|
||||
|
||||
// Author is the URL to the user that triggered the follow
|
||||
Author *URL `json:"author"`
|
||||
// Followee is the URL to the user that is being followed
|
||||
Followee *URL `json:"followee"`
|
||||
}
|
||||
|
||||
func (f Follow) MarshalJSON() ([]byte, error) {
|
||||
type follow Follow
|
||||
f2 := follow(f)
|
||||
f2.Type = "Follow"
|
||||
return json.Marshal(f2)
|
||||
}
|
||||
|
||||
type FollowAccept struct {
|
||||
Entity
|
||||
|
||||
// Author is the URL to the user that accepted the follow
|
||||
Author *URL `json:"author"`
|
||||
// Follower is the URL to the user that is now following the followee
|
||||
Follower *URL `json:"follower"`
|
||||
}
|
||||
|
||||
func (f FollowAccept) MarshalJSON() ([]byte, error) {
|
||||
type followAccept FollowAccept
|
||||
f2 := followAccept(f)
|
||||
f2.Type = "FollowAccept"
|
||||
return json.Marshal(f2)
|
||||
}
|
||||
|
||||
type FollowReject struct {
|
||||
Entity
|
||||
|
||||
// Author is the URL to the user that rejected the follow
|
||||
Author *URL `json:"author"`
|
||||
// Follower is the URL to the user that is no longer following the followee
|
||||
Follower *URL `json:"follower"`
|
||||
}
|
||||
|
||||
func (f FollowReject) MarshalJSON() ([]byte, error) {
|
||||
type followReject FollowReject
|
||||
f2 := followReject(f)
|
||||
f2.Type = "FollowReject"
|
||||
return json.Marshal(f2)
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
package lysand
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type Undo struct {
|
||||
Entity
|
||||
|
||||
// Author is the URL to the user that triggered the undo action
|
||||
Author *URL `json:"author"`
|
||||
// Object is the URL to the object that was undone
|
||||
Object *URL `json:"object"`
|
||||
}
|
||||
|
||||
func (u Undo) MarshalJSON() ([]byte, error) {
|
||||
type undo Undo
|
||||
u2 := undo(u)
|
||||
u2.Type = "Undo"
|
||||
return json.Marshal(u2)
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
package lysand
|
||||
|
||||
import "encoding/json"
|
||||
|
||||
type Note Publication
|
||||
|
||||
func (n Note) MarshalJSON() ([]byte, error) {
|
||||
type note Note
|
||||
n2 := note(n)
|
||||
n2.Type = "Note"
|
||||
return json.Marshal(n2)
|
||||
}
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
package lysand
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
// Patch is a type that represents a modification to a note. For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://lysand.org/objects/patch
|
||||
type Patch struct {
|
||||
Note
|
||||
|
||||
// PatchedID is the ID of the publication that was patched.
|
||||
// https://lysand.org/objects/patch#patched-id
|
||||
PatchedID uuid.UUID `json:"patched_id"`
|
||||
|
||||
// PatchedAt is the time that the publication was patched.
|
||||
// https://lysand.org/objects/patch#patched-at
|
||||
PatchedAt Time `json:"patched_at"`
|
||||
}
|
||||
|
||||
func (p Patch) MarshalJSON() ([]byte, error) {
|
||||
type patch Patch
|
||||
p2 := patch(p)
|
||||
p2.Type = "Patch"
|
||||
return json.Marshal(p2)
|
||||
}
|
||||
32
pkg/versia/action_delete.go
Normal file
32
pkg/versia/action_delete.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package versia
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
versiautils "github.com/lysand-org/versia-go/pkg/versia/utils"
|
||||
)
|
||||
|
||||
// Delete signals the deletion of an entity. For more information, see the [Spec].
|
||||
// This entity does not have a URI.
|
||||
//
|
||||
// Implementations must ensure that the author of the Delete entity has the authorization to delete the target entity.
|
||||
//
|
||||
// [Spec]: https://versia.pub/entities/delete
|
||||
type Delete struct {
|
||||
Entity
|
||||
|
||||
// Author is the URL to the user that triggered the deletion
|
||||
Author *versiautils.URL `json:"author"`
|
||||
|
||||
// DeletedType is the type of the object that is being deleted
|
||||
DeletedType string `json:"deleted_type"`
|
||||
|
||||
// Deleted is the URL to the object that is being deleted
|
||||
Deleted *versiautils.URL `json:"deleted"`
|
||||
}
|
||||
|
||||
func (d Delete) MarshalJSON() ([]byte, error) {
|
||||
type a Delete
|
||||
d2 := a(d)
|
||||
d2.Type = "Delete"
|
||||
return json.Marshal(d2)
|
||||
}
|
||||
99
pkg/versia/action_follow.go
Normal file
99
pkg/versia/action_follow.go
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package versia
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
versiautils "github.com/lysand-org/versia-go/pkg/versia/utils"
|
||||
)
|
||||
|
||||
// Follow defines a follow relationship between two users. For more information, see the [Spec].
|
||||
//
|
||||
// Once a follow relationship is established, the followee's instance should send all new notes from the followee to
|
||||
// the follower's inbox.
|
||||
//
|
||||
// [Spec]: https://versia.pub/entities/follow
|
||||
type Follow struct {
|
||||
Entity
|
||||
|
||||
// Author is the URL to the user that triggered the follow
|
||||
Author *versiautils.URL `json:"author"`
|
||||
|
||||
// Followee is the URL to the user that is being followed
|
||||
Followee *versiautils.URL `json:"followee"`
|
||||
}
|
||||
|
||||
func (f Follow) MarshalJSON() ([]byte, error) {
|
||||
type follow Follow
|
||||
f2 := follow(f)
|
||||
f2.Type = "Follow"
|
||||
return json.Marshal(f2)
|
||||
}
|
||||
|
||||
// FollowAccept accepts a Follow request, which will form the follow relationship between the two parties.
|
||||
// For more information, see the [Spec].
|
||||
//
|
||||
// This can only be sent by the Followee.
|
||||
//
|
||||
// [Spec]: https://versia.pub/entities/follow-accept
|
||||
type FollowAccept struct {
|
||||
Entity
|
||||
|
||||
// Author is the URL to the user that accepted the follow
|
||||
Author *versiautils.URL `json:"author"`
|
||||
|
||||
// Follower is the URL to the user that is now following the followee
|
||||
Follower *versiautils.URL `json:"follower"`
|
||||
}
|
||||
|
||||
func (f FollowAccept) MarshalJSON() ([]byte, error) {
|
||||
type followAccept FollowAccept
|
||||
f2 := followAccept(f)
|
||||
f2.Type = "FollowAccept"
|
||||
return json.Marshal(f2)
|
||||
}
|
||||
|
||||
// FollowReject rejects a Follow request, which will dismiss the follow relationship between the two parties.
|
||||
// For more information, see the [Spec].
|
||||
//
|
||||
// This can only be sent by the Followee and should not be confused with Unfollow, which can only be sent by the Follower.
|
||||
// FollowReject can still be sent after the relationship has been formed.
|
||||
//
|
||||
// [Spec]: https://versia.pub/entities/follow-reject
|
||||
type FollowReject struct {
|
||||
Entity
|
||||
|
||||
// Author is the URL to the user that rejected the follow
|
||||
Author *versiautils.URL `json:"author"`
|
||||
|
||||
// Follower is the URL to the user that is no longer following the followee
|
||||
Follower *versiautils.URL `json:"follower"`
|
||||
}
|
||||
|
||||
func (f FollowReject) MarshalJSON() ([]byte, error) {
|
||||
type followReject FollowReject
|
||||
f2 := followReject(f)
|
||||
f2.Type = "FollowReject"
|
||||
return json.Marshal(f2)
|
||||
}
|
||||
|
||||
// Unfollow disbands request, which will disband the follow relationship between the two parties.
|
||||
// For more information, see the [Spec].
|
||||
//
|
||||
// This can only be sent by the Follower and should not be confused with FollowReject, which can only be sent by the Followee.
|
||||
//
|
||||
// [Spec]: https://versia.pub/entities/unfollow
|
||||
type Unfollow struct {
|
||||
Entity
|
||||
|
||||
// Author is the URL to the user that unfollowed the followee
|
||||
Author *versiautils.URL `json:"author"`
|
||||
|
||||
// Followee is the URL to the user that has been followed
|
||||
Followee *versiautils.URL `json:"follower"`
|
||||
}
|
||||
|
||||
func (f Unfollow) MarshalJSON() ([]byte, error) {
|
||||
type a Unfollow
|
||||
u := a(f)
|
||||
u.Type = "Unfollow"
|
||||
return json.Marshal(u)
|
||||
}
|
||||
32
pkg/versia/actor_group.go
Normal file
32
pkg/versia/actor_group.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package versia
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
versiautils "github.com/lysand-org/versia-go/pkg/versia/utils"
|
||||
)
|
||||
|
||||
// Group is a way to organize users and notes into communities. For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://versia.pub/entities/pub
|
||||
type Group struct {
|
||||
Entity
|
||||
|
||||
// Name is the group's name / title.
|
||||
Name versiautils.TextContentTypeMap `json:"name"`
|
||||
|
||||
// Description is a description of the group's contents / purpose.
|
||||
Description versiautils.TextContentTypeMap `json:"description"`
|
||||
|
||||
// Members is a list of URLs of the group's members.
|
||||
Members []versiautils.URL `json:"members"`
|
||||
|
||||
// Notes is a URL to the collection of notes associated with this group.
|
||||
Notes *versiautils.URL `json:"notes"`
|
||||
}
|
||||
|
||||
func (g Group) MarshalJSON() ([]byte, error) {
|
||||
type a Group
|
||||
g2 := a(g)
|
||||
g2.Type = "Group"
|
||||
return json.Marshal(g2)
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package lysand
|
||||
package versia
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
versiacrypto "github.com/lysand-org/versia-go/pkg/versia/crypto"
|
||||
versiautils "github.com/lysand-org/versia-go/pkg/versia/utils"
|
||||
)
|
||||
|
||||
// User represents a user object in the Lysand protocol. For more information, see the [Spec].
|
||||
|
|
@ -32,39 +34,39 @@ type User struct {
|
|||
|
||||
// Avatar is the avatar of the user in different image content types.
|
||||
// https://lysand.org/objects/user#avatar
|
||||
Avatar ImageContentTypeMap `json:"avatar,omitempty"`
|
||||
Avatar versiautils.ImageContentTypeMap `json:"avatar,omitempty"`
|
||||
|
||||
// Header is the header image of the user in different image content types.
|
||||
// https://lysand.org/objects/user#header
|
||||
Header ImageContentTypeMap `json:"header,omitempty"`
|
||||
Header versiautils.ImageContentTypeMap `json:"header,omitempty"`
|
||||
|
||||
// Bio is the biography of the user in different text content types.
|
||||
// https://lysand.org/objects/user#bio
|
||||
Bio TextContentTypeMap `json:"bio"`
|
||||
Bio versiautils.TextContentTypeMap `json:"bio"`
|
||||
|
||||
// Fields is a list of fields that the user has filled out.
|
||||
// https://lysand.org/objects/user#fields
|
||||
Fields []Field `json:"fields,omitempty"`
|
||||
Fields []UserField `json:"fields,omitempty"`
|
||||
|
||||
// Featured is the featured posts of the user.
|
||||
// https://lysand.org/objects/user#featured
|
||||
Featured *URL `json:"featured"`
|
||||
Featured *versiautils.URL `json:"featured"`
|
||||
|
||||
// Followers is the followers of the user.
|
||||
// https://lysand.org/objects/user#followers
|
||||
Followers *URL `json:"followers"`
|
||||
Followers *versiautils.URL `json:"followers"`
|
||||
|
||||
// Following is the users that the user is following.
|
||||
// https://lysand.org/objects/user#following
|
||||
Following *URL `json:"following"`
|
||||
Following *versiautils.URL `json:"following"`
|
||||
|
||||
// Inbox is the inbox of the user.
|
||||
// https://lysand.org/objects/user#posts
|
||||
Inbox *URL `json:"inbox"`
|
||||
Inbox *versiautils.URL `json:"inbox"`
|
||||
|
||||
// Outbox is the outbox of the user.
|
||||
// https://lysand.org/objects/user#outbox
|
||||
Outbox *URL `json:"outbox"`
|
||||
Outbox *versiautils.URL `json:"outbox"`
|
||||
}
|
||||
|
||||
func (u User) MarshalJSON() ([]byte, error) {
|
||||
|
|
@ -74,7 +76,50 @@ func (u User) MarshalJSON() ([]byte, error) {
|
|||
return json.Marshal(u2)
|
||||
}
|
||||
|
||||
type Field struct {
|
||||
Key TextContentTypeMap `json:"key"`
|
||||
Value TextContentTypeMap `json:"value"`
|
||||
type UserField struct {
|
||||
Key versiautils.TextContentTypeMap `json:"key"`
|
||||
Value versiautils.TextContentTypeMap `json:"value"`
|
||||
}
|
||||
|
||||
// UserPublicKey represents a public key for a user. For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://lysand.org/security/keys#public-key-cryptography
|
||||
type UserPublicKey struct {
|
||||
Actor *versiautils.URL `json:"actor"`
|
||||
|
||||
// Algorithm can only be `ed25519` for now
|
||||
Algorithm string `json:"algorithm"`
|
||||
|
||||
Key *versiacrypto.SPKIPublicKey `json:"-"`
|
||||
RawKey json.RawMessage `json:"key"`
|
||||
}
|
||||
|
||||
func (k *UserPublicKey) UnmarshalJSON(raw []byte) error {
|
||||
type t UserPublicKey
|
||||
k2 := (*t)(k)
|
||||
|
||||
if err := json.Unmarshal(raw, k2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var err error
|
||||
if k2.Key, err = versiacrypto.UnmarshalSPKIPubKey(k2.Algorithm, k2.RawKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*k = UserPublicKey(*k2)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k UserPublicKey) MarshalJSON() ([]byte, error) {
|
||||
type t UserPublicKey
|
||||
k2 := t(k)
|
||||
|
||||
var err error
|
||||
if k2.RawKey, err = k2.Key.MarshalJSON(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(k2)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versia
|
||||
|
||||
// Attachment is a file or other piece of content that is attached to a post. For more information, see the [Spec].
|
||||
//
|
||||
31
pkg/versia/collection.go
Normal file
31
pkg/versia/collection.go
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
package versia
|
||||
|
||||
import versiautils "github.com/lysand-org/versia-go/pkg/versia/utils"
|
||||
|
||||
// Collection is a paginated group of entities. For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://versia.pub/structures/collection
|
||||
type Collection[T any] struct {
|
||||
// Author represents the author of the collection. `nil` is used to represent the instance.
|
||||
Author *versiautils.URL `json:"author"`
|
||||
|
||||
// First is a URI to the first page of the collection.
|
||||
First *versiautils.URL `json:"first"`
|
||||
|
||||
// Last is a URI to the last page of the collection.
|
||||
// If the collection only has one page, this should be the same as First.
|
||||
Last *versiautils.URL `json:"last"`
|
||||
|
||||
// Total is a count of all entities in the collection across all pages.
|
||||
Total uint64 `json:"total"`
|
||||
|
||||
// Next is a URI to the next page of the collection. If there's no next page, this should be `nil`.
|
||||
Next *versiautils.URL `json:"next"`
|
||||
|
||||
// Previous is a URI to the previous page of the collection. If there's no next page, this should be `nil`.
|
||||
// FIXME(spec): The spec uses `prev` instead of `previous` as the field name.
|
||||
Previous *versiautils.URL `json:"previous"`
|
||||
|
||||
// Items is a list of T for the current page of the collection.
|
||||
Items []T `json:"items"`
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versiacrypto
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versiacrypto
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versiacrypto
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -1,23 +1,30 @@
|
|||
package lysand
|
||||
package versiacrypto
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ed25519"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
versiacrypto "github.com/lysand-org/versia-go/pkg/lysand/crypto"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// SignatureData is a combination of HTTP method, URL (only url.URL#Path and url.URL#RawQuery are required),
|
||||
// a nonce and the Base64 encoded SHA256 hash of the request body.
|
||||
// For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://versia.pub/signatures
|
||||
type SignatureData struct {
|
||||
// RequestMethod is the *lowercase* HTTP method of the request
|
||||
RequestMethod string
|
||||
|
||||
// Nonce is a random byte array, used to prevent replay attacks
|
||||
Nonce string
|
||||
|
||||
// RawPath is the path of the request, without the query string
|
||||
URL *url.URL
|
||||
|
||||
// Digest is the SHA-256 hash of the request body
|
||||
Digest []byte
|
||||
}
|
||||
|
|
@ -31,14 +38,16 @@ func NewSignatureData(method, nonce string, u *url.URL, digest []byte) *Signatur
|
|||
}
|
||||
}
|
||||
|
||||
// String returns the payload to sign
|
||||
func (s *SignatureData) String() string {
|
||||
return fmt.Sprintf("%s %s?%s %s %s", strings.ToLower(s.RequestMethod), s.URL.Path, s.URL.RawQuery, s.Nonce, base64.StdEncoding.EncodeToString(s.Digest))
|
||||
}
|
||||
|
||||
// Validate validate that the SignatureData belongs to the provided public key and matches the provided signature.
|
||||
func (s *SignatureData) Validate(pubKey crypto.PublicKey, signature []byte) bool {
|
||||
data := []byte(s.String())
|
||||
|
||||
verify, err := versiacrypto.NewVerify(pubKey)
|
||||
verify, err := NewVerify(pubKey)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
return false
|
||||
|
|
@ -47,15 +56,21 @@ func (s *SignatureData) Validate(pubKey crypto.PublicKey, signature []byte) bool
|
|||
return verify(data, signature)
|
||||
}
|
||||
|
||||
// Sign signs the SignatureData with the provided private key.
|
||||
func (s *SignatureData) Sign(privKey ed25519.PrivateKey) []byte {
|
||||
return ed25519.Sign(privKey, []byte(s.String()))
|
||||
}
|
||||
|
||||
// Signer is an object, with which requests can be signed with the user's private key.
|
||||
// For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://versia.pub/signatures
|
||||
type Signer struct {
|
||||
PrivateKey ed25519.PrivateKey
|
||||
UserURL *url.URL
|
||||
}
|
||||
|
||||
// Sign signs a signature data and returns the headers to inject into the response.
|
||||
func (s Signer) Sign(signatureData SignatureData) *FederationHeaders {
|
||||
return &FederationHeaders{
|
||||
SignedBy: s.UserURL,
|
||||
|
|
@ -64,11 +79,16 @@ func (s Signer) Sign(signatureData SignatureData) *FederationHeaders {
|
|||
}
|
||||
}
|
||||
|
||||
// Verifier is an object, with which requests can be verified against a user's public key.
|
||||
// For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://versia.pub/signatures
|
||||
type Verifier struct {
|
||||
PublicKey crypto.PublicKey
|
||||
}
|
||||
|
||||
// Verify verifies a request against the public key provided to it duration object creation.
|
||||
func (v Verifier) Verify(method string, u *url.URL, body []byte, fedHeaders *FederationHeaders) bool {
|
||||
return NewSignatureData(method, fedHeaders.Nonce, u, versiacrypto.SHA256(body)).
|
||||
return NewSignatureData(method, fedHeaders.Nonce, u, SHA256(body)).
|
||||
Validate(v.PublicKey, fedHeaders.Signature)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versiacrypto
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
|
|
@ -7,49 +7,6 @@ import (
|
|||
"encoding/json"
|
||||
)
|
||||
|
||||
// UserPublicKey represents a public key for a user. For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://lysand.org/security/keys#public-key-cryptography
|
||||
type UserPublicKey struct {
|
||||
Actor *URL `json:"actor"`
|
||||
|
||||
// Algorithm can only be `ed25519` for now
|
||||
Algorithm string `json:"algorithm"`
|
||||
|
||||
Key *SPKIPublicKey `json:"-"`
|
||||
RawKey json.RawMessage `json:"key"`
|
||||
}
|
||||
|
||||
func (k *UserPublicKey) UnmarshalJSON(raw []byte) error {
|
||||
type t UserPublicKey
|
||||
k2 := (*t)(k)
|
||||
|
||||
if err := json.Unmarshal(raw, k2); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var err error
|
||||
if k2.Key, err = unmarshalSPKIPubKey(k2.Algorithm, k2.RawKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
*k = UserPublicKey(*k2)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k UserPublicKey) MarshalJSON() ([]byte, error) {
|
||||
type t UserPublicKey
|
||||
k2 := t(k)
|
||||
|
||||
var err error
|
||||
if k2.RawKey, err = k2.Key.MarshalJSON(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return json.Marshal(k2)
|
||||
}
|
||||
|
||||
// SPKIPublicKey is a type that represents a [ed25519.PublicKey] in the SPKI
|
||||
// format.
|
||||
type SPKIPublicKey struct {
|
||||
|
|
@ -57,7 +14,7 @@ type SPKIPublicKey struct {
|
|||
Algorithm string
|
||||
}
|
||||
|
||||
func unmarshalSPKIPubKey(algorithm string, raw []byte) (*SPKIPublicKey, error) {
|
||||
func UnmarshalSPKIPubKey(algorithm string, raw []byte) (*SPKIPublicKey, error) {
|
||||
rawStr := ""
|
||||
if err := json.Unmarshal(raw, &rawStr); err != nil {
|
||||
return nil, err
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versiacrypto
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versiacrypto
|
||||
|
||||
func must[In any, Out any](fn func(In) (Out, error), v In) Out {
|
||||
out, err := fn(v)
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
package lysand
|
||||
package versia
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
versiautils "github.com/lysand-org/versia-go/pkg/versia/utils"
|
||||
)
|
||||
|
||||
// Entity is the base type for all Lysand entities. For more information, see the [Spec].
|
||||
|
|
@ -15,10 +16,10 @@ type Entity struct {
|
|||
ID uuid.UUID `json:"id"`
|
||||
|
||||
// URI is the URL to the entity
|
||||
URI *URL `json:"uri"`
|
||||
URI *versiautils.URL `json:"uri"`
|
||||
|
||||
// CreatedAt is the time the entity was created
|
||||
CreatedAt Time `json:"created_at"`
|
||||
CreatedAt versiautils.Time `json:"created_at"`
|
||||
|
||||
// Extensions is a map of active extensions
|
||||
// https://lysand.org/objects/server-metadata#extensions
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versia
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versia
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
@ -16,20 +16,14 @@ func ParseInboxObject(raw json.RawMessage) (any, error) {
|
|||
}
|
||||
|
||||
switch i.Type {
|
||||
case "Publication":
|
||||
m := Publication{}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
case "Note":
|
||||
m := Note{}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
case "Patch":
|
||||
m := Patch{}
|
||||
case "Group":
|
||||
m := Group{}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -52,21 +46,21 @@ func ParseInboxObject(raw json.RawMessage) (any, error) {
|
|||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
case "Undo":
|
||||
m := Undo{}
|
||||
case "Unfollow":
|
||||
m := Unfollow{}
|
||||
if err := json.Unmarshal(raw, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
default:
|
||||
return nil, ErrUnknownType{Type: i.Type}
|
||||
return nil, UnknownEntityTypeError{Type: i.Type}
|
||||
}
|
||||
}
|
||||
|
||||
type ErrUnknownType struct {
|
||||
type UnknownEntityTypeError struct {
|
||||
Type string
|
||||
}
|
||||
|
||||
func (e ErrUnknownType) Error() string {
|
||||
return fmt.Sprintf("unknown inbox object type: %s", e.Type)
|
||||
func (e UnknownEntityTypeError) Error() string {
|
||||
return fmt.Sprintf("unknown entity type: %s", e.Type)
|
||||
}
|
||||
|
|
@ -1,7 +1,9 @@
|
|||
package lysand
|
||||
package versia
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
versiacrypto "github.com/lysand-org/versia-go/pkg/versia/crypto"
|
||||
versiautils "github.com/lysand-org/versia-go/pkg/versia/utils"
|
||||
)
|
||||
|
||||
// InstanceMetadata represents the metadata of a Lysand instance. For more information, see the [Spec].
|
||||
|
|
@ -29,19 +31,19 @@ type InstanceMetadata struct {
|
|||
PublicKey InstancePublicKey `json:"public_key"`
|
||||
|
||||
// SharedInbox is the URL to the instance's shared inbox
|
||||
SharedInbox *URL `json:"shared_inbox,omitempty"`
|
||||
SharedInbox *versiautils.URL `json:"shared_inbox,omitempty"`
|
||||
|
||||
// Moderators is a URL to a collection of moderators
|
||||
Moderators *URL `json:"moderators,omitempty"`
|
||||
Moderators *versiautils.URL `json:"moderators,omitempty"`
|
||||
|
||||
// Admins is a URL to a collection of administrators
|
||||
Admins *URL `json:"admins,omitempty"`
|
||||
Admins *versiautils.URL `json:"admins,omitempty"`
|
||||
|
||||
// Logo is the URL to the instance's logo
|
||||
Logo *ImageContentTypeMap `json:"logo,omitempty"`
|
||||
Logo *versiautils.ImageContentTypeMap `json:"logo,omitempty"`
|
||||
|
||||
// Banner is the URL to the instance's banner
|
||||
Banner *ImageContentTypeMap `json:"banner,omitempty"`
|
||||
Banner *versiautils.ImageContentTypeMap `json:"banner,omitempty"`
|
||||
|
||||
// Software is information about the instance software
|
||||
Software InstanceSoftware `json:"software"`
|
||||
|
|
@ -79,8 +81,8 @@ type InstancePublicKey struct {
|
|||
// Algorithm can only be `ed25519` for now
|
||||
Algorithm string `json:"algorithm"`
|
||||
|
||||
Key *SPKIPublicKey `json:"-"`
|
||||
RawKey json.RawMessage `json:"key"`
|
||||
Key *versiacrypto.SPKIPublicKey `json:"-"`
|
||||
RawKey json.RawMessage `json:"key"`
|
||||
}
|
||||
|
||||
func (k *InstancePublicKey) UnmarshalJSON(raw []byte) error {
|
||||
|
|
@ -91,7 +93,7 @@ func (k *InstancePublicKey) UnmarshalJSON(raw []byte) error {
|
|||
}
|
||||
|
||||
var err error
|
||||
if k2.Key, err = unmarshalSPKIPubKey(k2.Algorithm, k2.RawKey); err != nil {
|
||||
if k2.Key, err = versiacrypto.UnmarshalSPKIPubKey(k2.Algorithm, k2.RawKey); err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -1,34 +1,41 @@
|
|||
package lysand
|
||||
package versia
|
||||
|
||||
// PublicationVisibility is the visibility of a publication. For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://lysand.org/objects/publications#visibility
|
||||
type PublicationVisibility string
|
||||
|
||||
const (
|
||||
// PublicationVisiblePublic means that the publication is visible to everyone.
|
||||
PublicationVisiblePublic PublicationVisibility = "public"
|
||||
// PublicationVisibleUnlisted means that the publication is visible everyone, but should not appear in public timelines and search results.
|
||||
PublicationVisibleUnlisted PublicationVisibility = "unlisted"
|
||||
// PublicationVisibleFollowers means that the publication is visible to followers only.
|
||||
PublicationVisibleFollowers PublicationVisibility = "followers"
|
||||
// PublicationVisibleDirect means that the publication is a direct message, and is visible only to the mentioned users.
|
||||
PublicationVisibleDirect PublicationVisibility = "direct"
|
||||
import (
|
||||
"encoding/json"
|
||||
versiautils "github.com/lysand-org/versia-go/pkg/versia/utils"
|
||||
)
|
||||
|
||||
// Publication is a publication object. For more information, see the [Spec].
|
||||
// NoteVisibility is the visibility of a note. For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://lysand.org/objects/publications
|
||||
type Publication struct {
|
||||
// TODO:
|
||||
// [Spec]: https://lysand.org/objects/publications#visibility
|
||||
type NoteVisibility string
|
||||
|
||||
const (
|
||||
// NoteVisiblePublic means that the Note is visible to everyone.
|
||||
NoteVisiblePublic NoteVisibility = "public"
|
||||
// NoteVisibleUnlisted means that the Note is visible everyone, but should not appear in public timelines and search results.
|
||||
NoteVisibleUnlisted NoteVisibility = "unlisted"
|
||||
// NoteVisibleFollowers means that the Note is visible to followers only.
|
||||
NoteVisibleFollowers NoteVisibility = "followers"
|
||||
// NoteVisibleDirect means that the Note is a direct message, and is visible only to the mentioned users.
|
||||
NoteVisibleDirect NoteVisibility = "direct"
|
||||
)
|
||||
|
||||
// Note is a published message, similar to a tweet (from Twitter) or a toot (from Mastodon).
|
||||
// For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://versia.pub/entities/note
|
||||
type Note struct {
|
||||
Entity
|
||||
|
||||
// Author is the URL to the user
|
||||
// https://lysand.org/objects/publications#author
|
||||
Author *URL `json:"author"`
|
||||
Author *versiautils.URL `json:"author"`
|
||||
|
||||
// Content is the content of the publication
|
||||
// https://lysand.org/objects/publications#content
|
||||
Content TextContentTypeMap `json:"content,omitempty"`
|
||||
Content versiautils.TextContentTypeMap `json:"content,omitempty"`
|
||||
|
||||
// Category is the category of the publication
|
||||
// https://lysand.org/objects/publications#category
|
||||
|
|
@ -44,23 +51,23 @@ type Publication struct {
|
|||
|
||||
// Group is the URL to a group
|
||||
// https://lysand.org/objects/publications#group
|
||||
Group *URL `json:"group,omitempty"`
|
||||
Group *versiautils.URL `json:"group,omitempty"`
|
||||
|
||||
// Attachments is a list of attachment objects, keyed by their MIME type
|
||||
// https://lysand.org/objects/publications#attachments
|
||||
Attachments []ContentTypeMap[Attachment] `json:"attachments,omitempty"`
|
||||
Attachments []versiautils.ContentTypeMap[Attachment] `json:"attachments,omitempty"`
|
||||
|
||||
// RepliesTo is the URL to the publication being replied to
|
||||
// https://lysand.org/objects/publications#replies-to
|
||||
RepliesTo *URL `json:"replies_to,omitempty"`
|
||||
RepliesTo *versiautils.URL `json:"replies_to,omitempty"`
|
||||
|
||||
// Quoting is the URL to the publication being quoted
|
||||
// https://lysand.org/objects/publications#quotes
|
||||
Quoting *URL `json:"quoting,omitempty"`
|
||||
Quoting *versiautils.URL `json:"quoting,omitempty"`
|
||||
|
||||
// Mentions is a list of URLs to users
|
||||
// https://lysand.org/objects/publications#mentionshttps://lysand.org/objects/publications#mentions
|
||||
Mentions []URL `json:"mentions,omitempty"`
|
||||
Mentions []versiautils.URL `json:"mentions,omitempty"`
|
||||
|
||||
// Subject is the subject of the publication
|
||||
// https://lysand.org/objects/publications#subject
|
||||
|
|
@ -72,27 +79,34 @@ type Publication struct {
|
|||
|
||||
// Visibility is the visibility of the publication
|
||||
// https://lysand.org/objects/publications#visibility
|
||||
Visibility PublicationVisibility `json:"visibility"`
|
||||
Visibility NoteVisibility `json:"visibility"`
|
||||
}
|
||||
|
||||
func (p Note) MarshalJSON() ([]byte, error) {
|
||||
type a Note
|
||||
n2 := a(p)
|
||||
n2.Type = "Note"
|
||||
return json.Marshal(n2)
|
||||
}
|
||||
|
||||
// LinkPreview is a preview of a link. For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://lysand.org/objects/publications#types
|
||||
type LinkPreview struct {
|
||||
Link URL `json:"link"`
|
||||
Title string `json:"title"`
|
||||
Description *string `json:"description"`
|
||||
Image *URL `json:"image"`
|
||||
Icon *URL `json:"icon"`
|
||||
Link *versiautils.URL `json:"link"`
|
||||
Title string `json:"title"`
|
||||
Description *string `json:"description"`
|
||||
Image *versiautils.URL `json:"image"`
|
||||
Icon *versiautils.URL `json:"icon"`
|
||||
}
|
||||
|
||||
// Device is the device that creates publications. For more information, see the [Spec].
|
||||
//
|
||||
// [Spec]: https://lysand.org/objects/publications#types
|
||||
type Device struct {
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version,omitempty"`
|
||||
URL *URL `json:"url,omitempty"`
|
||||
Name string `json:"name"`
|
||||
Version string `json:"version,omitempty"`
|
||||
URL *versiautils.URL `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
// CategoryType is the type of publication. For more information, see the [Spec].
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versiautils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versiautils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package lysand
|
||||
package versiautils
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
Loading…
Add table
Add a link
Reference in a new issue