diff --git a/chat/handlers/blog.go b/chat/handlers/blog.go index 1858d6c9e..6a83381d9 100644 --- a/chat/handlers/blog.go +++ b/chat/handlers/blog.go @@ -86,7 +86,7 @@ func (h *BlogHandler) getPostsWoUsers(ctx context.Context, blogs []*db.Blog) ([] query.Set(utils.FileParam, utils.SetImagePreviewExtension(fileParam)) dumbUrl.RawQuery = query.Encode() - publicPreviewUrl, err := makeUrlPublic(dumbUrl.String(), utils.UrlStorageEmbedPreview, false, post.ChatId, post.MessageId) + publicPreviewUrl, err := makeUrlPublic(dumbUrl.String(), utils.UrlStorageEmbedPreview, post.ChatId, post.MessageId) if err != nil { h.lgr.WithTracing(ctx).Warnf("Unagle to change url: %v", err) break @@ -503,7 +503,7 @@ func PatchStorageUrlToPublic(ctx context.Context, lgr *logger.Logger, text strin original, originalExists := maybeImage.Attr("data-original") if originalExists { // we have 2 tags - preview (small, tag attr) and original (data-original attr) if utils.ContainsUrl(lgr, wlArr, original) { // original - newurl, err := makeUrlPublic(original, "", false, overrideChatId, overrideMessageId) + newurl, err := makeUrlPublic(original, "", overrideChatId, overrideMessageId) if err != nil { lgr.WithTracing(ctx).Warnf("Unagle to change url: %v", err) return @@ -513,7 +513,7 @@ func PatchStorageUrlToPublic(ctx context.Context, lgr *logger.Logger, text strin src, srcExists := maybeImage.Attr("src") // preview if srcExists && utils.ContainsUrl(lgr, wlArr, src) { - newurl, err := makeUrlPublic(src, utils.UrlStorageEmbedPreview, false, overrideChatId, overrideMessageId) + newurl, err := makeUrlPublic(src, utils.UrlStorageEmbedPreview, overrideChatId, overrideMessageId) if err != nil { lgr.WithTracing(ctx).Warnf("Unagle to change url: %v", err) return @@ -544,7 +544,8 @@ func (h *BlogHandler) getFileParam(src string) (string, error) { const OverrideMessageId = "overrideMessageId" const OverrideChatId = "overrideChatId" -func makeUrlPublic(src string, additionalSegment string, addTime bool, overrideChatId, overrideMessageId int64) (string, error) { +// see also storage/services/files.go :: makeUrlPublic +func makeUrlPublic(src string, additionalSegment string, overrideChatId, overrideMessageId int64) (string, error) { if strings.HasPrefix(src, "/api/storage/assets/") { // don't touch built-in default urls (used for video-by-link, audio) return src, nil } @@ -555,14 +556,10 @@ func makeUrlPublic(src string, additionalSegment string, addTime bool, overrideC return "", err } - parsed.Path = utils.UrlApiPrefix + utils.UrlStoragePublicGetFile + additionalSegment + parsed.Path = utils.UrlStoragePublicGetFile + additionalSegment query := parsed.Query() - if addTime { - addTimeToUrlValues(&query) - } - query.Set(OverrideMessageId, utils.Int64ToString(overrideMessageId)) query.Set(OverrideChatId, utils.Int64ToString(overrideChatId)) diff --git a/chat/utils/utils.go b/chat/utils/utils.go index a43128a82..8ef674b67 100644 --- a/chat/utils/utils.go +++ b/chat/utils/utils.go @@ -225,8 +225,7 @@ func Min(a, b int) int { } const FileParam = "file" -const UrlApiPrefix = "/api" -const UrlStoragePublicGetFile = "/storage/public/download" +const UrlStoragePublicGetFile = "/api/storage/public/download" const UrlStorageEmbedPreview = "/embed/preview" func SetImagePreviewExtension(key string) string { diff --git a/event/dto/rabbitmq.go b/event/dto/rabbitmq.go index 117af14ff..c3cceb018 100644 --- a/event/dto/rabbitmq.go +++ b/event/dto/rabbitmq.go @@ -140,7 +140,7 @@ type FileInfoDto struct { Id string `json:"id"` Filename string `json:"filename"` Url string `json:"url"` - PublicUrl *string `json:"publicUrl"` + PublishedUrl *string `json:"publishedUrl"` PreviewUrl *string `json:"previewUrl"` Size int64 `json:"size"` CanDelete bool `json:"canDelete"` diff --git a/event/graph/generated.go b/event/graph/generated.go index a42c7d503..f08550ae4 100644 --- a/event/graph/generated.go +++ b/event/graph/generated.go @@ -174,7 +174,7 @@ type ComplexityRoot struct { Owner func(childComplexity int) int OwnerID func(childComplexity int) int PreviewURL func(childComplexity int) int - PublicURL func(childComplexity int) int + PublishedURL func(childComplexity int) int Size func(childComplexity int) int URL func(childComplexity int) int } @@ -1120,12 +1120,12 @@ func (e *executableSchema) Complexity(typeName, field string, childComplexity in return e.complexity.FileInfoDto.PreviewURL(childComplexity), true - case "FileInfoDto.publicUrl": - if e.complexity.FileInfoDto.PublicURL == nil { + case "FileInfoDto.publishedUrl": + if e.complexity.FileInfoDto.PublishedURL == nil { break } - return e.complexity.FileInfoDto.PublicURL(childComplexity), true + return e.complexity.FileInfoDto.PublishedURL(childComplexity), true case "FileInfoDto.size": if e.complexity.FileInfoDto.Size == nil { @@ -6197,8 +6197,8 @@ func (ec *executionContext) fieldContext_FileInfoDto_url(_ context.Context, fiel return fc, nil } -func (ec *executionContext) _FileInfoDto_publicUrl(ctx context.Context, field graphql.CollectedField, obj *model.FileInfoDto) (ret graphql.Marshaler) { - fc, err := ec.fieldContext_FileInfoDto_publicUrl(ctx, field) +func (ec *executionContext) _FileInfoDto_publishedUrl(ctx context.Context, field graphql.CollectedField, obj *model.FileInfoDto) (ret graphql.Marshaler) { + fc, err := ec.fieldContext_FileInfoDto_publishedUrl(ctx, field) if err != nil { return graphql.Null } @@ -6211,7 +6211,7 @@ func (ec *executionContext) _FileInfoDto_publicUrl(ctx context.Context, field gr }() resTmp, err := ec.ResolverMiddleware(ctx, func(rctx context.Context) (interface{}, error) { ctx = rctx // use context from middleware stack in children - return obj.PublicURL, nil + return obj.PublishedURL, nil }) if err != nil { ec.Error(ctx, err) @@ -6225,7 +6225,7 @@ func (ec *executionContext) _FileInfoDto_publicUrl(ctx context.Context, field gr return ec.marshalOString2áš–string(ctx, field.Selections, res) } -func (ec *executionContext) fieldContext_FileInfoDto_publicUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { +func (ec *executionContext) fieldContext_FileInfoDto_publishedUrl(_ context.Context, field graphql.CollectedField) (fc *graphql.FieldContext, err error) { fc = &graphql.FieldContext{ Object: "FileInfoDto", Field: field, @@ -13113,8 +13113,8 @@ func (ec *executionContext) fieldContext_WrappedFileInfoDto_fileInfoDto(_ contex return ec.fieldContext_FileInfoDto_filename(ctx, field) case "url": return ec.fieldContext_FileInfoDto_url(ctx, field) - case "publicUrl": - return ec.fieldContext_FileInfoDto_publicUrl(ctx, field) + case "publishedUrl": + return ec.fieldContext_FileInfoDto_publishedUrl(ctx, field) case "previewUrl": return ec.fieldContext_FileInfoDto_previewUrl(ctx, field) case "size": @@ -15725,8 +15725,8 @@ func (ec *executionContext) _FileInfoDto(ctx context.Context, sel ast.SelectionS if out.Values[i] == graphql.Null { out.Invalids++ } - case "publicUrl": - out.Values[i] = ec._FileInfoDto_publicUrl(ctx, field, obj) + case "publishedUrl": + out.Values[i] = ec._FileInfoDto_publishedUrl(ctx, field, obj) case "previewUrl": out.Values[i] = ec._FileInfoDto_previewUrl(ctx, field, obj) case "size": diff --git a/event/graph/model/models_gen.go b/event/graph/model/models_gen.go index 1210cc8d2..c8d0ddfc3 100644 --- a/event/graph/model/models_gen.go +++ b/event/graph/model/models_gen.go @@ -124,7 +124,7 @@ type FileInfoDto struct { ID string `json:"id"` Filename string `json:"filename"` URL string `json:"url"` - PublicURL *string `json:"publicUrl"` + PublishedURL *string `json:"publishedUrl"` PreviewURL *string `json:"previewUrl"` Size int64 `json:"size"` CanDelete bool `json:"canDelete"` diff --git a/event/graph/schema.graphqls b/event/graph/schema.graphqls index 2e2a8340a..ec85d0b1f 100644 --- a/event/graph/schema.graphqls +++ b/event/graph/schema.graphqls @@ -152,7 +152,7 @@ type FileInfoDto { id: String! filename: String! url: String! - publicUrl: String + publishedUrl: String previewUrl: String size: Int64! canDelete: Boolean! diff --git a/event/graph/schema.resolvers.go b/event/graph/schema.resolvers.go index 36311527a..673fef38c 100644 --- a/event/graph/schema.resolvers.go +++ b/event/graph/schema.resolvers.go @@ -593,7 +593,7 @@ func convertToChatEvent(e *dto.ChatEvent) *model.ChatEvent { ID: fileEvent.FileInfoDto.Id, Filename: fileEvent.FileInfoDto.Filename, URL: fileEvent.FileInfoDto.Url, - PublicURL: fileEvent.FileInfoDto.PublicUrl, + PublishedURL: fileEvent.FileInfoDto.PublishedUrl, PreviewURL: fileEvent.FileInfoDto.PreviewUrl, Size: fileEvent.FileInfoDto.Size, CanDelete: fileEvent.FileInfoDto.CanDelete, diff --git a/frontend/src/ChatView.vue b/frontend/src/ChatView.vue index 8be533bbc..cd2fe960a 100644 --- a/frontend/src/ChatView.vue +++ b/frontend/src/ChatView.vue @@ -461,7 +461,7 @@ export default { id filename url - publicUrl + publishedUrl previewUrl size canDelete diff --git a/frontend/src/FileListContextMenu.vue b/frontend/src/FileListContextMenu.vue index 64fa913b6..9eed12f59 100644 --- a/frontend/src/FileListContextMenu.vue +++ b/frontend/src/FileListContextMenu.vue @@ -104,7 +104,7 @@ export default { } if (this.menuableItem.canShare) { - if (!this.menuableItem.publicUrl) { + if (!this.menuableItem.publishedUrl) { ret.push({ title: this.$vuetify.locale.t('$vuetify.share_file'), icon: 'mdi-export', diff --git a/frontend/src/FileListModal.vue b/frontend/src/FileListModal.vue index 763ed7729..cf668255e 100644 --- a/frontend/src/FileListModal.vue +++ b/frontend/src/FileListModal.vue @@ -64,7 +64,7 @@ {{ formattedSize(item.size) }} {{ $vuetify.locale.t('$vuetify.files_by') }} {{item.owner?.login}} {{$vuetify.locale.t('$vuetify.time_at')}} {{getDate(item)}} - + {{ $vuetify.locale.t('$vuetify.files_public_url') }} @@ -85,11 +85,11 @@ mdi-pencil @@ -306,7 +306,7 @@ export default { }, shareFile(dto) { axios.put(`/api/storage/publish/file`, {id: dto.id, public: true}).then((resp)=>{ - const link = resp.data.publicUrl; + const link = resp.data.publishedUrl; if (link) { navigator.clipboard.writeText(getUrlPrefix() + link); this.setTempNotification(this.$vuetify.locale.t('$vuetify.published_file_link_copied')); diff --git a/public/common/components/FileListModal.vue b/public/common/components/FileListModal.vue index 65c205800..4e73d9764 100644 --- a/public/common/components/FileListModal.vue +++ b/public/common/components/FileListModal.vue @@ -64,9 +64,6 @@ {{ formattedSize(item.size) }} by {{item.owner?.login}} at {{getDate(item)}} - - Public url - @@ -174,7 +171,8 @@ export default { searchString: null, showSearchButton: true, markInstance: null, - chatId: null, + chatId: null, // overrideChatId + messageId: null, // overrideMessageId fileUploadingSessionType: null, correlationId: null, fileListMode: false, @@ -283,14 +281,15 @@ export default { setStoredFileListMode(newValue); }, hasLength, - isCachedRelevantToArguments({fileItemUuid, chatId}) { - return this.fileItemUuid == fileItemUuid && this.chatId == chatId + isCachedRelevantToArguments({fileItemUuid, chatId, messageId}) { + return this.fileItemUuid == fileItemUuid && this.chatId == chatId && this.messageId == messageId }, - initializeWithArguments({fileItemUuid, messageEditing, messageIdToDetachFiles, chatId, fileUploadingSessionType, correlationId}) { + initializeWithArguments({fileItemUuid, messageEditing, messageIdToDetachFiles, chatId, messageId, fileUploadingSessionType, correlationId}) { this.messageIdToDetachFiles = messageIdToDetachFiles; this.isMessageEditing = messageEditing; this.fileItemUuid = fileItemUuid; this.chatId = chatId; + this.messageId = messageId; // just pass them to FileUploadModal this.fileUploadingSessionType = fileUploadingSessionType; @@ -300,12 +299,14 @@ export default { // blog post: blogDto.messageId // public message: messageDto.messageItem.id // TODO curl -Ss --url 'http://localhost:1236/api/storage/public/1019?overrideChatId=1019&overrideMessageId=1' | jq - return axios.get(`/api/storage/${this.chatId}`, { + return axios.get(`/api/storage/public/${this.chatId}`, { params: { page: this.translatePage(), size: PAGE_SIZE_SMALL, fileItemUuid : this.fileItemUuid ? this.fileItemUuid : '', - searchString: this.searchString + searchString: this.searchString, + overrideChatId: this.chatId, + overrideMessageId: this.messageId, }, }) }, @@ -379,6 +380,7 @@ export default { }, clearOnReset() { this.chatId = null; + this.messageId = null; this.fileItemUuid = null; this.searchString = null; }, diff --git a/public/pages/blog/post/@id/BlogPost.vue b/public/pages/blog/post/@id/BlogPost.vue index d6e786ebf..719b4c4b8 100644 --- a/public/pages/blog/post/@id/BlogPost.vue +++ b/public/pages/blog/post/@id/BlogPost.vue @@ -142,7 +142,11 @@ export default { onClickTrap(e) }, onFilesClicked(item) { - const obj = {chatId: this.pageContext.data.blogDto.chatId, fileItemUuid : item.fileItemUuid}; + const obj = { + chatId: this.pageContext.data.blogDto.chatId, + messageId: item.id, + fileItemUuid : item.fileItemUuid + }; bus.emit(OPEN_VIEW_FILES_DIALOG, obj); }, }, diff --git a/storage/config/config-dev/config.yml b/storage/config/config-dev/config.yml index 1c904e929..a40a83f03 100644 --- a/storage/config/config-dev/config.yml +++ b/storage/config/config-dev/config.yml @@ -49,9 +49,9 @@ minio: secured: false internalEndpoint: 127.0.0.1:39000 interContainerUrl: http://minio:9000 - publicUrlPrefix: /api/s3 - publicDownloadTtl: 24h - publicUploadTtl: 24h + externalS3UrlPrefix: /api/s3 + presignDownloadTtl: 24h + presignUploadTtl: 24h accessKeyId: AKIAIOSFODNN7EXAMPLE secretAccessKey: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY location: "europe-east" diff --git a/storage/dto/files.go b/storage/dto/files.go index a85b239e2..61ffd4c78 100644 --- a/storage/dto/files.go +++ b/storage/dto/files.go @@ -8,7 +8,7 @@ type FileInfoDto struct { Id string `json:"id"` Filename string `json:"filename"` Url string `json:"url"` - PublicUrl *string `json:"publicUrl"` + PublishedUrl *string `json:"publishedUrl"` PreviewUrl *string `json:"previewUrl"` Size int64 `json:"size"` CanDelete bool `json:"canDelete"` @@ -20,8 +20,8 @@ type FileInfoDto struct { CanPlayAsVideo bool `json:"canPlayAsVideo"` CanShowAsImage bool `json:"canShowAsImage"` CanPlayAsAudio bool `json:"canPlayAsAudio"` - FileItemUuid string `json:"fileItemUuid"` - CorrelationId *string `json:"correlationId"` + FileItemUuid string `json:"fileItemUuid"` + CorrelationId *string `json:"correlationId"` } type WrappedFileInfoDto struct { diff --git a/storage/handlers/files.go b/storage/handlers/files.go index 8f3e68277..99ce69824 100644 --- a/storage/handlers/files.go +++ b/storage/handlers/files.go @@ -175,7 +175,7 @@ func (h *FilesHandler) InitMultipartUpload(c echo.Context) error { return err } - uploadDuration := viper.GetDuration("minio.publicUploadTtl") + uploadDuration := viper.GetDuration("minio.presignUploadTtl") chunkSize := viper.GetInt64("minio.multipart.chunkSize") chunksNum := int(reqDto.FileSize / chunkSize) @@ -367,6 +367,14 @@ func getFileItemUuid(fileId string) string { } func (h *FilesHandler) ListHandler(c echo.Context) error { + return h.listHandler(c, false) +} + +func (h *FilesHandler) ListHandlerPublic(c echo.Context) error { + return h.listHandler(c, true) +} + +func (h *FilesHandler) listHandler(c echo.Context, public bool) error { var userPrincipalDto, _ = c.Get(utils.USER_PRINCIPAL_DTO).(*auth.AuthResult) chatId, err := utils.ParseInt64(c.Param("chatId")) @@ -375,9 +383,14 @@ func (h *FilesHandler) ListHandler(c echo.Context) error { } var userId *int64 - if userPrincipalDto != nil { - userId = &userPrincipalDto.UserId + if !public { + if userPrincipalDto == nil { + return c.NoContent(http.StatusUnauthorized) + } else { + userId = &userPrincipalDto.UserId + } } + // else userPrincipalDto == nil and userId == nil fileItemUuid := c.QueryParam("fileItemUuid") @@ -406,7 +419,7 @@ func (h *FilesHandler) ListHandler(c echo.Context) error { filter := h.getFilterFunction(searchString) - list, count, err := h.filesService.GetListFilesInFileItem(c.Request().Context(), userId, bucketName, filenameChatPrefix, chatId, filter, true, filesSize, filesOffset) + list, count, err := h.filesService.GetListFilesInFileItem(c.Request().Context(), public, overrideChatId, overrideMessageId, userId, bucketName, filenameChatPrefix, chatId, filter, true, filesSize, filesOffset) if err != nil { return err } @@ -865,13 +878,13 @@ func (h *FilesHandler) SetPublic(c echo.Context) error { return c.NoContent(http.StatusInternalServerError) } - publicUrl, err := h.filesService.GetPublicUrl(bindTo.Public, objectInfo.Key) + publishedUrl, err := h.filesService.GetPublishedUrl(bindTo.Public, objectInfo.Key) if err != nil { h.lgr.WithTracing(c.Request().Context()).Errorf("Error get public url: %v", err) return err } - return c.JSON(http.StatusOK, &utils.H{"status": "ok", "publicUrl": publicUrl}) + return c.JSON(http.StatusOK, &utils.H{"status": "ok", "publishedUrl": publishedUrl}) } type CountResponse struct { @@ -1144,7 +1157,7 @@ func (h *FilesHandler) ListCandidatesForEmbed(c echo.Context) error { filter := h.getFilterByType(requestedMediaType) - items, count, err := h.filesService.GetListFilesInFileItem(c.Request().Context(), &userPrincipalDto.UserId, bucketName, filenameChatPrefix, chatId, filter, false, filesSize, filesOffset) + items, count, err := h.filesService.GetListFilesInFileItem(c.Request().Context(), false, utils.ChatIdNonExistent, utils.MessageIdNonExistent, &userPrincipalDto.UserId, bucketName, filenameChatPrefix, chatId, filter, false, filesSize, filesOffset) if err != nil { return err } @@ -1187,7 +1200,7 @@ func (h *FilesHandler) CountEmbed(c echo.Context) error { filter := h.getFilterByType(requestedMediaType) - _, count, err := h.filesService.GetListFilesInFileItem(c.Request().Context(), &userPrincipalDto.UserId, bucketName, filenameChatPrefix, chatId, filter, false, 10, 0) + _, count, err := h.filesService.GetListFilesInFileItem(c.Request().Context(), false, utils.ChatIdNonExistent, utils.MessageIdNonExistent, &userPrincipalDto.UserId, bucketName, filenameChatPrefix, chatId, filter, false, 10, 0) if err != nil { return err } diff --git a/storage/main.go b/storage/main.go index 03b09a031..277573e6c 100644 --- a/storage/main.go +++ b/storage/main.go @@ -205,7 +205,7 @@ func configureEcho( e.PUT("/api/storage/:chatId/upload/finish", fh.FinishMultipartUpload) e.PUT("/api/storage/:chatId/replace/file", fh.ReplaceHandler) e.GET("/api/storage/:chatId", fh.ListHandler) - e.GET("/api/storage/public/:chatId", fh.ListHandler) + e.GET("/api/storage/public/:chatId", fh.ListHandlerPublic) e.POST("/api/storage/view/list", fh.ViewListHandler) e.POST("/api/storage/public/view/list", fh.ViewListHandler) e.POST("/api/storage/view/status", fh.ViewStatusHandler) diff --git a/storage/services/event.go b/storage/services/event.go index 507352e18..dd12a8e15 100644 --- a/storage/services/event.go +++ b/storage/services/event.go @@ -91,7 +91,7 @@ func (s *EventService) SendToParticipants(ctx context.Context, normalizedKey str if eventType == utils.FILE_CREATED || eventType == utils.FILE_UPDATED { if response.objectInfo != nil { userId := &participantId - fileInfo, err = s.filesService.GetFileInfo(ctx, userId, *response.objectInfo, chatId, response.tagging, false) + fileInfo, err = s.filesService.GetFileInfo(ctx, false, utils.ChatIdNonExistent, utils.MessageIdNonExistent, userId, *response.objectInfo, response.tagging, false) if err != nil { s.lgr.WithTracing(ctx).Errorf("Error get file info: %v, skipping", err) continue diff --git a/storage/services/files.go b/storage/services/files.go index 0ae68ad09..a913c4d9c 100644 --- a/storage/services/files.go +++ b/storage/services/files.go @@ -40,6 +40,8 @@ func NewFilesService( func (h *FilesService) GetListFilesInFileItem( c context.Context, + public bool, + overrideChatId, overrideMessageId int64, behalfUserId *int64, bucket, filenameChatPrefix string, chatId int64, @@ -47,6 +49,10 @@ func (h *FilesService) GetListFilesInFileItem( requestOwners bool, size, offset int, ) ([]*dto.FileInfoDto, int, error) { + if !public && behalfUserId == nil { + return nil, 0, errors.New("wrong invariant") + } + var objects <-chan minio.ObjectInfo = h.minio.ListObjects(c, bucket, minio.ListObjectsOptions{ WithMetadata: true, Prefix: filenameChatPrefix, @@ -69,7 +75,7 @@ func (h *FilesService) GetListFilesInFileItem( continue } - info, err := h.GetFileInfo(c, behalfUserId, objInfo, chatId, tagging, true) + info, err := h.GetFileInfo(c, public, overrideChatId, overrideMessageId, behalfUserId, objInfo, tagging, true) if err != nil { h.lgr.WithTracing(c).Errorf("Error get file info: %v, skipping", err) continue @@ -184,7 +190,7 @@ func (h *FilesService) GetCount(ctx context.Context, filenameChatPrefix string) } func (h *FilesService) GetTemporaryDownloadUrl(ctx context.Context, aKey string) (string, time.Duration, error) { - ttl := viper.GetDuration("minio.publicDownloadTtl") + ttl := viper.GetDuration("minio.presignDownloadTtl") u, err := h.minio.PresignedGetObject(ctx, h.minioConfig.Files, aKey, ttl, url.Values{}) if err != nil { @@ -214,8 +220,8 @@ func (h *FilesService) GetConstantDownloadUrl(aKey string) (string, error) { } func ChangeMinioUrl(url *url.URL) (string, error) { - publicUrlPrefix := viper.GetString("minio.publicUrlPrefix") - parsed, err := url.Parse(publicUrlPrefix) + externalS3UrlPrefix := viper.GetString("minio.externalS3UrlPrefix") + parsed, err := url.Parse(externalS3UrlPrefix) if err != nil { return "", err } @@ -229,7 +235,7 @@ func ChangeMinioUrl(url *url.URL) (string, error) { return stringV, nil } -func (h *FilesService) GetPublicUrl(public bool, fileName string) (*string, error) { +func (h *FilesService) GetPublishedUrl(public bool, fileName string) (*string, error) { if !public { return nil, nil } @@ -266,8 +272,10 @@ func (h *FilesService) GetAnonymousPreviewUrl(c context.Context, fileName string return anUrl, nil } -func (h *FilesService) GetFileInfo(c context.Context, behalfUserId *int64, objInfo minio.ObjectInfo, chatId int64, tagging *tags.Tags, hasAmzPrefix bool) (*dto.FileInfoDto, error) { - previewUrl := h.GetPreviewUrlSmart(c, objInfo.Key) +func (h *FilesService) GetFileInfo(c context.Context, public bool, overrideChatId, overrideMessageId int64, behalfUserId *int64, objInfo minio.ObjectInfo, tagging *tags.Tags, hasAmzPrefix bool) (*dto.FileInfoDto, error) { + if !public && behalfUserId == nil { + return nil, errors.New("wrong invariant") + } metadata := objInfo.UserMetadata @@ -279,21 +287,15 @@ func (h *FilesService) GetFileInfo(c context.Context, behalfUserId *int64, objIn filename := ReadFilename(objInfo.Key) - public, err := DeserializeTags(tagging) + published, err := DeserializeTags(tagging) if err != nil { h.lgr.WithTracing(c).Errorf("Error get tags: %v", err) return nil, err } - publicUrl, err := h.GetPublicUrl(public, objInfo.Key) + publishedUrl, err := h.GetPublishedUrl(published, objInfo.Key) if err != nil { - h.lgr.WithTracing(c).Errorf("Error get public url: %v", err) - return nil, err - } - - downloadUrl, err := h.GetConstantDownloadUrl(objInfo.Key) - if err != nil { - h.lgr.WithTracing(c).Errorf("Error during getting downlad url %v", err) + h.lgr.WithTracing(c).Errorf("Error get published url: %v", err) return nil, err } @@ -306,11 +308,44 @@ func (h *FilesService) GetFileInfo(c context.Context, behalfUserId *int64, objIn theCorrelationId = &correlationId } + var downloadUrl string + var previewUrl *string + var canDelete, canEdit, canShare bool - if behalfUserId != nil { + + downloadUrltmp, err := h.GetConstantDownloadUrl(objInfo.Key) + if err != nil { + h.lgr.WithTracing(c).Errorf("Error during getting downlad url %v", err) + return nil, err + } + + previewUrltmp := h.GetPreviewUrlSmart(c, objInfo.Key) + + if !public { + // normal flow canDelete = fileOwnerId == *behalfUserId canEdit = fileOwnerId == *behalfUserId && utils.IsPlainText(objInfo.Key) canShare = fileOwnerId == *behalfUserId + + downloadUrl = downloadUrltmp + previewUrl = previewUrltmp + } else { + // public microservice flow - user clicks on FileListModal + // it's safe becasue we already checked the access before + downloadUrl, err = makeUrlPublic(downloadUrltmp, "", overrideChatId, overrideMessageId) + if err != nil { + h.lgr.WithTracing(c).Errorf("Error during getting downlad url %v", err) + return nil, err + } + + if previewUrltmp != nil { + previewUrlpublic, err := makeUrlPublic(*previewUrltmp, utils.UrlStorageEmbedPreview, overrideChatId, overrideMessageId) + if err != nil { + h.lgr.WithTracing(c).Errorf("Error during getting downlad url %v", err) + return nil, err + } + previewUrl = &previewUrlpublic + } } info := &dto.FileInfoDto{ @@ -323,7 +358,7 @@ func (h *FilesService) GetFileInfo(c context.Context, behalfUserId *int64, objIn CanShare: canShare, LastModified: objInfo.LastModified, OwnerId: fileOwnerId, - PublicUrl: publicUrl, + PublishedUrl: publishedUrl, PreviewUrl: previewUrl, CanPlayAsVideo: utils.IsVideo(objInfo.Key), CanShowAsImage: utils.IsImage(objInfo.Key), @@ -334,6 +369,33 @@ func (h *FilesService) GetFileInfo(c context.Context, behalfUserId *int64, objIn return info, nil } +// prepares url to use ib lublic microservice +// in case getting file list +// see also chat/handlers/blog.go :: makeUrlPublic +func makeUrlPublic(src string, additionalSegment string, overrideChatId, overrideMessageId int64) (string, error) { + if strings.HasPrefix(src, "/api/storage/assets/") { // don't touch built-in default urls (used for video-by-link, audio) + return src, nil + } + + // we add time in order not to cache the video itself + parsed, err := url.Parse(src) + if err != nil { + return "", err + } + + parsed.Path = utils.UrlStoragePublicGetFile + additionalSegment + + query := parsed.Query() + + query.Set(utils.OverrideMessageId, utils.Int64ToString(overrideMessageId)) + query.Set(utils.OverrideChatId, utils.Int64ToString(overrideChatId)) + + parsed.RawQuery = query.Encode() + + newurl := parsed.String() + return newurl, nil +} + const Media_image = "image" const Media_video = "video" const Media_audio = "audio" @@ -410,7 +472,7 @@ func (h *FilesService) getPreviewUrl(c context.Context, aKey string, requestedMe return previewUrl } -const publicKey = "public" +const publishedKey = "published" const ownerIdKey = "ownerid" const chatIdKey = "chatid" @@ -545,9 +607,9 @@ func MessageRecordingKey(hasAmzPrefix bool) string { return prefix + strings.Title(messageRecordingKey) } -func SerializeTags(public bool) map[string]string { +func SerializeTags(published bool) map[string]string { var userTags = map[string]string{} - userTags[publicKey] = fmt.Sprintf("%v", public) + userTags[publishedKey] = fmt.Sprintf("%v", published) return userTags } @@ -557,11 +619,11 @@ func DeserializeTags(tagging *tags.Tags) (bool, error) { } var tagsMap map[string]string = tagging.ToMap() - publicString, ok := tagsMap[publicKey] + publishedString, ok := tagsMap[publishedKey] if !ok { return false, nil } - return utils.ParseBoolean(publicString) + return utils.ParseBoolean(publishedString) } func GetUsersRemotelyOrEmpty(lgr *logger.Logger, userIdSet map[int64]bool, restClient *client.RestClient, c context.Context) map[int64]*dto.User { diff --git a/storage/utils/utils.go b/storage/utils/utils.go index 09cf7d667..e3d740252 100644 --- a/storage/utils/utils.go +++ b/storage/utils/utils.go @@ -294,6 +294,7 @@ const UrlStoragePublicPreviewFile = "/api/storage/public/download/embed/preview" const UrlStorageGetFile = "/api/storage/download" const UrlStorageGetFilePublicExternal = "/api/storage/public/download" const UrlBasePreview = "/api/storage/embed/preview" +const UrlStorageEmbedPreview = "/embed/preview" const UrlBasePublicPreview = "/api/storage/public/download/embed/preview" // returns monotonically decreasing lexically sequence to use S3's lexical sorting