Bulk-load visitors and skip GeoLite lookup in list
The /api/support/tickets list endpoint was running, per page of 20 rows: - 20 **************** calls (N+1) - 20 calls into GeoLookupService.lookup, each of ...
Add findBySessionGuidIn for bulk loading
Used by the support-ticket list endpoint to fetch all visitors for a page in one query instead of N separate findBySessionGuid calls.
Pass folder to loadThread retry button
loadThread takes (threadId, folder); the error-state retry button only passed threadId, breaking the kamo-internal build. Match the call to the signature.
Rebuild to pick up Member JsonIdentityInfo fix from kamo-shared-library
kamo-shared-library b57d156 fixes @JsonIdentityInfo(property = "uniqueId") → "id" on Member, resolving 400 errors on PUT /api/security/org/{id}.
Correct JsonIdentityInfo property from 'uniqueId' to 'id'
Member entity declared @JsonIdentityInfo(property = "uniqueId") but the actual primary key field is named 'id'. Jackson threw "cannot find property with name 'u...
Add /ws/public-chat-native plain WebSocket endpoint alongside SockJS
Registers a second STOMP endpoint without SockJS at /ws/public-chat-native so that the frontend can connect with a native WebSocket (brokerURL) without needing ...
Remove SSR guard causing hydration mismatch; drop SockJS for native WebSocket
- Hero.tsx: remove typeof window check around ParticleField — ParticleField already has ssr:false so the guard produced a server/client tree mismatch (React #...
Per-ticket unread badge + 'New messages only' filter
- Ticket list shows a chat-bubble badge with the unread count next to ticket# whenever the session has messages newer than the agent's last viewed message. ...
Broadcast unread counts to agent on visitor message
After a visitor sends a support message, push the latest unread-counts payload to **************** so the agent's ticket-list badge and navtop counter update in...
Include SUPPORT_TICKET sessions in unread membership query
findChatMembershipsByMemberId now returns memberships for both CHAT and SUPPORT_TICKET session types, so the existing per-member unread-count infrastructure (ge...
Clear unread indicators when conversation is opened
ThreadDetailView now dispatches email:folder-changed after markConversationRead succeeds so MailButton refetches the navtop inbox badge count. ConversationListV...
Move CORS to Traefik via kamo-middlewares, remove in-app CorsFilter
The CorsFilter @Bean in the Spring app was silently not applying headers after WebConfig.java was removed. CORS is now handled entirely at the Traefik layer by ...
Use /public/{mainDomain}-{tld} path matching actual MinIO bucket layout
The getThemePath() was generating /domain/{tld}/{split}/kamosite paths that don't exist in MinIO — theme files live in the public bucket under kamocrm-com/, bad...
Break min-w-0 chain to contain email scroll in body column only
Add min-w-0 to outer container and main content flex div so flex children cannot expand to email min-content width, which was causing the page-wide horizontal s...
Hoist providers above ToolProviders
Same root cause as the ChatWebSocketProvider hoist: SupportTicketWindow calls **************** and ChatBox calls useUnread().markAsRead(), but both tool windows...
Add suppressHydrationWarning and fix broken placeholder images
suppressHydrationWarning on <html> prevents React error #418 caused by browser extensions (e.g. translate, dark mode) mutating html attributes between SSR and c...
Allow /domain/ path prefix on theme IngressRoute for CORS
The theme config at theme.kamocrm.com/domain/... was not matched by the existing route rules (only /public/ and /internal/ were allowed), so Traefik returned a ...
Remove duplicate CorsFilter and strip upstream CORS headers in gateway
WebConfig.java defined a second CorsFilter bean competing with CorsConfig.java's bean, risking duplicate header writes. Deleted it so only one CorsFilter exists...
Remove duplicate CorsFilter bean causing double Access-Control-Allow-Origin header
The explicit CorsFilter bean caused a second CORS filter alongside Spring Security's built-in CORS support (which uses CorsConfigurationSource). Both filters re...
Remove debug console.log statements and add missing routes
Remove all temporary debug logging added during WebSocket tracing from ChatBox, TicketChat, MemberItem, ChatTool, and useChatWebSocket; add redirect pages for /...
Prevent message header from expanding column on long addresses
The header's left flex child had no min-w-0, so long unbreakable email addresses in From/To/Cc forced its min-content width to grow, dragging the header (and th...
Hoist ChatWebSocketProvider above ToolProviders
ToolDock (and its tool windows like SupportTicketWindow → TicketChat) is mounted inside ToolProviders, which sits ABOVE PresenceWrapper in the tree to avoid unm...
Drop Shadow DOM in email renderer so overflow-x-auto fires
Shadow DOM was the root cause. Wide email content (fixed-width tables) inside the shadow tree painted past the host without contributing to the host's layout bo...
Align ChatWebSocketContext import path to match provider
TicketChat and ChatNotificationListener imported the context via the '@/app/contexts/...' alias while PresenceWrapper (the provider) imports it via the relative...
Temporary console.log to trace TicketChat STOMP subscription path
Adds console logs at every stage of the subscribe pipeline (effect fire, guard pass, subscribe() call, attachSessionSubscription, client.subscribe, /app/chat/su...
Force shadow host containment with inline styles + contain: layout
Class-based overflow-x-auto on the shadow host wasn't reliably winning against the shadow ':host { all: initial }' reset, so wide email content (fixed-width tab...
Scroll wide email content inside shadow DOM, not outside
The previous attempts at adding overflow-x-auto to light DOM ancestors could not catch wide email content because the shadow tree's visual paint overflow does n...
Use getHasAttachments() to match Boolean wrapper type
**************** is a Boolean (wrapper), so Lombok generates getHasAttachments() not isHasAttachments(). The latter only exists for primitive boolean. Use Boole...
Expand JPA repository scan to include commerce package
The new kamo-shared-library brings in **************** which requires **************** (in com.kamo.z.shared.commerce). MediaService only scanned donotmodify.da...
Restore HashMap import in ChatWebSocketController
The previous refactor removed the HashMap import but the handleTyping method still constructs a HashMap for the typing event payload, causing the Forgejo build ...
Add min-w-0 to email content div so overflow-x-auto fires
Without min-w-0, the flex-col child's default min-width:auto resolves to min-content, letting wide email tables expand the div past the column width. The outer ...
Remove duplicate connectStomp call in sendMessage
When the first message creates a new session, onSessionCreated updates props which triggers useEffect to call connectStomp. The inline call in sendMessage was r...
Proactively create NATS consumer at session creation to prevent message loss
Previously the NATS JetStream push consumer was only created when a STOMP client sent /app/chat/subscribe. With DeliverPolicy.New, any messages published before...
Close reconnect gap causing dropped real-time messages
Remove `connected` from subscription useEffect deps in TicketChat and ChatBox. Previously, every WebSocket reconnect triggered the effect cleanup which called u...
Allow system user to manage org branding and fix logo auth
- Add system user bypass as first guard in canManageOrgBranding so platform system user has full branding control over all non-parent orgs - Fix uploadLogo en...
Contain horizontal overflow in email view pane to 3rd column
- Add overflow-x-hidden to MessageBrowser outer container as hard boundary - Add overflow-x-hidden to column 3 wrapper and overflow-x-auto to email content area...
Production fail2ban with PJSIP jails and persistent state
- Add fail2ban-configmap.yaml with [asterisk] and [freepbx-web] jails, incremental ban escalation (1h→24h→30d ceiling), LAN ignoreip, and a custom asterisk-...
Replace Coqui TTS with Piper TTS (en_US-norman-medium)
Coqui VCTK model has no named speakers — replace with Piper TTS which has the exact norman[medium] voice. Piper binary (~3 MB) and model (~60 MB) are downloaded...
Reset stale hasAttachments on sync and accumulate correctly
- Reset has_attachments=false at start of bulk sync so stale true values from old false-positive MIME detection are cleared before re-indexing - Accumulate ha...
Add resetHasAttachmentsForFolder query to ConversationRepository
Allows sync to reset stale has_attachments flags before re-indexing so corrected MIME detection can rebuild the field from actual attachment data.
Integrate Coqui TTS with norman voice (VCTK p267)
- Deploy coqui-tts server (VCTK VITS model, CPU-only) as ClusterIP service on port 5002 with model cache at /var/lib/coqui-tts on k1m1 - Inject coqui_tts.agi ...
Pre-populate FROM on reply/forward and fix send fromAddress
- DraftMessage now carries fromAddress so it flows from openNewMessage through to the composer - MessageBrowser stores the active mailbox emailAddress (includ...
Support fromAddress override on send
Adds FromAddressRequest to EmailSendRequest so the frontend can specify which alias/shared/mailbox address to send from. SmtpOnlyProvider now uses that address ...
Add LAN DNS pod for split-DNS on k1m1
Deploys a dnsmasq pod with hostNetwork on k1m1 that listens on 192.168.4.22:53. Overrides pbx.k1.kluster.kamocrm.com -> 192.168.4.22 so LAN phones reach the PBX...
Add local_net for split-DNS / LAN client RTP fix
Phones on the same LAN as k1m1 connect via split DNS (domain resolves to 192.168.4.22 internally). Without local_net, Asterisk still advertises the external dom...
Persist sorcery contact=memory fix and block SIP scanner in startup.sh
- Inject [res_pjsip] contact=memory into sorcery.conf before Asterisk starts; the default sorcery-astdb SQLite backend silently drops all REGISTER contacts - ...
Sub-tab content fills full content-area width
Drop the centered max-width constraint (was 920 / 1200 / 680 / 720 depending on tab) on SubTabShell. The page already lives inside SettingsView's content column...
Fix attachment parsing and hasAttachment false positives
Three fixes to EmailImapService: 1. toEnvelope: narrow multipart check to multipart/mixed only — plain HTML+text emails (multipart/alternative) no longer sh...
Add download button to classic view attachment section
The classic view (MessageBrowser) had its own attachment list that was missing the download button — only the conversation view was fixed previously. Also align...
Add com.kamo.z.shared.commerce to EnableJpaRepositories scan
**************** (required by **************** in the shared library) lives under com.kamo.z.shared.commerce but was not included in the JPA repository scan, ca...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.