Files
zeekay 8feb24ea1f One name for this fork, and it is ours
The server module named github.com/hanzoai/team/server/v8 — a path that
resolves to the retired workspace's repository, not this one — while
server/public still published github.com/mattermost/mattermost/server/public.
Both are github.com/hanzoteam/server/... now, which is where this code
actually lives.

That second half is what made the binary unbootable. Upstream's public path
stayed claimed by upstream, mattermost-plugin-ai imported it, and the build
linked public/model twice; public/plugin's gob.Register then ran twice under
one name and panicked at init, before main. Reverting the rename traded the
brand for a working binary. Rebranding the plugin alongside it needs no such
trade: the agents fork is github.com/hanzoteam/agents/v2 against our public
module, so no module named mattermost/mattermost is in the graph at all.

Measured: `go list -deps ./cmd/mattermost` names exactly one public/model, and
it is ours.
2026-08-28 23:35:36 -07:00

176 lines
6.6 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package platform
import (
"bytes"
"encoding/json"
"github.com/hanzoteam/server/server/public/model"
"github.com/hanzoteam/server/server/public/shared/mlog"
"github.com/hanzoteam/server/server/v8/einterfaces"
)
func (ps *PlatformService) RegisterClusterHandlers() {
ps.clusterIFace.RegisterClusterMessageHandler(model.ClusterEventPublish, ps.ClusterPublishHandler)
ps.clusterIFace.RegisterClusterMessageHandler(model.ClusterEventUpdateStatus, ps.ClusterUpdateStatusHandler)
ps.clusterIFace.RegisterClusterMessageHandler(model.ClusterEventInvalidateAllCaches, ps.ClusterInvalidateAllCachesHandler)
ps.clusterIFace.RegisterClusterMessageHandler(model.ClusterEventInvalidateWebConnCacheForUser, ps.clusterInvalidateWebConnSessionCacheForUserHandler)
ps.clusterIFace.RegisterClusterMessageHandler(model.ClusterEventBusyStateChanged, ps.clusterBusyStateChgHandler)
ps.clusterIFace.RegisterClusterMessageHandler(model.ClusterEventUpdateSessionAttributes, ps.ClusterUpdateSessionAttributesHandler)
ps.clusterIFace.RegisterClusterMessageHandler(model.ClusterEventClearSessionCacheForUser, ps.clusterClearSessionCacheForUserHandler)
ps.clusterIFace.RegisterClusterMessageHandler(model.ClusterEventClearSessionCacheForAllUsers, ps.clusterClearSessionCacheForAllUsersHandler)
for e, h := range ps.additionalClusterHandlers {
ps.clusterIFace.RegisterClusterMessageHandler(e, h)
}
}
func (ps *PlatformService) RegisterClusterMessageHandler(ev model.ClusterEvent, h einterfaces.ClusterMessageHandler) {
ps.additionalClusterHandlers[ev] = h
}
func (ps *PlatformService) ClusterPublishHandler(msg *model.ClusterMessage) {
event, err := model.WebSocketEventFromJSON(bytes.NewReader(msg.Data))
if err != nil {
ps.logger.Warn("Failed to decode event from JSON", mlog.Err(err))
return
}
ps.PublishSkipClusterSend(event)
}
func (ps *PlatformService) ClusterUpdateStatusHandler(msg *model.ClusterMessage) {
var status model.Status
if err := json.Unmarshal(msg.Data, &status); err != nil {
ps.logger.Warn("Failed to decode status from JSON", mlog.Err(err))
}
if err := ps.statusCache.SetWithDefaultExpiry(status.UserId, status); err != nil {
ps.logger.Warn("Failed to store the status in the cache", mlog.String("user_id", status.UserId), mlog.Err(err))
}
}
func (ps *PlatformService) ClusterUpdateSessionAttributesHandler(msg *model.ClusterMessage) {
var payload model.SessionAttributesClusterPayload
if err := json.Unmarshal(msg.Data, &payload); err != nil {
ps.logger.Warn("Failed to decode session attributes from JSON", mlog.Err(err))
return
}
if err := ps.Store.SessionAttribute().Refresh(payload.SessionID, payload.Attrs, payload.Timestamp); err != nil {
ps.logger.Warn("Failed to merge session attributes from cluster", mlog.String("session_id", payload.SessionID), mlog.Err(err))
}
}
func (ps *PlatformService) ClusterInvalidateAllCachesHandler(msg *model.ClusterMessage) {
if err := ps.InvalidateAllCachesSkipSend(); err != nil {
ps.logger.Error("Error validating caches from cluster message", mlog.Err(err))
}
}
func (ps *PlatformService) clusterInvalidateWebConnSessionCacheForUserHandler(msg *model.ClusterMessage) {
ps.invalidateWebConnSessionCacheForUserSkipClusterSend(string(msg.Data))
}
func (ps *PlatformService) ClearSessionCacheForUserSkipClusterSend(userID string) {
ps.ClearUserSessionCacheLocal(userID)
ps.invalidateWebConnSessionCacheForUserSkipClusterSend(userID)
}
// ClearSessionCacheForAllUsersSkipClusterSend purges the in-memory
// session cache and invalidates every WebConn on this node. The hub
// fan-out runs even if the cache purge fails; the purge error is
// returned so wrappers can propagate it.
func (ps *PlatformService) ClearSessionCacheForAllUsersSkipClusterSend() error {
ps.logger.Info("Purging sessions cache")
err := ps.ClearAllUsersSessionCacheLocal()
if err != nil {
ps.logger.Error("Failed to purge session cache", mlog.Err(err))
}
ps.invalidateWebConnSessionCacheForAllUsersSkipClusterSend()
return err
}
func (ps *PlatformService) clusterClearSessionCacheForUserHandler(msg *model.ClusterMessage) {
ps.ClearSessionCacheForUserSkipClusterSend(string(msg.Data))
}
func (ps *PlatformService) clusterClearSessionCacheForAllUsersHandler(msg *model.ClusterMessage) {
if err := ps.ClearSessionCacheForAllUsersSkipClusterSend(); err != nil {
ps.logger.Error("Failed to clear session cache for all users from cluster handler", mlog.Err(err))
}
}
func (ps *PlatformService) clusterBusyStateChgHandler(msg *model.ClusterMessage) {
var sbs model.ServerBusyState
if err := json.Unmarshal(msg.Data, &sbs); err != nil {
ps.logger.Warn("Failed to decode server busy state from JSON", mlog.Err(err))
}
ps.Busy.ClusterEventChanged(&sbs)
if sbs.Busy {
ps.logger.Warn("server busy state activated via cluster event - non-critical services disabled", mlog.Int("expires_sec", sbs.Expires))
} else {
ps.logger.Info("server busy state cleared via cluster event - non-critical services enabled")
}
}
func (ps *PlatformService) invalidateWebConnSessionCacheForUserSkipClusterSend(userID string) {
hub := ps.GetHubForUserId(userID)
if hub != nil {
hub.InvalidateUser(userID)
}
}
// invalidateWebConnSessionCacheForAllUsersSkipClusterSend signals
// every hub on this node to invalidate the cached session state of
// all of its WebConns. Companion to ClearAllUsersSessionCacheLocal.
func (ps *PlatformService) invalidateWebConnSessionCacheForAllUsersSkipClusterSend() {
for _, hub := range ps.hubs {
if hub != nil {
hub.InvalidateAll()
}
}
}
func (ps *PlatformService) InvalidateAllCachesSkipSend() *model.AppError {
ps.logger.Info("Purging all caches")
if err := ps.ClearAllUsersSessionCacheLocal(); err != nil {
ps.logger.Error("Failed to purge session cache", mlog.Err(err))
}
if err := ps.statusCache.Purge(); err != nil {
ps.logger.Warn("Failed to clear the status cache", mlog.Err(err))
}
ps.Store.Team().ClearCaches()
ps.Store.Channel().ClearCaches()
ps.Store.User().ClearCaches()
ps.Store.Post().ClearCaches()
ps.Store.FileInfo().ClearCaches()
ps.Store.Webhook().ClearCaches()
if err := linkCache.Purge(); err != nil {
ps.logger.Warn("Failed to clear the link cache", mlog.Err(err))
}
ps.LoadLicense()
return nil
}
func (ps *PlatformService) InvalidateAllCaches() *model.AppError {
if err := ps.InvalidateAllCachesSkipSend(); err != nil {
return err
}
if ps.clusterIFace != nil {
msg := &model.ClusterMessage{
Event: model.ClusterEventInvalidateAllCaches,
SendType: model.ClusterSendReliable,
WaitForAllToSend: true,
}
ps.clusterIFace.SendClusterMessage(msg)
}
return nil
}