HH-564: harden durable realtime publish boundaries (#139)

* fix(HH-564): harden durable realtime enqueue

* fix(HH-564): wire production SSE stream

---------

Co-authored-by: Rogee <rogee@ipao.vip>
This commit is contained in:
Rogee
2026-08-23 23:38:00 +08:00
committed by GitHub
co-authored by rogee
parent cb0b554e94
commit f36606a4f2
14 changed files with 423 additions and 12 deletions
@@ -113,7 +113,7 @@ func (h *CompanyHandler) publishCompanyEvent(accountID uint, eventType string, c
if h.eventPublisher == nil || company == nil {
return
}
h.eventPublisher.PublishEvent(accountID, eventType, serializeCompany(context.Background(), h.svc.DB(), company))
publishRealtimeEvent(h.eventPublisher, accountID, eventType, serializeCompany(context.Background(), h.svc.DB(), company))
}
// Create creates a new company.
@@ -289,7 +289,7 @@ func (h *ContactHandler) publishContactEvent(accountID uint, eventType string, c
if h.eventPublisher == nil || contact == nil {
return
}
h.eventPublisher.PublishEvent(accountID, eventType, serializeCRMContact(context.Background(), h.svc.DB(), contact, true))
publishRealtimeEvent(h.eventPublisher, accountID, eventType, serializeCRMContact(context.Background(), h.svc.DB(), contact, true))
}
// @Summary Delete a contact
@@ -11,9 +11,20 @@ import (
"github.com/gin-gonic/gin"
"github.com/gochat/gochat/internal/middleware"
"github.com/gochat/gochat/internal/model"
"github.com/gochat/gochat/internal/ws"
applogger "github.com/gochat/gochat/pkg/logger"
"gorm.io/gorm"
)
func publishRealtimeEvent(publisher *ws.EventPublisher, accountID uint, eventType string, payload any) {
if publisher == nil {
return
}
if err := publisher.PublishEvent(accountID, eventType, payload); err != nil {
applogger.L().Errorf("publish realtime event %s for account %d: %v", eventType, accountID, err)
}
}
func bindJSONWrappedOrRaw(c *gin.Context, wrapperKey string, target any) error {
if c.Request.Body == nil {
return fmt.Errorf("empty request body")
@@ -320,7 +320,7 @@ func (h *NotificationHandler) publishNotificationEvent(ctx context.Context, acco
applogger.L().Warnf("notification event %s total count: %v", eventType, err)
return
}
h.eventPublisher.PublishEvent(accountID, eventType, gin.H{
publishRealtimeEvent(h.eventPublisher, accountID, eventType, gin.H{
"notification": h.serializeNotification(ctx, notification),
"unread_count": unreadCount,
"count": total,
@@ -0,0 +1,30 @@
package v1
import (
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/require"
"github.com/gochat/gochat/internal/worker"
"github.com/gochat/gochat/internal/ws"
applogger "github.com/gochat/gochat/pkg/logger"
)
func TestPublishRealtimeEventLogsEnqueueFailure(t *testing.T) {
logPath := filepath.Join(t.TempDir(), "realtime.log")
require.NoError(t, applogger.Init(applogger.Config{Level: "error", Format: "console", Output: logPath, ErrorOutput: logPath}))
t.Cleanup(func() {
require.NoError(t, applogger.Init(applogger.Config{Level: "info", Format: "console", Output: "stdout", ErrorOutput: "stderr"}))
})
publisher := ws.NewEventPublisherLocal(nil, nil)
publisher.SetWorkerPool(worker.NewWorkerPool(nil))
publishRealtimeEvent(publisher, 42, ws.EventContactUpdated, map[string]any{"id": 7})
applogger.Sync()
logs, err := os.ReadFile(logPath)
require.NoError(t, err)
require.Contains(t, string(logs), "publish realtime event contact.updated for account 42: worker database is required")
}