mirror of
https://github.com/mattermost/focalboard.git
synced 2025-03-23 20:52:42 +02:00
Logger for FocalBoard server (#466)
- structured, asynchronous logging - supports discreet log levels, including custom levels - supports output to console, files, and all common log aggregators. - supports JSON, plain text and GELF formats - lazy formatting and writing
This commit is contained in:
parent
fe6b0d04b3
commit
417de9f837
@ -18,5 +18,6 @@
|
|||||||
"localOnly": false,
|
"localOnly": false,
|
||||||
"enableLocalMode": true,
|
"enableLocalMode": true,
|
||||||
"localModeSocketLocation": "/var/tmp/focalboard_local.socket",
|
"localModeSocketLocation": "/var/tmp/focalboard_local.socket",
|
||||||
"authMode": "native"
|
"authMode": "native",
|
||||||
|
"logging_file": "logging.json"
|
||||||
}
|
}
|
||||||
|
45
logging.json
Normal file
45
logging.json
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"console": {
|
||||||
|
"type": "console",
|
||||||
|
"options": {
|
||||||
|
"out": "stdout"
|
||||||
|
},
|
||||||
|
"format": "plain",
|
||||||
|
"format_options": {
|
||||||
|
"min_level_len": 5,
|
||||||
|
"min_msg_len": 40,
|
||||||
|
"enable_color": 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},
|
||||||
|
{"id": 500, "name": "telemetry", "color":34}
|
||||||
|
],
|
||||||
|
"maxqueuesize": 1000
|
||||||
|
},
|
||||||
|
"file": {
|
||||||
|
"type": "file",
|
||||||
|
"options": {
|
||||||
|
"filename": "focalboard-server.log",
|
||||||
|
"max_size": 1000000,
|
||||||
|
"max_age": 1,
|
||||||
|
"max_backups": 10,
|
||||||
|
"compress": true
|
||||||
|
},
|
||||||
|
"format": "json",
|
||||||
|
"levels": [
|
||||||
|
{"id": 5, "name": "debug"},
|
||||||
|
{"id": 4, "name": "info"},
|
||||||
|
{"id": 3, "name": "warn"},
|
||||||
|
{"id": 2, "name": "error"},
|
||||||
|
{"id": 1, "name": "fatal", "stacktrace": true},
|
||||||
|
{"id": 0, "name": "panic", "stacktrace": true},
|
||||||
|
{"id": 500, "name": "telemetry"}
|
||||||
|
],
|
||||||
|
"maxqueuesize": 1000
|
||||||
|
}
|
||||||
|
}
|
@ -3,11 +3,11 @@ package api
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AdminSetPasswordData struct {
|
type AdminSetPasswordData struct {
|
||||||
@ -20,29 +20,29 @@ func (a *API) handleAdminSetPassword(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
requestBody, err := ioutil.ReadAll(r.Body)
|
requestBody, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestData AdminSetPasswordData
|
var requestData AdminSetPasswordData
|
||||||
err = json.Unmarshal(requestBody, &requestData)
|
err = json.Unmarshal(requestBody, &requestData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !strings.Contains(requestData.Password, "") {
|
if !strings.Contains(requestData.Password, "") {
|
||||||
errorResponse(w, http.StatusBadRequest, "password is required", err)
|
a.errorResponse(w, http.StatusBadRequest, "password is required", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.app().UpdateUserPassword(username, requestData.Password)
|
err = a.app().UpdateUserPassword(username, requestData.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("AdminSetPassword, username: %s", username)
|
a.logger.Debug("AdminSetPassword, username: %s", mlog.String("username", username))
|
||||||
|
|
||||||
jsonStringResponse(w, http.StatusOK, "{}")
|
jsonStringResponse(w, http.StatusOK, "{}")
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -16,6 +15,7 @@ import (
|
|||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/mattermost/focalboard/server/app"
|
"github.com/mattermost/focalboard/server/app"
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
"github.com/mattermost/focalboard/server/utils"
|
"github.com/mattermost/focalboard/server/utils"
|
||||||
)
|
)
|
||||||
@ -38,13 +38,15 @@ type API struct {
|
|||||||
authService string
|
authService string
|
||||||
singleUserToken string
|
singleUserToken string
|
||||||
MattermostAuth bool
|
MattermostAuth bool
|
||||||
|
logger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAPI(appBuilder func() *app.App, singleUserToken string, authService string) *API {
|
func NewAPI(appBuilder func() *app.App, singleUserToken string, authService string, logger *mlog.Logger) *API {
|
||||||
return &API{
|
return &API{
|
||||||
appBuilder: appBuilder,
|
appBuilder: appBuilder,
|
||||||
singleUserToken: singleUserToken,
|
singleUserToken: singleUserToken,
|
||||||
authService: authService,
|
authService: authService,
|
||||||
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,8 +95,8 @@ func (a *API) RegisterAdminRoutes(r *mux.Router) {
|
|||||||
func (a *API) requireCSRFToken(next http.Handler) http.Handler {
|
func (a *API) requireCSRFToken(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if !a.checkCSRFToken(r) {
|
if !a.checkCSRFToken(r) {
|
||||||
log.Println("checkCSRFToken FAILED")
|
a.logger.Error("checkCSRFToken FAILED")
|
||||||
errorResponse(w, http.StatusBadRequest, "", nil)
|
a.errorResponse(w, http.StatusBadRequest, "", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,7 +124,7 @@ func (a *API) hasValidReadTokenForBlock(r *http.Request, container store.Contain
|
|||||||
|
|
||||||
isValid, err := a.app().IsValidReadToken(container, blockID, readToken)
|
isValid, err := a.app().IsValidReadToken(container, blockID, readToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("IsValidReadToken ERROR: %v", err)
|
a.logger.Error("IsValidReadToken ERROR", mlog.Err(err))
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,21 +226,25 @@ func (a *API) handleGetBlocks(w http.ResponseWriter, r *http.Request) {
|
|||||||
blockType := query.Get("type")
|
blockType := query.Get("type")
|
||||||
container, err := a.getContainer(r)
|
container, err := a.getContainer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
noContainerErrorResponse(w, err)
|
a.noContainerErrorResponse(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks, err := a.app().GetBlocks(*container, parentID, blockType)
|
blocks, err := a.app().GetBlocks(*container, parentID, blockType)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// log.Printf("GetBlocks parentID: %s, type: %s, %d result(s)", parentID, blockType, len(blocks))
|
a.logger.Debug("GetBlocks",
|
||||||
|
mlog.String("parentID", parentID),
|
||||||
|
mlog.String("blockType", blockType),
|
||||||
|
mlog.Int("block_count", len(blocks)),
|
||||||
|
)
|
||||||
|
|
||||||
json, err := json.Marshal(blocks)
|
json, err := json.Marshal(blocks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,13 +298,13 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
container, err := a.getContainer(r)
|
container, err := a.getContainer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
noContainerErrorResponse(w, err)
|
a.noContainerErrorResponse(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
requestBody, err := ioutil.ReadAll(r.Body)
|
requestBody, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -306,7 +312,7 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = json.Unmarshal(requestBody, &blocks)
|
err = json.Unmarshal(requestBody, &blocks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,19 +320,19 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Error checking
|
// Error checking
|
||||||
if len(block.Type) < 1 {
|
if len(block.Type) < 1 {
|
||||||
message := fmt.Sprintf("missing type for block id %s", block.ID)
|
message := fmt.Sprintf("missing type for block id %s", block.ID)
|
||||||
errorResponse(w, http.StatusBadRequest, message, nil)
|
a.errorResponse(w, http.StatusBadRequest, message, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if block.CreateAt < 1 {
|
if block.CreateAt < 1 {
|
||||||
message := fmt.Sprintf("invalid createAt for block id %s", block.ID)
|
message := fmt.Sprintf("invalid createAt for block id %s", block.ID)
|
||||||
errorResponse(w, http.StatusBadRequest, message, nil)
|
a.errorResponse(w, http.StatusBadRequest, message, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if block.UpdateAt < 1 {
|
if block.UpdateAt < 1 {
|
||||||
message := fmt.Sprintf("invalid UpdateAt for block id %s", block.ID)
|
message := fmt.Sprintf("invalid UpdateAt for block id %s", block.ID)
|
||||||
errorResponse(w, http.StatusBadRequest, message, nil)
|
a.errorResponse(w, http.StatusBadRequest, message, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -335,11 +341,11 @@ func (a *API) handlePostBlocks(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = a.app().InsertBlocks(*container, blocks)
|
err = a.app().InsertBlocks(*container, blocks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("POST Blocks %d block(s)", len(blocks))
|
a.logger.Debug("POST Blocks", mlog.Int("block_count", len(blocks)))
|
||||||
jsonStringResponse(w, http.StatusOK, "{}")
|
jsonStringResponse(w, http.StatusOK, "{}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,13 +380,13 @@ func (a *API) handleGetUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
user, err := a.app().GetUser(userID)
|
user, err := a.app().GetUser(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
userData, err := json.Marshal(user)
|
userData, err := json.Marshal(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,14 +430,14 @@ func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) {
|
|||||||
} else {
|
} else {
|
||||||
user, err = a.app().GetUser(session.UserID)
|
user, err = a.app().GetUser(session.UserID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
userData, err := json.Marshal(user)
|
userData, err := json.Marshal(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -476,18 +482,18 @@ func (a *API) handleDeleteBlock(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
container, err := a.getContainer(r)
|
container, err := a.getContainer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
noContainerErrorResponse(w, err)
|
a.noContainerErrorResponse(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.app().DeleteBlock(*container, blockID, userID)
|
err = a.app().DeleteBlock(*container, blockID, userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("DELETE Block %s", blockID)
|
a.logger.Debug("DELETE Block", mlog.String("blockID", blockID))
|
||||||
jsonStringResponse(w, http.StatusOK, "{}")
|
jsonStringResponse(w, http.StatusOK, "{}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -536,7 +542,7 @@ func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
container, err := a.getContainerAllowingReadTokenForBlock(r, blockID)
|
container, err := a.getContainerAllowingReadTokenForBlock(r, blockID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
noContainerErrorResponse(w, err)
|
a.noContainerErrorResponse(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,21 +553,25 @@ func (a *API) handleGetSubTree(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if levels != 2 && levels != 3 {
|
if levels != 2 && levels != 3 {
|
||||||
log.Printf(`ERROR Invalid levels: %d`, levels)
|
a.logger.Error("Invalid levels", mlog.Int64("levels", levels))
|
||||||
errorResponse(w, http.StatusBadRequest, "invalid levels", nil)
|
a.errorResponse(w, http.StatusBadRequest, "invalid levels", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
blocks, err := a.app().GetSubTree(*container, blockID, int(levels))
|
blocks, err := a.app().GetSubTree(*container, blockID, int(levels))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("GetSubTree (%v) blockID: %s, %d result(s)", levels, blockID, len(blocks))
|
a.logger.Debug("GetSubTree",
|
||||||
|
mlog.Int64("levels", levels),
|
||||||
|
mlog.String("blockID", blockID),
|
||||||
|
mlog.Int("block_count", len(blocks)),
|
||||||
|
)
|
||||||
json, err := json.Marshal(blocks)
|
json, err := json.Marshal(blocks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -600,7 +610,7 @@ func (a *API) handleExport(w http.ResponseWriter, r *http.Request) {
|
|||||||
rootID := query.Get("root_id")
|
rootID := query.Get("root_id")
|
||||||
container, err := a.getContainer(r)
|
container, err := a.getContainer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
noContainerErrorResponse(w, err)
|
a.noContainerErrorResponse(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -611,13 +621,13 @@ func (a *API) handleExport(w http.ResponseWriter, r *http.Request) {
|
|||||||
blocks, err = a.app().GetBlocksWithRootID(*container, rootID)
|
blocks, err = a.app().GetBlocksWithRootID(*container, rootID)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("%d raw block(s)", len(blocks))
|
a.logger.Debug("raw blocks", mlog.Int("block_count", len(blocks)))
|
||||||
blocks = filterOrphanBlocks(blocks)
|
blocks = filterOrphanBlocks(blocks)
|
||||||
log.Printf("EXPORT %d filtered block(s)", len(blocks))
|
a.logger.Debug("EXPORT filtered blocks", mlog.Int("block_count", len(blocks)))
|
||||||
|
|
||||||
json, err := json.Marshal(blocks)
|
json, err := json.Marshal(blocks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -702,13 +712,13 @@ func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
container, err := a.getContainer(r)
|
container, err := a.getContainer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
noContainerErrorResponse(w, err)
|
a.noContainerErrorResponse(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
requestBody, err := ioutil.ReadAll(r.Body)
|
requestBody, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -716,7 +726,7 @@ func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = json.Unmarshal(requestBody, &blocks)
|
err = json.Unmarshal(requestBody, &blocks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -724,11 +734,11 @@ func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = a.app().InsertBlocks(*container, blocks)
|
err = a.app().InsertBlocks(*container, blocks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("IMPORT Blocks %d block(s)", len(blocks))
|
a.logger.Debug("IMPORT Blocks", mlog.Int("block_count", len(blocks)))
|
||||||
jsonStringResponse(w, http.StatusOK, "{}")
|
jsonStringResponse(w, http.StatusOK, "{}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -770,23 +780,23 @@ func (a *API) handleGetSharing(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
container, err := a.getContainer(r)
|
container, err := a.getContainer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
noContainerErrorResponse(w, err)
|
a.noContainerErrorResponse(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sharing, err := a.app().GetSharing(*container, rootID)
|
sharing, err := a.app().GetSharing(*container, rootID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
sharingData, err := json.Marshal(sharing)
|
sharingData, err := json.Marshal(sharing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("GET sharing %s", rootID)
|
a.logger.Debug("GET sharing", mlog.String("rootID", rootID))
|
||||||
jsonBytesResponse(w, http.StatusOK, sharingData)
|
jsonBytesResponse(w, http.StatusOK, sharingData)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -827,13 +837,13 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
container, err := a.getContainer(r)
|
container, err := a.getContainer(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
noContainerErrorResponse(w, err)
|
a.noContainerErrorResponse(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
requestBody, err := ioutil.ReadAll(r.Body)
|
requestBody, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -841,7 +851,7 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = json.Unmarshal(requestBody, &sharing)
|
err = json.Unmarshal(requestBody, &sharing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -856,11 +866,11 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
err = a.app().UpsertSharing(*container, sharing)
|
err = a.app().UpsertSharing(*container, sharing)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("POST sharing %s", sharing.ID)
|
a.logger.Debug("POST sharing", mlog.String("sharingID", sharing.ID))
|
||||||
jsonStringResponse(w, http.StatusOK, "{}")
|
jsonStringResponse(w, http.StatusOK, "{}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -902,29 +912,29 @@ func (a *API) handleGetWorkspace(w http.ResponseWriter, r *http.Request) {
|
|||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
session := ctx.Value("session").(*model.Session)
|
session := ctx.Value("session").(*model.Session)
|
||||||
if !a.app().DoesUserHaveWorkspaceAccess(session.UserID, workspaceID) {
|
if !a.app().DoesUserHaveWorkspaceAccess(session.UserID, workspaceID) {
|
||||||
errorResponse(w, http.StatusUnauthorized, "", nil)
|
a.errorResponse(w, http.StatusUnauthorized, "", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
workspace, err = a.app().GetWorkspace(workspaceID)
|
workspace, err = a.app().GetWorkspace(workspaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
}
|
}
|
||||||
if workspace == nil {
|
if workspace == nil {
|
||||||
errorResponse(w, http.StatusUnauthorized, "", nil)
|
a.errorResponse(w, http.StatusUnauthorized, "", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
workspace, err = a.app().GetRootWorkspace()
|
workspace, err = a.app().GetRootWorkspace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
workspaceData, err := json.Marshal(workspace)
|
workspaceData, err := json.Marshal(workspace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -957,7 +967,7 @@ func (a *API) handlePostWorkspaceRegenerateSignupToken(w http.ResponseWriter, r
|
|||||||
|
|
||||||
workspace, err := a.app().GetRootWorkspace()
|
workspace, err := a.app().GetRootWorkspace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -965,7 +975,7 @@ func (a *API) handlePostWorkspaceRegenerateSignupToken(w http.ResponseWriter, r
|
|||||||
|
|
||||||
err = a.app().UpsertWorkspaceSignupToken(*workspace)
|
err = a.app().UpsertWorkspaceSignupToken(*workspace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1018,7 +1028,7 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Caller must have access to the root block's container
|
// Caller must have access to the root block's container
|
||||||
_, err := a.getContainerAllowingReadTokenForBlock(r, rootID)
|
_, err := a.getContainerAllowingReadTokenForBlock(r, rootID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
noContainerErrorResponse(w, err)
|
a.noContainerErrorResponse(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1033,7 +1043,7 @@ func (a *API) handleServeFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
fileReader, err := a.app().GetFileReader(workspaceID, rootID, filename)
|
fileReader, err := a.app().GetFileReader(workspaceID, rootID, filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer fileReader.Close()
|
defer fileReader.Close()
|
||||||
@ -1092,7 +1102,7 @@ func (a *API) handleUploadFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
// Caller must have access to the root block's container
|
// Caller must have access to the root block's container
|
||||||
_, err := a.getContainerAllowingReadTokenForBlock(r, rootID)
|
_, err := a.getContainerAllowingReadTokenForBlock(r, rootID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
noContainerErrorResponse(w, err)
|
a.noContainerErrorResponse(w, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1106,14 +1116,17 @@ func (a *API) handleUploadFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
fileId, err := a.app().SaveFile(file, workspaceID, rootID, handle.Filename)
|
fileId, err := a.app().SaveFile(file, workspaceID, rootID, handle.Filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("uploadFile, filename: %s, fileId: %s", handle.Filename, fileId)
|
a.logger.Debug("uploadFile",
|
||||||
|
mlog.String("filename", handle.Filename),
|
||||||
|
mlog.String("fileID", fileId),
|
||||||
|
)
|
||||||
data, err := json.Marshal(FileUploadResponse{FileID: fileId})
|
data, err := json.Marshal(FileUploadResponse{FileID: fileId})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1122,6 +1135,39 @@ func (a *API) handleUploadFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Response helpers
|
// Response helpers
|
||||||
|
|
||||||
|
func (a *API) errorResponse(w http.ResponseWriter, code int, message string, sourceError error) {
|
||||||
|
a.logger.Error("API ERROR",
|
||||||
|
mlog.Int("code", code),
|
||||||
|
mlog.Err(sourceError),
|
||||||
|
)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
data, err := json.Marshal(model.ErrorResponse{Error: message, ErrorCode: code})
|
||||||
|
if err != nil {
|
||||||
|
data = []byte("{}")
|
||||||
|
}
|
||||||
|
w.WriteHeader(code)
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) errorResponseWithCode(w http.ResponseWriter, statusCode int, errorCode int, message string, sourceError error) {
|
||||||
|
a.logger.Error("API ERROR",
|
||||||
|
mlog.Int("status", statusCode),
|
||||||
|
mlog.Int("code", errorCode),
|
||||||
|
mlog.Err(sourceError),
|
||||||
|
)
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
data, err := json.Marshal(model.ErrorResponse{Error: message, ErrorCode: errorCode})
|
||||||
|
if err != nil {
|
||||||
|
data = []byte("{}")
|
||||||
|
}
|
||||||
|
w.WriteHeader(statusCode)
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *API) noContainerErrorResponse(w http.ResponseWriter, sourceError error) {
|
||||||
|
a.errorResponseWithCode(w, http.StatusBadRequest, ERROR_NO_WORKSPACE_CODE, ERROR_NO_WORKSPACE_MESSAGE, sourceError)
|
||||||
|
}
|
||||||
|
|
||||||
func jsonStringResponse(w http.ResponseWriter, code int, message string) {
|
func jsonStringResponse(w http.ResponseWriter, code int, message string) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(code)
|
w.WriteHeader(code)
|
||||||
@ -1134,32 +1180,6 @@ func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) {
|
|||||||
w.Write(json)
|
w.Write(json)
|
||||||
}
|
}
|
||||||
|
|
||||||
func errorResponse(w http.ResponseWriter, code int, message string, sourceError error) {
|
|
||||||
log.Printf("API ERROR %d, err: %v\n", code, sourceError)
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
data, err := json.Marshal(model.ErrorResponse{Error: message, ErrorCode: code})
|
|
||||||
if err != nil {
|
|
||||||
data = []byte("{}")
|
|
||||||
}
|
|
||||||
w.WriteHeader(code)
|
|
||||||
w.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func errorResponseWithCode(w http.ResponseWriter, statusCode int, errorCode int, message string, sourceError error) {
|
|
||||||
log.Printf("API ERROR status %d, errorCode: %d, err: %v\n", statusCode, errorCode, sourceError)
|
|
||||||
w.Header().Set("Content-Type", "application/json")
|
|
||||||
data, err := json.Marshal(model.ErrorResponse{Error: message, ErrorCode: errorCode})
|
|
||||||
if err != nil {
|
|
||||||
data = []byte("{}")
|
|
||||||
}
|
|
||||||
w.WriteHeader(statusCode)
|
|
||||||
w.Write(data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func noContainerErrorResponse(w http.ResponseWriter, sourceError error) {
|
|
||||||
errorResponseWithCode(w, http.StatusBadRequest, ERROR_NO_WORKSPACE_CODE, ERROR_NO_WORKSPACE_MESSAGE, sourceError)
|
|
||||||
}
|
|
||||||
|
|
||||||
func addUserID(rw http.ResponseWriter, req *http.Request, next http.Handler) {
|
func addUserID(rw http.ResponseWriter, req *http.Request, next http.Handler) {
|
||||||
ctx := context.WithValue(req.Context(), "userid", req.Header.Get("userid"))
|
ctx := context.WithValue(req.Context(), "userid", req.Header.Get("userid"))
|
||||||
req = req.WithContext(ctx)
|
req = req.WithContext(ctx)
|
||||||
|
@ -5,7 +5,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
@ -15,6 +14,7 @@ import (
|
|||||||
serverContext "github.com/mattermost/focalboard/server/context"
|
serverContext "github.com/mattermost/focalboard/server/context"
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
"github.com/mattermost/focalboard/server/services/auth"
|
"github.com/mattermost/focalboard/server/services/auth"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// LoginRequest is a login request
|
// LoginRequest is a login request
|
||||||
@ -154,32 +154,32 @@ func (a *API) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if len(a.singleUserToken) > 0 {
|
if len(a.singleUserToken) > 0 {
|
||||||
// Not permitted in single-user mode
|
// Not permitted in single-user mode
|
||||||
errorResponse(w, http.StatusUnauthorized, "", nil)
|
a.errorResponse(w, http.StatusUnauthorized, "", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
requestBody, err := ioutil.ReadAll(r.Body)
|
requestBody, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var loginData LoginRequest
|
var loginData LoginRequest
|
||||||
err = json.Unmarshal(requestBody, &loginData)
|
err = json.Unmarshal(requestBody, &loginData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if loginData.Type == "normal" {
|
if loginData.Type == "normal" {
|
||||||
token, err := a.app().Login(loginData.Username, loginData.Email, loginData.Password, loginData.MfaToken)
|
token, err := a.app().Login(loginData.Username, loginData.Email, loginData.Password, loginData.MfaToken)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusUnauthorized, "incorrect login", err)
|
a.errorResponse(w, http.StatusUnauthorized, "incorrect login", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
json, err := json.Marshal(LoginResponse{Token: token})
|
json, err := json.Marshal(LoginResponse{Token: token})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,7 +187,7 @@ func (a *API) handleLogin(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
errorResponse(w, http.StatusBadRequest, "invalid login type", nil)
|
a.errorResponse(w, http.StatusBadRequest, "invalid login type", nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
|
func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -217,20 +217,20 @@ func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if len(a.singleUserToken) > 0 {
|
if len(a.singleUserToken) > 0 {
|
||||||
// Not permitted in single-user mode
|
// Not permitted in single-user mode
|
||||||
errorResponse(w, http.StatusUnauthorized, "", nil)
|
a.errorResponse(w, http.StatusUnauthorized, "", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
requestBody, err := ioutil.ReadAll(r.Body)
|
requestBody, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var registerData RegisterRequest
|
var registerData RegisterRequest
|
||||||
err = json.Unmarshal(requestBody, ®isterData)
|
err = json.Unmarshal(requestBody, ®isterData)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,35 +238,35 @@ func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
|
|||||||
if len(registerData.Token) > 0 {
|
if len(registerData.Token) > 0 {
|
||||||
workspace, err := a.app().GetRootWorkspace()
|
workspace, err := a.app().GetRootWorkspace()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if registerData.Token != workspace.SignupToken {
|
if registerData.Token != workspace.SignupToken {
|
||||||
errorResponse(w, http.StatusUnauthorized, "", nil)
|
a.errorResponse(w, http.StatusUnauthorized, "", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No signup token, check if no active users
|
// No signup token, check if no active users
|
||||||
userCount, err := a.app().GetRegisteredUserCount()
|
userCount, err := a.app().GetRegisteredUserCount()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if userCount > 0 {
|
if userCount > 0 {
|
||||||
errorResponse(w, http.StatusUnauthorized, "", nil)
|
a.errorResponse(w, http.StatusUnauthorized, "", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = registerData.IsValid(); err != nil {
|
if err = registerData.IsValid(); err != nil {
|
||||||
errorResponse(w, http.StatusBadRequest, err.Error(), err)
|
a.errorResponse(w, http.StatusBadRequest, err.Error(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = a.app().RegisterUser(registerData.Username, registerData.Email, registerData.Password)
|
err = a.app().RegisterUser(registerData.Username, registerData.Email, registerData.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusBadRequest, err.Error(), err)
|
a.errorResponse(w, http.StatusBadRequest, err.Error(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -309,7 +309,7 @@ func (a *API) handleChangePassword(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
if len(a.singleUserToken) > 0 {
|
if len(a.singleUserToken) > 0 {
|
||||||
// Not permitted in single-user mode
|
// Not permitted in single-user mode
|
||||||
errorResponse(w, http.StatusUnauthorized, "", nil)
|
a.errorResponse(w, http.StatusUnauthorized, "", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -318,23 +318,23 @@ func (a *API) handleChangePassword(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
requestBody, err := ioutil.ReadAll(r.Body)
|
requestBody, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var requestData ChangePasswordRequest
|
var requestData ChangePasswordRequest
|
||||||
if err := json.Unmarshal(requestBody, &requestData); err != nil {
|
if err := json.Unmarshal(requestBody, &requestData); err != nil {
|
||||||
errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = requestData.IsValid(); err != nil {
|
if err = requestData.IsValid(); err != nil {
|
||||||
errorResponse(w, http.StatusBadRequest, err.Error(), err)
|
a.errorResponse(w, http.StatusBadRequest, err.Error(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = a.app().ChangePassword(userID, requestData.OldPassword, requestData.NewPassword); err != nil {
|
if err = a.app().ChangePassword(userID, requestData.OldPassword, requestData.NewPassword); err != nil {
|
||||||
errorResponse(w, http.StatusBadRequest, err.Error(), err)
|
a.errorResponse(w, http.StatusBadRequest, err.Error(), err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -349,10 +349,10 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)
|
|||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
token, _ := auth.ParseAuthTokenFromRequest(r)
|
token, _ := auth.ParseAuthTokenFromRequest(r)
|
||||||
|
|
||||||
log.Printf(`Single User: %v`, len(a.singleUserToken) > 0)
|
a.logger.Debug(`attachSession`, mlog.Bool("single_user", len(a.singleUserToken) > 0))
|
||||||
if len(a.singleUserToken) > 0 {
|
if len(a.singleUserToken) > 0 {
|
||||||
if required && (token != a.singleUserToken) {
|
if required && (token != a.singleUserToken) {
|
||||||
errorResponse(w, http.StatusUnauthorized, "", nil)
|
a.errorResponse(w, http.StatusUnauthorized, "", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,7 +391,7 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)
|
|||||||
session, err := a.app().GetSession(token)
|
session, err := a.app().GetSession(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if required {
|
if required {
|
||||||
errorResponse(w, http.StatusUnauthorized, "", err)
|
a.errorResponse(w, http.StatusUnauthorized, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -401,8 +401,12 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
authService := session.AuthService
|
authService := session.AuthService
|
||||||
if authService != a.authService {
|
if authService != a.authService {
|
||||||
log.Printf(`Session '%s' authService mismatch '%s' instead of '%s'`, session.ID, authService, a.authService)
|
a.logger.Error(`Session authService mismatch`,
|
||||||
errorResponse(w, http.StatusUnauthorized, "", err)
|
mlog.String("sessionID", session.ID),
|
||||||
|
mlog.String("want", a.authService),
|
||||||
|
mlog.String("got", authService),
|
||||||
|
)
|
||||||
|
a.errorResponse(w, http.StatusUnauthorized, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,7 +420,7 @@ func (a *API) adminRequired(handler func(w http.ResponseWriter, r *http.Request)
|
|||||||
// Currently, admin APIs require local unix connections
|
// Currently, admin APIs require local unix connections
|
||||||
conn := serverContext.GetContextConn(r)
|
conn := serverContext.GetContextConn(r)
|
||||||
if _, isUnix := conn.(*net.UnixConn); !isUnix {
|
if _, isUnix := conn.(*net.UnixConn); !isUnix {
|
||||||
errorResponse(w, http.StatusUnauthorized, "", nil)
|
a.errorResponse(w, http.StatusUnauthorized, "", nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,9 +3,11 @@ package app
|
|||||||
import (
|
import (
|
||||||
"github.com/mattermost/focalboard/server/auth"
|
"github.com/mattermost/focalboard/server/auth"
|
||||||
"github.com/mattermost/focalboard/server/services/config"
|
"github.com/mattermost/focalboard/server/services/config"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
"github.com/mattermost/focalboard/server/services/webhook"
|
"github.com/mattermost/focalboard/server/services/webhook"
|
||||||
"github.com/mattermost/focalboard/server/ws"
|
"github.com/mattermost/focalboard/server/ws"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v5/shared/filestore"
|
"github.com/mattermost/mattermost-server/v5/shared/filestore"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -16,6 +18,7 @@ type App struct {
|
|||||||
wsServer *ws.Server
|
wsServer *ws.Server
|
||||||
filesBackend filestore.FileBackend
|
filesBackend filestore.FileBackend
|
||||||
webhook *webhook.Client
|
webhook *webhook.Client
|
||||||
|
logger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(
|
func New(
|
||||||
@ -25,6 +28,7 @@ func New(
|
|||||||
wsServer *ws.Server,
|
wsServer *ws.Server,
|
||||||
filesBackend filestore.FileBackend,
|
filesBackend filestore.FileBackend,
|
||||||
webhook *webhook.Client,
|
webhook *webhook.Client,
|
||||||
|
logger *mlog.Logger,
|
||||||
) *App {
|
) *App {
|
||||||
return &App{
|
return &App{
|
||||||
config: config,
|
config: config,
|
||||||
@ -33,5 +37,6 @@ func New(
|
|||||||
wsServer: wsServer,
|
wsServer: wsServer,
|
||||||
filesBackend: filesBackend,
|
filesBackend: filesBackend,
|
||||||
webhook: webhook,
|
webhook: webhook,
|
||||||
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
"github.com/mattermost/focalboard/server/services/auth"
|
"github.com/mattermost/focalboard/server/services/auth"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -80,7 +79,7 @@ func (a *App) Login(username, email, password, mfaToken string) (string, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !auth.ComparePassword(user.Password, password) {
|
if !auth.ComparePassword(user.Password, password) {
|
||||||
log.Printf("Invalid password for userID: %s\n", user.ID)
|
a.logger.Debug("Invalid password for user", mlog.String("userID", user.ID))
|
||||||
return "", errors.New("invalid username or password")
|
return "", errors.New("invalid username or password")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,7 +174,7 @@ func (a *App) ChangePassword(userID, oldPassword, newPassword string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !auth.ComparePassword(user.Password, oldPassword) {
|
if !auth.ComparePassword(user.Password, oldPassword) {
|
||||||
log.Printf("Invalid password for userID: %s\n", user.ID)
|
a.logger.Debug("Invalid password for user", mlog.String("userID", user.ID))
|
||||||
return errors.New("invalid username or password")
|
return errors.New("invalid username or password")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,12 +4,13 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/utils"
|
"github.com/mattermost/focalboard/server/utils"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v5/shared/filestore"
|
"github.com/mattermost/mattermost-server/v5/shared/filestore"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -46,9 +47,9 @@ func (a *App) GetFileReader(workspaceID, rootID, filename string) (filestore.Rea
|
|||||||
if oldExists {
|
if oldExists {
|
||||||
err := a.filesBackend.MoveFile(filename, filePath)
|
err := a.filesBackend.MoveFile(filename, filePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR moving old file from '%s' to '%s'", filename, filePath)
|
a.logger.Error("ERROR moving file", mlog.String("old", filename), mlog.String("new", filePath))
|
||||||
} else {
|
} else {
|
||||||
log.Printf("Moved old file from '%s' to '%s'", filename, filePath)
|
a.logger.Debug("Moved file", mlog.String("old", filename), mlog.String("new", filePath))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,11 @@ import (
|
|||||||
|
|
||||||
"github.com/mattermost/focalboard/server/auth"
|
"github.com/mattermost/focalboard/server/auth"
|
||||||
"github.com/mattermost/focalboard/server/services/config"
|
"github.com/mattermost/focalboard/server/services/config"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/services/store/mockstore"
|
"github.com/mattermost/focalboard/server/services/store/mockstore"
|
||||||
"github.com/mattermost/focalboard/server/services/webhook"
|
"github.com/mattermost/focalboard/server/services/webhook"
|
||||||
"github.com/mattermost/focalboard/server/ws"
|
"github.com/mattermost/focalboard/server/ws"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v5/shared/filestore/mocks"
|
"github.com/mattermost/mattermost-server/v5/shared/filestore/mocks"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -27,10 +29,12 @@ func SetupTestHelper(t *testing.T) *TestHelper {
|
|||||||
cfg := config.Configuration{}
|
cfg := config.Configuration{}
|
||||||
store := mockstore.NewMockStore(ctrl)
|
store := mockstore.NewMockStore(ctrl)
|
||||||
auth := auth.New(&cfg, store)
|
auth := auth.New(&cfg, store)
|
||||||
|
logger := mlog.NewLogger()
|
||||||
|
logger.Configure("", cfg.LoggingEscapedJson)
|
||||||
sessionToken := "TESTTOKEN"
|
sessionToken := "TESTTOKEN"
|
||||||
wsserver := ws.NewServer(auth, sessionToken, false)
|
wsserver := ws.NewServer(auth, sessionToken, false, logger)
|
||||||
webhook := webhook.NewClient(&cfg)
|
webhook := webhook.NewClient(&cfg, logger)
|
||||||
app2 := New(&cfg, store, auth, wsserver, &mocks.FileBackend{}, webhook)
|
app2 := New(&cfg, store, auth, wsserver, &mocks.FileBackend{}, webhook, logger)
|
||||||
|
|
||||||
return &TestHelper{
|
return &TestHelper{
|
||||||
App: app2,
|
App: app2,
|
||||||
|
@ -2,9 +2,9 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/utils"
|
"github.com/mattermost/focalboard/server/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -18,16 +18,16 @@ func (a *App) GetRootWorkspace() (*model.Workspace, error) {
|
|||||||
}
|
}
|
||||||
err := a.store.UpsertWorkspaceSignupToken(*workspace)
|
err := a.store.UpsertWorkspaceSignupToken(*workspace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Unable to initialize workspace", err)
|
a.logger.Fatal("Unable to initialize workspace", mlog.Err(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
workspace, err = a.store.GetWorkspace(workspaceID)
|
workspace, err = a.store.GetWorkspace(workspaceID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("Unable to get initialized workspace", err)
|
a.logger.Fatal("Unable to get initialized workspace", mlog.Err(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("initialized workspace")
|
a.logger.Info("initialized workspace")
|
||||||
}
|
}
|
||||||
|
|
||||||
return workspace, nil
|
return workspace, nil
|
||||||
|
@ -12,6 +12,7 @@ require (
|
|||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/lib/pq v1.10.0
|
github.com/lib/pq v1.10.0
|
||||||
github.com/magiconair/properties v1.8.5 // indirect
|
github.com/magiconair/properties v1.8.5 // indirect
|
||||||
|
github.com/mattermost/logr/v2 v2.0.0-20210525034931-179e4b3c986d
|
||||||
github.com/mattermost/mattermost-server/v5 v5.3.2-0.20210524045451-a4f7df6f6e3c
|
github.com/mattermost/mattermost-server/v5 v5.3.2-0.20210524045451-a4f7df6f6e3c
|
||||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||||
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
github.com/mitchellh/mapstructure v1.4.1 // indirect
|
||||||
@ -25,7 +26,7 @@ require (
|
|||||||
github.com/spf13/viper v1.7.1
|
github.com/spf13/viper v1.7.1
|
||||||
github.com/stretchr/testify v1.7.0
|
github.com/stretchr/testify v1.7.0
|
||||||
github.com/tidwall/gjson v1.7.3 // indirect
|
github.com/tidwall/gjson v1.7.3 // indirect
|
||||||
go.uber.org/zap v1.16.0
|
go.uber.org/zap v1.16.0 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||||
golang.org/x/sys v0.0.0-20210324051608-47abb6519492 // indirect
|
golang.org/x/sys v0.0.0-20210324051608-47abb6519492 // indirect
|
||||||
|
@ -606,6 +606,8 @@ github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d h1:/RJ/UV7M5c7L2TQ
|
|||||||
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d/go.mod h1:HLbgMEI5K131jpxGazJ97AxfPDt31osq36YS1oxFQPQ=
|
github.com/mattermost/ldap v0.0.0-20201202150706-ee0e6284187d/go.mod h1:HLbgMEI5K131jpxGazJ97AxfPDt31osq36YS1oxFQPQ=
|
||||||
github.com/mattermost/logr v1.0.13 h1:6F/fM3csvH6Oy5sUpJuW7YyZSzZZAhJm5VcgKMxA2P8=
|
github.com/mattermost/logr v1.0.13 h1:6F/fM3csvH6Oy5sUpJuW7YyZSzZZAhJm5VcgKMxA2P8=
|
||||||
github.com/mattermost/logr v1.0.13/go.mod h1:Mt4DPu1NXMe6JxPdwCC0XBoxXmN9eXOIRPoZarU2PXs=
|
github.com/mattermost/logr v1.0.13/go.mod h1:Mt4DPu1NXMe6JxPdwCC0XBoxXmN9eXOIRPoZarU2PXs=
|
||||||
|
github.com/mattermost/logr/v2 v2.0.0-20210525034931-179e4b3c986d h1:MfG19tMusEOb0k/UBrLQFYhHnLOWt8vcuGDzDQ1fvJw=
|
||||||
|
github.com/mattermost/logr/v2 v2.0.0-20210525034931-179e4b3c986d/go.mod h1:mpPp935r5dIkFDo2y9Q87cQWhFR/4xXpNh0k/y8Hmwg=
|
||||||
github.com/mattermost/mattermost-server/v5 v5.3.2-0.20210524045451-a4f7df6f6e3c h1:p0C9yt6UYyTExEeHjBPBUCwCMAyTWvwAEc2/plNuZL4=
|
github.com/mattermost/mattermost-server/v5 v5.3.2-0.20210524045451-a4f7df6f6e3c h1:p0C9yt6UYyTExEeHjBPBUCwCMAyTWvwAEc2/plNuZL4=
|
||||||
github.com/mattermost/mattermost-server/v5 v5.3.2-0.20210524045451-a4f7df6f6e3c/go.mod h1:6CqGEG0Vnhrl23h8LB+lcOIT8KIUhzbJ7qhXlV7Ek9U=
|
github.com/mattermost/mattermost-server/v5 v5.3.2-0.20210524045451-a4f7df6f6e3c/go.mod h1:6CqGEG0Vnhrl23h8LB+lcOIT8KIUhzbJ7qhXlV7Ek9U=
|
||||||
github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0/go.mod h1:nV5bfVpT//+B1RPD2JvRnxbkLmJEYXmRaaVl15fsXjs=
|
github.com/mattermost/rsc v0.0.0-20160330161541-bbaefb05eaa0/go.mod h1:nV5bfVpT//+B1RPD2JvRnxbkLmJEYXmRaaVl15fsXjs=
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package integrationtests
|
package integrationtests
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"time"
|
"time"
|
||||||
@ -9,6 +8,7 @@ import (
|
|||||||
"github.com/mattermost/focalboard/server/client"
|
"github.com/mattermost/focalboard/server/client"
|
||||||
"github.com/mattermost/focalboard/server/server"
|
"github.com/mattermost/focalboard/server/server"
|
||||||
"github.com/mattermost/focalboard/server/services/config"
|
"github.com/mattermost/focalboard/server/services/config"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestHelper struct {
|
type TestHelper struct {
|
||||||
@ -27,22 +27,47 @@ func getTestConfig() *config.Configuration {
|
|||||||
connectionString = ":memory:"
|
connectionString = ":memory:"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logging := []byte(`
|
||||||
|
{
|
||||||
|
"testing": {
|
||||||
|
"type": "console",
|
||||||
|
"options": {
|
||||||
|
"out": "stdout"
|
||||||
|
},
|
||||||
|
"format": "plain",
|
||||||
|
"format_options": {
|
||||||
|
"delim": " "
|
||||||
|
},
|
||||||
|
"levels": [
|
||||||
|
{"id": 5, "name": "debug"},
|
||||||
|
{"id": 4, "name": "info"},
|
||||||
|
{"id": 3, "name": "warn"},
|
||||||
|
{"id": 2, "name": "error", "stacktrace": true},
|
||||||
|
{"id": 1, "name": "fatal", "stacktrace": true},
|
||||||
|
{"id": 0, "name": "panic", "stacktrace": true}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}`)
|
||||||
|
|
||||||
return &config.Configuration{
|
return &config.Configuration{
|
||||||
ServerRoot: "http://localhost:8888",
|
ServerRoot: "http://localhost:8888",
|
||||||
Port: 8888,
|
Port: 8888,
|
||||||
DBType: dbType,
|
DBType: dbType,
|
||||||
DBConfigString: connectionString,
|
DBConfigString: connectionString,
|
||||||
DBTablePrefix: "test_",
|
DBTablePrefix: "test_",
|
||||||
WebPath: "./pack",
|
WebPath: "./pack",
|
||||||
FilesDriver: "local",
|
FilesDriver: "local",
|
||||||
FilesPath: "./files",
|
FilesPath: "./files",
|
||||||
|
LoggingEscapedJson: string(logging),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupTestHelper() *TestHelper {
|
func SetupTestHelper() *TestHelper {
|
||||||
sessionToken := "TESTTOKEN"
|
sessionToken := "TESTTOKEN"
|
||||||
th := &TestHelper{}
|
th := &TestHelper{}
|
||||||
srv, err := server.New(getTestConfig(), sessionToken)
|
logger := mlog.NewLogger()
|
||||||
|
logger.Configure("", getTestConfig().LoggingEscapedJson)
|
||||||
|
srv, err := server.New(getTestConfig(), sessionToken, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@ -61,10 +86,10 @@ func (th *TestHelper) InitBasic() *TestHelper {
|
|||||||
|
|
||||||
for {
|
for {
|
||||||
URL := th.Server.Config().ServerRoot
|
URL := th.Server.Config().ServerRoot
|
||||||
log.Printf("Polling server at %v", URL)
|
th.Server.Logger().Info("Polling server", mlog.String("url", URL))
|
||||||
resp, err := http.Get(URL)
|
resp, err := http.Get(URL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println("Polling failed:", err)
|
th.Server.Logger().Error("Polling failed", mlog.Err(err))
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -72,12 +97,12 @@ func (th *TestHelper) InitBasic() *TestHelper {
|
|||||||
|
|
||||||
// Currently returns 404
|
// Currently returns 404
|
||||||
// if resp.StatusCode != http.StatusOK {
|
// if resp.StatusCode != http.StatusOK {
|
||||||
// log.Println("Not OK:", resp.StatusCode)
|
// th.Server.Logger().Error("Not OK", mlog.Int("statusCode", resp.StatusCode))
|
||||||
// continue
|
// continue
|
||||||
// }
|
// }
|
||||||
|
|
||||||
// Reached this point: server is up and running!
|
// Reached this point: server is up and running!
|
||||||
log.Println("Server ping OK, statusCode:", resp.StatusCode)
|
th.Server.Logger().Info("Server ping OK", mlog.Int("statusCode", resp.StatusCode))
|
||||||
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -86,6 +111,8 @@ func (th *TestHelper) InitBasic() *TestHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) TearDown() {
|
func (th *TestHelper) TearDown() {
|
||||||
|
defer th.Server.Logger().Shutdown()
|
||||||
|
|
||||||
err := th.Server.Shutdown()
|
err := th.Server.Shutdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -38,6 +38,9 @@ import (
|
|||||||
"github.com/mattermost/focalboard/server/server"
|
"github.com/mattermost/focalboard/server/server"
|
||||||
"github.com/mattermost/focalboard/server/services/config"
|
"github.com/mattermost/focalboard/server/services/config"
|
||||||
)
|
)
|
||||||
|
import (
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
|
)
|
||||||
|
|
||||||
// Active server used with shared code (dll)
|
// Active server used with shared code (dll)
|
||||||
var pServer *server.Server
|
var pServer *server.Server
|
||||||
@ -58,13 +61,13 @@ func isProcessRunning(pid int) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// monitorPid is used to keep the server lifetime in sync with another (client app) process
|
// monitorPid is used to keep the server lifetime in sync with another (client app) process
|
||||||
func monitorPid(pid int) {
|
func monitorPid(pid int, logger *mlog.Logger) {
|
||||||
log.Printf("Monitoring PID: %d", pid)
|
logger.Info("Monitoring PID", mlog.Int("pid", pid))
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
if !isProcessRunning(pid) {
|
if !isProcessRunning(pid) {
|
||||||
log.Printf("Monitored process not found, exiting.")
|
logger.Info("Monitored process not found, exiting.")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,18 +76,17 @@ func monitorPid(pid int) {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func logInfo() {
|
func logInfo(logger *mlog.Logger) {
|
||||||
log.Println("Focalboard Server")
|
logger.Info("FocalBoard Server",
|
||||||
log.Println("Version: " + model.CurrentVersion)
|
mlog.String("version", model.CurrentVersion),
|
||||||
log.Println("Edition: " + model.Edition)
|
mlog.String("edition", model.Edition),
|
||||||
log.Println("Build Number: " + model.BuildNumber)
|
mlog.String("build_number", model.BuildNumber),
|
||||||
log.Println("Build Date: " + model.BuildDate)
|
mlog.String("build_date", model.BuildDate),
|
||||||
log.Println("Build Hash: " + model.BuildHash)
|
mlog.String("build_hash", model.BuildHash),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
logInfo()
|
|
||||||
|
|
||||||
// config.json file
|
// config.json file
|
||||||
config, err := config.ReadConfigFile()
|
config, err := config.ReadConfigFile()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -92,6 +94,21 @@ func main() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger := mlog.NewLogger()
|
||||||
|
err = logger.Configure(config.LoggingFile, config.LoggingEscapedJson)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error in config file for logger: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer logger.Shutdown()
|
||||||
|
|
||||||
|
if logger.HasTargets() {
|
||||||
|
restore := logger.RedirectStdLog(mlog.Info, mlog.String("src", "stdlog"))
|
||||||
|
defer restore()
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo(logger)
|
||||||
|
|
||||||
// Command line args
|
// Command line args
|
||||||
pMonitorPid := flag.Int("monitorpid", -1, "a process ID")
|
pMonitorPid := flag.Int("monitorpid", -1, "a process ID")
|
||||||
pPort := flag.Int("port", config.Port, "the port number")
|
pPort := flag.Int("port", config.Port, "the port number")
|
||||||
@ -109,42 +126,42 @@ func main() {
|
|||||||
if singleUser {
|
if singleUser {
|
||||||
singleUserToken = os.Getenv("FOCALBOARD_SINGLE_USER_TOKEN")
|
singleUserToken = os.Getenv("FOCALBOARD_SINGLE_USER_TOKEN")
|
||||||
if len(singleUserToken) < 1 {
|
if len(singleUserToken) < 1 {
|
||||||
log.Fatal("The FOCALBOARD_SINGLE_USER_TOKEN environment variable must be set for single user mode ")
|
logger.Fatal("The FOCALBOARD_SINGLE_USER_TOKEN environment variable must be set for single user mode ")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.Printf("Single user mode")
|
logger.Info("Single user mode")
|
||||||
}
|
}
|
||||||
|
|
||||||
if pMonitorPid != nil && *pMonitorPid > 0 {
|
if pMonitorPid != nil && *pMonitorPid > 0 {
|
||||||
monitorPid(*pMonitorPid)
|
monitorPid(*pMonitorPid, logger)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override config from commandline
|
// Override config from commandline
|
||||||
|
|
||||||
if pDBType != nil && len(*pDBType) > 0 {
|
if pDBType != nil && len(*pDBType) > 0 {
|
||||||
config.DBType = *pDBType
|
config.DBType = *pDBType
|
||||||
log.Printf("DBType from commandline: %s", *pDBType)
|
logger.Info("DBType from commandline", mlog.String("DBType", *pDBType))
|
||||||
}
|
}
|
||||||
|
|
||||||
if pDBConfig != nil && len(*pDBConfig) > 0 {
|
if pDBConfig != nil && len(*pDBConfig) > 0 {
|
||||||
config.DBConfigString = *pDBConfig
|
config.DBConfigString = *pDBConfig
|
||||||
// Don't echo, as the confix string may contain passwords
|
// Don't echo, as the confix string may contain passwords
|
||||||
log.Printf("DBConfigString overriden from commandline")
|
logger.Info("DBConfigString overriden from commandline")
|
||||||
}
|
}
|
||||||
|
|
||||||
if pPort != nil && *pPort > 0 && *pPort != config.Port {
|
if pPort != nil && *pPort > 0 && *pPort != config.Port {
|
||||||
// Override port
|
// Override port
|
||||||
log.Printf("Port from commandline: %d", *pPort)
|
logger.Info("Port from commandline", mlog.Int("port", *pPort))
|
||||||
config.Port = *pPort
|
config.Port = *pPort
|
||||||
}
|
}
|
||||||
|
|
||||||
server, err := server.New(config, singleUserToken)
|
server, err := server.New(config, singleUserToken, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("server.New ERROR: ", err)
|
logger.Fatal("server.New ERROR", mlog.Err(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := server.Start(); err != nil {
|
if err := server.Start(); err != nil {
|
||||||
log.Fatal("server.Start ERROR: ", err)
|
logger.Fatal("server.Start ERROR", mlog.Err(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setting up signal capturing
|
// Setting up signal capturing
|
||||||
@ -176,8 +193,6 @@ func StopServer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func startServer(webPath string, filesPath string, port int, singleUserToken, dbConfigString string) {
|
func startServer(webPath string, filesPath string, port int, singleUserToken, dbConfigString string) {
|
||||||
logInfo()
|
|
||||||
|
|
||||||
if pServer != nil {
|
if pServer != nil {
|
||||||
stopServer()
|
stopServer()
|
||||||
pServer = nil
|
pServer = nil
|
||||||
@ -190,6 +205,15 @@ func startServer(webPath string, filesPath string, port int, singleUserToken, db
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger := mlog.NewLogger()
|
||||||
|
err = logger.Configure(config.LoggingFile, config.LoggingEscapedJson)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error in config file for logger: ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo(logger)
|
||||||
|
|
||||||
if len(filesPath) > 0 {
|
if len(filesPath) > 0 {
|
||||||
config.FilesPath = filesPath
|
config.FilesPath = filesPath
|
||||||
}
|
}
|
||||||
@ -206,13 +230,13 @@ func startServer(webPath string, filesPath string, port int, singleUserToken, db
|
|||||||
config.DBConfigString = dbConfigString
|
config.DBConfigString = dbConfigString
|
||||||
}
|
}
|
||||||
|
|
||||||
pServer, err = server.New(config, singleUserToken)
|
pServer, err = server.New(config, singleUserToken, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("server.New ERROR: ", err)
|
logger.Fatal("server.New ERROR", mlog.Err(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := pServer.Start(); err != nil {
|
if err := pServer.Start(); err != nil {
|
||||||
log.Fatal("server.Start ERROR: ", err)
|
logger.Fatal("server.Start ERROR", mlog.Err(err))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -223,6 +247,8 @@ func stopServer() {
|
|||||||
|
|
||||||
err := pServer.Shutdown()
|
err := pServer.Shutdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("server.Shutdown ERROR: ", err)
|
pServer.Logger().Error("server.Shutdown ERROR", mlog.Err(err))
|
||||||
}
|
}
|
||||||
|
pServer.Logger().Shutdown()
|
||||||
|
pServer = nil
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,6 @@ import (
|
|||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
@ -21,6 +19,7 @@ import (
|
|||||||
"github.com/mattermost/focalboard/server/context"
|
"github.com/mattermost/focalboard/server/context"
|
||||||
appModel "github.com/mattermost/focalboard/server/model"
|
appModel "github.com/mattermost/focalboard/server/model"
|
||||||
"github.com/mattermost/focalboard/server/services/config"
|
"github.com/mattermost/focalboard/server/services/config"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/services/prometheus"
|
"github.com/mattermost/focalboard/server/services/prometheus"
|
||||||
"github.com/mattermost/focalboard/server/services/scheduler"
|
"github.com/mattermost/focalboard/server/services/scheduler"
|
||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
@ -30,9 +29,10 @@ import (
|
|||||||
"github.com/mattermost/focalboard/server/services/webhook"
|
"github.com/mattermost/focalboard/server/services/webhook"
|
||||||
"github.com/mattermost/focalboard/server/web"
|
"github.com/mattermost/focalboard/server/web"
|
||||||
"github.com/mattermost/focalboard/server/ws"
|
"github.com/mattermost/focalboard/server/ws"
|
||||||
|
"github.com/oklog/run"
|
||||||
|
|
||||||
"github.com/mattermost/mattermost-server/v5/shared/filestore"
|
"github.com/mattermost/mattermost-server/v5/shared/filestore"
|
||||||
"github.com/mattermost/mattermost-server/v5/utils"
|
"github.com/mattermost/mattermost-server/v5/utils"
|
||||||
"github.com/oklog/run"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -49,7 +49,7 @@ type Server struct {
|
|||||||
store store.Store
|
store store.Store
|
||||||
filesBackend filestore.FileBackend
|
filesBackend filestore.FileBackend
|
||||||
telemetry *telemetry.Service
|
telemetry *telemetry.Service
|
||||||
logger *zap.Logger
|
logger *mlog.Logger
|
||||||
cleanUpSessionsTask *scheduler.ScheduledTask
|
cleanUpSessionsTask *scheduler.ScheduledTask
|
||||||
promServer *prometheus.Service
|
promServer *prometheus.Service
|
||||||
promInstrumentor *prometheus.Instrumentor
|
promInstrumentor *prometheus.Instrumentor
|
||||||
@ -60,16 +60,11 @@ type Server struct {
|
|||||||
appBuilder func() *app.App
|
appBuilder func() *app.App
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(cfg *config.Configuration, singleUserToken string) (*Server, error) {
|
func New(cfg *config.Configuration, singleUserToken string, logger *mlog.Logger) (*Server, error) {
|
||||||
logger, err := zap.NewProduction()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var db store.Store
|
var db store.Store
|
||||||
db, err = sqlstore.New(cfg.DBType, cfg.DBConfigString, cfg.DBTablePrefix)
|
db, err := sqlstore.New(cfg.DBType, cfg.DBConfigString, cfg.DBTablePrefix, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("Unable to start the database", err)
|
logger.Error("Unable to start the database", mlog.Err(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if cfg.AuthMode == "mattermost" {
|
if cfg.AuthMode == "mattermost" {
|
||||||
@ -83,7 +78,7 @@ func New(cfg *config.Configuration, singleUserToken string) (*Server, error) {
|
|||||||
|
|
||||||
authenticator := auth.New(cfg, db)
|
authenticator := auth.New(cfg, db)
|
||||||
|
|
||||||
wsServer := ws.NewServer(authenticator, singleUserToken, cfg.AuthMode == "mattermost")
|
wsServer := ws.NewServer(authenticator, singleUserToken, cfg.AuthMode == "mattermost", logger)
|
||||||
|
|
||||||
filesBackendSettings := filestore.FileBackendSettings{}
|
filesBackendSettings := filestore.FileBackendSettings{}
|
||||||
filesBackendSettings.DriverName = cfg.FilesDriver
|
filesBackendSettings.DriverName = cfg.FilesDriver
|
||||||
@ -101,15 +96,15 @@ func New(cfg *config.Configuration, singleUserToken string) (*Server, error) {
|
|||||||
|
|
||||||
filesBackend, appErr := filestore.NewFileBackend(filesBackendSettings)
|
filesBackend, appErr := filestore.NewFileBackend(filesBackendSettings)
|
||||||
if appErr != nil {
|
if appErr != nil {
|
||||||
log.Print("Unable to initialize the files storage")
|
logger.Error("Unable to initialize the files storage", mlog.Err(appErr))
|
||||||
|
|
||||||
return nil, errors.New("unable to initialize the files storage")
|
return nil, errors.New("unable to initialize the files storage")
|
||||||
}
|
}
|
||||||
|
|
||||||
webhookClient := webhook.NewClient(cfg)
|
webhookClient := webhook.NewClient(cfg, logger)
|
||||||
|
|
||||||
appBuilder := func() *app.App { return app.New(cfg, db, authenticator, wsServer, filesBackend, webhookClient) }
|
appBuilder := func() *app.App { return app.New(cfg, db, authenticator, wsServer, filesBackend, webhookClient, logger) }
|
||||||
focalboardAPI := api.NewAPI(appBuilder, singleUserToken, cfg.AuthMode)
|
focalboardAPI := api.NewAPI(appBuilder, singleUserToken, cfg.AuthMode, logger)
|
||||||
|
|
||||||
// Local router for admin APIs
|
// Local router for admin APIs
|
||||||
localRouter := mux.NewRouter()
|
localRouter := mux.NewRouter()
|
||||||
@ -117,11 +112,11 @@ func New(cfg *config.Configuration, singleUserToken string) (*Server, error) {
|
|||||||
|
|
||||||
// Init workspace
|
// Init workspace
|
||||||
if _, err = appBuilder().GetRootWorkspace(); err != nil {
|
if _, err = appBuilder().GetRootWorkspace(); err != nil {
|
||||||
log.Print("Unable to get root workspace", err)
|
logger.Error("Unable to get root workspace", mlog.Err(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
webServer := web.NewServer(cfg.WebPath, cfg.ServerRoot, cfg.Port, cfg.UseSSL, cfg.LocalOnly)
|
webServer := web.NewServer(cfg.WebPath, cfg.ServerRoot, cfg.Port, cfg.UseSSL, cfg.LocalOnly, logger)
|
||||||
webServer.AddRoutes(wsServer)
|
webServer.AddRoutes(wsServer)
|
||||||
webServer.AddRoutes(focalboardAPI)
|
webServer.AddRoutes(focalboardAPI)
|
||||||
|
|
||||||
@ -160,7 +155,7 @@ func New(cfg *config.Configuration, singleUserToken string) (*Server, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
telemetryService := telemetry.New(telemetryID, zap.NewStdLog(logger))
|
telemetryService := telemetry.New(telemetryID, logger.StdLogger(mlog.Telemetry))
|
||||||
telemetryService.RegisterTracker("server", func() map[string]interface{} {
|
telemetryService.RegisterTracker("server", func() map[string]interface{} {
|
||||||
return map[string]interface{}{
|
return map[string]interface{}{
|
||||||
"version": appModel.CurrentVersion,
|
"version": appModel.CurrentVersion,
|
||||||
@ -226,7 +221,7 @@ func (s *Server) Start() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := s.store.CleanUpSessions(secondsAgo); err != nil {
|
if err := s.store.CleanUpSessions(secondsAgo); err != nil {
|
||||||
s.logger.Error("Unable to clean up the sessions", zap.Error(err))
|
s.logger.Error("Unable to clean up the sessions", mlog.Err(err))
|
||||||
}
|
}
|
||||||
}, cleanupSessionTaskFrequency)
|
}, cleanupSessionTaskFrequency)
|
||||||
|
|
||||||
@ -267,7 +262,7 @@ func (s *Server) Shutdown() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := s.telemetry.Shutdown(); err != nil {
|
if err := s.telemetry.Shutdown(); err != nil {
|
||||||
s.logger.Warn("Error occurred when shutting down telemetry", zap.Error(err))
|
s.logger.Warn("Error occurred when shutting down telemetry", mlog.Err(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
defer s.logger.Info("Server.Shutdown")
|
defer s.logger.Info("Server.Shutdown")
|
||||||
@ -279,6 +274,10 @@ func (s *Server) Config() *config.Configuration {
|
|||||||
return s.config
|
return s.config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) Logger() *mlog.Logger {
|
||||||
|
return s.logger
|
||||||
|
}
|
||||||
|
|
||||||
// Local server
|
// Local server
|
||||||
|
|
||||||
func (s *Server) startLocalModeServer() error {
|
func (s *Server) startLocalModeServer() error {
|
||||||
@ -289,7 +288,7 @@ func (s *Server) startLocalModeServer() error {
|
|||||||
|
|
||||||
// TODO: Close and delete socket file on shutdown
|
// TODO: Close and delete socket file on shutdown
|
||||||
if err := syscall.Unlink(s.config.LocalModeSocketLocation); err != nil {
|
if err := syscall.Unlink(s.config.LocalModeSocketLocation); err != nil {
|
||||||
log.Print("Unable to unlink socket.", err)
|
s.logger.Error("Unable to unlink socket.", mlog.Err(err))
|
||||||
}
|
}
|
||||||
|
|
||||||
socket := s.config.LocalModeSocketLocation
|
socket := s.config.LocalModeSocketLocation
|
||||||
@ -302,10 +301,10 @@ func (s *Server) startLocalModeServer() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
log.Println("Starting unix socket server")
|
s.logger.Info("Starting unix socket server")
|
||||||
err = s.localModeServer.Serve(unixListener)
|
err = s.localModeServer.Serve(unixListener)
|
||||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||||
log.Printf("Error starting unix socket server: %v", err)
|
s.logger.Error("Error starting unix socket server", mlog.Err(err))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
@ -48,6 +48,9 @@ type Configuration struct {
|
|||||||
LocalModeSocketLocation string `json:"localModeSocketLocation" mapstructure:"localModeSocketLocation"`
|
LocalModeSocketLocation string `json:"localModeSocketLocation" mapstructure:"localModeSocketLocation"`
|
||||||
|
|
||||||
AuthMode string `json:"authMode" mapstructure:"authMode"`
|
AuthMode string `json:"authMode" mapstructure:"authMode"`
|
||||||
|
|
||||||
|
LoggingFile string `json:"logging_file" mapstructure:"logging_file"`
|
||||||
|
LoggingEscapedJson string `json:"logging_escaped_json" mapstructure:"logging_escaped_json"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadConfigFile read the configuration from the filesystem.
|
// ReadConfigFile read the configuration from the filesystem.
|
||||||
|
40
server/services/mlog/levels.go
Normal file
40
server/services/mlog/levels.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
package mlog
|
||||||
|
|
||||||
|
import "github.com/mattermost/logr/v2"
|
||||||
|
|
||||||
|
// Standard levels
|
||||||
|
var (
|
||||||
|
Panic = logr.Panic // ID = 0
|
||||||
|
Fatal = logr.Fatal // ID = 1
|
||||||
|
Error = logr.Error // ID = 2
|
||||||
|
Warn = logr.Warn // ID = 3
|
||||||
|
Info = logr.Info // ID = 4
|
||||||
|
Debug = logr.Debug // ID = 5
|
||||||
|
Trace = logr.Trace // ID = 6
|
||||||
|
StdAll = []Level{Panic, Fatal, Error, Warn, Info, Debug, Trace}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Register custom (discrete) levels here.
|
||||||
|
// !!!!! Custom ID's must be between 20 and 32,768 !!!!!!
|
||||||
|
var (
|
||||||
|
/* Example
|
||||||
|
// used by the audit system
|
||||||
|
AuditAPI = Level{ID: 100, Name: "audit-api"}
|
||||||
|
AuditContent = Level{ID: 101, Name: "audit-content"}
|
||||||
|
AuditPerms = Level{ID: 102, Name: "audit-permissions"}
|
||||||
|
AuditCLI = Level{ID: 103, Name: "audit-cli"}
|
||||||
|
*/
|
||||||
|
|
||||||
|
// add more here ...
|
||||||
|
Telemetry = Level{ID: 500, Name: "telemetry"}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Combinations for LogM (log multi)
|
||||||
|
var (
|
||||||
|
/* Example
|
||||||
|
MAuditAll = []Level{AuditAPI, AuditContent, AuditPerms, AuditCLI}
|
||||||
|
*/
|
||||||
|
)
|
256
server/services/mlog/mlog.go
Normal file
256
server/services/mlog/mlog.go
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
// Package mlog provides a simple wrapper around Logr.
|
||||||
|
package mlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/mattermost/logr/v2"
|
||||||
|
logrcfg "github.com/mattermost/logr/v2/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ShutdownTimeout = time.Second * 15
|
||||||
|
)
|
||||||
|
|
||||||
|
// Type and function aliases from Logr to limit the spread of dependencies throughout Focalboard.
|
||||||
|
type Field = logr.Field
|
||||||
|
type Level = logr.Level
|
||||||
|
|
||||||
|
// Any picks the best supported field type based on type of val.
|
||||||
|
// For best performance when passing a struct (or struct pointer),
|
||||||
|
// implement `logr.LogWriter` on the struct, otherwise reflection
|
||||||
|
// will be used to generate a string representation.
|
||||||
|
var Any = logr.Any
|
||||||
|
|
||||||
|
// Int64 constructs a field containing a key and Int64 value.
|
||||||
|
var Int64 = logr.Int64
|
||||||
|
|
||||||
|
// Int32 constructs a field containing a key and Int32 value.
|
||||||
|
var Int32 = logr.Int32
|
||||||
|
|
||||||
|
// Int constructs a field containing a key and Int value.
|
||||||
|
var Int = logr.Int
|
||||||
|
|
||||||
|
// Uint64 constructs a field containing a key and Uint64 value.
|
||||||
|
var Uint64 = logr.Uint64
|
||||||
|
|
||||||
|
// Uint32 constructs a field containing a key and Uint32 value.
|
||||||
|
var Uint32 = logr.Uint32
|
||||||
|
|
||||||
|
// Uint constructs a field containing a key and Uint value.
|
||||||
|
var Uint = logr.Uint
|
||||||
|
|
||||||
|
// Float64 constructs a field containing a key and Float64 value.
|
||||||
|
var Float64 = logr.Float64
|
||||||
|
|
||||||
|
// Float32 constructs a field containing a key and Float32 value.
|
||||||
|
var Float32 = logr.Float32
|
||||||
|
|
||||||
|
// String constructs a field containing a key and String value.
|
||||||
|
var String = logr.String
|
||||||
|
|
||||||
|
// Stringer constructs a field containing a key and a fmt.Stringer value.
|
||||||
|
// The fmt.Stringer's `String` method is called lazily.
|
||||||
|
var Stringer = logr.Stringer
|
||||||
|
|
||||||
|
// Err constructs a field containing a default key ("error") and error value.
|
||||||
|
var Err = logr.Err
|
||||||
|
|
||||||
|
// NamedErr constructs a field containing a key and error value.
|
||||||
|
var NamedErr = logr.NamedErr
|
||||||
|
|
||||||
|
// Bool constructs a field containing a key and bool value.
|
||||||
|
var Bool = logr.Bool
|
||||||
|
|
||||||
|
// Time constructs a field containing a key and time.Time value.
|
||||||
|
var Time = logr.Time
|
||||||
|
|
||||||
|
// Duration constructs a field containing a key and time.Duration value.
|
||||||
|
var Duration = logr.Duration
|
||||||
|
|
||||||
|
// Millis constructs a field containing a key and timestamp value.
|
||||||
|
// The timestamp is expected to be milliseconds since Jan 1, 1970 UTC.
|
||||||
|
var Millis = logr.Millis
|
||||||
|
|
||||||
|
// Array constructs a field containing a key and array value.
|
||||||
|
var Array = logr.Array
|
||||||
|
|
||||||
|
// Map constructs a field containing a key and map value.
|
||||||
|
var Map = logr.Map
|
||||||
|
|
||||||
|
// LoggerConfig is a map of LogTarget configurations.
|
||||||
|
type LoggerConfig map[string]logrcfg.TargetCfg
|
||||||
|
|
||||||
|
func (lc LoggerConfig) append(cfg LoggerConfig) {
|
||||||
|
for k, v := range cfg {
|
||||||
|
lc[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logger provides a thin wrapper around a Logr instance. This is a struct instead of an interface
|
||||||
|
// so that there are no allocations on the heap each interface method invocation. Normally not
|
||||||
|
// something to be concerned about, but logging calls for disabled levels should have as little CPU
|
||||||
|
// and memory impact as possible. Most of these wrapper calls will be inlined as well.
|
||||||
|
type Logger struct {
|
||||||
|
log *logr.Logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewLogger creates a new Logger instance which can be configured via `(*Logger).Configure`
|
||||||
|
func NewLogger() *Logger {
|
||||||
|
lgr, _ := logr.New()
|
||||||
|
log := lgr.NewLogger()
|
||||||
|
|
||||||
|
return &Logger{
|
||||||
|
log: &log,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Configure provides a new configuration for this logger.
|
||||||
|
// Zero or more sources of config can be provided, with target name collisions resolved using the
|
||||||
|
// following precedence:
|
||||||
|
// cfgFile > cfgJson
|
||||||
|
func (l *Logger) Configure(cfgFile string, cfgEscaped string) error {
|
||||||
|
cfgMap := make(LoggerConfig)
|
||||||
|
|
||||||
|
// Add config from file
|
||||||
|
if cfgFile != "" {
|
||||||
|
if b, err := ioutil.ReadFile(string(cfgFile)); err != nil {
|
||||||
|
return fmt.Errorf("error reading logger config file %s: %w", cfgFile, err)
|
||||||
|
} else {
|
||||||
|
var mapCfgFile LoggerConfig
|
||||||
|
if err := json.Unmarshal(b, &mapCfgFile); err != nil {
|
||||||
|
return fmt.Errorf("error decoding logger config file %s: %w", cfgFile, err)
|
||||||
|
}
|
||||||
|
cfgMap.append(mapCfgFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add config from escaped json string
|
||||||
|
if cfgEscaped != "" {
|
||||||
|
if b, err := decodeEscapedJSONString(string(cfgEscaped)); err != nil {
|
||||||
|
return fmt.Errorf("error unescaping logger config as escaped json: %w", err)
|
||||||
|
} else {
|
||||||
|
var mapCfgEscaped LoggerConfig
|
||||||
|
if err := json.Unmarshal(b, &mapCfgEscaped); err != nil {
|
||||||
|
return fmt.Errorf("error decoding logger config as escaped json: %w", err)
|
||||||
|
}
|
||||||
|
cfgMap.append(mapCfgEscaped)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(cfgMap) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return logrcfg.ConfigureTargets(l.log.Logr(), cfgMap, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func decodeEscapedJSONString(s string) ([]byte, error) {
|
||||||
|
type wrapper struct {
|
||||||
|
wrap string
|
||||||
|
}
|
||||||
|
var wrapped wrapper
|
||||||
|
ss := fmt.Sprintf("{\"wrap\":%s}", s)
|
||||||
|
if err := json.Unmarshal([]byte(ss), &wrapped); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return []byte(wrapped.wrap), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// With creates a new Logger with the specified fields. This is a light-weight
|
||||||
|
// operation and can be called on demand.
|
||||||
|
func (l *Logger) With(fields ...Field) *Logger {
|
||||||
|
logWith := l.log.With(fields...)
|
||||||
|
return &Logger{
|
||||||
|
log: &logWith,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLevelEnabled returns true only if at least one log target is
|
||||||
|
// configured to emit the specified log level. Use this check when
|
||||||
|
// gathering the log info may be expensive.
|
||||||
|
//
|
||||||
|
// Note, transformations and serializations done via fields are already
|
||||||
|
// lazily evaluated and don't require this check beforehand.
|
||||||
|
func (l *Logger) IsLevelEnabled(level Level) bool {
|
||||||
|
return l.IsLevelEnabled(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log emits the log record for any targets configured for the specified level.
|
||||||
|
func (l *Logger) Log(level Level, msg string, fields ...Field) {
|
||||||
|
l.log.Log(level, msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogM emits the log record for any targets configured for the specified levels.
|
||||||
|
// Equivalent to calling `Log` once for each level.
|
||||||
|
func (l *Logger) LogM(levels []Level, msg string, fields ...Field) {
|
||||||
|
l.log.LogM(levels, msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience method equivalent to calling `Log` with the `Trace` level.
|
||||||
|
func (l *Logger) Trace(msg string, fields ...Field) {
|
||||||
|
l.log.Trace(msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience method equivalent to calling `Log` with the `Debug` level.
|
||||||
|
func (l *Logger) Debug(msg string, fields ...Field) {
|
||||||
|
l.log.Debug(msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience method equivalent to calling `Log` with the `Info` level.
|
||||||
|
func (l *Logger) Info(msg string, fields ...Field) {
|
||||||
|
l.log.Info(msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience method equivalent to calling `Log` with the `Warn` level.
|
||||||
|
func (l *Logger) Warn(msg string, fields ...Field) {
|
||||||
|
l.log.Warn(msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience method equivalent to calling `Log` with the `Error` level.
|
||||||
|
func (l *Logger) Error(msg string, fields ...Field) {
|
||||||
|
l.log.Error(msg, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convenience method equivalent to calling `Log` with the `Fatal` level,
|
||||||
|
// followed by `os.Exit(1)`.
|
||||||
|
func (l *Logger) Fatal(msg string, fields ...Field) {
|
||||||
|
l.log.Log(logr.Fatal, msg, fields...)
|
||||||
|
l.Shutdown()
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HasTargets returns true if at least one log target has been added.
|
||||||
|
func (l *Logger) HasTargets() bool {
|
||||||
|
return l.log.Logr().HasTargets()
|
||||||
|
}
|
||||||
|
|
||||||
|
// StdLogger creates a standard logger backed by this logger.
|
||||||
|
// All log records are output with the specified level.
|
||||||
|
func (l *Logger) StdLogger(level Level) *log.Logger {
|
||||||
|
return l.log.StdLogger(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RedirectStdLog redirects output from the standard library's package-global logger
|
||||||
|
// to this logger at the specified level and with zero or more Field's. Since this logger already
|
||||||
|
// handles caller annotations, timestamps, etc., it automatically disables the standard
|
||||||
|
// library's annotations and prefixing.
|
||||||
|
// A function is returned that restores the original prefix and flags and resets the standard
|
||||||
|
// library's output to os.Stdout.
|
||||||
|
func (l *Logger) RedirectStdLog(level Level, fields ...Field) func() {
|
||||||
|
return l.log.Logr().RedirectStdLog(level, fields...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown shuts down the logger after making best efforts to flush any
|
||||||
|
// remaining records.
|
||||||
|
func (l *Logger) Shutdown() error {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), ShutdownTimeout)
|
||||||
|
defer cancel()
|
||||||
|
return l.log.Logr().ShutdownWithTimeout(ctx)
|
||||||
|
}
|
59
server/services/mlog/tlog.go
Normal file
59
server/services/mlog/tlog.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package mlog
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/logr/v2"
|
||||||
|
"github.com/mattermost/logr/v2/formatters"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CreateTestLogger creates a logger for unit tests. Log records are output to `(*testing.T)Log`
|
||||||
|
func CreateTestLogger(t *testing.T, levels ...Field) (logger *Logger) {
|
||||||
|
logger = NewLogger()
|
||||||
|
|
||||||
|
filter := logr.NewCustomFilter(StdAll...)
|
||||||
|
formatter := &formatters.Plain{}
|
||||||
|
target := newTestingTarget(t)
|
||||||
|
|
||||||
|
logger.log.Logr().AddTarget(target, "test", filter, formatter, 1000)
|
||||||
|
return logger
|
||||||
|
}
|
||||||
|
|
||||||
|
// testingTarget is a simple log target that writes to the testing log.
|
||||||
|
type testingTarget struct {
|
||||||
|
mux sync.Mutex
|
||||||
|
t *testing.T
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTestingTarget(t *testing.T) *testingTarget {
|
||||||
|
return &testingTarget{
|
||||||
|
t: t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init is called once to initialize the target.
|
||||||
|
func (tt *testingTarget) Init() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write outputs bytes to this file target.
|
||||||
|
func (tt *testingTarget) Write(p []byte, rec *logr.LogRec) (int, error) {
|
||||||
|
tt.mux.Lock()
|
||||||
|
defer tt.mux.Unlock()
|
||||||
|
|
||||||
|
if tt.t != nil {
|
||||||
|
tt.t.Log(string(p))
|
||||||
|
}
|
||||||
|
return len(p), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shutdown is called once to free/close any resources.
|
||||||
|
// Target queue is already drained when this is called.
|
||||||
|
func (tt *testingTarget) Shutdown() error {
|
||||||
|
tt.mux.Lock()
|
||||||
|
defer tt.mux.Unlock()
|
||||||
|
|
||||||
|
tt.t = nil
|
||||||
|
return nil
|
||||||
|
}
|
@ -5,12 +5,12 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
_ "github.com/lib/pq"
|
_ "github.com/lib/pq"
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
_ "github.com/mattn/go-sqlite3"
|
_ "github.com/mattn/go-sqlite3"
|
||||||
)
|
)
|
||||||
@ -37,12 +37,12 @@ func (s *SQLStore) GetBlocksWithParentAndType(c store.Container, parentID string
|
|||||||
|
|
||||||
rows, err := query.Query()
|
rows, err := query.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`getBlocksWithParentAndType ERROR: %v`, err)
|
s.logger.Error(`getBlocksWithParentAndType ERROR`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocksFromRows(rows)
|
return s.blocksFromRows(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) GetBlocksWithParent(c store.Container, parentID string) ([]model.Block, error) {
|
func (s *SQLStore) GetBlocksWithParent(c store.Container, parentID string) ([]model.Block, error) {
|
||||||
@ -66,12 +66,12 @@ func (s *SQLStore) GetBlocksWithParent(c store.Container, parentID string) ([]mo
|
|||||||
|
|
||||||
rows, err := query.Query()
|
rows, err := query.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`getBlocksWithParent ERROR: %v`, err)
|
s.logger.Error(`getBlocksWithParent ERROR`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocksFromRows(rows)
|
return s.blocksFromRows(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) GetBlocksWithRootID(c store.Container, rootID string) ([]model.Block, error) {
|
func (s *SQLStore) GetBlocksWithRootID(c store.Container, rootID string) ([]model.Block, error) {
|
||||||
@ -95,12 +95,12 @@ func (s *SQLStore) GetBlocksWithRootID(c store.Container, rootID string) ([]mode
|
|||||||
|
|
||||||
rows, err := query.Query()
|
rows, err := query.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`GetBlocksWithRootID ERROR: %v`, err)
|
s.logger.Error(`GetBlocksWithRootID ERROR`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocksFromRows(rows)
|
return s.blocksFromRows(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) GetBlocksWithType(c store.Container, blockType string) ([]model.Block, error) {
|
func (s *SQLStore) GetBlocksWithType(c store.Container, blockType string) ([]model.Block, error) {
|
||||||
@ -124,12 +124,12 @@ func (s *SQLStore) GetBlocksWithType(c store.Container, blockType string) ([]mod
|
|||||||
|
|
||||||
rows, err := query.Query()
|
rows, err := query.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`getBlocksWithParentAndType ERROR: %v`, err)
|
s.logger.Error(`getBlocksWithParentAndType ERROR`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocksFromRows(rows)
|
return s.blocksFromRows(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSubTree2 returns blocks within 2 levels of the given blockID
|
// GetSubTree2 returns blocks within 2 levels of the given blockID
|
||||||
@ -154,12 +154,12 @@ func (s *SQLStore) GetSubTree2(c store.Container, blockID string) ([]model.Block
|
|||||||
|
|
||||||
rows, err := query.Query()
|
rows, err := query.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`getSubTree ERROR: %v`, err)
|
s.logger.Error(`getSubTree ERROR`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocksFromRows(rows)
|
return s.blocksFromRows(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSubTree3 returns blocks within 3 levels of the given blockID
|
// GetSubTree3 returns blocks within 3 levels of the given blockID
|
||||||
@ -192,12 +192,12 @@ func (s *SQLStore) GetSubTree3(c store.Container, blockID string) ([]model.Block
|
|||||||
|
|
||||||
rows, err := query.Query()
|
rows, err := query.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`getSubTree3 ERROR: %v`, err)
|
s.logger.Error(`getSubTree3 ERROR`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocksFromRows(rows)
|
return s.blocksFromRows(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) GetAllBlocks(c store.Container) ([]model.Block, error) {
|
func (s *SQLStore) GetAllBlocks(c store.Container) ([]model.Block, error) {
|
||||||
@ -220,15 +220,15 @@ func (s *SQLStore) GetAllBlocks(c store.Container) ([]model.Block, error) {
|
|||||||
|
|
||||||
rows, err := query.Query()
|
rows, err := query.Query()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`getAllBlocks ERROR: %v`, err)
|
s.logger.Error(`getAllBlocks ERROR`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return blocksFromRows(rows)
|
return s.blocksFromRows(rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
func blocksFromRows(rows *sql.Rows) ([]model.Block, error) {
|
func (s *SQLStore) blocksFromRows(rows *sql.Rows) ([]model.Block, error) {
|
||||||
defer rows.Close()
|
defer rows.Close()
|
||||||
|
|
||||||
results := []model.Block{}
|
results := []model.Block{}
|
||||||
@ -252,7 +252,7 @@ func blocksFromRows(rows *sql.Rows) ([]model.Block, error) {
|
|||||||
&block.DeleteAt)
|
&block.DeleteAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// handle this error
|
// handle this error
|
||||||
log.Printf(`ERROR blocksFromRows: %v`, err)
|
s.logger.Error(`ERROR blocksFromRows`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -264,7 +264,7 @@ func blocksFromRows(rows *sql.Rows) ([]model.Block, error) {
|
|||||||
err = json.Unmarshal([]byte(fieldsJSON), &block.Fields)
|
err = json.Unmarshal([]byte(fieldsJSON), &block.Fields)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// handle this error
|
// handle this error
|
||||||
log.Printf(`ERROR blocksFromRows fields: %v`, err)
|
s.logger.Error(`ERROR blocksFromRows fields`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ package sqlstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
"github.com/mattermost/focalboard/server/services/store/sqlstore/initializations"
|
"github.com/mattermost/focalboard/server/services/store/sqlstore/initializations"
|
||||||
)
|
)
|
||||||
@ -25,7 +25,7 @@ func (s *SQLStore) InitializeTemplates() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) importInitialTemplates() error {
|
func (s *SQLStore) importInitialTemplates() error {
|
||||||
log.Printf("importInitialTemplates")
|
s.logger.Debug("importInitialTemplates")
|
||||||
blocksJSON := initializations.MustAsset("templates.json")
|
blocksJSON := initializations.MustAsset("templates.json")
|
||||||
|
|
||||||
var archive model.Archive
|
var archive model.Archive
|
||||||
@ -38,9 +38,13 @@ func (s *SQLStore) importInitialTemplates() error {
|
|||||||
WorkspaceID: "0",
|
WorkspaceID: "0",
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("Inserting %d blocks", len(archive.Blocks))
|
s.logger.Debug("Inserting blocks", mlog.Int("block_count", len(archive.Blocks)))
|
||||||
for _, block := range archive.Blocks {
|
for _, block := range archive.Blocks {
|
||||||
// log.Printf("\t%v %v %v", block.ID, block.Type, block.Title)
|
s.logger.Trace("insert block",
|
||||||
|
mlog.String("blockID", block.ID),
|
||||||
|
mlog.String("block_type", block.Type),
|
||||||
|
mlog.String("block_title", block.Title),
|
||||||
|
)
|
||||||
err := s.InsertBlock(globalContainer, block)
|
err := s.InsertBlock(globalContainer, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -62,7 +66,7 @@ func (s *SQLStore) isInitializationNeeded() (bool, error) {
|
|||||||
var count int
|
var count int
|
||||||
err := row.Scan(&count)
|
err := row.Scan(&count)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
s.logger.Fatal("isInitializationNeeded", mlog.Err(err))
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,9 +2,9 @@ package sqlstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"log"
|
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -15,27 +15,28 @@ const (
|
|||||||
|
|
||||||
// SQLStore is a SQL database.
|
// SQLStore is a SQL database.
|
||||||
type SQLStore struct {
|
type SQLStore struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
dbType string
|
dbType string
|
||||||
tablePrefix string
|
tablePrefix string
|
||||||
connectionString string
|
connectionString string
|
||||||
|
logger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new SQL implementation of the store.
|
// New creates a new SQL implementation of the store.
|
||||||
func New(dbType, connectionString string, tablePrefix string) (*SQLStore, error) {
|
func New(dbType, connectionString string, tablePrefix string, logger *mlog.Logger) (*SQLStore, error) {
|
||||||
log.Println("connectDatabase", dbType, connectionString)
|
logger.Info("connectDatabase", mlog.String("dbType", dbType), mlog.String("connStr", connectionString))
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
db, err := sql.Open(dbType, connectionString)
|
db, err := sql.Open(dbType, connectionString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Print("connectDatabase: ", err)
|
logger.Error("connectDatabase failed", mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = db.Ping()
|
err = db.Ping()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`Database Ping failed: %v`, err)
|
logger.Error(`Database Ping failed`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -45,18 +46,19 @@ func New(dbType, connectionString string, tablePrefix string) (*SQLStore, error)
|
|||||||
dbType: dbType,
|
dbType: dbType,
|
||||||
tablePrefix: tablePrefix,
|
tablePrefix: tablePrefix,
|
||||||
connectionString: connectionString,
|
connectionString: connectionString,
|
||||||
|
logger: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.Migrate()
|
err = store.Migrate()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`Table creation / migration failed: %v`, err)
|
logger.Error(`Table creation / migration failed`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = store.InitializeTemplates()
|
err = store.InitializeTemplates()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`InitializeTemplates failed: %v`, err)
|
logger.Error(`InitializeTemplates failed`, mlog.Err(err))
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
"github.com/mattermost/focalboard/server/services/store/storetests"
|
"github.com/mattermost/focalboard/server/services/store/storetests"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -20,10 +21,13 @@ func SetupTests(t *testing.T) (store.Store, func()) {
|
|||||||
connectionString = ":memory:"
|
connectionString = ":memory:"
|
||||||
}
|
}
|
||||||
|
|
||||||
store, err := New(dbType, connectionString, "test_")
|
logger := mlog.CreateTestLogger(t)
|
||||||
|
|
||||||
|
store, err := New(dbType, connectionString, "test_", logger)
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
tearDown := func() {
|
tearDown := func() {
|
||||||
|
defer logger.Shutdown()
|
||||||
err = store.Shutdown()
|
err = store.Shutdown()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
@ -2,10 +2,10 @@ package sqlstore
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
)
|
)
|
||||||
@ -98,7 +98,7 @@ func (s *SQLStore) GetWorkspace(ID string) (*model.Workspace, error) {
|
|||||||
|
|
||||||
err = json.Unmarshal([]byte(settingsJSON), &workspace.Settings)
|
err = json.Unmarshal([]byte(settingsJSON), &workspace.Settings)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(`ERROR GetWorkspace settings json.Unmarshal: %v`, err)
|
s.logger.Error(`ERROR GetWorkspace settings json.Unmarshal`, mlog.Err(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,11 +3,11 @@ package webhook
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
"github.com/mattermost/focalboard/server/services/config"
|
"github.com/mattermost/focalboard/server/services/config"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NotifyUpdate calls webhooks
|
// NotifyUpdate calls webhooks
|
||||||
@ -18,22 +18,24 @@ func (wh *Client) NotifyUpdate(block model.Block) {
|
|||||||
|
|
||||||
json, err := json.Marshal(block)
|
json, err := json.Marshal(block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal("NotifyUpdate: json.Marshal", err)
|
wh.logger.Fatal("NotifyUpdate: json.Marshal", mlog.Err(err))
|
||||||
}
|
}
|
||||||
for _, url := range wh.config.WebhookUpdate {
|
for _, url := range wh.config.WebhookUpdate {
|
||||||
http.Post(url, "application/json", bytes.NewBuffer(json))
|
http.Post(url, "application/json", bytes.NewBuffer(json))
|
||||||
log.Printf("webhook.NotifyUpdate: %s", url)
|
wh.logger.Debug("webhook.NotifyUpdate", mlog.String("url", url))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client is a webhook client
|
// Client is a webhook client
|
||||||
type Client struct {
|
type Client struct {
|
||||||
config *config.Configuration
|
config *config.Configuration
|
||||||
|
logger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a new Client
|
// NewClient creates a new Client
|
||||||
func NewClient(config *config.Configuration) *Client {
|
func NewClient(config *config.Configuration, logger *mlog.Logger) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
config: config,
|
config: config,
|
||||||
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package web
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
@ -11,6 +10,7 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// RoutedService defines the interface that is needed for any service to
|
// RoutedService defines the interface that is needed for any service to
|
||||||
@ -29,10 +29,11 @@ type Server struct {
|
|||||||
port int
|
port int
|
||||||
ssl bool
|
ssl bool
|
||||||
localOnly bool
|
localOnly bool
|
||||||
|
logger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new instance of the webserver.
|
// NewServer creates a new instance of the webserver.
|
||||||
func NewServer(rootPath string, serverRoot string, port int, ssl, localOnly bool) *Server {
|
func NewServer(rootPath string, serverRoot string, port int, ssl, localOnly bool, logger *mlog.Logger) *Server {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
|
|
||||||
var addr string
|
var addr string
|
||||||
@ -45,7 +46,7 @@ func NewServer(rootPath string, serverRoot string, port int, ssl, localOnly bool
|
|||||||
baseURL := ""
|
baseURL := ""
|
||||||
url, err := url.Parse(serverRoot)
|
url, err := url.Parse(serverRoot)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Invalid ServerRoot setting: %v\n", err)
|
logger.Error("Invalid ServerRoot setting", mlog.Err(err))
|
||||||
}
|
}
|
||||||
baseURL = url.Path
|
baseURL = url.Path
|
||||||
|
|
||||||
@ -58,6 +59,7 @@ func NewServer(rootPath string, serverRoot string, port int, ssl, localOnly bool
|
|||||||
rootPath: rootPath,
|
rootPath: rootPath,
|
||||||
port: port,
|
port: port,
|
||||||
ssl: ssl,
|
ssl: ssl,
|
||||||
|
logger: logger,
|
||||||
}
|
}
|
||||||
|
|
||||||
return ws
|
return ws
|
||||||
@ -78,13 +80,13 @@ func (ws *Server) registerRoutes() {
|
|||||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
indexTemplate, err := template.New("index").ParseFiles(path.Join(ws.rootPath, "index.html"))
|
indexTemplate, err := template.New("index").ParseFiles(path.Join(ws.rootPath, "index.html"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Unable to serve the index.html fil, err: %v\n", err)
|
ws.logger.Error("Unable to serve the index.html file", mlog.Err(err))
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = indexTemplate.ExecuteTemplate(w, "index.html", map[string]string{"BaseURL": ws.baseURL})
|
err = indexTemplate.ExecuteTemplate(w, "index.html", map[string]string{"BaseURL": ws.baseURL})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("Unable to serve the index.html fil, err: %v\n", err)
|
ws.logger.Error("Unable to serve the index.html file", mlog.Err(err))
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -95,28 +97,28 @@ func (ws *Server) registerRoutes() {
|
|||||||
func (ws *Server) Start() {
|
func (ws *Server) Start() {
|
||||||
ws.registerRoutes()
|
ws.registerRoutes()
|
||||||
if ws.port == -1 {
|
if ws.port == -1 {
|
||||||
log.Print("server not bind to any port\n")
|
ws.logger.Error("server not bind to any port")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
isSSL := ws.ssl && fileExists("./cert/cert.pem") && fileExists("./cert/key.pem")
|
isSSL := ws.ssl && fileExists("./cert/cert.pem") && fileExists("./cert/key.pem")
|
||||||
if isSSL {
|
if isSSL {
|
||||||
log.Printf("https server started on :%d\n", ws.port)
|
ws.logger.Info("https server started", mlog.Int("port", ws.port))
|
||||||
go func() {
|
go func() {
|
||||||
if err := ws.ListenAndServeTLS("./cert/cert.pem", "./cert/key.pem"); err != nil {
|
if err := ws.ListenAndServeTLS("./cert/cert.pem", "./cert/key.pem"); err != nil {
|
||||||
log.Fatalf("ListenAndServeTLS: %v", err)
|
ws.logger.Fatal("ListenAndServeTLS", mlog.Err(err))
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("http server started on :%d\n", ws.port)
|
ws.logger.Info("http server started", mlog.Int("port", ws.port))
|
||||||
go func() {
|
go func() {
|
||||||
if err := ws.ListenAndServe(); err != http.ErrServerClosed {
|
if err := ws.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
log.Fatalf("ListenAndServe: %v", err)
|
ws.logger.Fatal("ListenAndServeTLS", mlog.Err(err))
|
||||||
}
|
}
|
||||||
log.Println("http server stopped")
|
ws.logger.Info("http server stopped")
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
"github.com/mattermost/focalboard/server/auth"
|
"github.com/mattermost/focalboard/server/auth"
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -32,6 +33,7 @@ type Server struct {
|
|||||||
hub Hub
|
hub Hub
|
||||||
singleUserToken string
|
singleUserToken string
|
||||||
isMattermostAuth bool
|
isMattermostAuth bool
|
||||||
|
logger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMsg is sent on block updates
|
// UpdateMsg is sent on block updates
|
||||||
@ -68,7 +70,7 @@ type websocketSession struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new Server.
|
// NewServer creates a new Server.
|
||||||
func NewServer(auth *auth.Auth, singleUserToken string, isMattermostAuth bool) *Server {
|
func NewServer(auth *auth.Auth, singleUserToken string, isMattermostAuth bool, logger *mlog.Logger) *Server {
|
||||||
return &Server{
|
return &Server{
|
||||||
listeners: make(map[string][]*websocket.Conn),
|
listeners: make(map[string][]*websocket.Conn),
|
||||||
upgrader: websocket.Upgrader{
|
upgrader: websocket.Upgrader{
|
||||||
@ -79,6 +81,7 @@ func NewServer(auth *auth.Auth, singleUserToken string, isMattermostAuth bool) *
|
|||||||
auth: auth,
|
auth: auth,
|
||||||
singleUserToken: singleUserToken,
|
singleUserToken: singleUserToken,
|
||||||
isMattermostAuth: isMattermostAuth,
|
isMattermostAuth: isMattermostAuth,
|
||||||
|
logger: logger,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,13 +94,13 @@ func (ws *Server) handleWebSocketOnChange(w http.ResponseWriter, r *http.Request
|
|||||||
// Upgrade initial GET request to a websocket
|
// Upgrade initial GET request to a websocket
|
||||||
client, err := ws.upgrader.Upgrade(w, r, nil)
|
client, err := ws.upgrader.Upgrade(w, r, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR upgrading to websocket: %v", err)
|
ws.logger.Error("ERROR upgrading to websocket", mlog.Err(err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we close the connection when the function returns
|
// Make sure we close the connection when the function returns
|
||||||
defer func() {
|
defer func() {
|
||||||
log.Printf("DISCONNECT WebSocket onChange, client: %s", client.RemoteAddr())
|
ws.logger.Debug("DISCONNECT WebSocket onChange", mlog.Stringer("client", client.RemoteAddr()))
|
||||||
|
|
||||||
// Remove client from listeners
|
// Remove client from listeners
|
||||||
ws.removeListener(client)
|
ws.removeListener(client)
|
||||||
@ -119,7 +122,10 @@ func (ws *Server) handleWebSocketOnChange(w http.ResponseWriter, r *http.Request
|
|||||||
for {
|
for {
|
||||||
_, p, err := client.ReadMessage()
|
_, p, err := client.ReadMessage()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("ERROR WebSocket onChange, client: %s, err: %v", client.RemoteAddr(), err)
|
ws.logger.Error("ERROR WebSocket onChange",
|
||||||
|
mlog.Stringer("client", client.RemoteAddr()),
|
||||||
|
mlog.Err(err),
|
||||||
|
)
|
||||||
ws.removeListener(client)
|
ws.removeListener(client)
|
||||||
|
|
||||||
break
|
break
|
||||||
@ -130,7 +136,7 @@ func (ws *Server) handleWebSocketOnChange(w http.ResponseWriter, r *http.Request
|
|||||||
err = json.Unmarshal(p, &command)
|
err = json.Unmarshal(p, &command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// handle this error
|
// handle this error
|
||||||
log.Printf(`ERROR webSocket parsing command JSON: %v`, string(p))
|
ws.logger.Error(`ERROR webSocket parsing command`, mlog.String("json", string(p)))
|
||||||
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -139,23 +145,32 @@ func (ws *Server) handleWebSocketOnChange(w http.ResponseWriter, r *http.Request
|
|||||||
if ws.auth.DoesUserHaveWorkspaceAccess(userID, command.WorkspaceID) {
|
if ws.auth.DoesUserHaveWorkspaceAccess(userID, command.WorkspaceID) {
|
||||||
wsSession.workspaceID = command.WorkspaceID
|
wsSession.workspaceID = command.WorkspaceID
|
||||||
} else {
|
} else {
|
||||||
log.Printf(`ERROR User doesn't have permissions to read the workspace: %s`, command.WorkspaceID)
|
ws.logger.Error(`ERROR User doesn't have permissions to read the workspace`, mlog.String("workspaceID", command.WorkspaceID))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch command.Action {
|
switch command.Action {
|
||||||
case "AUTH":
|
case "AUTH":
|
||||||
log.Printf(`Command: AUTH, client: %s`, client.RemoteAddr())
|
ws.logger.Debug(`Command: AUTH`, mlog.Stringer("client", client.RemoteAddr()))
|
||||||
ws.authenticateListener(&wsSession, command.WorkspaceID, command.Token)
|
ws.authenticateListener(&wsSession, command.WorkspaceID, command.Token)
|
||||||
case "ADD":
|
case "ADD":
|
||||||
log.Printf(`Command: Add workspaceID: %s, blockIDs: %v, client: %s`, wsSession.workspaceID, command.BlockIDs, client.RemoteAddr())
|
ws.logger.Debug(`Command: ADD`,
|
||||||
|
mlog.String("workspaceID", wsSession.workspaceID),
|
||||||
|
mlog.Array("blockIDs", command.BlockIDs),
|
||||||
|
mlog.Stringer("client", client.RemoteAddr()),
|
||||||
|
)
|
||||||
ws.addListener(&wsSession, &command)
|
ws.addListener(&wsSession, &command)
|
||||||
case "REMOVE":
|
case "REMOVE":
|
||||||
log.Printf(`Command: Remove workspaceID: %s, blockID: %v, client: %s`, wsSession.workspaceID, command.BlockIDs, client.RemoteAddr())
|
ws.logger.Debug(`Command: REMOVE`,
|
||||||
|
mlog.String("workspaceID", wsSession.workspaceID),
|
||||||
|
mlog.Array("blockIDs", command.BlockIDs),
|
||||||
|
mlog.Stringer("client", client.RemoteAddr()),
|
||||||
|
)
|
||||||
|
|
||||||
ws.removeListenerFromBlocks(&wsSession, &command)
|
ws.removeListenerFromBlocks(&wsSession, &command)
|
||||||
default:
|
default:
|
||||||
log.Printf(`ERROR webSocket command, invalid action: %v`, command.Action)
|
ws.logger.Error(`ERROR webSocket command, invalid action`, mlog.String("action", command.Action))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -177,7 +192,7 @@ func (ws *Server) isValidSessionToken(token, workspaceID string) bool {
|
|||||||
func (ws *Server) authenticateListener(wsSession *websocketSession, workspaceID, token string) {
|
func (ws *Server) authenticateListener(wsSession *websocketSession, workspaceID, token string) {
|
||||||
if wsSession.isAuthenticated {
|
if wsSession.isAuthenticated {
|
||||||
// Do not allow multiple auth calls (for security)
|
// Do not allow multiple auth calls (for security)
|
||||||
log.Printf("authenticateListener: Ignoring already authenticated session")
|
ws.logger.Debug("authenticateListener: Ignoring already authenticated session", mlog.String("workspaceID", workspaceID))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -192,7 +207,7 @@ func (ws *Server) authenticateListener(wsSession *websocketSession, workspaceID,
|
|||||||
|
|
||||||
wsSession.workspaceID = workspaceID
|
wsSession.workspaceID = workspaceID
|
||||||
wsSession.isAuthenticated = true
|
wsSession.isAuthenticated = true
|
||||||
log.Printf("authenticateListener: Authenticated, workspaceID: %s", workspaceID)
|
ws.logger.Debug("authenticateListener: Authenticated", mlog.String("workspaceID", workspaceID))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ws *Server) getAuthenticatedWorkspaceID(wsSession *websocketSession, command *WebsocketCommand) (string, error) {
|
func (ws *Server) getAuthenticatedWorkspaceID(wsSession *websocketSession, command *WebsocketCommand) (string, error) {
|
||||||
@ -203,7 +218,7 @@ func (ws *Server) getAuthenticatedWorkspaceID(wsSession *websocketSession, comma
|
|||||||
// If not authenticated, try to authenticate the read token against the supplied workspaceID
|
// If not authenticated, try to authenticate the read token against the supplied workspaceID
|
||||||
workspaceID := command.WorkspaceID
|
workspaceID := command.WorkspaceID
|
||||||
if len(workspaceID) == 0 {
|
if len(workspaceID) == 0 {
|
||||||
log.Printf("getAuthenticatedWorkspaceID: No workspace")
|
ws.logger.Error("getAuthenticatedWorkspaceID: No workspace")
|
||||||
return "", errors.New("No workspace")
|
return "", errors.New("No workspace")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,8 +249,8 @@ func makeItemID(workspaceID, blockID string) string {
|
|||||||
func (ws *Server) addListener(wsSession *websocketSession, command *WebsocketCommand) {
|
func (ws *Server) addListener(wsSession *websocketSession, command *WebsocketCommand) {
|
||||||
workspaceID, err := ws.getAuthenticatedWorkspaceID(wsSession, command)
|
workspaceID, err := ws.getAuthenticatedWorkspaceID(wsSession, command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("addListener: NOT AUTHENTICATED, ERROR: %v", err)
|
ws.logger.Error("addListener: NOT AUTHENTICATED", mlog.Err(err))
|
||||||
sendError(wsSession.client, "not authenticated")
|
ws.sendError(wsSession.client, "not authenticated")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,8 +287,8 @@ func (ws *Server) removeListener(client *websocket.Conn) {
|
|||||||
func (ws *Server) removeListenerFromBlocks(wsSession *websocketSession, command *WebsocketCommand) {
|
func (ws *Server) removeListenerFromBlocks(wsSession *websocketSession, command *WebsocketCommand) {
|
||||||
workspaceID, err := ws.getAuthenticatedWorkspaceID(wsSession, command)
|
workspaceID, err := ws.getAuthenticatedWorkspaceID(wsSession, command)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("addListener: NOT AUTHENTICATED, ERROR: %v", err)
|
ws.logger.Error("addListener: NOT AUTHENTICATED", mlog.Err(err))
|
||||||
sendError(wsSession.client, "not authenticated")
|
ws.sendError(wsSession.client, "not authenticated")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,14 +315,14 @@ func (ws *Server) removeListenerFromBlocks(wsSession *websocketSession, command
|
|||||||
ws.mu.Unlock()
|
ws.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendError(conn *websocket.Conn, message string) {
|
func (ws *Server) sendError(conn *websocket.Conn, message string) {
|
||||||
errorMsg := ErrorMsg{
|
errorMsg := ErrorMsg{
|
||||||
Error: message,
|
Error: message,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := conn.WriteJSON(errorMsg)
|
err := conn.WriteJSON(errorMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("sendError error: %v", err)
|
ws.logger.Error("sendError error", mlog.Err(err))
|
||||||
conn.Close()
|
conn.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -372,7 +387,10 @@ func (ws *Server) BroadcastBlockChange(workspaceID string, block model.Block) {
|
|||||||
|
|
||||||
for _, blockID := range blockIDsToNotify {
|
for _, blockID := range blockIDsToNotify {
|
||||||
listeners := ws.getListeners(workspaceID, blockID)
|
listeners := ws.getListeners(workspaceID, blockID)
|
||||||
log.Printf("%d listener(s) for blockID: %s", len(listeners), blockID)
|
ws.logger.Debug("listener(s) for blockID",
|
||||||
|
mlog.Int("listener_count", len(listeners)),
|
||||||
|
mlog.String("blockID", blockID),
|
||||||
|
)
|
||||||
|
|
||||||
message := UpdateMsg{
|
message := UpdateMsg{
|
||||||
Action: "UPDATE_BLOCK",
|
Action: "UPDATE_BLOCK",
|
||||||
@ -388,11 +406,15 @@ func (ws *Server) BroadcastBlockChange(workspaceID string, block model.Block) {
|
|||||||
|
|
||||||
if listeners != nil {
|
if listeners != nil {
|
||||||
for _, listener := range listeners {
|
for _, listener := range listeners {
|
||||||
log.Printf("Broadcast change, workspaceID: %s, blockID: %s, remoteAddr: %s", workspaceID, blockID, listener.RemoteAddr())
|
ws.logger.Debug("Broadcast change",
|
||||||
|
mlog.String("workspaceID", workspaceID),
|
||||||
|
mlog.String("blockID", blockID),
|
||||||
|
mlog.Stringer("remoteAddr", listener.RemoteAddr()),
|
||||||
|
)
|
||||||
|
|
||||||
err := listener.WriteJSON(message)
|
err := listener.WriteJSON(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("broadcast error: %v", err)
|
ws.logger.Error("broadcast error", mlog.Err(err))
|
||||||
listener.Close()
|
listener.Close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
346
webapp/package-lock.json
generated
346
webapp/package-lock.json
generated
@ -8,6 +8,7 @@
|
|||||||
"name": "focalboard",
|
"name": "focalboard",
|
||||||
"version": "0.6.7",
|
"version": "0.6.7",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"cypress": "^6.8.0",
|
||||||
"emoji-mart": "^3.0.1",
|
"emoji-mart": "^3.0.1",
|
||||||
"imagemin-gifsicle": "^7.0.0",
|
"imagemin-gifsicle": "^7.0.0",
|
||||||
"imagemin-mozjpeg": "^9.0.0",
|
"imagemin-mozjpeg": "^9.0.0",
|
||||||
@ -1312,6 +1313,7 @@
|
|||||||
"jest-resolve": "^26.6.2",
|
"jest-resolve": "^26.6.2",
|
||||||
"jest-util": "^26.6.2",
|
"jest-util": "^26.6.2",
|
||||||
"jest-worker": "^26.6.2",
|
"jest-worker": "^26.6.2",
|
||||||
|
"node-notifier": "^8.0.0",
|
||||||
"slash": "^3.0.0",
|
"slash": "^3.0.0",
|
||||||
"source-map": "^0.6.0",
|
"source-map": "^0.6.0",
|
||||||
"string-length": "^4.0.1",
|
"string-length": "^4.0.1",
|
||||||
@ -3788,6 +3790,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"anymatch": "~3.1.1",
|
"anymatch": "~3.1.1",
|
||||||
"braces": "~3.0.2",
|
"braces": "~3.0.2",
|
||||||
|
"fsevents": "~2.3.1",
|
||||||
"glob-parent": "~5.1.0",
|
"glob-parent": "~5.1.0",
|
||||||
"is-binary-path": "~2.1.0",
|
"is-binary-path": "~2.1.0",
|
||||||
"is-glob": "~4.0.1",
|
"is-glob": "~4.0.1",
|
||||||
@ -9511,7 +9514,350 @@
|
|||||||
"node": ">= 10.14.2"
|
"node": ">= 10.14.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
<<<<<<< HEAD
|
||||||
|
"node_modules/jest-each/node_modules/pretty-format": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jest/types": "^26.6.2",
|
||||||
|
"ansi-regex": "^5.0.0",
|
||||||
|
"ansi-styles": "^4.0.0",
|
||||||
|
"react-is": "^17.0.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-each/node_modules/react-is": {
|
||||||
|
"version": "17.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.1.tgz",
|
||||||
|
"integrity": "sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"node_modules/jest-environment-jsdom": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-jgPqCruTlt3Kwqg5/WVFyHIOJHsiAvhcp2qiR2QQstuG9yWox5+iHpU3ZrcBxW14T4fe5Z68jAfLRh7joCSP2Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jest/environment": "^26.6.2",
|
||||||
|
"@jest/fake-timers": "^26.6.2",
|
||||||
|
"@jest/types": "^26.6.2",
|
||||||
|
"@types/node": "*",
|
||||||
|
"jest-mock": "^26.6.2",
|
||||||
|
"jest-util": "^26.6.2",
|
||||||
|
"jsdom": "^16.4.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-environment-jsdom/node_modules/@jest/types": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||||
|
"@types/istanbul-reports": "^3.0.0",
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/yargs": "^15.0.0",
|
||||||
|
"chalk": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-environment-jsdom/node_modules/@types/istanbul-reports": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-report": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-environment-jsdom/node_modules/chalk": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-environment-jsdom/node_modules/jest-util": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jest/types": "^26.6.2",
|
||||||
|
"@types/node": "*",
|
||||||
|
"chalk": "^4.0.0",
|
||||||
|
"graceful-fs": "^4.2.4",
|
||||||
|
"is-ci": "^2.0.0",
|
||||||
|
"micromatch": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-environment-node": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-zhtMio3Exty18dy8ee8eJ9kjnRyZC1N4C1Nt/VShN1apyXc8rWGtJ9lI7vqiWcyyXS4BVSEn9lxAM2D+07/Tag==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jest/environment": "^26.6.2",
|
||||||
|
"@jest/fake-timers": "^26.6.2",
|
||||||
|
"@jest/types": "^26.6.2",
|
||||||
|
"@types/node": "*",
|
||||||
|
"jest-mock": "^26.6.2",
|
||||||
|
"jest-util": "^26.6.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-environment-node/node_modules/@jest/types": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||||
|
"@types/istanbul-reports": "^3.0.0",
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/yargs": "^15.0.0",
|
||||||
|
"chalk": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-environment-node/node_modules/@types/istanbul-reports": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-report": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-environment-node/node_modules/chalk": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-environment-node/node_modules/jest-util": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jest/types": "^26.6.2",
|
||||||
|
"@types/node": "*",
|
||||||
|
"chalk": "^4.0.0",
|
||||||
|
"graceful-fs": "^4.2.4",
|
||||||
|
"is-ci": "^2.0.0",
|
||||||
|
"micromatch": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-get-type": {
|
||||||
|
"version": "26.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz",
|
||||||
|
"integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==",
|
||||||
|
"dev": true,
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-haste-map": {
|
"node_modules/jest-haste-map": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jest/types": "^26.6.2",
|
||||||
|
"@types/graceful-fs": "^4.1.2",
|
||||||
|
"@types/node": "*",
|
||||||
|
"anymatch": "^3.0.3",
|
||||||
|
"fb-watchman": "^2.0.0",
|
||||||
|
"fsevents": "^2.1.2",
|
||||||
|
"graceful-fs": "^4.2.4",
|
||||||
|
"jest-regex-util": "^26.0.0",
|
||||||
|
"jest-serializer": "^26.6.2",
|
||||||
|
"jest-util": "^26.6.2",
|
||||||
|
"jest-worker": "^26.6.2",
|
||||||
|
"micromatch": "^4.0.2",
|
||||||
|
"sane": "^4.0.3",
|
||||||
|
"walker": "^1.0.7"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"fsevents": "^2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-haste-map/node_modules/@jest/types": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||||
|
"@types/istanbul-reports": "^3.0.0",
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/yargs": "^15.0.0",
|
||||||
|
"chalk": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-haste-map/node_modules/@types/istanbul-reports": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-report": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-haste-map/node_modules/chalk": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-haste-map/node_modules/jest-util": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@jest/types": "^26.6.2",
|
||||||
|
"@types/node": "*",
|
||||||
|
"chalk": "^4.0.0",
|
||||||
|
"graceful-fs": "^4.2.4",
|
||||||
|
"is-ci": "^2.0.0",
|
||||||
|
"micromatch": "^4.0.2"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-haste-map/node_modules/jest-worker": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/node": "*",
|
||||||
|
"merge-stream": "^2.0.0",
|
||||||
|
"supports-color": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.13.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-jasmine2": {
|
||||||
|
"version": "26.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.6.3.tgz",
|
||||||
|
"integrity": "sha512-kPKUrQtc8aYwBV7CqBg5pu+tmYXlvFlSFYn18ev4gPFtrRzB15N2gW/Roew3187q2w2eHuu0MU9TJz6w0/nPEg==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/traverse": "^7.1.0",
|
||||||
|
"@jest/environment": "^26.6.2",
|
||||||
|
"@jest/source-map": "^26.6.2",
|
||||||
|
"@jest/test-result": "^26.6.2",
|
||||||
|
"@jest/types": "^26.6.2",
|
||||||
|
"@types/node": "*",
|
||||||
|
"chalk": "^4.0.0",
|
||||||
|
"co": "^4.6.0",
|
||||||
|
"expect": "^26.6.2",
|
||||||
|
"is-generator-fn": "^2.0.0",
|
||||||
|
"jest-each": "^26.6.2",
|
||||||
|
"jest-matcher-utils": "^26.6.2",
|
||||||
|
"jest-message-util": "^26.6.2",
|
||||||
|
"jest-runtime": "^26.6.3",
|
||||||
|
"jest-snapshot": "^26.6.2",
|
||||||
|
"jest-util": "^26.6.2",
|
||||||
|
"pretty-format": "^26.6.2",
|
||||||
|
"throat": "^5.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-jasmine2/node_modules/@jest/types": {
|
||||||
|
"version": "26.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz",
|
||||||
|
"integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-coverage": "^2.0.0",
|
||||||
|
"@types/istanbul-reports": "^3.0.0",
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/yargs": "^15.0.0",
|
||||||
|
"chalk": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.14.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-jasmine2/node_modules/@types/istanbul-reports": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@types/istanbul-lib-report": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-jasmine2/node_modules/chalk": {
|
||||||
|
"version": "4.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||||
|
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||||
|
"dev": true,
|
||||||
|
"dependencies": {
|
||||||
|
"ansi-styles": "^4.1.0",
|
||||||
|
"supports-color": "^7.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/jest-jasmine2/node_modules/jest-util": {
|
||||||
|
=======
|
||||||
|
"node_modules/jest-haste-map": {
|
||||||
|
>>>>>>> upstream/main
|
||||||
"version": "26.6.2",
|
"version": "26.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz",
|
||||||
"integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==",
|
"integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==",
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
"cypress:open": "cypress open"
|
"cypress:open": "cypress open"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"cypress": "^6.8.0",
|
||||||
"emoji-mart": "^3.0.1",
|
"emoji-mart": "^3.0.1",
|
||||||
"imagemin-gifsicle": "^7.0.0",
|
"imagemin-gifsicle": "^7.0.0",
|
||||||
"imagemin-mozjpeg": "^9.0.0",
|
"imagemin-mozjpeg": "^9.0.0",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user