Minimize app dependency on platform service (#21548)
Этот коммит содержится в:
коммит произвёл
GitHub
родитель
91ee9ed2eb
Коммит
d8e2859b0b
@@ -1491,7 +1491,7 @@ func viewChannel(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
c.App.UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
||||
c.App.Srv().Platform().UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
||||
c.ExtendSessionExpiryIfNeeded(w, r)
|
||||
|
||||
// Returning {"status": "OK", ...} for backwards compatibility
|
||||
|
||||
@@ -103,7 +103,7 @@ func createPost(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
c.App.SetStatusOnline(c.AppContext.Session().UserId, false)
|
||||
}
|
||||
|
||||
c.App.UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
||||
c.App.Srv().Platform().UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
||||
c.ExtendSessionExpiryIfNeeded(w, r)
|
||||
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
|
||||
@@ -56,7 +56,7 @@ func getGraphQLUser(ctx context.Context, id string) (*user, error) {
|
||||
}
|
||||
}
|
||||
|
||||
c.App.UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
||||
c.App.Srv().Platform().UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
||||
|
||||
return &user{*usr}, nil
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ func getUser(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
} else {
|
||||
c.App.SanitizeProfile(user, c.IsSystemAdmin())
|
||||
}
|
||||
c.App.UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
||||
c.App.Srv().Platform().UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
||||
w.Header().Set(model.HeaderEtagServer, etag)
|
||||
if err := json.NewEncoder(w).Encode(user); err != nil {
|
||||
c.Logger.Warn("Error while writing response", mlog.Err(err))
|
||||
@@ -864,7 +864,7 @@ func getUsers(c *Context, w http.ResponseWriter, r *http.Request) {
|
||||
if etag != "" {
|
||||
w.Header().Set(model.HeaderEtagServer, etag)
|
||||
}
|
||||
c.App.UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
||||
c.App.Srv().Platform().UpdateLastActivityAtIfNeeded(*c.AppContext.Session())
|
||||
|
||||
js, err := json.Marshal(profiles)
|
||||
if err != nil {
|
||||
|
||||
@@ -886,7 +886,6 @@ type AppIface interface {
|
||||
IsLeader() bool
|
||||
IsPasswordValid(password string) *model.AppError
|
||||
IsPhase2MigrationCompleted() *model.AppError
|
||||
IsUserAway(lastActivityAt int64) bool
|
||||
IsUserSignUpAllowed() *model.AppError
|
||||
JoinChannel(c request.CTX, channel *model.Channel, userID string) *model.AppError
|
||||
JoinDefaultChannels(c request.CTX, teamID string, user *model.User, shouldBeAdmin bool, userRequestorId string) *model.AppError
|
||||
@@ -1113,7 +1112,6 @@ type AppIface interface {
|
||||
UpdateHashedPassword(user *model.User, newHashedPassword string) *model.AppError
|
||||
UpdateHashedPasswordByUserId(userID, newHashedPassword string) *model.AppError
|
||||
UpdateIncomingWebhook(oldHook, updatedHook *model.IncomingWebhook) (*model.IncomingWebhook, *model.AppError)
|
||||
UpdateLastActivityAtIfNeeded(session model.Session)
|
||||
UpdateMfa(c request.CTX, activate bool, userID, token string) *model.AppError
|
||||
UpdateMobileAppBadge(userID string)
|
||||
UpdateOAuthApp(oldApp, updatedApp *model.OAuthApp) (*model.OAuthApp, *model.AppError)
|
||||
|
||||
@@ -11941,23 +11941,6 @@ func (a *OpenTracingAppLayer) IsPhase2MigrationCompleted() *model.AppError {
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) IsUserAway(lastActivityAt int64) bool {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.IsUserAway")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
resultVar0 := a.app.IsUserAway(lastActivityAt)
|
||||
|
||||
return resultVar0
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) IsUserSignUpAllowed() *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.IsUserSignUpAllowed")
|
||||
@@ -17315,21 +17298,6 @@ func (a *OpenTracingAppLayer) UpdateIncomingWebhook(oldHook *model.IncomingWebho
|
||||
return resultVar0, resultVar1
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UpdateLastActivityAtIfNeeded(session model.Session) {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateLastActivityAtIfNeeded")
|
||||
|
||||
a.ctx = newCtx
|
||||
a.app.Srv().Store().SetContext(newCtx)
|
||||
defer func() {
|
||||
a.app.Srv().Store().SetContext(origCtx)
|
||||
a.ctx = origCtx
|
||||
}()
|
||||
|
||||
defer span.Finish()
|
||||
a.app.UpdateLastActivityAtIfNeeded(session)
|
||||
}
|
||||
|
||||
func (a *OpenTracingAppLayer) UpdateMfa(c request.CTX, activate bool, userID string, token string) *model.AppError {
|
||||
origCtx := a.ctx
|
||||
span, newCtx := tracing.StartSpanWithParentByContext(a.ctx, "app.UpdateMfa")
|
||||
|
||||
@@ -181,7 +181,7 @@ func setupTestHelper(dbStore store.Store, enterprise bool, includeCacheLayer boo
|
||||
th.Service.SetLicense(nil)
|
||||
}
|
||||
|
||||
err = th.Service.Start(th.Suite)
|
||||
err = th.Service.Start()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
@@ -39,20 +39,6 @@ func (_m *SuiteIFace) GetSession(token string) (*model.Session, *model.AppError)
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// IsUserAway provides a mock function with given fields: lastActivityAt
|
||||
func (_m *SuiteIFace) IsUserAway(lastActivityAt int64) bool {
|
||||
ret := _m.Called(lastActivityAt)
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func(int64) bool); ok {
|
||||
r0 = rf(lastActivityAt)
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// RolesGrantPermission provides a mock function with given fields: roleNames, permissionId
|
||||
func (_m *SuiteIFace) RolesGrantPermission(roleNames []string, permissionId string) bool {
|
||||
ret := _m.Called(roleNames, permissionId)
|
||||
@@ -67,31 +53,6 @@ func (_m *SuiteIFace) RolesGrantPermission(roleNames []string, permissionId stri
|
||||
return r0
|
||||
}
|
||||
|
||||
// SetStatusAwayIfNeeded provides a mock function with given fields: userID, manual
|
||||
func (_m *SuiteIFace) SetStatusAwayIfNeeded(userID string, manual bool) {
|
||||
_m.Called(userID, manual)
|
||||
}
|
||||
|
||||
// SetStatusLastActivityAt provides a mock function with given fields: userID, activityAt
|
||||
func (_m *SuiteIFace) SetStatusLastActivityAt(userID string, activityAt int64) {
|
||||
_m.Called(userID, activityAt)
|
||||
}
|
||||
|
||||
// SetStatusOffline provides a mock function with given fields: userID, manual
|
||||
func (_m *SuiteIFace) SetStatusOffline(userID string, manual bool) {
|
||||
_m.Called(userID, manual)
|
||||
}
|
||||
|
||||
// SetStatusOnline provides a mock function with given fields: userID, manual
|
||||
func (_m *SuiteIFace) SetStatusOnline(userID string, manual bool) {
|
||||
_m.Called(userID, manual)
|
||||
}
|
||||
|
||||
// UpdateLastActivityAtIfNeeded provides a mock function with given fields: session
|
||||
func (_m *SuiteIFace) UpdateLastActivityAtIfNeeded(session model.Session) {
|
||||
_m.Called(session)
|
||||
}
|
||||
|
||||
// UserCanSeeOtherUser provides a mock function with given fields: userID, otherUserId
|
||||
func (_m *SuiteIFace) UserCanSeeOtherUser(userID string, otherUserId string) (bool, *model.AppError) {
|
||||
ret := _m.Called(userID, otherUserId)
|
||||
|
||||
@@ -304,8 +304,8 @@ func New(sc ServiceConfig, options ...Option) (*PlatformService, error) {
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func (ps *PlatformService) Start(suite SuiteIFace) error {
|
||||
ps.hubStart(suite)
|
||||
func (ps *PlatformService) Start() error {
|
||||
ps.hubStart()
|
||||
|
||||
ps.configListenerId = ps.AddConfigListener(func(_, _ *model.Config) {
|
||||
ps.regenerateClientConfig()
|
||||
|
||||
@@ -212,3 +212,198 @@ func (ps *PlatformService) GetStatus(userID string) (*model.Status, *model.AppEr
|
||||
|
||||
return status, nil
|
||||
}
|
||||
|
||||
// SetStatusLastActivityAt sets the last activity at for a user on the local app server and updates
|
||||
// status to away if needed. Used by the WS to set status to away if an 'online' device disconnects
|
||||
// while an 'away' device is still connected
|
||||
func (ps *PlatformService) SetStatusLastActivityAt(userID string, activityAt int64) {
|
||||
var status *model.Status
|
||||
var err *model.AppError
|
||||
if status, err = ps.GetStatus(userID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
status.LastActivityAt = activityAt
|
||||
|
||||
ps.AddStatusCacheSkipClusterSend(status)
|
||||
ps.SetStatusAwayIfNeeded(userID, false)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) UpdateLastActivityAtIfNeeded(session model.Session) {
|
||||
now := model.GetMillis()
|
||||
|
||||
ps.UpdateWebConnUserActivity(session, now)
|
||||
|
||||
if now-session.LastActivityAt < model.SessionActivityTimeout {
|
||||
return
|
||||
}
|
||||
|
||||
if err := ps.Store.Session().UpdateLastActivityAt(session.Id, now); err != nil {
|
||||
mlog.Warn("Failed to update LastActivityAt", mlog.String("user_id", session.UserId), mlog.String("session_id", session.Id), mlog.Err(err))
|
||||
}
|
||||
|
||||
session.LastActivityAt = now
|
||||
ps.AddSessionToCache(&session)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) SetStatusOnline(userID string, manual bool) {
|
||||
if !*ps.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
broadcast := false
|
||||
|
||||
var oldStatus string = model.StatusOffline
|
||||
var oldTime int64
|
||||
var oldManual bool
|
||||
var status *model.Status
|
||||
var err *model.AppError
|
||||
|
||||
if status, err = ps.GetStatus(userID); err != nil {
|
||||
status = &model.Status{UserId: userID, Status: model.StatusOnline, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
|
||||
broadcast = true
|
||||
} else {
|
||||
if status.Manual && !manual {
|
||||
return // manually set status always overrides non-manual one
|
||||
}
|
||||
|
||||
if status.Status != model.StatusOnline {
|
||||
broadcast = true
|
||||
}
|
||||
|
||||
oldStatus = status.Status
|
||||
oldTime = status.LastActivityAt
|
||||
oldManual = status.Manual
|
||||
|
||||
status.Status = model.StatusOnline
|
||||
status.Manual = false // for "online" there's no manual setting
|
||||
status.LastActivityAt = model.GetMillis()
|
||||
}
|
||||
|
||||
ps.AddStatusCache(status)
|
||||
|
||||
// Only update the database if the status has changed, the status has been manually set,
|
||||
// or enough time has passed since the previous action
|
||||
if status.Status != oldStatus || status.Manual != oldManual || status.LastActivityAt-oldTime > model.StatusMinUpdateTime {
|
||||
if broadcast {
|
||||
if err := ps.Store.Status().SaveOrUpdate(status); err != nil {
|
||||
mlog.Warn("Failed to save status", mlog.String("user_id", userID), mlog.Err(err), mlog.String("user_id", userID))
|
||||
}
|
||||
} else {
|
||||
if err := ps.Store.Status().UpdateLastActivityAt(status.UserId, status.LastActivityAt); err != nil {
|
||||
mlog.Error("Failed to save status", mlog.String("user_id", userID), mlog.Err(err), mlog.String("user_id", userID))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if broadcast {
|
||||
ps.BroadcastStatus(status)
|
||||
}
|
||||
}
|
||||
|
||||
func (ps *PlatformService) SetStatusOffline(userID string, manual bool) {
|
||||
if !*ps.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := ps.GetStatus(userID)
|
||||
if err == nil && status.Manual && !manual {
|
||||
return // manually set status always overrides non-manual one
|
||||
}
|
||||
|
||||
status = &model.Status{UserId: userID, Status: model.StatusOffline, Manual: manual, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
|
||||
|
||||
ps.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) SetStatusAwayIfNeeded(userID string, manual bool) {
|
||||
if !*ps.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := ps.GetStatus(userID)
|
||||
|
||||
if err != nil {
|
||||
status = &model.Status{UserId: userID, Status: model.StatusOffline, Manual: manual, LastActivityAt: 0, ActiveChannel: ""}
|
||||
}
|
||||
|
||||
if !manual && status.Manual {
|
||||
return // manually set status always overrides non-manual one
|
||||
}
|
||||
|
||||
if !manual {
|
||||
if status.Status == model.StatusAway {
|
||||
return
|
||||
}
|
||||
|
||||
if !ps.isUserAway(status.LastActivityAt) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
status.Status = model.StatusAway
|
||||
status.Manual = manual
|
||||
status.ActiveChannel = ""
|
||||
|
||||
ps.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
// SetStatusDoNotDisturbTimed takes endtime in unix epoch format in UTC
|
||||
// and sets status of given userId to dnd which will be restored back after endtime
|
||||
func (ps *PlatformService) SetStatusDoNotDisturbTimed(userId string, endtime int64) {
|
||||
if !*ps.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := ps.GetStatus(userId)
|
||||
|
||||
if err != nil {
|
||||
status = &model.Status{UserId: userId, Status: model.StatusOffline, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
|
||||
}
|
||||
|
||||
status.PrevStatus = status.Status
|
||||
status.Status = model.StatusDnd
|
||||
status.Manual = true
|
||||
|
||||
status.DNDEndTime = endtime
|
||||
|
||||
ps.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) SetStatusDoNotDisturb(userID string) {
|
||||
if !*ps.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := ps.GetStatus(userID)
|
||||
|
||||
if err != nil {
|
||||
status = &model.Status{UserId: userID, Status: model.StatusOffline, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
|
||||
}
|
||||
|
||||
status.Status = model.StatusDnd
|
||||
status.Manual = true
|
||||
|
||||
ps.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) SetStatusOutOfOffice(userID string) {
|
||||
if !*ps.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := ps.GetStatus(userID)
|
||||
|
||||
if err != nil {
|
||||
status = &model.Status{UserId: userID, Status: model.StatusOutOfOffice, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
|
||||
}
|
||||
|
||||
status.Status = model.StatusOutOfOffice
|
||||
status.Manual = true
|
||||
|
||||
ps.SaveAndBroadcastStatus(status)
|
||||
}
|
||||
|
||||
func (ps *PlatformService) isUserAway(lastActivityAt int64) bool {
|
||||
return model.GetMillis()-lastActivityAt >= *ps.Config().TeamSettings.UserStatusAwayTimeout*1000
|
||||
}
|
||||
|
||||
@@ -165,8 +165,8 @@ func (ps *PlatformService) PopulateWebConnConfig(s *model.Session, cfg *WebConnC
|
||||
func (ps *PlatformService) NewWebConn(cfg *WebConnConfig, suite SuiteIFace, runner HookRunner) *WebConn {
|
||||
if cfg.Session.UserId != "" {
|
||||
ps.Go(func() {
|
||||
suite.SetStatusOnline(cfg.Session.UserId, false)
|
||||
suite.UpdateLastActivityAtIfNeeded(cfg.Session)
|
||||
ps.SetStatusOnline(cfg.Session.UserId, false)
|
||||
ps.UpdateLastActivityAtIfNeeded(cfg.Session)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -344,7 +344,7 @@ func (wc *WebConn) readPump() {
|
||||
}
|
||||
if wc.IsAuthenticated() {
|
||||
wc.Platform.Go(func() {
|
||||
wc.Suite.SetStatusAwayIfNeeded(wc.UserId, false)
|
||||
wc.Platform.SetStatusAwayIfNeeded(wc.UserId, false)
|
||||
})
|
||||
}
|
||||
return nil
|
||||
|
||||
@@ -21,12 +21,6 @@ const (
|
||||
)
|
||||
|
||||
type SuiteIFace interface {
|
||||
SetStatusLastActivityAt(userID string, activityAt int64)
|
||||
SetStatusOffline(userID string, manual bool)
|
||||
IsUserAway(lastActivityAt int64) bool
|
||||
SetStatusOnline(userID string, manual bool)
|
||||
UpdateLastActivityAtIfNeeded(session model.Session)
|
||||
SetStatusAwayIfNeeded(userID string, manual bool)
|
||||
GetSession(token string) (*model.Session, *model.AppError)
|
||||
RolesGrantPermission(roleNames []string, permissionId string) bool
|
||||
UserCanSeeOtherUser(userID string, otherUserId string) (bool, *model.AppError)
|
||||
@@ -95,7 +89,7 @@ func newWebHub(ps *PlatformService) *Hub {
|
||||
}
|
||||
|
||||
// hubStart starts all the hubs.
|
||||
func (ps *PlatformService) hubStart(suite SuiteIFace) {
|
||||
func (ps *PlatformService) hubStart() {
|
||||
// Total number of hubs is twice the number of CPUs.
|
||||
numberOfHubs := runtime.NumCPU() * 2
|
||||
ps.logger.Info("Starting websocket hubs", mlog.Int("number_of_hubs", numberOfHubs))
|
||||
@@ -105,7 +99,7 @@ func (ps *PlatformService) hubStart(suite SuiteIFace) {
|
||||
for i := 0; i < numberOfHubs; i++ {
|
||||
hubs[i] = newWebHub(ps)
|
||||
hubs[i].connectionIndex = i
|
||||
hubs[i].Start(suite)
|
||||
hubs[i].Start()
|
||||
}
|
||||
// Assigning to the hubs slice without any mutex is fine because it is only assigned once
|
||||
// during the start of the program and always read from after that.
|
||||
@@ -366,7 +360,7 @@ func (h *Hub) Stop() {
|
||||
}
|
||||
|
||||
// Start starts the hub.
|
||||
func (h *Hub) Start(suite SuiteIFace) {
|
||||
func (h *Hub) Start() {
|
||||
var doStart func()
|
||||
var doRecoverableStart func()
|
||||
var doRecover func()
|
||||
@@ -439,7 +433,7 @@ func (h *Hub) Start(suite SuiteIFace) {
|
||||
conns := connIndex.ForUser(webConn.UserId)
|
||||
if len(conns) == 0 || areAllInactive(conns) {
|
||||
h.platform.Go(func() {
|
||||
suite.SetStatusOffline(webConn.UserId, false)
|
||||
h.platform.SetStatusOffline(webConn.UserId, false)
|
||||
})
|
||||
continue
|
||||
}
|
||||
@@ -453,9 +447,9 @@ func (h *Hub) Start(suite SuiteIFace) {
|
||||
}
|
||||
}
|
||||
|
||||
if suite.IsUserAway(latestActivity) {
|
||||
if h.platform.isUserAway(latestActivity) {
|
||||
h.platform.Go(func() {
|
||||
suite.SetStatusLastActivityAt(webConn.UserId, latestActivity)
|
||||
h.platform.SetStatusLastActivityAt(webConn.UserId, latestActivity)
|
||||
})
|
||||
}
|
||||
case userID := <-h.invalidateUser:
|
||||
@@ -522,7 +516,7 @@ func (h *Hub) Start(suite SuiteIFace) {
|
||||
case <-h.stop:
|
||||
for webConn := range connIndex.All() {
|
||||
webConn.Close()
|
||||
suite.SetStatusOffline(webConn.UserId, false)
|
||||
h.platform.SetStatusOffline(webConn.UserId, false)
|
||||
}
|
||||
|
||||
h.explicitStop = true
|
||||
|
||||
@@ -67,7 +67,7 @@ func TestHubStopWithMultipleConnections(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
th.Service.Start(th.Suite)
|
||||
th.Service.Start()
|
||||
wc1 := registerDummyWebConn(t, th, s.Listener.Addr(), session)
|
||||
wc2 := registerDummyWebConn(t, th, s.Listener.Addr(), session)
|
||||
wc3 := registerDummyWebConn(t, th, s.Listener.Addr(), session)
|
||||
@@ -90,7 +90,7 @@ func TestHubStopRaceCondition(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
th.Service.Start(th.Suite)
|
||||
th.Service.Start()
|
||||
wc1 := registerDummyWebConn(t, th, s.Listener.Addr(), session)
|
||||
defer wc1.Close()
|
||||
|
||||
@@ -467,18 +467,13 @@ func TestHubIsRegistered(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
|
||||
mockSuite := &platform_mocks.SuiteIFace{}
|
||||
mockSuite.On("SetStatusOnline", th.BasicUser.Id, false).Return()
|
||||
mockSuite.On("UpdateLastActivityAtIfNeeded", *session).Return()
|
||||
mockSuite.On("GetSession", session.Token).Return(session, nil)
|
||||
mockSuite.On("IsUserAway", mock.Anything).Return(false)
|
||||
mockSuite.On("SetStatusOffline", th.BasicUser.Id, false).Return()
|
||||
|
||||
th.Suite = mockSuite
|
||||
|
||||
s := httptest.NewServer(dummyWebsocketHandler(t))
|
||||
defer s.Close()
|
||||
|
||||
th.Service.Start(th.Suite)
|
||||
th.Service.Start()
|
||||
wc1 := registerDummyWebConn(t, th, s.Listener.Addr(), session)
|
||||
wc2 := registerDummyWebConn(t, th, s.Listener.Addr(), session)
|
||||
wc3 := registerDummyWebConn(t, th, s.Listener.Addr(), session)
|
||||
@@ -486,9 +481,7 @@ func TestHubIsRegistered(t *testing.T) {
|
||||
defer wc2.Close()
|
||||
defer wc3.Close()
|
||||
|
||||
session1 := wc1.session.Load().(*model.Session)
|
||||
|
||||
assert.True(t, th.Service.SessionIsRegistered(*session1))
|
||||
assert.True(t, th.Service.SessionIsRegistered(*wc1.session.Load().(*model.Session)))
|
||||
assert.True(t, th.Service.SessionIsRegistered(*wc2.session.Load().(*model.Session)))
|
||||
assert.True(t, th.Service.SessionIsRegistered(*wc3.session.Load().(*model.Session)))
|
||||
|
||||
@@ -551,7 +544,7 @@ func BenchmarkGetHubForUserId(b *testing.B) {
|
||||
th := Setup(b).InitBasic()
|
||||
defer th.TearDown()
|
||||
|
||||
th.Service.Start(th.Suite)
|
||||
th.Service.Start()
|
||||
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
|
||||
@@ -59,8 +59,8 @@ func (wr *WebSocketRouter) ServeWebSocket(conn *WebConn, r *model.WebSocketReque
|
||||
conn.Platform.HubRegister(conn)
|
||||
|
||||
conn.Platform.Go(func() {
|
||||
conn.Suite.SetStatusOnline(session.UserId, false)
|
||||
conn.Suite.UpdateLastActivityAtIfNeeded(*session)
|
||||
conn.Platform.SetStatusOnline(session.UserId, false)
|
||||
conn.Platform.UpdateLastActivityAtIfNeeded(*session)
|
||||
})
|
||||
|
||||
resp := model.NewWebSocketResponse(model.StatusOk, r.Seq, nil)
|
||||
|
||||
@@ -287,7 +287,7 @@ func NewServer(options ...Option) (*Server, error) {
|
||||
// It is important to initialize the hub only after the global logger is set
|
||||
// to avoid race conditions while logging from inside the hub.
|
||||
// Step 5: Start hub in platform which the hub depends on s.Channels() (step 4)
|
||||
s.platform.Start(New(ServerConnector(s.Channels())))
|
||||
s.platform.Start()
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Everything below this is not order sensitive and safe to be moved around.
|
||||
|
||||
@@ -235,23 +235,6 @@ func (a *App) AttachDeviceId(sessionID string, deviceID string, expiresAt int64)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (a *App) UpdateLastActivityAtIfNeeded(session model.Session) {
|
||||
now := model.GetMillis()
|
||||
|
||||
a.UpdateWebConnUserActivity(session, now)
|
||||
|
||||
if now-session.LastActivityAt < model.SessionActivityTimeout {
|
||||
return
|
||||
}
|
||||
|
||||
if err := a.Srv().Store().Session().UpdateLastActivityAt(session.Id, now); err != nil {
|
||||
mlog.Warn("Failed to update LastActivityAt", mlog.String("user_id", session.UserId), mlog.String("session_id", session.Id), mlog.Err(err))
|
||||
}
|
||||
|
||||
session.LastActivityAt = now
|
||||
a.ch.srv.platform.AddSessionToCache(&session)
|
||||
}
|
||||
|
||||
// ExtendSessionExpiryIfNeeded extends Session.ExpiresAt based on session lengths in config.
|
||||
// A new ExpiresAt is only written if enough time has elapsed since last update.
|
||||
// Returns true only if the session was extended.
|
||||
|
||||
159
app/status.go
159
app/status.go
@@ -21,174 +21,33 @@ func (a *App) GetUserStatusesByIds(userIDs []string) ([]*model.Status, *model.Ap
|
||||
// status to away if needed. Used by the WS to set status to away if an 'online' device disconnects
|
||||
// while an 'away' device is still connected
|
||||
func (a *App) SetStatusLastActivityAt(userID string, activityAt int64) {
|
||||
var status *model.Status
|
||||
var err *model.AppError
|
||||
if status, err = a.GetStatus(userID); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
status.LastActivityAt = activityAt
|
||||
|
||||
a.Srv().Platform().AddStatusCacheSkipClusterSend(status)
|
||||
a.SetStatusAwayIfNeeded(userID, false)
|
||||
a.Srv().Platform().SetStatusLastActivityAt(userID, activityAt)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusOnline(userID string, manual bool) {
|
||||
if !*a.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
broadcast := false
|
||||
|
||||
var oldStatus string = model.StatusOffline
|
||||
var oldTime int64
|
||||
var oldManual bool
|
||||
var status *model.Status
|
||||
var err *model.AppError
|
||||
|
||||
if status, err = a.GetStatus(userID); err != nil {
|
||||
status = &model.Status{UserId: userID, Status: model.StatusOnline, Manual: false, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
|
||||
broadcast = true
|
||||
} else {
|
||||
if status.Manual && !manual {
|
||||
return // manually set status always overrides non-manual one
|
||||
}
|
||||
|
||||
if status.Status != model.StatusOnline {
|
||||
broadcast = true
|
||||
}
|
||||
|
||||
oldStatus = status.Status
|
||||
oldTime = status.LastActivityAt
|
||||
oldManual = status.Manual
|
||||
|
||||
status.Status = model.StatusOnline
|
||||
status.Manual = false // for "online" there's no manual setting
|
||||
status.LastActivityAt = model.GetMillis()
|
||||
}
|
||||
|
||||
a.Srv().Platform().AddStatusCache(status)
|
||||
|
||||
// Only update the database if the status has changed, the status has been manually set,
|
||||
// or enough time has passed since the previous action
|
||||
if status.Status != oldStatus || status.Manual != oldManual || status.LastActivityAt-oldTime > model.StatusMinUpdateTime {
|
||||
if broadcast {
|
||||
if err := a.Srv().Store().Status().SaveOrUpdate(status); err != nil {
|
||||
mlog.Warn("Failed to save status", mlog.String("user_id", userID), mlog.Err(err), mlog.String("user_id", userID))
|
||||
}
|
||||
} else {
|
||||
if err := a.Srv().Store().Status().UpdateLastActivityAt(status.UserId, status.LastActivityAt); err != nil {
|
||||
mlog.Error("Failed to save status", mlog.String("user_id", userID), mlog.Err(err), mlog.String("user_id", userID))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if broadcast {
|
||||
a.Srv().Platform().BroadcastStatus(status)
|
||||
}
|
||||
a.Srv().Platform().SetStatusOnline(userID, manual)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusOffline(userID string, manual bool) {
|
||||
if !*a.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := a.GetStatus(userID)
|
||||
if err == nil && status.Manual && !manual {
|
||||
return // manually set status always overrides non-manual one
|
||||
}
|
||||
|
||||
status = &model.Status{UserId: userID, Status: model.StatusOffline, Manual: manual, LastActivityAt: model.GetMillis(), ActiveChannel: ""}
|
||||
|
||||
a.Srv().Platform().SaveAndBroadcastStatus(status)
|
||||
a.Srv().Platform().SetStatusOffline(userID, manual)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusAwayIfNeeded(userID string, manual bool) {
|
||||
if !*a.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := a.GetStatus(userID)
|
||||
|
||||
if err != nil {
|
||||
status = &model.Status{UserId: userID, Status: model.StatusOffline, Manual: manual, LastActivityAt: 0, ActiveChannel: ""}
|
||||
}
|
||||
|
||||
if !manual && status.Manual {
|
||||
return // manually set status always overrides non-manual one
|
||||
}
|
||||
|
||||
if !manual {
|
||||
if status.Status == model.StatusAway {
|
||||
return
|
||||
}
|
||||
|
||||
if !a.IsUserAway(status.LastActivityAt) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
status.Status = model.StatusAway
|
||||
status.Manual = manual
|
||||
status.ActiveChannel = ""
|
||||
|
||||
a.Srv().Platform().SaveAndBroadcastStatus(status)
|
||||
a.Srv().Platform().SetStatusAwayIfNeeded(userID, manual)
|
||||
}
|
||||
|
||||
// SetStatusDoNotDisturbTimed takes endtime in unix epoch format in UTC
|
||||
// and sets status of given userId to dnd which will be restored back after endtime
|
||||
func (a *App) SetStatusDoNotDisturbTimed(userId string, endtime int64) {
|
||||
if !*a.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := a.GetStatus(userId)
|
||||
|
||||
if err != nil {
|
||||
status = &model.Status{UserId: userId, Status: model.StatusOffline, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
|
||||
}
|
||||
|
||||
status.PrevStatus = status.Status
|
||||
status.Status = model.StatusDnd
|
||||
status.Manual = true
|
||||
|
||||
status.DNDEndTime = endtime
|
||||
|
||||
a.Srv().Platform().SaveAndBroadcastStatus(status)
|
||||
a.Srv().Platform().SetStatusDoNotDisturbTimed(userId, endtime)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusDoNotDisturb(userID string) {
|
||||
if !*a.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := a.GetStatus(userID)
|
||||
|
||||
if err != nil {
|
||||
status = &model.Status{UserId: userID, Status: model.StatusOffline, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
|
||||
}
|
||||
|
||||
status.Status = model.StatusDnd
|
||||
status.Manual = true
|
||||
|
||||
a.Srv().Platform().SaveAndBroadcastStatus(status)
|
||||
a.Srv().Platform().SetStatusDoNotDisturb(userID)
|
||||
}
|
||||
|
||||
func (a *App) SetStatusOutOfOffice(userID string) {
|
||||
if !*a.Config().ServiceSettings.EnableUserStatuses {
|
||||
return
|
||||
}
|
||||
|
||||
status, err := a.GetStatus(userID)
|
||||
|
||||
if err != nil {
|
||||
status = &model.Status{UserId: userID, Status: model.StatusOutOfOffice, Manual: false, LastActivityAt: 0, ActiveChannel: ""}
|
||||
}
|
||||
|
||||
status.Status = model.StatusOutOfOffice
|
||||
status.Manual = true
|
||||
|
||||
a.Srv().Platform().SaveAndBroadcastStatus(status)
|
||||
a.Srv().Platform().SetStatusOutOfOffice(userID)
|
||||
}
|
||||
|
||||
func (a *App) GetStatusFromCache(userID string) *model.Status {
|
||||
@@ -199,10 +58,6 @@ func (a *App) GetStatus(userID string) (*model.Status, *model.AppError) {
|
||||
return a.Srv().Platform().GetStatus(userID)
|
||||
}
|
||||
|
||||
func (a *App) IsUserAway(lastActivityAt int64) bool {
|
||||
return model.GetMillis()-lastActivityAt >= *a.Config().TeamSettings.UserStatusAwayTimeout*1000
|
||||
}
|
||||
|
||||
// UpdateDNDStatusOfUsers is a recurring task which is started when server starts
|
||||
// which unsets dnd status of users if needed and saves and broadcasts it
|
||||
func (a *App) UpdateDNDStatusOfUsers() {
|
||||
|
||||
Ссылка в новой задаче
Block a user