From 0881256103e6cb6c86d2eff0af4c29c29a50a0a5 Mon Sep 17 00:00:00 2001 From: richjohnrambo Date: Thu, 24 Sep 2026 14:27:17 +0800 Subject: [PATCH] fix: retain cultivate feed media references --- server/db/mysql/adapter.go | 4 ++++ server/db/mysql/tenant_adapter.go | 4 ++++ server/internal_cultivate.go | 19 +++++++++++++++++-- server/internal_cultivate_test.go | 14 ++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/server/db/mysql/adapter.go b/server/db/mysql/adapter.go index a188e84..7219c6a 100644 --- a/server/db/mysql/adapter.go +++ b/server/db/mysql/adapter.go @@ -4615,6 +4615,10 @@ func (a *adapter) FileUserHasAccess(fid string, uid t.Uid) (bool, error) { SELECT COUNT(*) FROM im_fileuploads AS fu WHERE (fu.id=? OR fu.location=? OR fu.location LIKE ?) AND fu.status=? AND ( fu.userid=? + OR EXISTS ( + SELECT 1 FROM im_filemsglinks AS fml + WHERE fml.fileid=fu.id AND fml.topic LIKE 'cvp%' + ) OR EXISTS ( SELECT 1 FROM im_filemsglinks AS fml WHERE fml.fileid=fu.id AND fml.userid=? diff --git a/server/db/mysql/tenant_adapter.go b/server/db/mysql/tenant_adapter.go index fe80b01..cb46a77 100644 --- a/server/db/mysql/tenant_adapter.go +++ b/server/db/mysql/tenant_adapter.go @@ -5303,6 +5303,10 @@ func (a *adapter) TenantFileUserHasAccess(id t.TenantID, fid string, uid t.Uid) SELECT COUNT(*) FROM im_fileuploads AS fu WHERE fu.tenant_id=? AND (fu.id=? OR fu.location=? OR fu.location LIKE ?) AND fu.status=? AND ( fu.userid=? + OR EXISTS ( + SELECT 1 FROM im_filemsglinks AS fml + WHERE fml.tenant_id=fu.tenant_id AND fml.fileid=fu.id AND fml.topic LIKE 'cvp%%' + ) OR EXISTS ( SELECT 1 FROM im_filemsglinks AS fml WHERE fml.tenant_id=fu.tenant_id AND fml.fileid=fu.id AND fml.userid=? diff --git a/server/internal_cultivate.go b/server/internal_cultivate.go index 8e96621..711bb1a 100644 --- a/server/internal_cultivate.go +++ b/server/internal_cultivate.go @@ -23,8 +23,9 @@ type cultivateRelationsRequest struct { } type cultivateFilesRequest struct { - UserID int64 `json:"user_id"` - FileIDs []string `json:"file_ids"` + UserID int64 `json:"user_id"` + ReferenceID int64 `json:"reference_id"` + FileIDs []string `json:"file_ids"` } type cultivateReviewRequest struct { @@ -162,6 +163,7 @@ func serveCultivateFiles(w http.ResponseWriter, r *http.Request, tenantID types. } uid := store.EncodeUid(body.UserID) items := make([]map[string]any, 0, len(body.FileIDs)) + fileIDs := make([]string, 0, len(body.FileIDs)) for _, supplied := range body.FileIDs { fileID := normalizeIMUploadedFileID(supplied) file, err := store.Files.Get(tenantID, fileID) @@ -173,11 +175,24 @@ func serveCultivateFiles(w http.ResponseWriter, r *http.Request, tenantID types. writeIMError(w, r, http.StatusUnprocessableEntity, 30000, imReasonMalformedRequest, "invalid file", map[string]any{"file_id": supplied}) return } + fileIDs = append(fileIDs, fileID) items = append(items, map[string]any{"file_id": fileID, "mime_type": file.MimeType, "size": file.Size, "review_status": cultivateFileReviewStatus(tenantID, fileID)}) } + if body.ReferenceID > 0 { + for index, fileID := range fileIDs { + if err := store.Files.LinkAttachments(tenantID, cultivateFileReferenceTopic(body.ReferenceID, index), types.ZeroUid, []string{fileID}); err != nil { + writeIMError(w, r, http.StatusInternalServerError, 90000, imReasonInternal, "failed to retain file", nil) + return + } + } + } writeIMSuccess(w, r, map[string]any{"items": items}) } +func cultivateFileReferenceTopic(referenceID int64, index int) string { + return "cvp" + strconv.FormatInt(referenceID, 36) + "x" + strconv.FormatInt(int64(index), 36) +} + func fileOwner(file *types.FileDef) string { if file == nil { return "" diff --git a/server/internal_cultivate_test.go b/server/internal_cultivate_test.go index 6cc56df..b2cc198 100644 --- a/server/internal_cultivate_test.go +++ b/server/internal_cultivate_test.go @@ -46,3 +46,17 @@ func TestInternalCultivateUsesSharedInternalCredential(t *testing.T) { t.Fatalf("shared internal credential was rejected: status=%d body=%s", rec.Code, rec.Body.String()) } } + +func TestCultivateFileReferenceTopic(t *testing.T) { + first := cultivateFileReferenceTopic(1577072518975639552, 0) + second := cultivateFileReferenceTopic(1577072518975639552, 1) + if first == second { + t.Fatalf("file references must be unique per media item: %q", first) + } + if len(first) > 25 || len(second) > 25 { + t.Fatalf("file reference exceeds im_filemsglinks.topic capacity: %q %q", first, second) + } + if first != cultivateFileReferenceTopic(1577072518975639552, 0) { + t.Fatalf("file reference is not deterministic: %q", first) + } +}