Add MemberVoipConfigController for member VOIP config with extension assignment
Add findByAssignedMemberId query methods to PhoneExtensionRepository
Probe master model endpoint and fix appliedModel org ID
- Replace appliedModel.source-based gate with a direct probe of /api/security/master-model on mount; r.ok=false (409) means the org is governed by a branch ...
Gate censorship tab options by applied model constraints
- Hide EDIT MASTER MODEL button when org is governed by a branch type (use appliedModel.source instead of unreliable org context branchTypeID) - Hide ATF fiel...
Persist active mailbox in URL query params
Switching mailboxes now updates the URL (?mailboxType, ?mailboxId, ?mailbox) so refreshing the browser restores the same mailbox. MessageBrowser reads window.lo...
Mark-as-read and message fetch for shared/member mailboxes
Classic mode now loads shared mailbox messages via the correct /messages/{folder} endpoint (was calling the non-existent /conversations endpoint). Fetching a fu...
Mark-as-read for shared and member mailboxes
EmailController.setFlags now accepts an optional mailboxId query param and routes through resolveProviderWithMailbox so member-mailbox flags are set against the...
Show accurate provider connection status from isConnected field
Replaces the old logic that showed "Connected" for any provider with no sync error (including brand-new providers that were never tested). New three-state badg...
Persist connection test result to isConnected field
- test() endpoint saves isConnected=true/false to DB after every health check - update() resets isConnected to null when configJson changes (credentials may h...
Add isConnected field to VoipProviderInstance
Nullable Boolean: null = never tested, true = last test passed, false = failed. Requires KamoInitializerService schema migration to add IS_CONNECTED column.
Use extensionId as external ID, not Relay node id
FreePBX returns the same Relay id (ZXh0ZW5zaW9uOg== = "extension:") for every extension because the id field is not populated. Using it caused all extensions to...
Handle IMAPInputStream in getMessage to prevent ClassCastException
JavaMail can return an IMAPInputStream instead of a decoded String from part.getContent() when message content has not been pre-fetched. The direct (String) cas...
Show active offer banner with live millisecond countdown
Fetches the active promotion server-side on the home page and renders a HeroOfferBanner between the subheadline and the free-forever callout. The banner shows t...
Wire Available/Unavailable switch to backend and fix OFFLINE presence bug
- Rename Paused/Active chip to Available/Unavailable in NavTop - Fetch initial availability on mount and persist toggles via new me/availability API - Add isAva...
Add isAvailable to by-department response and me/availability endpoints
Rewrite all GQL queries/mutations against live schema introspection
Verified every field name and type against the actual FreePBX 17 schema. Full set of corrections: Queries: - fetchAllExtensions: remove non-existent 'voicemail...
Pass selectedFolder to ThreadDetailView so email:folder-changed fires with a valid folder
conversation.folder is always undefined because the Java ConversationEntity has folderIds (text[]) not a folder string field. This caused loadThread to call mar...
Correct GraphQL field name to fetchAsteriskDetails
FreePBX API returns "Did you mean fetchAsteriskDetails?" — rename the health check query and its data path accessor accordingly.
Add XHR/Accept headers and String response parsing to GraphQL client
Same pattern as FreePBXTokenCache: add X-Requested-With + Accept: application/json to suppress FreePBX's HTML redirect responses, and switch all three call site...
Sync mailbox dropdown to primary mailbox on load
Dropdown defaulted to first list item because selectedMailbox initialized without a mailboxId, causing currentValue to resolve to bare 'personal' which matched ...
Raise file upload size limit to 500MB
Spring Boot defaults cap multipart parts at 1MB, causing background image uploads to fail with 500 in request.getParts(). Match SecurityService's existing 500MB...
Surface HTTP errors so upload failures are visible
Replaced silent console.warn with a thrown Error on non-OK status, so the user sees the actual failure reason (status + response body) instead of silently proce...
Add Accept+XHR headers and raw-string response parsing for token endpoint
FreePBX was returning text/html when called via cluster DNS because the Host header didn't match the configured PBX_HOSTNAME. Adding Accept: application/json an...
Drive minimized-tab entry through animation controls
Switching animate={...} to animate={tabControls} broke the entry animation: tabControls has no queued target on mount, so the button stayed at its initial { y: ...
Correct OAuth2 scope strings for FreePBX API
FreePBX scopes are top-level identifiers: 'gql' (all GraphQL) and 'rest' (all REST). The format 'gql:read' means GQL for a module named 'read', which doesn't ex...
Live unread badge + bounce on new agent message
- Keeps LiveSupportPanel mounted offscreen while the widget is minimized (only when a support session exists) so the STOMP subscription stays alive and onUn...
Expose port 8088 directly on ClusterIP service
Service only had port 80→8088 mapping. Internal services using **************** (e.g. VOIPService API token requests) were timing out because port 8088 had no l...
Trust backend pendingCount, drop client offsets
Backend pendingCount is now unread-based (counts SUPPORT_TICKET sessions with messages newer than the owner's lastViewed). When the agent opens a ticket, markAs...
Broadcast pending count + unread on agent read & visitor send
- **************** now delegates to **************** so the count reflects sessions with unread visitor messages and decrements when the agent calls markA...
Unread-based count + lookup-by-session helper
Adds **************** so the navtop SupportButton badge can count support tickets that actually need attention (have unread visitor messages) instead of just PR...
Add missing fail2ban-config ConfigMap for k1m1
Deployment references fail2ban-config ConfigMap (jail.local + asterisk-security.conf) but it did not exist, blocking the pod at MountVolume.SetUp and preventing...
Rebuild to pull latest kamo-shared-library
Picks up the new **************** queries and the SUPPORT_TICKET branch in ****************
MarkAsRead after agent send so lastViewed advances
On support tickets, both visitor and agent messages are stored under the same systemMember, so the unread-count query is member-agnostic and relies entirely on ...
Support tickets need member-agnostic unread counts
Visitor messages on support tickets are stored under the same systemMember that the agent authenticates as, so the existing CHAT-session unread queries — which ...
Add logging to uploadBackground endpoint
Log org/index, file info, userId, auth decisions, and MinIO staging key to trace why background uploads are not appearing in staging bucket.
Add logging to upload-background proxy route
Log org/index, file name/size/type, and upstream response status to diagnose why background uploads are not reaching APIService.
Add Location column to ticket datagrid
Reads **************** straight off the SupportTicketDTO (now snapshot on the ticket entity at creation time — no extra round trip). Renders flag + city/region/...
Store geo on ticket at creation, drop GeoLite reads
**************** now captures the visitor's geo once (via GeoLookupService) and persists ip/country/city/subdivision on the SupportTicket itself. SupportTicket...
Snapshot visitor geo on ticket entity
Adds VISITOR_IP_ADDRESS, VISITOR_COUNTRY_CODE, VISITOR_COUNTRY_NAME, VISITOR_CITY, VISITOR_SUBDIVISION columns. Captured at ticket creation so the list/detail e...
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.