mirror of
https://github.com/status-im/status-go.git
synced 2025-01-17 18:22:13 +00:00
0794edc3db
Fixes https://github.com/status-im/status-desktop/issues/16688 Since we use the local image server to show the community image, the URL never changes when we update the image, since it's served using a query string containing the community ID. eg: `https://Localhost:46739/communityDescriptionImages?communityID=0x03c5ece7da362d31199fb02d632f85fdf853af57d89c3204b4d1e90c6ec13bb23c&name=thumbnail` Because of that, the clients cannot know if the image was updated, so they had to force update the image every time, which was inefficient. We discovered this issue when I refactored the community client code in Desktop so that we only update the changed properties of a community instead of reseting the whole thing. The solution I came up with in the PR is to add a `version` to the URL when we detect that the image changed. This let's the clients detect when the image was updated without having to do any extra logic.
308 lines
9.5 KiB
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()
|
|
}
|