diff --git a/cmd/templates.go b/cmd/templates.go index 52e9cf0a..c8eda7d4 100644 --- a/cmd/templates.go +++ b/cmd/templates.go @@ -53,6 +53,9 @@ func handleCreateTemplate(r *fastglue.Request) error { if req.Name == "" { return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "`name`"), nil, envelope.InputError) } + if req.Type == "" { + return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "`type`"), nil, envelope.InputError) + } template, err := app.tmpl.Create(req) if err != nil { return sendErrorEnvelope(r, err) @@ -77,6 +80,9 @@ func handleUpdateTemplate(r *fastglue.Request) error { if req.Name == "" { return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "`name`"), nil, envelope.InputError) } + if req.Type == "" { + return r.SendErrorEnvelope(fasthttp.StatusBadRequest, app.i18n.Ts("globals.messages.empty", "name", "`type`"), nil, envelope.InputError) + } updatedTemplate, err := app.tmpl.Update(id, req) if err != nil { return sendErrorEnvelope(r, err) diff --git a/internal/conversation/conversation.go b/internal/conversation/conversation.go index cfcf9303..056eb3f2 100644 --- a/internal/conversation/conversation.go +++ b/internal/conversation/conversation.go @@ -141,7 +141,7 @@ type mediaStore interface { SetContentID(id int, contentID string) error GetByModel(id int, model string) ([]mmodels.Media, error) GetByContentIDs(contentIDs []string, conversationUUID string) ([]mmodels.Media, error) - ContentIDExists(contentID string) (bool, string, error) + ContentIDExists(contentID, conversationUUID string) (bool, string, error) Upload(fileName, contentType string, content io.ReadSeeker) (string, string, error) UploadAndInsert(fileName, contentType, contentID string, modelType null.String, modelID null.Int, content io.ReadSeeker, fileSize int, disposition null.String, meta []byte) (mmodels.Media, error) } diff --git a/internal/conversation/message.go b/internal/conversation/message.go index 3fa7c875..822a496c 100644 --- a/internal/conversation/message.go +++ b/internal/conversation/message.go @@ -1425,7 +1425,7 @@ func (m *Manager) findExistingMedia(rawContentID, conversationUUID string) (stri if !strings.HasPrefix(rawContentID, "ldsk-") { storedCID = conversationUUID + "_" + rawContentID } - exists, mediaUUID, err := m.mediaStore.ContentIDExists(storedCID) + exists, mediaUUID, err := m.mediaStore.ContentIDExists(storedCID, conversationUUID) if err != nil { m.lo.Error("error checking media existence by content ID", "content_id", storedCID, "error", err) } diff --git a/internal/conversation/queries.sql b/internal/conversation/queries.sql index 72f08d9d..6b1434fb 100644 --- a/internal/conversation/queries.sql +++ b/internal/conversation/queries.sql @@ -204,10 +204,10 @@ LEFT JOIN LATERAL ( ORDER BY se.created_at DESC LIMIT 1 ) nxt_resp_event ON true -WHERE +WHERE ($1 > 0 AND c.id = $1) - OR - ($2::uuid IS NOT NULL AND c.uuid = $2::uuid) + OR + (NULLIF($2, '')::uuid IS NOT NULL AND c.uuid = NULLIF($2, '')::uuid) OR ($3::TEXT != '' AND c.reference_number = $3::TEXT) @@ -799,7 +799,7 @@ ORDER BY cd.updated_at DESC; DELETE FROM conversation_drafts WHERE conversation_id IN ( SELECT id FROM conversations - WHERE ($1 > 0 AND id = $1) OR ($2::uuid IS NOT NULL AND uuid = $2::uuid) + WHERE ($1 > 0 AND id = $1) OR (NULLIF($2, '')::uuid IS NOT NULL AND uuid = NULLIF($2, '')::uuid) ) AND user_id = $3; -- name: delete-stale-drafts diff --git a/internal/media/media.go b/internal/media/media.go index baaf22e5..84719e52 100644 --- a/internal/media/media.go +++ b/internal/media/media.go @@ -180,10 +180,13 @@ func (m *Manager) SetContentID(id int, contentID string) error { return nil } -// ContentIDExists checks if a content_id exists in the database and returns the UUID of the media file. -func (m *Manager) ContentIDExists(contentID string) (bool, string, error) { +// ContentIDExists reports whether a media row with the given content_id is linked to a message in the given conversation. Scoped this way so an orphan media row (e.g., from a partial failure) doesn't short-circuit a retry into skipping the upload. +func (m *Manager) ContentIDExists(contentID, conversationUUID string) (bool, string, error) { + if contentID == "" || conversationUUID == "" { + return false, "", nil + } var uuid string - if err := m.queries.ContentIDExists.Get(&uuid, contentID); err != nil { + if err := m.queries.ContentIDExists.Get(&uuid, contentID, conversationUUID); err != nil { if errors.Is(err, sql.ErrNoRows) { return false, "", nil } diff --git a/internal/media/queries.sql b/internal/media/queries.sql index 78fdfbf8..bbebc1d8 100644 --- a/internal/media/queries.sql +++ b/internal/media/queries.sql @@ -17,10 +17,10 @@ RETURNING id; -- name: get-media SELECT id, created_at, updated_at, "uuid", store, filename, content_type, content_id, model_id, model_type, disposition, "size", meta FROM media -WHERE +WHERE ($1 > 0 AND id = $1) OR - ($2 != '' AND uuid = $2::uuid) + ($2 != '' AND uuid = NULLIF($2, '')::uuid) -- name: get-media-by-uuid SELECT id, created_at, updated_at, "uuid", store, filename, content_type, content_id, model_id, model_type, disposition, "size", meta @@ -51,7 +51,12 @@ WHERE model_type = 'messages' AND created_at < NOW() - INTERVAL '7 days'; -- name: content-id-exists -SELECT uuid FROM media WHERE content_id = $1; +SELECT m.uuid +FROM media m +INNER JOIN conversation_messages cm ON cm.id = m.model_id +WHERE m.model_type = 'messages' + AND m.content_id = $1 + AND cm.conversation_id = (SELECT id FROM conversations WHERE uuid = $2::uuid LIMIT 1); -- name: get-media-by-content-ids SELECT m.id, m.created_at, m.updated_at, m."uuid", m.store, m.filename, m.content_type, m.content_id, m.model_id, m.model_type, m.disposition, m."size", m.meta