4b0fb92fba
- provides support for compiling Boards directly into the Mattermost suite server - a ServicesAPI interface replaces the PluginAPI to allow for implementations coming from pluginAPI and suite server. - a new product package provides a place to register Boards as a suite product and handles life-cycle events - a new boards package replaces much of the mattermost-plugin logic, allowing this to be shared between plugin and product - Boards now uses module workspaces; run make setup-go-work
305 lines
6.8 KiB
Go
305 lines
6.8 KiB
Go
// Server for Focalboard
|
|
package main
|
|
|
|
import (
|
|
"C"
|
|
"flag"
|
|
"log"
|
|
"os"
|
|
"os/signal"
|
|
"syscall"
|
|
"time"
|
|
|
|
"github.com/mattermost/focalboard/server/model"
|
|
"github.com/mattermost/focalboard/server/server"
|
|
"github.com/mattermost/focalboard/server/services/config"
|
|
"github.com/mattermost/focalboard/server/services/permissions/localpermissions"
|
|
)
|
|
import (
|
|
"github.com/mattermost/mattermost-server/v6/shared/mlog"
|
|
)
|
|
|
|
// Active server used with shared code (dll)
|
|
var pServer *server.Server
|
|
|
|
const (
|
|
timeBetweenPidMonitoringChecks = 2 * time.Second
|
|
)
|
|
|
|
func isProcessRunning(pid int) bool {
|
|
process, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
|
|
err = process.Signal(syscall.Signal(0))
|
|
|
|
return err == nil
|
|
}
|
|
|
|
// monitorPid is used to keep the server lifetime in sync with another (client app) process
|
|
func monitorPid(pid int, logger *mlog.Logger) {
|
|
logger.Info("Monitoring PID", mlog.Int("pid", pid))
|
|
|
|
go func() {
|
|
for {
|
|
if !isProcessRunning(pid) {
|
|
logger.Info("Monitored process not found, exiting.")
|
|
os.Exit(1)
|
|
}
|
|
|
|
time.Sleep(timeBetweenPidMonitoringChecks)
|
|
}
|
|
}()
|
|
}
|
|
|
|
func logInfo(logger *mlog.Logger) {
|
|
logger.Info("FocalBoard Server",
|
|
mlog.String("version", model.CurrentVersion),
|
|
mlog.String("edition", model.Edition),
|
|
mlog.String("build_number", model.BuildNumber),
|
|
mlog.String("build_date", model.BuildDate),
|
|
mlog.String("build_hash", model.BuildHash),
|
|
)
|
|
}
|
|
|
|
func main() {
|
|
// Command line args
|
|
pMonitorPid := flag.Int("monitorpid", -1, "a process ID")
|
|
pPort := flag.Int("port", 0, "the port number")
|
|
pSingleUser := flag.Bool("single-user", false, "single user mode")
|
|
pDBType := flag.String("dbtype", "", "Database type")
|
|
pDBConfig := flag.String("dbconfig", "", "Database config")
|
|
pConfigFilePath := flag.String(
|
|
"config",
|
|
"",
|
|
"Location of the JSON config file",
|
|
)
|
|
flag.Parse()
|
|
|
|
config, err := config.ReadConfigFile(*pConfigFilePath)
|
|
if err != nil {
|
|
log.Fatal("Unable to read the config file: ", err)
|
|
return
|
|
}
|
|
|
|
logger, _ := mlog.NewLogger()
|
|
cfgJSON := config.LoggingCfgJSON
|
|
if config.LoggingCfgFile == "" && cfgJSON == "" {
|
|
// if no logging defined, use default config (console output)
|
|
cfgJSON = defaultLoggingConfig()
|
|
}
|
|
err = logger.Configure(config.LoggingCfgFile, cfgJSON, nil)
|
|
if err != nil {
|
|
log.Fatal("Error in config file for logger: ", err)
|
|
return
|
|
}
|
|
defer func() { _ = logger.Shutdown() }()
|
|
|
|
if logger.HasTargets() {
|
|
restore := logger.RedirectStdLog(mlog.LvlInfo, mlog.String("src", "stdlog"))
|
|
defer restore()
|
|
}
|
|
|
|
logInfo(logger)
|
|
|
|
singleUser := false
|
|
if pSingleUser != nil {
|
|
singleUser = *pSingleUser
|
|
}
|
|
|
|
singleUserToken := ""
|
|
if singleUser {
|
|
singleUserToken = os.Getenv("FOCALBOARD_SINGLE_USER_TOKEN")
|
|
if len(singleUserToken) < 1 {
|
|
logger.Fatal("The FOCALBOARD_SINGLE_USER_TOKEN environment variable must be set for single user mode ")
|
|
return
|
|
}
|
|
logger.Info("Single user mode")
|
|
}
|
|
|
|
if pMonitorPid != nil && *pMonitorPid > 0 {
|
|
monitorPid(*pMonitorPid, logger)
|
|
}
|
|
|
|
// Override config from commandline
|
|
|
|
if pDBType != nil && len(*pDBType) > 0 {
|
|
config.DBType = *pDBType
|
|
logger.Info("DBType from commandline", mlog.String("DBType", *pDBType))
|
|
}
|
|
|
|
if pDBConfig != nil && len(*pDBConfig) > 0 {
|
|
config.DBConfigString = *pDBConfig
|
|
// Don't echo, as the confix string may contain passwords
|
|
logger.Info("DBConfigString overriden from commandline")
|
|
}
|
|
|
|
if pPort != nil && *pPort > 0 && *pPort != config.Port {
|
|
// Override port
|
|
logger.Info("Port from commandline", mlog.Int("port", *pPort))
|
|
config.Port = *pPort
|
|
}
|
|
|
|
db, err := server.NewStore(config, singleUser, logger)
|
|
if err != nil {
|
|
logger.Fatal("server.NewStore ERROR", mlog.Err(err))
|
|
}
|
|
|
|
permissionsService := localpermissions.New(db, logger)
|
|
|
|
params := server.Params{
|
|
Cfg: config,
|
|
SingleUserToken: singleUserToken,
|
|
DBStore: db,
|
|
Logger: logger,
|
|
PermissionsService: permissionsService,
|
|
}
|
|
|
|
server, err := server.New(params)
|
|
if err != nil {
|
|
logger.Fatal("server.New ERROR", mlog.Err(err))
|
|
}
|
|
|
|
if err := server.Start(); err != nil {
|
|
logger.Fatal("server.Start ERROR", mlog.Err(err))
|
|
}
|
|
|
|
// Setting up signal capturing
|
|
stop := make(chan os.Signal, 1)
|
|
signal.Notify(stop, os.Interrupt)
|
|
|
|
// Waiting for SIGINT (pkill -2)
|
|
<-stop
|
|
|
|
_ = server.Shutdown()
|
|
}
|
|
|
|
// StartServer starts the server
|
|
//export StartServer
|
|
func StartServer(webPath *C.char, filesPath *C.char, port int, singleUserToken, dbConfigString, configFilePath *C.char) {
|
|
startServer(
|
|
C.GoString(webPath),
|
|
C.GoString(filesPath),
|
|
port,
|
|
C.GoString(singleUserToken),
|
|
C.GoString(dbConfigString),
|
|
C.GoString(configFilePath),
|
|
)
|
|
}
|
|
|
|
// StopServer stops the server
|
|
//export StopServer
|
|
func StopServer() {
|
|
stopServer()
|
|
}
|
|
|
|
func startServer(webPath string, filesPath string, port int, singleUserToken, dbConfigString, configFilePath string) {
|
|
if pServer != nil {
|
|
stopServer()
|
|
pServer = nil
|
|
}
|
|
|
|
// config.json file
|
|
config, err := config.ReadConfigFile(configFilePath)
|
|
if err != nil {
|
|
log.Fatal("Unable to read the config file: ", err)
|
|
return
|
|
}
|
|
|
|
logger, _ := mlog.NewLogger()
|
|
err = logger.Configure(config.LoggingCfgFile, config.LoggingCfgJSON, nil)
|
|
if err != nil {
|
|
log.Fatal("Error in config file for logger: ", err)
|
|
return
|
|
}
|
|
|
|
logInfo(logger)
|
|
|
|
if len(filesPath) > 0 {
|
|
config.FilesPath = filesPath
|
|
}
|
|
|
|
if len(webPath) > 0 {
|
|
config.WebPath = webPath
|
|
}
|
|
|
|
if port > 0 {
|
|
config.Port = port
|
|
}
|
|
|
|
if len(dbConfigString) > 0 {
|
|
config.DBConfigString = dbConfigString
|
|
}
|
|
|
|
singleUser := len(singleUserToken) > 0
|
|
db, err := server.NewStore(config, singleUser, logger)
|
|
if err != nil {
|
|
logger.Fatal("server.NewStore ERROR", mlog.Err(err))
|
|
}
|
|
|
|
permissionsService := localpermissions.New(db, logger)
|
|
|
|
params := server.Params{
|
|
Cfg: config,
|
|
SingleUserToken: singleUserToken,
|
|
DBStore: db,
|
|
Logger: logger,
|
|
PermissionsService: permissionsService,
|
|
}
|
|
|
|
pServer, err = server.New(params)
|
|
if err != nil {
|
|
logger.Fatal("server.New ERROR", mlog.Err(err))
|
|
}
|
|
|
|
if err := pServer.Start(); err != nil {
|
|
logger.Fatal("server.Start ERROR", mlog.Err(err))
|
|
}
|
|
}
|
|
|
|
func stopServer() {
|
|
if pServer == nil {
|
|
return
|
|
}
|
|
|
|
logger := pServer.Logger()
|
|
|
|
err := pServer.Shutdown()
|
|
if err != nil {
|
|
logger.Error("server.Shutdown ERROR", mlog.Err(err))
|
|
}
|
|
|
|
if l, ok := logger.(*mlog.Logger); ok {
|
|
_ = l.Shutdown()
|
|
}
|
|
pServer = nil
|
|
}
|
|
|
|
func defaultLoggingConfig() string {
|
|
return `
|
|
{
|
|
"def": {
|
|
"type": "console",
|
|
"options": {
|
|
"out": "stdout"
|
|
},
|
|
"format": "plain",
|
|
"format_options": {
|
|
"delim": " ",
|
|
"min_level_len": 5,
|
|
"min_msg_len": 40,
|
|
"enable_color": true,
|
|
"enable_caller": true
|
|
},
|
|
"levels": [
|
|
{"id": 5, "name": "debug"},
|
|
{"id": 4, "name": "info", "color": 36},
|
|
{"id": 3, "name": "warn"},
|
|
{"id": 2, "name": "error", "color": 31},
|
|
{"id": 1, "name": "fatal", "stacktrace": true},
|
|
{"id": 0, "name": "panic", "stacktrace": true}
|
|
]
|
|
}
|
|
}`
|
|
}
|