Documentation
¶
Index ¶
- type AggregateVotes
- type AggregateVotesInput
- type Authenticate
- type AuthenticateInput
- type Blockchain
- type Buffer
- type Cache
- type CreateComment
- type CreateCommentInput
- type CreateThread
- type CreateThreadInput
- type CreateVote
- type CreateVoteInput
- type Database
- type DeleteComment
- type DeleteCommentInput
- type DeleteThread
- type DeleteThreadInput
- type GetChallenge
- type GetChallengeInput
- type GetComments
- type GetCommentsInput
- type GetThread
- type GetThreadInput
- type GetThreads
- type GetThreadsInput
- type GetUser
- type GetUserInput
- type HydrateUsers
- type HydrateUsersInput
- type RateLimit
- type RateLimitInput
- type SafeProxy
- type Signin
- type SigninInput
- type Storage
- type Stream
- type UploadImage
- type UploadImageInput
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type AggregateVotes ¶
type AggregateVotes struct {
// contains filtered or unexported fields
}
func NewAggregateVotesUseCase ¶
func NewAggregateVotesUseCase(logger common.Logger, database Database) *AggregateVotes
func (*AggregateVotes) Execute ¶
func (u *AggregateVotes) Execute(ctx context.Context, input AggregateVotesInput)
votes are deduped in several ways:
- first we could have many votes for the same thread/comment, so we mark them as dirty based on votes for that id and only aggregate once
- secondly, there is no guarantee that the votes in the slice are in-order or that the vote we are processing is the latest vote for that thread/comment. so we check the vote's updatedAt timestamp against the latest vote for that thread/comment in the db and only aggregate if the vote is newer
TODO: Could do some batch processing in the db here
type AggregateVotesInput ¶
type Authenticate ¶
type Authenticate struct {
// contains filtered or unexported fields
}
func NewAuthenticateUseCase ¶
func NewAuthenticateUseCase(settings settings.Settings) *Authenticate
func (*Authenticate) Execute ¶
func (u *Authenticate) Execute(ctx context.Context, input *AuthenticateInput) (string, error)
type AuthenticateInput ¶
type AuthenticateInput struct {
Token string
}
type Blockchain ¶
type Blockchain interface { Start(ctx context.Context) Shutdown(ctx context.Context) GetNameByAddress(ctx context.Context, address string) (*string, error) GetAvatarURIByName(ctx context.Context, name string) (*string, error) GetNFTURI(ctx context.Context, standard string, address string, id string) (string, error) }
type Buffer ¶
type Buffer map[string]AggregateVotesInput
type CreateComment ¶
type CreateComment struct {
// contains filtered or unexported fields
}
func NewCreateCommentUseCase ¶
func NewCreateCommentUseCase(database Database, storage Storage) *CreateComment
func (*CreateComment) Execute ¶
func (u *CreateComment) Execute(ctx context.Context, input CreateCommentInput) (entities.Comment, error)
type CreateCommentInput ¶
type CreateThread ¶
type CreateThread struct {
// contains filtered or unexported fields
}
func NewCreateThreadUseCase ¶
func NewCreateThreadUseCase(logger common.Logger, storage Storage, database Database) *CreateThread
func (*CreateThread) Execute ¶
func (u *CreateThread) Execute(ctx context.Context, input CreateThreadInput) (entities.Thread, error)
type CreateThreadInput ¶
type CreateVote ¶
type CreateVote struct {
// contains filtered or unexported fields
}
func NewCreateVoteUseCase ¶
func NewCreateVoteUseCase(stream Stream, logger common.Logger) *CreateVote
func (*CreateVote) Execute ¶
func (u *CreateVote) Execute(ctx context.Context, input CreateVoteInput) error
type CreateVoteInput ¶
type Database ¶
type Database interface { Start(ctx context.Context) Shutdown(ctx context.Context) GetChallengeByAddress(ctx context.Context, address string) (entities.Challenge, error) SaveChallenge(ctx context.Context, challenge entities.Challenge) error GetUserByAddress(ctx context.Context, address string) (entities.User, error) GetThreads(ctx context.Context, limit int64) ([]entities.Thread, error) GetThreadById(ctx context.Context, threadId int64) (entities.Thread, error) GetComments(ctx context.Context, threadId int64, offset int64, limit int64) ([]entities.Comment, int64, error) GetCommentById(ctx context.Context, commentId int64) (entities.Comment, error) UpsertUser(ctx context.Context, address string) error UpdateUser(ctx context.Context, address string, name *string, avatar *entities.Image) error CreateComment(ctx context.Context, threadId int64, address string, repliedToCommentId *int64, content string, imageFileName string, imageUrl string, imageContentType string) (entities.Comment, error) CreateThread(ctx context.Context, address string, title string, content string, imageFileName string, imageUrl string, imageContentType string) (entities.Thread, error) CreateVote(ctx context.Context, vote entities.Vote) error DeleteThread(ctx context.Context, threadId int64) error DeleteComment(ctx context.Context, commentId int64) error AggregateVotes(ctx context.Context, id int64, voteType common.VoteType) error }
type DeleteComment ¶
type DeleteComment struct {
// contains filtered or unexported fields
}
func NewDeleteCommentUseCase ¶
func NewDeleteCommentUseCase(database Database) *DeleteComment
func (*DeleteComment) Execute ¶
func (u *DeleteComment) Execute(ctx context.Context, input DeleteCommentInput) error
type DeleteCommentInput ¶
type DeleteThread ¶
type DeleteThread struct {
// contains filtered or unexported fields
}
func NewDeleteThreadUseCase ¶
func NewDeleteThreadUseCase(database Database) *DeleteThread
func (*DeleteThread) Execute ¶
func (u *DeleteThread) Execute(ctx context.Context, input DeleteThreadInput) error
type DeleteThreadInput ¶
type GetChallenge ¶
type GetChallenge struct {
// contains filtered or unexported fields
}
func NewGetChallengeUseCase ¶
func NewGetChallengeUseCase(database Database) *GetChallenge
func (*GetChallenge) Execute ¶
func (u *GetChallenge) Execute(ctx context.Context, input *GetChallengeInput) (entities.Challenge, error)
type GetChallengeInput ¶
type GetChallengeInput struct {
Address string `validate:"eth_addr"`
}
type GetComments ¶
type GetComments struct {
// contains filtered or unexported fields
}
func NewGetCommentsUseCase ¶
func NewGetCommentsUseCase(database Database) *GetComments
func (*GetComments) Execute ¶
func (u *GetComments) Execute(ctx context.Context, input GetCommentsInput) ([]entities.Comment, int64, error)
type GetCommentsInput ¶
type GetThread ¶
type GetThread struct {
// contains filtered or unexported fields
}
func NewGetThreadUseCase ¶
type GetThreadInput ¶
type GetThreads ¶
type GetThreads struct {
// contains filtered or unexported fields
}
func NewGetThreadsUseCase ¶
func NewGetThreadsUseCase(database Database, logger common.Logger) *GetThreads
func (*GetThreads) Execute ¶
func (u *GetThreads) Execute(ctx context.Context, input GetThreadsInput) ([]entities.Thread, error)
Threads returned are random and thus the concept of pages/offset/count is not relevant
type GetThreadsInput ¶
type GetThreadsInput struct {
Limit int64 `validate:"gte=0,lte=100"`
}
type GetUserInput ¶
type GetUserInput struct {
Address string `validate:"eth_addr"`
}
type HydrateUsers ¶
type HydrateUsers struct {
// contains filtered or unexported fields
}
func NewHydrateUsersUseCase ¶
func NewHydrateUsersUseCase(logger common.Logger, blockchain Blockchain, database Database, storage Storage, safeProxy SafeProxy) *HydrateUsers
func (*HydrateUsers) Execute ¶
func (u *HydrateUsers) Execute(ctx context.Context, input HydrateUsersInput)
There be dragons below.
The general reasoning behind doing all this work async in the backend is that avatars are very dynamic, slow, unreliable and potentially very big. The goal is to have a fast, reliable and small profile pictures that can be rendered in a browser very quickly to not destroy page load times, SEO and user experience. The avatar image is thus resolved, uploaded and cached in our CDN and the url is stored in the database. But resolving the actual image url from an avatar text record is quite an involved process. See: https://docs.ens.domains/ens-improvement-proposals/ensip-12-avatar-text-records
Steps:
- Fetch the avatar text record from ENS using the name
- This record can point to any arbitrary server, so we proxy all the following http requests through a "safe" proxy to avoid leaking sensitive server information
- If nft uri detected, parse information and fetch the nft metadata uri from the contract then follow the metadata uri to get the image url.
- The resulting image url is hashed to derive a unique and idempotent file name
- If IPFS scheme detected on the url, resolve the https ipfs url
- Check if the file already exists in storage
- If not, download the image and upload it to our storage
TODO:
- Supported Data URIs
- Support other chains for NFT URIs
type HydrateUsersInput ¶
type HydrateUsersInput struct {
Addresses []string
}
type RateLimit ¶
type RateLimit struct {
// contains filtered or unexported fields
}
type RateLimitInput ¶
type Signin ¶
type Signin struct {
// contains filtered or unexported fields
}
func NewSigninUseCase ¶
type SigninInput ¶
type UploadImage ¶
type UploadImage struct {
// contains filtered or unexported fields
}
func NewUploadImageUsecase ¶
func NewUploadImageUsecase(logger common.Logger, storage Storage) *UploadImage
func (*UploadImage) Execute ¶
func (u *UploadImage) Execute(ctx context.Context, input UploadImageInput) (entities.Image, error)