MM-25394 session expired push notifications (#14732)
* new job type created that checks for expired mobile sessions and pushes notifications. * only send session expired notifications if ExtendSessionLengthWithActivity is enabled. * includes schema change: field added to Sessions table
Этот коммит содержится в:
19
jobs/expirynotify/expirynotify.go
Обычный файл
19
jobs/expirynotify/expirynotify.go
Обычный файл
@@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package expirynotify
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/v5/app"
|
||||
tjobs "github.com/mattermost/mattermost-server/v5/jobs/interfaces"
|
||||
)
|
||||
|
||||
type ExpiryNotifyJobInterfaceImpl struct {
|
||||
App *app.App
|
||||
}
|
||||
|
||||
func init() {
|
||||
app.RegisterJobsExpiryNotifyJobInterface(func(a *app.App) tjobs.ExpiryNotifyJobInterface {
|
||||
return &ExpiryNotifyJobInterfaceImpl{a}
|
||||
})
|
||||
}
|
||||
51
jobs/expirynotify/scheduler.go
Обычный файл
51
jobs/expirynotify/scheduler.go
Обычный файл
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package expirynotify
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/mattermost/mattermost-server/v5/app"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
)
|
||||
|
||||
const (
|
||||
SchedFreqMinutes = 10
|
||||
)
|
||||
|
||||
type Scheduler struct {
|
||||
App *app.App
|
||||
}
|
||||
|
||||
func (m *ExpiryNotifyJobInterfaceImpl) MakeScheduler() model.Scheduler {
|
||||
return &Scheduler{m.App}
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) Name() string {
|
||||
return JobName + "Scheduler"
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) JobType() string {
|
||||
return model.JOB_TYPE_EXPIRY_NOTIFY
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) Enabled(cfg *model.Config) bool {
|
||||
// Only enabled when ExtendSessionLengthWithActivity is enabled.
|
||||
return *cfg.ServiceSettings.ExtendSessionLengthWithActivity
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) NextScheduleTime(cfg *model.Config, now time.Time, pendingJobs bool, lastSuccessfulJob *model.Job) *time.Time {
|
||||
nextTime := time.Now().Add(SchedFreqMinutes * time.Minute)
|
||||
return &nextTime
|
||||
}
|
||||
|
||||
func (scheduler *Scheduler) ScheduleJob(cfg *model.Config, pendingJobs bool, lastSuccessfulJob *model.Job) (*model.Job, *model.AppError) {
|
||||
data := map[string]string{}
|
||||
|
||||
if job, err := scheduler.App.Srv().Jobs.CreateJob(model.JOB_TYPE_EXPIRY_NOTIFY, data); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return job, nil
|
||||
}
|
||||
}
|
||||
100
jobs/expirynotify/worker.go
Обычный файл
100
jobs/expirynotify/worker.go
Обычный файл
@@ -0,0 +1,100 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package expirynotify
|
||||
|
||||
import (
|
||||
"github.com/mattermost/mattermost-server/v5/app"
|
||||
"github.com/mattermost/mattermost-server/v5/jobs"
|
||||
"github.com/mattermost/mattermost-server/v5/mlog"
|
||||
"github.com/mattermost/mattermost-server/v5/model"
|
||||
)
|
||||
|
||||
const (
|
||||
JobName = "ExpiryNotify"
|
||||
)
|
||||
|
||||
type Worker struct {
|
||||
name string
|
||||
stop chan bool
|
||||
stopped chan bool
|
||||
jobs chan model.Job
|
||||
jobServer *jobs.JobServer
|
||||
app *app.App
|
||||
}
|
||||
|
||||
func (m *ExpiryNotifyJobInterfaceImpl) MakeWorker() model.Worker {
|
||||
worker := Worker{
|
||||
name: JobName,
|
||||
stop: make(chan bool, 1),
|
||||
stopped: make(chan bool, 1),
|
||||
jobs: make(chan model.Job),
|
||||
jobServer: m.App.Srv().Jobs,
|
||||
app: m.App,
|
||||
}
|
||||
return &worker
|
||||
}
|
||||
|
||||
func (worker *Worker) Run() {
|
||||
mlog.Debug("Worker started", mlog.String("worker", worker.name))
|
||||
|
||||
defer func() {
|
||||
mlog.Debug("Worker finished", mlog.String("worker", worker.name))
|
||||
worker.stopped <- true
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-worker.stop:
|
||||
mlog.Debug("Worker received stop signal", mlog.String("worker", worker.name))
|
||||
return
|
||||
case job := <-worker.jobs:
|
||||
mlog.Debug("Worker received a new candidate job.", mlog.String("worker", worker.name))
|
||||
worker.DoJob(&job)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *Worker) Stop() {
|
||||
mlog.Debug("Worker stopping", mlog.String("worker", worker.name))
|
||||
worker.stop <- true
|
||||
<-worker.stopped
|
||||
}
|
||||
|
||||
func (worker *Worker) JobChannel() chan<- model.Job {
|
||||
return worker.jobs
|
||||
}
|
||||
|
||||
func (worker *Worker) DoJob(job *model.Job) {
|
||||
if claimed, err := worker.jobServer.ClaimJob(job); err != nil {
|
||||
mlog.Warn("Worker experienced an error while trying to claim job",
|
||||
mlog.String("worker", worker.name),
|
||||
mlog.String("job_id", job.Id),
|
||||
mlog.String("error", err.Error()))
|
||||
return
|
||||
} else if !claimed {
|
||||
return
|
||||
}
|
||||
|
||||
if err := worker.app.NotifySessionsExpired(); err != nil {
|
||||
mlog.Error("Worker: Failed to notify clients of expired session", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error()))
|
||||
worker.setJobError(job, err)
|
||||
return
|
||||
}
|
||||
|
||||
mlog.Info("Worker: Job is complete", mlog.String("worker", worker.name), mlog.String("job_id", job.Id))
|
||||
worker.setJobSuccess(job)
|
||||
}
|
||||
|
||||
func (worker *Worker) setJobSuccess(job *model.Job) {
|
||||
if err := worker.app.Srv().Jobs.SetJobSuccess(job); err != nil {
|
||||
mlog.Error("Worker: Failed to set success for job", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error()))
|
||||
worker.setJobError(job, err)
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *Worker) setJobError(job *model.Job, appError *model.AppError) {
|
||||
if err := worker.app.Srv().Jobs.SetJobError(job, appError); err != nil {
|
||||
mlog.Error("Worker: Failed to set job error", mlog.String("worker", worker.name), mlog.String("job_id", job.Id), mlog.String("error", err.Error()))
|
||||
}
|
||||
}
|
||||
11
jobs/interfaces/expirynotify_interface.go
Обычный файл
11
jobs/interfaces/expirynotify_interface.go
Обычный файл
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||
// See LICENSE.txt for license information.
|
||||
|
||||
package interfaces
|
||||
|
||||
import "github.com/mattermost/mattermost-server/v5/model"
|
||||
|
||||
type ExpiryNotifyJobInterface interface {
|
||||
MakeWorker() model.Worker
|
||||
MakeScheduler() model.Scheduler
|
||||
}
|
||||
@@ -128,6 +128,13 @@ func (watcher *Watcher) PollAndNotify() {
|
||||
default:
|
||||
}
|
||||
}
|
||||
} else if job.Type == model.JOB_TYPE_EXPIRY_NOTIFY {
|
||||
if watcher.workers.ExpiryNotify != nil {
|
||||
select {
|
||||
case watcher.workers.ExpiryNotify.JobChannel() <- *job:
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,6 +62,10 @@ func (srv *JobServer) InitSchedulers() *Schedulers {
|
||||
schedulers.schedulers = append(schedulers.schedulers, pluginsInterface.MakeScheduler())
|
||||
}
|
||||
|
||||
if expiryNotifyInterface := srv.ExpiryNotify; expiryNotifyInterface != nil {
|
||||
schedulers.schedulers = append(schedulers.schedulers, expiryNotifyInterface.MakeScheduler())
|
||||
}
|
||||
|
||||
schedulers.nextRunTimes = make([]*time.Time, len(schedulers.schedulers))
|
||||
return schedulers
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ type JobServer struct {
|
||||
Migrations tjobs.MigrationsJobInterface
|
||||
Plugins tjobs.PluginsJobInterface
|
||||
BleveIndexer tjobs.IndexerJobInterface
|
||||
ExpiryNotify tjobs.ExpiryNotifyJobInterface
|
||||
}
|
||||
|
||||
func NewJobServer(configService configservice.ConfigService, store store.Store) *JobServer {
|
||||
|
||||
@@ -24,6 +24,7 @@ type Workers struct {
|
||||
Migrations model.Worker
|
||||
Plugins model.Worker
|
||||
BleveIndexing model.Worker
|
||||
ExpiryNotify model.Worker
|
||||
|
||||
listenerId string
|
||||
}
|
||||
@@ -66,6 +67,9 @@ func (srv *JobServer) InitWorkers() *Workers {
|
||||
workers.BleveIndexing = bleveIndexerInterface.MakeWorker()
|
||||
}
|
||||
|
||||
if expiryNotifyInterface := srv.ExpiryNotify; expiryNotifyInterface != nil {
|
||||
workers.ExpiryNotify = expiryNotifyInterface.MakeWorker()
|
||||
}
|
||||
return workers
|
||||
}
|
||||
|
||||
@@ -105,6 +109,10 @@ func (workers *Workers) Start() *Workers {
|
||||
go workers.BleveIndexing.Run()
|
||||
}
|
||||
|
||||
if workers.ExpiryNotify != nil {
|
||||
go workers.ExpiryNotify.Run()
|
||||
}
|
||||
|
||||
go workers.Watcher.Start()
|
||||
})
|
||||
|
||||
@@ -202,6 +210,10 @@ func (workers *Workers) Stop() *Workers {
|
||||
workers.BleveIndexing.Stop()
|
||||
}
|
||||
|
||||
if workers.ExpiryNotify != nil {
|
||||
workers.ExpiryNotify.Stop()
|
||||
}
|
||||
|
||||
mlog.Info("Stopped workers")
|
||||
|
||||
return workers
|
||||
|
||||
Ссылка в новой задаче
Block a user