Commit Graph

172 Commits

Author SHA1 Message Date
Abhinav Raut a425c513d1 capitalize contact names taken from the email address 2026-10-19 15:17:50 +05:30
Abhinav Raut b07358b400 invalidate agent sessions in Redis 2026-10-10 02:42:48 +05:30
Abhinav Raut 58ee4193e2 move new tests into package test files and check reassignment access once 2026-10-07 23:22:46 +05:30
Abhinav Raut aae19c1e71 use cached agents for session and reassignment checks and index media uploader 2026-09-27 23:11:54 +05:30
Abhinav Raut ac5921e192 fix security issues found in repository review 2026-09-27 23:11:54 +05:30
Abhinav Raut 9f686ac568 remove redundant translations and semicolons 2026-09-25 02:06:02 +05:30
Abhinav Raut 240aef8282 fix bugs found while testing WhatsApp end to end 2026-09-23 16:41:50 +05:30
Abhinav Raut d2e2bcc3b6 Merge remote-tracking branch 'origin/main' into feat-whatsapp 2026-09-23 10:08:30 +05:30
Abhinav Raut ccba084d4b Merge remote-tracking branch 'origin/main' into libredesk-widget-improvements 2026-09-18 02:54:09 +05:30
Abhinav Raut 46e2e5250d escape wildcard characters in search queries 2026-09-18 01:04:54 +05:30
Abhinav Raut a41ce679cf merge main into WhatsApp branch 2026-09-17 00:46:54 +05:30
Abhinav Raut 89f58904d2 wip: add widget campaigns, help center in widget and transcript download 2026-09-16 02:11:44 +05:30
Abhinav Raut 52b6658e74 reject duplicate emails when creating a contact and reuse shared i18n keys 2026-09-08 01:44:16 +05:30
Abhinav Raut e8007b67fd Merge remote-tracking branch 'origin/main' into pr-312
# Conflicts:
#	frontend/apps/main/src/components/sidebar/Sidebar.vue
#	frontend/apps/main/src/features/contact/ContactsList.vue
#	internal/user/contact.go
#	internal/user/user.go
2026-09-08 01:29:04 +05:30
Abhinav Raut 8fd74635a6 Merge remote-tracking branch 'origin/main' into feat-whatsapp
# Conflicts:
#	cmd/conversation.go
#	cmd/media.go
#	cmd/messages.go
#	frontend/apps/main/src/components/sidebar/Sidebar.vue
#	frontend/apps/main/src/constants/emitterEvents.js
#	frontend/apps/main/src/features/conversation/Conversation.vue
#	frontend/apps/main/src/features/conversation/CreateConversation.vue
#	frontend/apps/main/src/features/conversation/ReplyBoxMenuBar.vue
#	frontend/apps/main/src/features/conversation/message/MessageBubble.vue
#	frontend/apps/main/src/stores/conversation.js
#	internal/conversation/models/models.go
#	internal/conversation/queries.sql
#	internal/migrations/v2.9.0.go
#	internal/search/queries.sql
#	internal/user/user.go
2026-09-03 01:37:02 +05:30
Abhinav Raut 51db29ab9d move agent lookup into its own sql queries 2026-09-01 22:45:07 +05:30
Abhinav Raut e2ca7c7e5f avoid stale lookup results 2026-08-28 02:19:57 +05:30
Abhinav Raut 18514cd021 search and paginate agents, teams, tags and macros instead of loading them all at boot 2026-08-28 01:43:59 +05:30
Abhinav Raut fd4a37ff76 add WhatsApp channel tests and bug fixes 2026-08-25 18:29:16 +05:30
Abhinav Raut b7c2ef93ee Merge remote-tracking branch 'origin/main' into feat-whatsapp
# Conflicts:
#	cmd/main.go
#	frontend/apps/main/src/features/conversation/Conversation.vue
#	frontend/apps/main/src/features/conversation/CreateConversation.vue
#	frontend/apps/main/src/features/conversation/list/ConversationListItem.vue
2026-08-23 03:00:22 +05:30
Abhinav Raut d0daeb8659 Merge remote-tracking branch 'origin/main' into reduce-memory-and-cpu-hotpaths 2026-08-19 21:54:16 +05:30
Abhinav Raut 4277ff501f fix sla apply atomicity and superseded sla cleanup, read static file bytes once 2026-08-19 14:03:29 +05:30
Abhinav Raut 71e46490a9 only upgrade the api secret hash that was just verified 2026-08-19 13:04:23 +05:30
Abhinav Raut 9708f78c79 reduce memory and cpu use in sla, static asset and api key paths
Profiled the prod alloc with pprof. Three things stood out.

SLA evaluation was refetching about 234k pending rows every 10 minutes and the
count only ever grew. A policy can leave the resolution time blank, which
stores a NULL resolution deadline. The evaluator needs a deadline to compare
against, so it could never set met_at or breached_at, and the row stayed
pending forever. The pending query now skips a metric with no deadline, and a
new set based query closes any pending row whose configured metrics have all
settled. It replaces the old per row status update, so there is one status
writer and the backlog drains itself on the first tick. No migration is needed
because nothing outside the sla package reads that status.

Static assets were copied into fasthttp's pooled per connection response
buffer on every request. That buffer keeps its grown size, so serving the
frontend bundle held about 165MB. SetBodyRaw points at the bytes instead of
copying them.

API key auth ran a full bcrypt compare on every request, which was 62 percent
of cpu samples. API secrets are 64 char random tokens, so bcrypt's work factor
buys nothing there. They are now hashed with sha256 and compared in constant
time. Old bcrypt hashes still verify and get upgraded in place on first use,
so no key needs reissuing. Password login still uses bcrypt.

This also fixes an unrelated bug the work turned up. apply-sla deleted pending
rows by status alone, so re-applying an SLA threw away a first response breach
that was already recorded. It now deletes only rows with nothing recorded and
closes the rest to their terminal status first.
2026-08-19 12:55:52 +05:30
Abhinav Raut 76d0ef4f40 cut restating comments from the test suite 2026-08-19 12:41:41 +05:30
Abhinav Raut fb4ad5f067 add cypress api and page sweep tests, fix agent and context link bugs they found 2026-08-19 03:18:39 +05:30
Abhinav Raut defc6535b9 send whatsapp attachments as one message per file
Meta accepts only one media per message and sends one status webhook per
message id, so a reply with 3 files is now sent as 3 requests from the reply
box instead of one message that half-succeeded. Drops the partial-send
tracking, markers and retry-attachments button that existed to paper over it.
2026-08-16 22:51:05 +05:30
Abhinav Raut f56db191ba remove redundant comments and tighten the ones worth keeping 2026-08-16 17:04:48 +05:30
Abhinav Raut 3ef1a73bc8 Merge remote-tracking branch 'origin/main' into feat-whatsapp 2026-08-16 16:49:13 +05:30
Abhinav Raut ee6bc6ec49 add csat responses to contact export and fix download revoke 2026-08-16 13:21:37 +05:30
Abhinav Raut 5735214d1a Merge remote-tracking branch 'origin/main' into worktree-gdpr
# Conflicts:
#	cmd/upgrade.go
#	frontend/apps/main/src/views/contact/ContactDetailView.vue
#	i18n/en-US.json
#	internal/media/queries.sql
#	internal/migrations/v2.6.0.go
#	internal/user/contact.go
#	schema.sql
2026-08-15 13:17:49 +05:30
Abhinav Raut fad797263f add edge case tests for contact resolution 2026-08-11 16:42:06 +05:30
Abhinav Raut adf6aa4270 fall back to email lookup on unknown external ID and let the form opt out of contact sync
Two contact resolution fixes in create conversation:

Under the reuse policy, an unknown external_user_id went straight to
insert. If the email belonged to an existing contact, that inserted a
duplicate contact sharing the email, since the unique email index only
covers rows without an ext_id. The ext_id lookup now falls back to the
email match before inserting. Regression test added.

The new conversation form sends name fields as a snapshot from contact
search, so a contacts:write agent submitting the form could overwrite a
contact that was renamed after the search. The form now sends
reuse_contact: true and the backend downgrades to the reuse policy when
it is set. API callers like an external CRM omit the flag and keep the
full sync/upsert behavior. The flag can only downgrade, never escalate.

Also from review: hoist the repeated password generation block into
newContactPassword, drop testutil.NewLogger and the duplicate
newTestI18n in transcript_test.go in favor of testutil.NewI18n, and
clean up stale comments.
2026-08-11 15:33:32 +05:30
Abhinav Raut b622fc3658 extract shared Postgres test harness into internal/testutil with a make test-db target 2026-08-11 14:40:48 +05:30
Abhinav Raut 1ffcee31b7 unify contact resolution into ResolveContact with an explicit policy and add integration tests 2026-08-11 13:34:44 +05:30
Abhinav Raut 246dd5ba00 scope external ID contact lookup to contacts only 2026-08-11 13:04:12 +05:30
Abhinav Raut f2b3bcd9b6 log enforce errors and skip password generation for existing contacts in create conversation 2026-08-11 13:00:05 +05:30
Abhinav Raut 00ec7ff934 send first message to the matched contact's stored email and pass external_user_id from the new conversation form 2026-08-11 12:21:08 +05:30
Abhinav Raut f986981b44 make no-permission contact resolution race-safe in create conversation API 2026-08-11 12:08:23 +05:30
Abhinav Raut 92a1ef8150 Merge branch 'help-center' into feat-whatsapp
# Conflicts:
#	cmd/conversation.go
#	cmd/upgrade.go
#	frontend/apps/main/src/api/index.js
#	frontend/apps/main/src/constants/emitterEvents.js
#	frontend/apps/main/src/features/contact/ContactForm.vue
#	frontend/apps/main/src/features/conversation/Conversation.vue
#	frontend/apps/main/src/features/conversation/CreateConversation.vue
#	frontend/apps/main/src/features/conversation/ReplyBox.vue
#	frontend/apps/main/src/features/conversation/ReplyBoxMenuBar.vue
#	frontend/apps/main/src/features/conversation/list/ConversationListItem.vue
#	frontend/apps/main/src/features/conversation/message/MessageBubble.vue
#	frontend/apps/main/src/features/conversation/message/attachment/BubbleAttachmentItem.vue
#	frontend/apps/main/src/features/conversation/sidebar/ConversationSideBarContact.vue
#	frontend/apps/main/src/views/admin/inbox/NewInbox.vue
#	frontend/shared-ui/constants/countries.js
#	go.mod
#	internal/search/models/models.go
#	internal/stringutil/stringutil.go
#	internal/user/queries.sql
2026-07-25 14:34:00 +05:30
Abhinav Raut ab2a1471e6 log avatar delete failures and export more contact fields
Address CodeRabbit review on PR #426.

Log the error when deleting a contact's avatar file fails so a
leftover avatar (personal data) does not go unnoticed.

Add availability_status, last_active_at, last_login_at, and enabled
to the contact data export since these are personal data under GDPR
right to access.
2026-07-18 12:51:57 +05:30
Abhinav Raut bfbcd0a885 add contact deletion and data export for GDPR compliance
First part of GDPR support (#244): the right to erasure and the right
to access.

- DELETE /api/v1/contacts/{id} permanently deletes a contact or
  visitor. Conversations, messages, notes, and participants go with it
  via DB cascades. The avatar file is removed too.
- GET /api/v1/contacts/{id}/export downloads a JSON file with
  everything stored about the contact: profile, custom attributes, and
  all conversations with their messages. Private notes stay internal.
- Both actions are gated by new permissions (contacts:delete,
  contacts:export), granted to Admin in migration v2.6.0, and recorded
  in the activity log with actor and IP.
- Contact page gets Export data and Delete contact buttons, with a
  confirm dialog for delete.
- The unlinked media cleaner now also removes attachment files whose
  message no longer exists. Before this, deleting a conversation left
  its attachments on disk forever.
2026-07-14 01:30:59 +05:30
Abhinav Raut dad99bf0ee render AI agent replies as markdown and fix assistant identity, handoff, and stats bugs
Replies from the AI agent are now converted from markdown to HTML with
goldmark before queueing, so bold, links, and lists render properly in the
widget, agent app, and email. The prompt now allows simple markdown. Raw
HTML in model output is escaped by goldmark, and both frontends sanitize
on render anyway.

Other fixes bundled in:
- validate avatar type and size before creating or updating an assistant,
  and roll back the assistant if the avatar upload fails after create
- return 404 from agent update and API key endpoints for AI assistant
  identity users, and hide assistants from mention and SLA user pickers
- reserve the autonomous assistant's built-in tool names so custom tools
  cannot shadow them
- apply resolve after the reply is posted so the CSAT survey follows the
  answer instead of preceding it
- unassign the assistant on handoff even when the fallback team is the
  same team
- count reopens by status category instead of status name, and exclude
  CSAT messages from the turn cap
- split oversized wrapper divs into child blocks when chunking KB HTML
  instead of truncating them
- return 404 when soft-deleting an already-deleted agent, and keep AI
  assistants (which have no email) visible in the compact users list
2026-07-12 23:12:44 +05:30
Abhinav Raut a7288e11b0 add AI assistant expectation message shown in the livechat widget 2026-07-12 13:29:59 +05:30
Abhinav Raut 3ba2b8281e Merge remote-tracking branch 'origin/main' into feat/ai-agent 2026-07-12 04:50:24 +05:30
Abhinav Raut 7186c95e40 add WIP autonomous AI agent that replies to assigned conversations
Medium (title + short body):
add WIP autonomous AI agent

New internal/aiagent package runs AI assistants that reply to customers
on conversations assigned to them, grounded on a knowledge base. Also mines
resolved conversations for FAQ suggestions. Adds admin UI and the v2.7.0
schema. Still work in progress.
2026-07-11 04:54:49 +05:30
Abhinav Raut 2e203607e5 fix duplicate contact creation on transient error during ext_id enrichment
CreateContact treated any SetExternalUserID failure as "ext_id taken" and
fell through to the upsert, so a transient DB error created a second
contact with the same email. Now only a unique violation or a contact
deleted mid-flight falls through, other errors are returned.
SetExternalUserID also reports whether a row was actually updated, and
dbutil error checks use errors.As so wrapped errors match. Widget JWTs
with no email now store NULL instead of an empty string.
2026-07-10 01:06:38 +05:30
Abhinav Raut fc71d30b8a accept phone country code in livechat JWT auth 2026-07-09 23:15:05 +05:30
Abhinav Raut 8a8d372a70 use one phone field in livechat JWT and fix phone validation 2026-07-09 02:27:08 +05:30
Abhinav Raut 8f658f274d add livechat pre-chat phone field and fix default launcher logo 2026-07-09 01:49:38 +05:30