status-go/server/server_media.go

308 lines
9.5 KiB
Go

package server
import (
"crypto/tls"
"database/sql"
"errors"
"fmt"
"net/url"
"strconv"
"github.com/status-im/status-go/ipfs"
"github.com/status-im/status-go/logutils"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/services/wallet/thirdparty"
"github.com/status-im/status-go/signal"
)
type MediaServerOption func(*MediaServer)
func WithMediaServerDisableTLS(disableTLS bool) MediaServerOption {
return func(s *MediaServer) {
s.disableTLS = disableTLS
}
}
type MediaServer struct {
Server
db *sql.DB
downloader *ipfs.Downloader
multiaccountsDB *multiaccounts.Database
walletDB *sql.DB
communityImagesReader func(communityID string) (map[string]*protobuf.IdentityImage, error)
communityTokenReader func(communityID string) ([]*protobuf.CommunityTokenMetadata, error)
communityImageVersionReader func(communityID string) uint32
// disableTLS controls whether the media server uses HTTP instead of HTTPS.
// Set to true to avoid TLS certificate issues with react-native-fast-image
// on Android, which has limitations with dynamic certificate updates.
// Pls check doc/use-status-backend-server.md in status-mobile for more details
disableTLS bool
}
func initMediaCertificate(disableTLS bool) (*tls.Certificate, error) {
if disableTLS {
return nil, nil
}
cert, _, err := generateMediaTLSCert()
if err != nil {
return nil, err
}
return cert, nil
}
// NewMediaServer returns a *MediaServer
func NewMediaServer(db *sql.DB, downloader *ipfs.Downloader, multiaccountsDB *multiaccounts.Database, walletDB *sql.DB, opts ...MediaServerOption) (*MediaServer, error) {
s := &MediaServer{
disableTLS: false,
db: db,
downloader: downloader,
multiaccountsDB: multiaccountsDB,
walletDB: walletDB,
}
for _, opt := range opts {
opt(s)
}
cert, err := initMediaCertificate(s.disableTLS)
if err != nil {
return nil, err
}
s.Server = NewServer(
cert,
Localhost,
signal.SendMediaServerStarted,
logutils.ZapLogger().Named("MediaServer"),
)
s.SetHandlers(HandlerPatternMap{
accountImagesPath: handleAccountImages(s.multiaccountsDB, s.logger),
accountInitialsPath: handleAccountInitials(s.multiaccountsDB, s.logger),
audioPath: handleAudio(s.db, s.logger),
contactImagesPath: handleContactImages(s.db, s.logger),
discordAttachmentsPath: handleDiscordAttachment(s.db, s.logger),
discordAuthorsPath: handleDiscordAuthorAvatar(s.db, s.logger),
generateQRCode: handleQRCodeGeneration(s.multiaccountsDB, s.logger),
imagesPath: handleImage(s.db, s.logger),
ipfsPath: handleIPFS(s.downloader, s.logger),
LinkPreviewThumbnailPath: handleLinkPreviewThumbnail(s.db, s.logger),
LinkPreviewFaviconPath: handleLinkPreviewFavicon(s.db, s.logger),
StatusLinkPreviewThumbnailPath: handleStatusLinkPreviewThumbnail(s.db, s.logger),
communityTokenImagesPath: handleCommunityTokenImages(s.db, s.logger),
communityDescriptionImagesPath: handleCommunityDescriptionImagesPath(s.db, s.getCommunityImage, s.logger),
communityDescriptionTokenImagesPath: handleCommunityDescriptionTokenImagesPath(s.db, s.getCommunityTokens, s.logger),
walletCommunityImagesPath: handleWalletCommunityImages(s.walletDB, s.logger),
walletCollectionImagesPath: handleWalletCollectionImages(s.walletDB, s.logger),
walletCollectibleImagesPath: handleWalletCollectibleImages(s.walletDB, s.logger),
})
return s, nil
}
func (s *MediaServer) MakeBaseURL() *url.URL {
return &url.URL{
Scheme: map[bool]string{true: "http", false: "https"}[s.disableTLS],
Host: s.mustGetHost(),
}
}
func (s *MediaServer) SetCommunityImageVersionReader(getFunc func(communityID string) uint32) {
s.communityImageVersionReader = getFunc
}
func (s *MediaServer) getCommunityImageVersion(communityID string) uint32 {
if s.communityImageVersionReader == nil {
return 0
}
return s.communityImageVersionReader(communityID)
}
func (s *MediaServer) SetCommunityImageReader(getFunc func(communityID string) (map[string]*protobuf.IdentityImage, error)) {
s.communityImagesReader = getFunc
}
func (s *MediaServer) getCommunityImage(communityID string) (map[string]*protobuf.IdentityImage, error) {
if s.communityImagesReader == nil {
return nil, errors.New("community image reader not set")
}
return s.communityImagesReader(communityID)
}
func (s *MediaServer) SetCommunityTokensReader(getFunc func(communityID string) ([]*protobuf.CommunityTokenMetadata, error)) {
s.communityTokenReader = getFunc
}
func (s *MediaServer) getCommunityTokens(communityID string) ([]*protobuf.CommunityTokenMetadata, error) {
if s.communityTokenReader == nil {
return nil, errors.New("community token reader not set")
}
return s.communityTokenReader(communityID)
}
func (s *MediaServer) MakeImageServerURL() string {
u := s.MakeBaseURL()
u.Path = basePath + "/"
return u.String()
}
func (s *MediaServer) MakeImageURL(id string) string {
u := s.MakeBaseURL()
u.Path = imagesPath
u.RawQuery = url.Values{"messageId": {id}}.Encode()
return u.String()
}
func (s *MediaServer) MakeLinkPreviewThumbnailURL(msgID string, previewURL string) string {
u := s.MakeBaseURL()
u.Path = LinkPreviewThumbnailPath
u.RawQuery = url.Values{"message-id": {msgID}, "url": {previewURL}}.Encode()
return u.String()
}
func (s *MediaServer) MakeStatusLinkPreviewThumbnailURL(msgID string, previewURL string, imageID common.MediaServerImageID) string {
u := s.MakeBaseURL()
u.Path = StatusLinkPreviewThumbnailPath
u.RawQuery = url.Values{"message-id": {msgID}, "url": {previewURL}, "image-id": {string(imageID)}}.Encode()
return u.String()
}
func (s *MediaServer) MakeLinkPreviewFaviconURL(msgID string, previewURL string) string {
u := s.MakeBaseURL()
u.Path = LinkPreviewFaviconPath
u.RawQuery = url.Values{"message-id": {msgID}, "url": {previewURL}}.Encode()
return u.String()
}
func (s *MediaServer) MakeDiscordAuthorAvatarURL(authorID string) string {
u := s.MakeBaseURL()
u.Path = discordAuthorsPath
u.RawQuery = url.Values{"authorId": {authorID}}.Encode()
return u.String()
}
func (s *MediaServer) MakeDiscordAttachmentURL(messageID string, id string) string {
u := s.MakeBaseURL()
u.Path = discordAttachmentsPath
u.RawQuery = url.Values{"messageId": {messageID}, "attachmentId": {id}}.Encode()
return u.String()
}
func (s *MediaServer) MakeAudioURL(id string) string {
u := s.MakeBaseURL()
u.Path = audioPath
u.RawQuery = url.Values{"messageId": {id}}.Encode()
return u.String()
}
func (s *MediaServer) MakeStickerURL(stickerHash string) string {
u := s.MakeBaseURL()
u.Path = ipfsPath
u.RawQuery = url.Values{"hash": {stickerHash}}.Encode()
return u.String()
}
func (s *MediaServer) MakeQRURL(qurul string,
allowProfileImage string,
level string,
size string,
keyUID string,
imageName string) string {
u := s.MakeBaseURL()
u.Path = generateQRCode
u.RawQuery = url.Values{"url": {qurul},
"level": {level},
"allowProfileImage": {allowProfileImage},
"size": {size},
"keyUid": {keyUID},
"imageName": {imageName}}.Encode()
return u.String()
}
func (s *MediaServer) MakeContactImageURL(publicKey string, imageType string) string {
u := s.MakeBaseURL()
u.Path = contactImagesPath
u.RawQuery = url.Values{"publicKey": {publicKey}, "imageName": {imageType}}.Encode()
return u.String()
}
func (s *MediaServer) MakeCommunityTokenImagesURL(communityID string, chainID uint64, symbol string) string {
u := s.MakeBaseURL()
u.Path = communityTokenImagesPath
u.RawQuery = url.Values{
"communityID": {communityID},
"chainID": {strconv.FormatUint(chainID, 10)},
"symbol": {symbol},
}.Encode()
return u.String()
}
func (s *MediaServer) MakeCommunityImageURL(communityID, name string) string {
u := s.MakeBaseURL()
u.Path = communityDescriptionImagesPath
u.RawQuery = url.Values{
"communityID": {communityID},
"name": {name},
"version": {fmt.Sprintf("%d", (s.getCommunityImageVersion(communityID)))},
}.Encode()
return u.String()
}
func (s *MediaServer) MakeCommunityDescriptionTokenImageURL(communityID, symbol string) string {
u := s.MakeBaseURL()
u.Path = communityDescriptionTokenImagesPath
u.RawQuery = url.Values{
"communityID": {communityID},
"symbol": {symbol},
}.Encode()
return u.String()
}
func (s *MediaServer) MakeWalletCommunityImagesURL(communityID string) string {
u := s.MakeBaseURL()
u.Path = walletCommunityImagesPath
u.RawQuery = url.Values{
"communityID": {communityID},
}.Encode()
return u.String()
}
func (s *MediaServer) MakeWalletCollectionImagesURL(contractID thirdparty.ContractID) string {
u := s.MakeBaseURL()
u.Path = walletCollectionImagesPath
u.RawQuery = url.Values{
"chainID": {contractID.ChainID.String()},
"contractAddress": {contractID.Address.Hex()},
}.Encode()
return u.String()
}
func (s *MediaServer) MakeWalletCollectibleImagesURL(collectibleID thirdparty.CollectibleUniqueID) string {
u := s.MakeBaseURL()
u.Path = walletCollectibleImagesPath
u.RawQuery = url.Values{
"chainID": {collectibleID.ContractID.ChainID.String()},
"contractAddress": {collectibleID.ContractID.Address.Hex()},
"tokenID": {collectibleID.TokenID.String()},
}.Encode()
return u.String()
}