focalboard/mattermost-plugin/server/boards/boardsapp.go
Doug Lauder 605c0079eb
Multi product architecture (#3309)
* skeleton lifecycle

* bare minimum to satisfy mm-server import

* added boards_imports.go

* move boards_imports.go to correct package

* bump mmserver version; remove replace in go.mod; use module workspaces; remove logger service

* rename product.go --> boards.go

* add FileInfoStore and Cloud services for product; create minimal pluginAPI interfaces for all packages

* rename Boards -> BoardsProduct

* compile success

* remove hooks service; guard for nil BoardsApp

* update to latest mmserver ver

* upgrade mmserver to master tip

* upgrade mmserver to master tip

* bump plugin-api to master tip

* fix users service

* fix OnActivate crash; normalize AppError returns

* fileBackend interface for server/app

* feature flag

* bump mmserver version

* fix linter errors

* make go.work when linting

* fix go.work creation for CI

* add execute flag for script

* fix more linter errors

* always create a go.work

* fix ci go.work

* OS agnostic go.work generator

* fix path

* fix path again

* partially disable cypress test

* fix case Id --> ID

* bump mmserver version

* include  in go.work for dev

* addressed review comments.

Co-authored-by: Mattermod <mattermod@users.noreply.github.com>
2022-07-15 07:51:50 +02:00

218 lines
6.6 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package boards
import (
"fmt"
"net/http"
"sync"
"github.com/mattermost/focalboard/server/auth"
"github.com/mattermost/focalboard/server/model"
"github.com/mattermost/focalboard/server/server"
"github.com/mattermost/focalboard/server/services/notify"
"github.com/mattermost/focalboard/server/services/permissions/mmpermissions"
"github.com/mattermost/focalboard/server/services/store"
"github.com/mattermost/focalboard/server/services/store/mattermostauthlayer"
"github.com/mattermost/focalboard/server/services/store/sqlstore"
"github.com/mattermost/focalboard/server/utils"
"github.com/mattermost/focalboard/server/ws"
mm_model "github.com/mattermost/mattermost-server/v6/model"
"github.com/mattermost/mattermost-server/v6/plugin"
"github.com/mattermost/mattermost-server/v6/shared/mlog"
"github.com/mattermost/mattermost-plugin-api/cluster"
)
const (
boardsFeatureFlagName = "BoardsFeatureFlags"
PluginName = "focalboard"
SharedBoardsName = "enablepublicsharedboards"
notifyFreqCardSecondsKey = "notify_freq_card_seconds"
notifyFreqBoardSecondsKey = "notify_freq_board_seconds"
)
type BoardsEmbed struct {
OriginalPath string `json:"originalPath"`
TeamID string `json:"teamID"`
ViewID string `json:"viewID"`
BoardID string `json:"boardID"`
CardID string `json:"cardID"`
ReadToken string `json:"readToken,omitempty"`
}
type BoardsApp struct {
// configurationLock synchronizes access to the configuration.
configurationLock sync.RWMutex
// configuration is the active plugin configuration. Consult getConfiguration and
// setConfiguration for usage.
configuration *configuration
server *server.Server
wsPluginAdapter ws.PluginAdapterInterface
servicesAPI model.ServicesAPI
logger mlog.LoggerIFace
}
func NewBoardsApp(api model.ServicesAPI) (*BoardsApp, error) {
mmconfig := api.GetConfig()
logger := api.GetLogger()
baseURL := ""
if mmconfig.ServiceSettings.SiteURL != nil {
baseURL = *mmconfig.ServiceSettings.SiteURL
}
serverID := api.GetDiagnosticID()
cfg := createBoardsConfig(*mmconfig, baseURL, serverID)
sqlDB, err := api.GetMasterDB()
if err != nil {
return nil, fmt.Errorf("cannot access database while initializing Boards: %w", err)
}
storeParams := sqlstore.Params{
DBType: cfg.DBType,
ConnectionString: cfg.DBConfigString,
TablePrefix: cfg.DBTablePrefix,
Logger: logger,
DB: sqlDB,
IsPlugin: true,
NewMutexFn: func(name string) (*cluster.Mutex, error) {
return cluster.NewMutex(&mutexAPIAdapter{api: api}, name)
},
ServicesAPI: api,
}
var db store.Store
db, err = sqlstore.New(storeParams)
if err != nil {
return nil, fmt.Errorf("error initializing the DB: %w", err)
}
if cfg.AuthMode == server.MattermostAuthMod {
layeredStore, err2 := mattermostauthlayer.New(cfg.DBType, sqlDB, db, logger, api, storeParams.TablePrefix)
if err2 != nil {
return nil, fmt.Errorf("error initializing the DB: %w", err2)
}
db = layeredStore
}
permissionsService := mmpermissions.New(db, api, logger)
wsPluginAdapter := ws.NewPluginAdapter(api, auth.New(cfg, db, permissionsService), db, logger)
backendParams := notifyBackendParams{
cfg: cfg,
servicesAPI: api,
appAPI: &appAPI{store: db},
permissions: permissionsService,
serverRoot: baseURL + "/boards",
logger: logger,
}
var notifyBackends []notify.Backend
mentionsBackend, err := createMentionsNotifyBackend(backendParams)
if err != nil {
return nil, fmt.Errorf("error creating mention notifications backend: %w", err)
}
notifyBackends = append(notifyBackends, mentionsBackend)
subscriptionsBackend, err2 := createSubscriptionsNotifyBackend(backendParams)
if err2 != nil {
return nil, fmt.Errorf("error creating subscription notifications backend: %w", err2)
}
notifyBackends = append(notifyBackends, subscriptionsBackend)
mentionsBackend.AddListener(subscriptionsBackend)
params := server.Params{
Cfg: cfg,
SingleUserToken: "",
DBStore: db,
Logger: logger,
ServerID: serverID,
WSAdapter: wsPluginAdapter,
NotifyBackends: notifyBackends,
PermissionsService: permissionsService,
IsPlugin: true,
}
server, err := server.New(params)
if err != nil {
return nil, fmt.Errorf("error initializing the server: %w", err)
}
backendParams.appAPI.init(db, server.App())
if utils.IsCloudLicense(api.GetLicense()) {
limits, err := api.GetCloudLimits()
if err != nil {
return nil, fmt.Errorf("error fetching cloud limits when starting Boards: %w", err)
}
if err := server.App().SetCloudLimits(limits); err != nil {
return nil, fmt.Errorf("error setting cloud limits when starting Boards: %w", err)
}
}
return &BoardsApp{
server: server,
wsPluginAdapter: wsPluginAdapter,
servicesAPI: api,
logger: logger,
}, nil
}
func (b *BoardsApp) Start() error {
if err := b.server.Start(); err != nil {
return fmt.Errorf("error starting Boards server: %w", err)
}
return nil
}
func (b *BoardsApp) Stop() error {
return b.server.Shutdown()
}
//
// These callbacks are called automatically by the suite server.
//
func (b *BoardsApp) MessageWillBePosted(_ *plugin.Context, post *mm_model.Post) (*mm_model.Post, string) {
return postWithBoardsEmbed(post), ""
}
func (b *BoardsApp) MessageWillBeUpdated(_ *plugin.Context, newPost, _ *mm_model.Post) (*mm_model.Post, string) {
return postWithBoardsEmbed(newPost), ""
}
func (b *BoardsApp) OnWebSocketConnect(webConnID, userID string) {
b.wsPluginAdapter.OnWebSocketConnect(webConnID, userID)
}
func (b *BoardsApp) OnWebSocketDisconnect(webConnID, userID string) {
b.wsPluginAdapter.OnWebSocketDisconnect(webConnID, userID)
}
func (b *BoardsApp) WebSocketMessageHasBeenPosted(webConnID, userID string, req *mm_model.WebSocketRequest) {
b.wsPluginAdapter.WebSocketMessageHasBeenPosted(webConnID, userID, req)
}
func (b *BoardsApp) OnPluginClusterEvent(_ *plugin.Context, ev mm_model.PluginClusterEvent) {
b.wsPluginAdapter.HandleClusterEvent(ev)
}
func (b *BoardsApp) OnCloudLimitsUpdated(limits *mm_model.ProductLimits) {
if err := b.server.App().SetCloudLimits(limits); err != nil {
b.logger.Error("Error setting the cloud limits for Boards", mlog.Err(err))
}
}
// ServeHTTP demonstrates a plugin that handles HTTP requests by greeting the world.
func (b *BoardsApp) ServeHTTP(_ *plugin.Context, w http.ResponseWriter, r *http.Request) {
router := b.server.GetRootRouter()
router.ServeHTTP(w, r)
}