feat(chats): delete a conversation from the chat 3-dot menu - #101
Open
sathyaprakash000 wants to merge 2 commits into
Open
feat(chats): delete a conversation from the chat 3-dot menu#101sathyaprakash000 wants to merge 2 commits into
sathyaprakash000 wants to merge 2 commits into
Conversation
Clearing a chat previously meant going into the database by hand. The chat
window's 3-dot menu now offers two admin-only actions:
* Delete chat history — removes every chat_history row, its reactions and
the read marker for one (wa_number, contact_number) pair, and unlinks the
conversation's stored media. The saved contact (name, tags, custom fields,
assignment) is kept, so the chat simply reopens empty.
* Delete contact & chat — the same sweep, plus the contacts row.
Backed by a new admin-gated DELETE /api/chat-history. The row deletes run in
one transaction; media paths are read inside it but unlinked only after the
commit, since a rollback can undo a DELETE but nothing can undo an unlink.
Unlinking goes through a resolveInMediaDir guard so a bad DB-stored path can
never reach outside MEDIA_DIR — the same containment the media read route
applies. Every deletion is recorded via auditLog.
The route is gated on role rather than assertContactAccess deliberately:
"is this your conversation?" is the right question for reading and replying,
but the wrong one for erasing. Deals and automation_executions are left
intact — they are CRM history, not chat history.
No schema change; all five tables touched already exist.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: KingArthur000 <forgemind.business@gmail.com>
Two follow-ups to the initial chat-deletion feature, mirroring what now runs
in the internal repo.
1. The sweep was incomplete — text survived a "delete". Now also removed:
* agent_runs — final_reply is the text the AI agent actually sent this
contact, and agent_run_steps (the full prompt / tool-call / output
transcript) cascades off the run. Keyed on wa_account_id, so the account
is resolved from the wa_number first.
* webhook_events — the raw Meta payloads, which carry verbatim message
text and media ids. Leaving these behind meant a deleted chat was still
fully readable in the webhook log. Matched by jsonpath on the four places
a contact's number appears in Meta's envelope (messages.from,
statuses.recipient_id, contacts.wa_id, message_echoes.to) so a different
contact's events are never caught.
2. Deletion is reachable from the chat list, not just inside an open chat.
Each contact row gets a hover kebab with the same two actions. The row was
a <button>, which cannot legally contain another button, so it is now a div
with role="button" plus Enter/Space handling — same behaviour, valid markup.
Also drops the admin-only gate: anyone who can open a conversation may erase
it, enforced server-side by the same assertContactAccess check used by the
rest of the chat routes. Every deletion is still written to the audit log.
Drive-by: removed a stray comma after the contacts .map() in ContactList,
which rendered as a literal "," under the list.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: KingArthur000 <forgemind.business@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds a way to delete a chat from inside the conversation itself. Clearing a chat previously meant going into the database by hand.
The chat window's 3-dot menu (alongside Contact info / Refresh / Export chat) gains two admin-only actions:
chat_historyrow, itsmessage_reactions, and theconversation_readsmarker for one(wa_number, contact_number)pair, and unlinks the conversation's stored media. The saved contact (name, tags, custom fields, assignment) is kept, so the chat simply reopens empty and future inbound messages land normally.contactsrow.Both go through the repo's existing
DeleteConfirmModal. On successChatsPageunselects the chat and refetches the contact list.Backed by a new admin-gated
DELETE /api/chat-history?waNumber=&contactNumber=[&withContact=1].Notes for a reviewer:
SELECTed inside it butunlinked only after the commit. A rollback can undo aDELETE, but nothing can undo anunlink— deleting files first would leave unreachable-media rows in a chat that still exists, which is strictly worse than the reverse failure (an orphan blob on disk). File cleanup is best-effort and never fails the request.resolveInMediaDirguard, mirroring the containment the media read route already applies, so a badmedia_storage_pathin the DB can never reach outsideMEDIA_DIR.persistOutboundMediawrites a per-message copy even when the source is a shared Media Library object, so removing a conversation's files cannot orphan the library.assertContactAccess. This is a semantic choice, not just a stricter check: "is this your conversation?" is the right question for reading and replying, but the wrong one for erasing — the person most attached to a chat is exactly the one you don't want quietly deleting it. The frontend menu items are hidden for non-admins to mirror the server gate.dealsandautomation_executionsare deliberately left intact — they're CRM history, not chat history.auditLog(chat.delete_history/chat.delete_with_contact) with the affected counts.Related issue
None — raised directly as a product request.
Type of change
How was this tested?
npx vitest runinfrontend/— 51 passed (3 files).node --test test/inbackend/— 20 passed.npx vite build— clean.node --checkon the modified route.db/migrations/that all five tables touched (chat_history,message_reactions,conversation_reads,contacts,user_audit_log) already exist, so no migration is needed. The same set is precedented by the existing/contacts/change-numberroute, which updates exactly these tables.Not run: the Playwright half of
npm test(test:e2e), which needs a running app; and I could not smoke-test the SQL against a live database — the runningforgechat-dbcontainer on this host belongs to a different Forgechat app (Prismapublic.*schema), not this repo'scoexistenceschema. Worth a manual pass on a real conversation with media before merge.Checklist
upstream/main.npm testinfrontend/) and tests were added where relevant.db/migrations/. (N/A — no schema change.)git commit -s, DCO).## [Unreleased]entry inCHANGELOG.mdif behaviour changed..envfiles, or generated artifacts are committed.🤖 Generated with Claude Code