You've already forked focalboard
mirror of
https://github.com/mattermost/focalboard.git
synced 2025-07-15 23:54:29 +02:00
First pass linter cleanup (#603)
* first pass linter cleanup * address review comments
This commit is contained in:
@ -12,10 +12,14 @@ linters-settings:
|
|||||||
enable-all: true
|
enable-all: true
|
||||||
disable:
|
disable:
|
||||||
- fieldalignment
|
- fieldalignment
|
||||||
|
lll:
|
||||||
|
line-length: 150
|
||||||
|
gomnd:
|
||||||
|
ignored-numbers: 10
|
||||||
|
|
||||||
|
|
||||||
linters:
|
linters:
|
||||||
disable-all: true
|
disable-all: true
|
||||||
|
|
||||||
enable:
|
enable:
|
||||||
- gofmt
|
- gofmt
|
||||||
- goimports
|
- goimports
|
||||||
@ -43,10 +47,10 @@ linters:
|
|||||||
- goconst
|
- goconst
|
||||||
- gocritic
|
- gocritic
|
||||||
- godot
|
- godot
|
||||||
- goerr113
|
# - goerr113
|
||||||
- goheader
|
- goheader
|
||||||
- golint
|
- golint
|
||||||
- gomnd
|
# - gomnd
|
||||||
- gomodguard
|
- gomodguard
|
||||||
- goprintffuncname
|
- goprintffuncname
|
||||||
- gosimple
|
- gosimple
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -22,13 +21,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HEADER_REQUESTED_WITH = "X-Requested-With"
|
HeaderRequestedWith = "X-Requested-With"
|
||||||
HEADER_REQUESTED_WITH_XML = "XMLHttpRequest"
|
HeaderRequestedWithXML = "XMLHttpRequest"
|
||||||
|
SingleUser = "single-user"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ERROR_NO_WORKSPACE_CODE = 1000
|
ErrorNoWorkspaceCode = 1000
|
||||||
ERROR_NO_WORKSPACE_MESSAGE = "No workspace"
|
ErrorNoWorkspaceMessage = "No workspace"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------------------------------
|
||||||
@ -105,13 +105,8 @@ func (a *API) requireCSRFToken(next http.Handler) http.Handler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) checkCSRFToken(r *http.Request) bool {
|
func (a *API) checkCSRFToken(r *http.Request) bool {
|
||||||
token := r.Header.Get(HEADER_REQUESTED_WITH)
|
token := r.Header.Get(HeaderRequestedWith)
|
||||||
|
return token == HeaderRequestedWithXML
|
||||||
if token == HEADER_REQUESTED_WITH_XML {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) hasValidReadTokenForBlock(r *http.Request, container store.Container, blockID string) bool {
|
func (a *API) hasValidReadTokenForBlock(r *http.Request, container store.Container, blockID string) bool {
|
||||||
@ -158,7 +153,7 @@ func (a *API) getContainerAllowingReadTokenForBlock(r *http.Request, blockID str
|
|||||||
return &container, nil
|
return &container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("Access denied to workspace")
|
return nil, errors.New("access denied to workspace")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Native auth: always use root workspace
|
// Native auth: always use root workspace
|
||||||
@ -176,7 +171,7 @@ func (a *API) getContainerAllowingReadTokenForBlock(r *http.Request, blockID str
|
|||||||
return &container, nil
|
return &container, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("Access denied to workspace")
|
return nil, errors.New("access denied to workspace")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) getContainer(r *http.Request) (*store.Container, error) {
|
func (a *API) getContainer(r *http.Request) (*store.Container, error) {
|
||||||
@ -263,7 +258,7 @@ func stampModifiedByUser(r *http.Request, blocks []model.Block, auditRec *audit.
|
|||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
session := ctx.Value("session").(*model.Session)
|
session := ctx.Value("session").(*model.Session)
|
||||||
userID := session.UserID
|
userID := session.UserID
|
||||||
if userID == "single-user" {
|
if userID == SingleUser {
|
||||||
userID = ""
|
userID = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -444,12 +439,12 @@ func (a *API) handleGetMe(w http.ResponseWriter, r *http.Request) {
|
|||||||
auditRec := a.makeAuditRecord(r, "getMe", audit.Fail)
|
auditRec := a.makeAuditRecord(r, "getMe", audit.Fail)
|
||||||
defer a.audit.LogRecord(audit.LevelRead, auditRec)
|
defer a.audit.LogRecord(audit.LevelRead, auditRec)
|
||||||
|
|
||||||
if session.UserID == "single-user" {
|
if session.UserID == SingleUser {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
user = &model.User{
|
user = &model.User{
|
||||||
ID: "single-user",
|
ID: SingleUser,
|
||||||
Username: "single-user",
|
Username: SingleUser,
|
||||||
Email: "single-user",
|
Email: SingleUser,
|
||||||
CreateAt: now,
|
CreateAt: now,
|
||||||
UpdateAt: now,
|
UpdateAt: now,
|
||||||
}
|
}
|
||||||
@ -659,12 +654,16 @@ func (a *API) handleExport(w http.ResponseWriter, r *http.Request) {
|
|||||||
defer a.audit.LogRecord(audit.LevelRead, auditRec)
|
defer a.audit.LogRecord(audit.LevelRead, auditRec)
|
||||||
auditRec.AddMeta("rootID", rootID)
|
auditRec.AddMeta("rootID", rootID)
|
||||||
|
|
||||||
blocks := []model.Block{}
|
var blocks []model.Block
|
||||||
if rootID == "" {
|
if rootID == "" {
|
||||||
blocks, err = a.app.GetAllBlocks(*container)
|
blocks, err = a.app.GetAllBlocks(*container)
|
||||||
} else {
|
} else {
|
||||||
blocks, err = a.app.GetBlocksWithRootID(*container, rootID)
|
blocks, err = a.app.GetBlocksWithRootID(*container, rootID)
|
||||||
}
|
}
|
||||||
|
if err != nil {
|
||||||
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
a.logger.Debug("raw blocks", mlog.Int("block_count", len(blocks)))
|
a.logger.Debug("raw blocks", mlog.Int("block_count", len(blocks)))
|
||||||
auditRec.AddMeta("rawCount", len(blocks))
|
auditRec.AddMeta("rawCount", len(blocks))
|
||||||
@ -720,15 +719,6 @@ func filterOrphanBlocks(blocks []model.Block) (ret []model.Block) {
|
|||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
func arrayContainsBlockWithID(array []model.Block, blockID string) bool {
|
|
||||||
for _, item := range array {
|
|
||||||
if item.ID == blockID {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
|
func (a *API) handleImport(w http.ResponseWriter, r *http.Request) {
|
||||||
// swagger:operation POST /api/v1/workspaces/{workspaceID}/blocks/import importBlocks
|
// swagger:operation POST /api/v1/workspaces/{workspaceID}/blocks/import importBlocks
|
||||||
//
|
//
|
||||||
@ -936,7 +926,7 @@ func (a *API) handlePostSharing(w http.ResponseWriter, r *http.Request) {
|
|||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
session := ctx.Value("session").(*model.Session)
|
session := ctx.Value("session").(*model.Session)
|
||||||
userID := session.UserID
|
userID := session.UserID
|
||||||
if userID == "single-user" {
|
if userID == SingleUser {
|
||||||
userID = ""
|
userID = ""
|
||||||
}
|
}
|
||||||
sharing.ModifiedBy = userID
|
sharing.ModifiedBy = userID
|
||||||
@ -1213,7 +1203,7 @@ func (a *API) handleUploadFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
auditRec.AddMeta("rootID", rootID)
|
auditRec.AddMeta("rootID", rootID)
|
||||||
auditRec.AddMeta("filename", handle.Filename)
|
auditRec.AddMeta("filename", handle.Filename)
|
||||||
|
|
||||||
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 {
|
||||||
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
@ -1221,9 +1211,9 @@ func (a *API) handleUploadFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
a.logger.Debug("uploadFile",
|
a.logger.Debug("uploadFile",
|
||||||
mlog.String("filename", handle.Filename),
|
mlog.String("filename", handle.Filename),
|
||||||
mlog.String("fileID", fileId),
|
mlog.String("fileID", fileID),
|
||||||
)
|
)
|
||||||
data, err := json.Marshal(FileUploadResponse{FileID: fileId})
|
data, err := json.Marshal(FileUploadResponse{FileID: fileID})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
@ -1231,7 +1221,7 @@ func (a *API) handleUploadFile(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
jsonBytesResponse(w, http.StatusOK, data)
|
jsonBytesResponse(w, http.StatusOK, data)
|
||||||
|
|
||||||
auditRec.AddMeta("fileID", fileId)
|
auditRec.AddMeta("fileID", fileID)
|
||||||
auditRec.Success()
|
auditRec.Success()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1269,7 +1259,7 @@ func (a *API) getWorkspaceUsers(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) {
|
||||||
a.errorResponse(w, http.StatusForbidden, "Access denied to workspace", errors.New("Access denied to workspace"))
|
a.errorResponse(w, http.StatusForbidden, "Access denied to workspace", errors.New("access denied to workspace"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1307,7 +1297,7 @@ func (a *API) errorResponse(w http.ResponseWriter, code int, message string, sou
|
|||||||
data = []byte("{}")
|
data = []byte("{}")
|
||||||
}
|
}
|
||||||
w.WriteHeader(code)
|
w.WriteHeader(code)
|
||||||
w.Write(data)
|
_, _ = w.Write(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) errorResponseWithCode(w http.ResponseWriter, statusCode int, errorCode int, message string, sourceError error) {
|
func (a *API) errorResponseWithCode(w http.ResponseWriter, statusCode int, errorCode int, message string, sourceError error) {
|
||||||
@ -1322,27 +1312,21 @@ func (a *API) errorResponseWithCode(w http.ResponseWriter, statusCode int, error
|
|||||||
data = []byte("{}")
|
data = []byte("{}")
|
||||||
}
|
}
|
||||||
w.WriteHeader(statusCode)
|
w.WriteHeader(statusCode)
|
||||||
w.Write(data)
|
_, _ = w.Write(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *API) noContainerErrorResponse(w http.ResponseWriter, sourceError error) {
|
func (a *API) noContainerErrorResponse(w http.ResponseWriter, sourceError error) {
|
||||||
a.errorResponseWithCode(w, http.StatusBadRequest, ERROR_NO_WORKSPACE_CODE, ERROR_NO_WORKSPACE_MESSAGE, sourceError)
|
a.errorResponseWithCode(w, http.StatusBadRequest, ErrorNoWorkspaceCode, ErrorNoWorkspaceMessage, sourceError)
|
||||||
}
|
}
|
||||||
|
|
||||||
func jsonStringResponse(w http.ResponseWriter, code int, message string) {
|
func jsonStringResponse(w http.ResponseWriter, code int, message string) { //nolint:unparam
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(code)
|
w.WriteHeader(code)
|
||||||
fmt.Fprint(w, message)
|
fmt.Fprint(w, message)
|
||||||
}
|
}
|
||||||
|
|
||||||
func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) {
|
func jsonBytesResponse(w http.ResponseWriter, code int, json []byte) { //nolint:unparam
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(code)
|
w.WriteHeader(code)
|
||||||
w.Write(json)
|
_, _ = w.Write(json)
|
||||||
}
|
|
||||||
|
|
||||||
func addUserID(rw http.ResponseWriter, req *http.Request, next http.Handler) {
|
|
||||||
ctx := context.WithValue(req.Context(), "userid", req.Header.Get("userid"))
|
|
||||||
req = req.WithContext(ctx)
|
|
||||||
next.ServeHTTP(rw, req)
|
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// makeAuditRecord creates an audit record pre-populated with data from the request.
|
// makeAuditRecord creates an audit record pre-populated with data from the request.
|
||||||
func (a *API) makeAuditRecord(r *http.Request, event string, initialStatus string) *audit.Record {
|
func (a *API) makeAuditRecord(r *http.Request, event string, initialStatus string) *audit.Record { //nolint:unparam
|
||||||
ctx := r.Context()
|
ctx := r.Context()
|
||||||
var sessionID string
|
var sessionID string
|
||||||
var userID string
|
var userID string
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -18,6 +19,10 @@ import (
|
|||||||
"github.com/mattermost/focalboard/server/services/mlog"
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
MinimumPasswordLength = 8
|
||||||
|
)
|
||||||
|
|
||||||
// LoginRequest is a login request
|
// LoginRequest is a login request
|
||||||
// swagger:model
|
// swagger:model
|
||||||
type LoginRequest struct {
|
type LoginRequest struct {
|
||||||
@ -102,13 +107,13 @@ type ChangePasswordRequest struct {
|
|||||||
NewPassword string `json:"newPassword"`
|
NewPassword string `json:"newPassword"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValid validates a password change request
|
// IsValid validates a password change request.
|
||||||
func (rd *ChangePasswordRequest) IsValid() error {
|
func (rd *ChangePasswordRequest) IsValid() error {
|
||||||
if rd.OldPassword == "" {
|
if rd.OldPassword == "" {
|
||||||
return errors.New("Old password is required")
|
return errors.New("old password is required")
|
||||||
}
|
}
|
||||||
if rd.NewPassword == "" {
|
if rd.NewPassword == "" {
|
||||||
return errors.New("New password is required")
|
return errors.New("new password is required")
|
||||||
}
|
}
|
||||||
if err := isValidPassword(rd.NewPassword); err != nil {
|
if err := isValidPassword(rd.NewPassword); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -118,8 +123,8 @@ func (rd *ChangePasswordRequest) IsValid() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isValidPassword(password string) error {
|
func isValidPassword(password string) error {
|
||||||
if len(password) < 8 {
|
if len(password) < MinimumPasswordLength {
|
||||||
return errors.New("Password must be at least 8 characters")
|
return fmt.Errorf("password must be at least %d characters", MinimumPasswordLength)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -243,9 +248,9 @@ func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
// Validate token
|
// Validate token
|
||||||
if len(registerData.Token) > 0 {
|
if len(registerData.Token) > 0 {
|
||||||
workspace, err := a.app.GetRootWorkspace()
|
workspace, err2 := a.app.GetRootWorkspace()
|
||||||
if err != nil {
|
if err2 != nil {
|
||||||
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -255,9 +260,9 @@ func (a *API) handleRegister(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No signup token, check if no active users
|
// No signup token, check if no active users
|
||||||
userCount, err := a.app.GetRegisteredUserCount()
|
userCount, err2 := a.app.GetRegisteredUserCount()
|
||||||
if err != nil {
|
if err2 != nil {
|
||||||
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err2)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if userCount > 0 {
|
if userCount > 0 {
|
||||||
@ -335,7 +340,7 @@ func (a *API) handleChangePassword(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var requestData ChangePasswordRequest
|
var requestData ChangePasswordRequest
|
||||||
if err := json.Unmarshal(requestBody, &requestData); err != nil {
|
if err = json.Unmarshal(requestBody, &requestData); err != nil {
|
||||||
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
a.errorResponse(w, http.StatusInternalServerError, "", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -374,9 +379,9 @@ func (a *API) attachSession(handler func(w http.ResponseWriter, r *http.Request)
|
|||||||
|
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
session := &model.Session{
|
session := &model.Session{
|
||||||
ID: "single-user",
|
ID: SingleUser,
|
||||||
Token: token,
|
Token: token,
|
||||||
UserID: "single-user",
|
UserID: SingleUser,
|
||||||
AuthService: a.authService,
|
AuthService: a.authService,
|
||||||
Props: map[string]interface{}{},
|
Props: map[string]interface{}{},
|
||||||
CreateAt: now,
|
CreateAt: now,
|
||||||
@ -441,6 +446,5 @@ func (a *API) adminRequired(handler func(w http.ResponseWriter, r *http.Request)
|
|||||||
}
|
}
|
||||||
|
|
||||||
handler(w, r)
|
handler(w, r)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,53 +10,61 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetSession Get a user active session and refresh the session if is needed
|
const (
|
||||||
|
DaysPerMonth = 30
|
||||||
|
DaysPerWeek = 7
|
||||||
|
HoursPerDay = 24
|
||||||
|
MinutesPerHour = 60
|
||||||
|
SecondsPerMinute = 60
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetSession Get a user active session and refresh the session if is needed.
|
||||||
func (a *App) GetSession(token string) (*model.Session, error) {
|
func (a *App) GetSession(token string) (*model.Session, error) {
|
||||||
return a.auth.GetSession(token)
|
return a.auth.GetSession(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValidReadToken validates the read token for a block
|
// IsValidReadToken validates the read token for a block.
|
||||||
func (a *App) IsValidReadToken(c store.Container, blockID string, readToken string) (bool, error) {
|
func (a *App) IsValidReadToken(c store.Container, blockID string, readToken string) (bool, error) {
|
||||||
return a.auth.IsValidReadToken(c, blockID, readToken)
|
return a.auth.IsValidReadToken(c, blockID, readToken)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetRegisteredUserCount returns the number of registered users
|
// GetRegisteredUserCount returns the number of registered users.
|
||||||
func (a *App) GetRegisteredUserCount() (int, error) {
|
func (a *App) GetRegisteredUserCount() (int, error) {
|
||||||
return a.store.GetRegisteredUserCount()
|
return a.store.GetRegisteredUserCount()
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDailyActiveUsers returns the number of daily active users
|
// GetDailyActiveUsers returns the number of daily active users.
|
||||||
func (a *App) GetDailyActiveUsers() (int, error) {
|
func (a *App) GetDailyActiveUsers() (int, error) {
|
||||||
secondsAgo := int64(60 * 60 * 24)
|
secondsAgo := int64(SecondsPerMinute * MinutesPerHour * HoursPerDay)
|
||||||
return a.store.GetActiveUserCount(secondsAgo)
|
return a.store.GetActiveUserCount(secondsAgo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWeeklyActiveUsers returns the number of weekly active users
|
// GetWeeklyActiveUsers returns the number of weekly active users.
|
||||||
func (a *App) GetWeeklyActiveUsers() (int, error) {
|
func (a *App) GetWeeklyActiveUsers() (int, error) {
|
||||||
secondsAgo := int64(60 * 60 * 24 * 7)
|
secondsAgo := int64(SecondsPerMinute * MinutesPerHour * HoursPerDay * DaysPerWeek)
|
||||||
return a.store.GetActiveUserCount(secondsAgo)
|
return a.store.GetActiveUserCount(secondsAgo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetMonthlyActiveUsers returns the number of monthly active users
|
// GetMonthlyActiveUsers returns the number of monthly active users.
|
||||||
func (a *App) GetMonthlyActiveUsers() (int, error) {
|
func (a *App) GetMonthlyActiveUsers() (int, error) {
|
||||||
secondsAgo := int64(60 * 60 * 24 * 30)
|
secondsAgo := int64(SecondsPerMinute * MinutesPerHour * HoursPerDay * DaysPerMonth)
|
||||||
return a.store.GetActiveUserCount(secondsAgo)
|
return a.store.GetActiveUserCount(secondsAgo)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUser Get an existing active user by id
|
// GetUser gets an existing active user by id.
|
||||||
func (a *App) GetUser(ID string) (*model.User, error) {
|
func (a *App) GetUser(id string) (*model.User, error) {
|
||||||
if len(ID) < 1 {
|
if len(id) < 1 {
|
||||||
return nil, errors.New("no user ID")
|
return nil, errors.New("no user ID")
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err := a.store.GetUserById(ID)
|
user, err := a.store.GetUserByID(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "unable to find user")
|
return nil, errors.Wrap(err, "unable to find user")
|
||||||
}
|
}
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login create a new user session if the authentication data is valid
|
// Login create a new user session if the authentication data is valid.
|
||||||
func (a *App) Login(username, email, password, mfaToken string) (string, error) {
|
func (a *App) Login(username, email, password, mfaToken string) (string, error) {
|
||||||
var user *model.User
|
var user *model.User
|
||||||
if username != "" {
|
if username != "" {
|
||||||
@ -110,7 +118,7 @@ func (a *App) Login(username, email, password, mfaToken string) (string, error)
|
|||||||
return session.Token, nil
|
return session.Token, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterUser create a new user if the provided data is valid
|
// RegisterUser creates a new user if the provided data is valid.
|
||||||
func (a *App) RegisterUser(username, email, password string) error {
|
func (a *App) RegisterUser(username, email, password string) error {
|
||||||
var user *model.User
|
var user *model.User
|
||||||
if username != "" {
|
if username != "" {
|
||||||
@ -169,7 +177,7 @@ func (a *App) ChangePassword(userID, oldPassword, newPassword string) error {
|
|||||||
var user *model.User
|
var user *model.User
|
||||||
if userID != "" {
|
if userID != "" {
|
||||||
var err error
|
var err error
|
||||||
user, err = a.store.GetUserById(userID)
|
user, err = a.store.GetUserByID(userID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "invalid username or password")
|
return errors.Wrap(err, "invalid username or password")
|
||||||
}
|
}
|
||||||
|
@ -70,8 +70,8 @@ func TestGetUser(t *testing.T) {
|
|||||||
{"success", "goodID", false},
|
{"success", "goodID", false},
|
||||||
}
|
}
|
||||||
|
|
||||||
th.Store.EXPECT().GetUserById("badID").Return(nil, errors.New("Bad Id"))
|
th.Store.EXPECT().GetUserByID("badID").Return(nil, errors.New("Bad Id"))
|
||||||
th.Store.EXPECT().GetUserById("goodID").Return(mockUser, nil)
|
th.Store.EXPECT().GetUserByID("goodID").Return(mockUser, nil)
|
||||||
|
|
||||||
for _, test := range testcases {
|
for _, test := range testcases {
|
||||||
t.Run(test.title, func(t *testing.T) {
|
t.Run(test.title, func(t *testing.T) {
|
||||||
@ -168,8 +168,8 @@ func TestChangePassword(t *testing.T) {
|
|||||||
{"success, using username", mockUser.ID, "testPassword", "newPassword", false},
|
{"success, using username", mockUser.ID, "testPassword", "newPassword", false},
|
||||||
}
|
}
|
||||||
|
|
||||||
th.Store.EXPECT().GetUserById("badID").Return(nil, errors.New("userID not found"))
|
th.Store.EXPECT().GetUserByID("badID").Return(nil, errors.New("userID not found"))
|
||||||
th.Store.EXPECT().GetUserById(mockUser.ID).Return(mockUser, nil).Times(2)
|
th.Store.EXPECT().GetUserByID(mockUser.ID).Return(mockUser, nil).Times(2)
|
||||||
th.Store.EXPECT().UpdateUserPasswordByID(mockUser.ID, gomock.Any()).Return(nil)
|
th.Store.EXPECT().UpdateUserPasswordByID(mockUser.ID, gomock.Any()).Return(nil)
|
||||||
|
|
||||||
for _, test := range testcases {
|
for _, test := range testcases {
|
||||||
|
@ -38,20 +38,7 @@ func (a *App) InsertBlock(c store.Container, block model.Block) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) InsertBlocks(c store.Container, blocks []model.Block) error {
|
func (a *App) InsertBlocks(c store.Container, blocks []model.Block) error {
|
||||||
blockIDsToNotify := []string{}
|
|
||||||
|
|
||||||
uniqueBlockIDs := make(map[string]bool)
|
|
||||||
|
|
||||||
for _, block := range blocks {
|
for _, block := range blocks {
|
||||||
if !uniqueBlockIDs[block.ID] {
|
|
||||||
blockIDsToNotify = append(blockIDsToNotify, block.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ParentID as empty string denotes a block at the root
|
|
||||||
if !uniqueBlockIDs[block.ParentID] {
|
|
||||||
blockIDsToNotify = append(blockIDsToNotify, block.ParentID)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := a.store.InsertBlock(c, block)
|
err := a.store.InsertBlock(c, block)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -78,16 +65,11 @@ func (a *App) GetAllBlocks(c store.Container) ([]model.Block, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) DeleteBlock(c store.Container, blockID string, modifiedBy string) error {
|
func (a *App) DeleteBlock(c store.Container, blockID string, modifiedBy string) error {
|
||||||
blockIDsToNotify := []string{blockID}
|
|
||||||
parentID, err := a.GetParentID(c, blockID)
|
parentID, err := a.GetParentID(c, blockID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(parentID) > 0 {
|
|
||||||
blockIDsToNotify = append(blockIDsToNotify, parentID)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = a.store.DeleteBlock(c, blockID, modifiedBy)
|
err = a.store.DeleteBlock(c, blockID, modifiedBy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -4,7 +4,6 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@ -40,16 +39,23 @@ func (a *App) GetFileReader(workspaceID, rootID, filename string) (filestore.Rea
|
|||||||
}
|
}
|
||||||
// FIXUP: Check the deprecated old location
|
// FIXUP: Check the deprecated old location
|
||||||
if workspaceID == "0" && !exists {
|
if workspaceID == "0" && !exists {
|
||||||
oldExists, err := a.filesBackend.FileExists(filename)
|
oldExists, err2 := a.filesBackend.FileExists(filename)
|
||||||
if err != nil {
|
if err2 != nil {
|
||||||
return nil, err
|
return nil, err2
|
||||||
}
|
}
|
||||||
if oldExists {
|
if oldExists {
|
||||||
err := a.filesBackend.MoveFile(filename, filePath)
|
err2 := a.filesBackend.MoveFile(filename, filePath)
|
||||||
if err != nil {
|
if err2 != nil {
|
||||||
a.logger.Error("ERROR moving file", mlog.String("old", filename), mlog.String("new", filePath))
|
a.logger.Error("ERROR moving file",
|
||||||
|
mlog.String("old", filename),
|
||||||
|
mlog.String("new", filePath),
|
||||||
|
mlog.Err(err2),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
a.logger.Debug("Moved file", mlog.String("old", filename), mlog.String("new", filePath))
|
a.logger.Debug("Moved file",
|
||||||
|
mlog.String("old", filename),
|
||||||
|
mlog.String("new", filePath),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -61,8 +67,3 @@ func (a *App) GetFileReader(workspaceID, rootID, filename string) (filestore.Rea
|
|||||||
|
|
||||||
return reader, nil
|
return reader, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func fileExists(path string) bool {
|
|
||||||
_, err := os.Stat(path)
|
|
||||||
return !os.IsNotExist(err)
|
|
||||||
}
|
|
||||||
|
@ -24,7 +24,6 @@ type TestHelper struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SetupTestHelper(t *testing.T) *TestHelper {
|
func SetupTestHelper(t *testing.T) *TestHelper {
|
||||||
|
|
||||||
ctrl := gomock.NewController(t)
|
ctrl := gomock.NewController(t)
|
||||||
defer ctrl.Finish()
|
defer ctrl.Finish()
|
||||||
cfg := config.Configuration{}
|
cfg := config.Configuration{}
|
||||||
|
@ -2,6 +2,7 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
@ -9,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func (a *App) GetSharing(c store.Container, rootID string) (*model.Sharing, error) {
|
func (a *App) GetSharing(c store.Container, rootID string) (*model.Sharing, error) {
|
||||||
sharing, err := a.store.GetSharing(c, rootID)
|
sharing, err := a.store.GetSharing(c, rootID)
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -19,7 +19,7 @@ func TestGetSharing(t *testing.T) {
|
|||||||
WorkspaceID: utils.CreateGUID(),
|
WorkspaceID: utils.CreateGUID(),
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("should get a sharing sucessfully", func(t *testing.T) {
|
t.Run("should get a sharing successfully", func(t *testing.T) {
|
||||||
want := &model.Sharing{
|
want := &model.Sharing{
|
||||||
ID: utils.CreateGUID(),
|
ID: utils.CreateGUID(),
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
@ -2,6 +2,7 @@ package app
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
|
||||||
"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/mlog"
|
||||||
@ -33,9 +34,9 @@ func (a *App) GetRootWorkspace() (*model.Workspace, error) {
|
|||||||
return workspace, nil
|
return workspace, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) GetWorkspace(ID string) (*model.Workspace, error) {
|
func (a *App) GetWorkspace(id string) (*model.Workspace, error) {
|
||||||
workspace, err := a.store.GetWorkspace(ID)
|
workspace, err := a.store.GetWorkspace(id)
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -10,18 +10,18 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Auth authenticates sessions
|
// Auth authenticates sessions.
|
||||||
type Auth struct {
|
type Auth struct {
|
||||||
config *config.Configuration
|
config *config.Configuration
|
||||||
store store.Store
|
store store.Store
|
||||||
}
|
}
|
||||||
|
|
||||||
// New returns a new Auth
|
// New returns a new Auth.
|
||||||
func New(config *config.Configuration, store store.Store) *Auth {
|
func New(config *config.Configuration, store store.Store) *Auth {
|
||||||
return &Auth{config: config, store: store}
|
return &Auth{config: config, store: store}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetSession Get a user active session and refresh the session if is needed
|
// GetSession Get a user active session and refresh the session if needed.
|
||||||
func (a *Auth) GetSession(token string) (*model.Session, error) {
|
func (a *Auth) GetSession(token string) (*model.Session, error) {
|
||||||
if len(token) < 1 {
|
if len(token) < 1 {
|
||||||
return nil, errors.New("no session token")
|
return nil, errors.New("no session token")
|
||||||
@ -32,12 +32,12 @@ func (a *Auth) GetSession(token string) (*model.Session, error) {
|
|||||||
return nil, errors.Wrap(err, "unable to get the session for the token")
|
return nil, errors.Wrap(err, "unable to get the session for the token")
|
||||||
}
|
}
|
||||||
if session.UpdateAt < (time.Now().Unix() - a.config.SessionRefreshTime) {
|
if session.UpdateAt < (time.Now().Unix() - a.config.SessionRefreshTime) {
|
||||||
a.store.RefreshSession(session)
|
_ = a.store.RefreshSession(session)
|
||||||
}
|
}
|
||||||
return session, nil
|
return session, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsValidReadToken validates the read token for a block
|
// IsValidReadToken validates the read token for a block.
|
||||||
func (a *Auth) IsValidReadToken(c store.Container, blockID string, readToken string) (bool, error) {
|
func (a *Auth) IsValidReadToken(c store.Container, blockID string, readToken string) (bool, error) {
|
||||||
rootID, err := a.store.GetRootID(c, blockID)
|
rootID, err := a.store.GetRootID(c, blockID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -45,7 +45,7 @@ func (a *Auth) IsValidReadToken(c store.Container, blockID string, readToken str
|
|||||||
}
|
}
|
||||||
|
|
||||||
sharing, err := a.store.GetSharing(c, rootID)
|
sharing, err := a.store.GetSharing(c, rootID)
|
||||||
if err == sql.ErrNoRows {
|
if errors.Is(err, sql.ErrNoRows) {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -41,11 +41,9 @@ func setupTestHelper(t *testing.T) *TestHelper {
|
|||||||
Session: *mockSession,
|
Session: *mockSession,
|
||||||
Store: mockStore,
|
Store: mockStore,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestGetSession(t *testing.T) {
|
func TestGetSession(t *testing.T) {
|
||||||
|
|
||||||
th := setupTestHelper(t)
|
th := setupTestHelper(t)
|
||||||
|
|
||||||
testcases := []struct {
|
testcases := []struct {
|
||||||
|
@ -13,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
API_URL_SUFFIX = "/api/v1"
|
APIURLSuffix = "/api/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Response struct {
|
type Response struct {
|
||||||
@ -57,10 +57,10 @@ func toJSON(v interface{}) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
Url string
|
URL string
|
||||||
ApiUrl string
|
APIURL string
|
||||||
HttpClient *http.Client
|
HTTPClient *http.Client
|
||||||
HttpHeader map[string]string
|
HTTPHeader map[string]string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewClient(url, sessionToken string) *Client {
|
func NewClient(url, sessionToken string) *Client {
|
||||||
@ -69,63 +69,63 @@ func NewClient(url, sessionToken string) *Client {
|
|||||||
"X-Requested-With": "XMLHttpRequest",
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
"Authorization": "Bearer " + sessionToken,
|
"Authorization": "Bearer " + sessionToken,
|
||||||
}
|
}
|
||||||
return &Client{url, url + API_URL_SUFFIX, &http.Client{}, headers}
|
return &Client{url, url + APIURLSuffix, &http.Client{}, headers}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DoApiGet(url, etag string) (*http.Response, error) {
|
func (c *Client) DoAPIGet(url, etag string) (*http.Response, error) {
|
||||||
return c.DoApiRequest(http.MethodGet, c.ApiUrl+url, "", etag)
|
return c.DoAPIRequest(http.MethodGet, c.APIURL+url, "", etag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DoApiPost(url, data string) (*http.Response, error) {
|
func (c *Client) DoAPIPost(url, data string) (*http.Response, error) {
|
||||||
return c.DoApiRequest(http.MethodPost, c.ApiUrl+url, data, "")
|
return c.DoAPIRequest(http.MethodPost, c.APIURL+url, data, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) doApiPostBytes(url string, data []byte) (*http.Response, error) {
|
func (c *Client) doAPIPostBytes(url string, data []byte) (*http.Response, error) {
|
||||||
return c.doApiRequestBytes(http.MethodPost, c.ApiUrl+url, data, "")
|
return c.doAPIRequestBytes(http.MethodPost, c.APIURL+url, data, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DoApiPut(url, data string) (*http.Response, error) {
|
func (c *Client) DoAPIPut(url, data string) (*http.Response, error) {
|
||||||
return c.DoApiRequest(http.MethodPut, c.ApiUrl+url, data, "")
|
return c.DoAPIRequest(http.MethodPut, c.APIURL+url, data, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) doApiPutBytes(url string, data []byte) (*http.Response, error) {
|
func (c *Client) doAPIPutBytes(url string, data []byte) (*http.Response, error) {
|
||||||
return c.doApiRequestBytes(http.MethodPut, c.ApiUrl+url, data, "")
|
return c.doAPIRequestBytes(http.MethodPut, c.APIURL+url, data, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DoApiDelete(url string) (*http.Response, error) {
|
func (c *Client) DoAPIDelete(url string) (*http.Response, error) {
|
||||||
return c.DoApiRequest(http.MethodDelete, c.ApiUrl+url, "", "")
|
return c.DoAPIRequest(http.MethodDelete, c.APIURL+url, "", "")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DoApiRequest(method, url, data, etag string) (*http.Response, error) {
|
func (c *Client) DoAPIRequest(method, url, data, etag string) (*http.Response, error) {
|
||||||
return c.doApiRequestReader(method, url, strings.NewReader(data), etag)
|
return c.doAPIRequestReader(method, url, strings.NewReader(data), etag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) doApiRequestBytes(method, url string, data []byte, etag string) (*http.Response, error) {
|
func (c *Client) doAPIRequestBytes(method, url string, data []byte, etag string) (*http.Response, error) {
|
||||||
return c.doApiRequestReader(method, url, bytes.NewReader(data), etag)
|
return c.doAPIRequestReader(method, url, bytes.NewReader(data), etag)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) doApiRequestReader(method, url string, data io.Reader, etag string) (*http.Response, error) {
|
func (c *Client) doAPIRequestReader(method, url string, data io.Reader, _ /* etag */ string) (*http.Response, error) {
|
||||||
rq, err := http.NewRequest(method, url, data)
|
rq, err := http.NewRequest(method, url, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.HttpHeader != nil && len(c.HttpHeader) > 0 {
|
if c.HTTPHeader != nil && len(c.HTTPHeader) > 0 {
|
||||||
for k, v := range c.HttpHeader {
|
for k, v := range c.HTTPHeader {
|
||||||
rq.Header.Set(k, v)
|
rq.Header.Set(k, v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rp, err := c.HttpClient.Do(rq)
|
rp, err := c.HTTPClient.Do(rq)
|
||||||
if err != nil || rp == nil {
|
if err != nil || rp == nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rp.StatusCode == 304 {
|
if rp.StatusCode == http.StatusNotModified {
|
||||||
return rp, nil
|
return rp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if rp.StatusCode >= 300 {
|
if rp.StatusCode >= http.StatusMultipleChoices {
|
||||||
defer closeBody(rp)
|
defer closeBody(rp)
|
||||||
b, err := ioutil.ReadAll(rp.Body)
|
b, err := ioutil.ReadAll(rp.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -150,7 +150,7 @@ func (c *Client) GetSubtreeRoute(id string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetBlocks() ([]model.Block, *Response) {
|
func (c *Client) GetBlocks() ([]model.Block, *Response) {
|
||||||
r, err := c.DoApiGet(c.GetBlocksRoute(), "")
|
r, err := c.DoAPIGet(c.GetBlocksRoute(), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, BuildErrorResponse(r, err)
|
return nil, BuildErrorResponse(r, err)
|
||||||
}
|
}
|
||||||
@ -160,7 +160,7 @@ func (c *Client) GetBlocks() ([]model.Block, *Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) InsertBlocks(blocks []model.Block) (bool, *Response) {
|
func (c *Client) InsertBlocks(blocks []model.Block) (bool, *Response) {
|
||||||
r, err := c.DoApiPost(c.GetBlocksRoute(), toJSON(blocks))
|
r, err := c.DoAPIPost(c.GetBlocksRoute(), toJSON(blocks))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, BuildErrorResponse(r, err)
|
return false, BuildErrorResponse(r, err)
|
||||||
}
|
}
|
||||||
@ -170,7 +170,7 @@ func (c *Client) InsertBlocks(blocks []model.Block) (bool, *Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) DeleteBlock(blockID string) (bool, *Response) {
|
func (c *Client) DeleteBlock(blockID string) (bool, *Response) {
|
||||||
r, err := c.DoApiDelete(c.GetBlockRoute(blockID))
|
r, err := c.DoAPIDelete(c.GetBlockRoute(blockID))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, BuildErrorResponse(r, err)
|
return false, BuildErrorResponse(r, err)
|
||||||
}
|
}
|
||||||
@ -180,7 +180,7 @@ func (c *Client) DeleteBlock(blockID string) (bool, *Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetSubtree(blockID string) ([]model.Block, *Response) {
|
func (c *Client) GetSubtree(blockID string) ([]model.Block, *Response) {
|
||||||
r, err := c.DoApiGet(c.GetSubtreeRoute(blockID), "")
|
r, err := c.DoAPIGet(c.GetSubtreeRoute(blockID), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, BuildErrorResponse(r, err)
|
return nil, BuildErrorResponse(r, err)
|
||||||
}
|
}
|
||||||
@ -196,7 +196,7 @@ func (c *Client) GetSharingRoute(rootID string) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) GetSharing(rootID string) (*model.Sharing, *Response) {
|
func (c *Client) GetSharing(rootID string) (*model.Sharing, *Response) {
|
||||||
r, err := c.DoApiGet(c.GetSharingRoute(rootID), "")
|
r, err := c.DoAPIGet(c.GetSharingRoute(rootID), "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, BuildErrorResponse(r, err)
|
return nil, BuildErrorResponse(r, err)
|
||||||
}
|
}
|
||||||
@ -207,7 +207,7 @@ func (c *Client) GetSharing(rootID string) (*model.Sharing, *Response) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) PostSharing(sharing model.Sharing) (bool, *Response) {
|
func (c *Client) PostSharing(sharing model.Sharing) (bool, *Response) {
|
||||||
r, err := c.DoApiPost(c.GetSharingRoute(sharing.ID), toJSON(sharing))
|
r, err := c.DoAPIPost(c.GetSharingRoute(sharing.ID), toJSON(sharing))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, BuildErrorResponse(r, err)
|
return false, BuildErrorResponse(r, err)
|
||||||
}
|
}
|
||||||
|
@ -12,12 +12,12 @@ type contextKey struct {
|
|||||||
|
|
||||||
var connContextKey = &contextKey{"http-conn"}
|
var connContextKey = &contextKey{"http-conn"}
|
||||||
|
|
||||||
// SetContextConn stores the connection in the request context
|
// SetContextConn stores the connection in the request context.
|
||||||
func SetContextConn(ctx context.Context, c net.Conn) context.Context {
|
func SetContextConn(ctx context.Context, c net.Conn) context.Context {
|
||||||
return context.WithValue(ctx, connContextKey, c)
|
return context.WithValue(ctx, connContextKey, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetContextConn gets the stored connection from the request context
|
// GetContextConn gets the stored connection from the request context.
|
||||||
func GetContextConn(r *http.Request) net.Conn {
|
func GetContextConn(r *http.Request) net.Conn {
|
||||||
value := r.Context().Value(connContextKey)
|
value := r.Context().Value(connContextKey)
|
||||||
if value == nil {
|
if value == nil {
|
||||||
|
@ -66,7 +66,9 @@ func SetupTestHelper() *TestHelper {
|
|||||||
sessionToken := "TESTTOKEN"
|
sessionToken := "TESTTOKEN"
|
||||||
th := &TestHelper{}
|
th := &TestHelper{}
|
||||||
logger := mlog.NewLogger()
|
logger := mlog.NewLogger()
|
||||||
logger.Configure("", getTestConfig().LoggingCfgJSON)
|
if err := logger.Configure("", getTestConfig().LoggingCfgJSON); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
srv, err := server.New(getTestConfig(), sessionToken, logger)
|
srv, err := server.New(getTestConfig(), sessionToken, logger)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@ -111,7 +113,7 @@ func (th *TestHelper) InitBasic() *TestHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (th *TestHelper) TearDown() {
|
func (th *TestHelper) TearDown() {
|
||||||
defer th.Server.Logger().Shutdown()
|
defer func() { _ = th.Server.Logger().Shutdown() }()
|
||||||
|
|
||||||
err := th.Server.Shutdown()
|
err := th.Server.Shutdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -105,7 +105,7 @@ func main() {
|
|||||||
log.Fatal("Error in config file for logger: ", err)
|
log.Fatal("Error in config file for logger: ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer logger.Shutdown()
|
defer func() { _ = logger.Shutdown() }()
|
||||||
|
|
||||||
if logger.HasTargets() {
|
if logger.HasTargets() {
|
||||||
restore := logger.RedirectStdLog(mlog.Info, mlog.String("src", "stdlog"))
|
restore := logger.RedirectStdLog(mlog.Info, mlog.String("src", "stdlog"))
|
||||||
@ -176,7 +176,7 @@ func main() {
|
|||||||
// Waiting for SIGINT (pkill -2)
|
// Waiting for SIGINT (pkill -2)
|
||||||
<-stop
|
<-stop
|
||||||
|
|
||||||
server.Shutdown()
|
_ = server.Shutdown()
|
||||||
}
|
}
|
||||||
|
|
||||||
// StartServer starts the server
|
// StartServer starts the server
|
||||||
@ -254,7 +254,7 @@ func stopServer() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
pServer.Logger().Error("server.Shutdown ERROR", mlog.Err(err))
|
pServer.Logger().Error("server.Shutdown ERROR", mlog.Err(err))
|
||||||
}
|
}
|
||||||
pServer.Logger().Shutdown()
|
_ = pServer.Logger().Shutdown()
|
||||||
pServer = nil
|
pServer = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ type Block struct {
|
|||||||
DeleteAt int64 `json:"deleteAt"`
|
DeleteAt int64 `json:"deleteAt"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Archive is an import / export archive
|
// Archive is an import / export archive.
|
||||||
type Archive struct {
|
type Archive struct {
|
||||||
Version int64 `json:"version"`
|
Version int64 `json:"version"`
|
||||||
Date int64 `json:"date"`
|
Date int64 `json:"date"`
|
||||||
@ -62,7 +62,7 @@ type Archive struct {
|
|||||||
|
|
||||||
func BlocksFromJSON(data io.Reader) []Block {
|
func BlocksFromJSON(data io.Reader) []Block {
|
||||||
var blocks []Block
|
var blocks []Block
|
||||||
json.NewDecoder(data).Decode(&blocks)
|
_ = json.NewDecoder(data).Decode(&blocks)
|
||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,6 @@ type Sharing struct {
|
|||||||
|
|
||||||
func SharingFromJSON(data io.Reader) Sharing {
|
func SharingFromJSON(data io.Reader) Sharing {
|
||||||
var sharing Sharing
|
var sharing Sharing
|
||||||
json.NewDecoder(data).Decode(&sharing)
|
_ = json.NewDecoder(data).Decode(&sharing)
|
||||||
return sharing
|
return sharing
|
||||||
}
|
}
|
||||||
|
@ -2,5 +2,5 @@ package server
|
|||||||
|
|
||||||
func (s *Server) initHandlers() {
|
func (s *Server) initHandlers() {
|
||||||
cfg := s.config
|
cfg := s.config
|
||||||
s.api.MattermostAuth = cfg.AuthMode == "mattermost"
|
s.api.MattermostAuth = cfg.AuthMode == MattermostAuthMod
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package server
|
package server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@ -43,6 +43,8 @@ const (
|
|||||||
|
|
||||||
//nolint:gomnd
|
//nolint:gomnd
|
||||||
minSessionExpiryTime = int64(60 * 60 * 24 * 31) // 31 days
|
minSessionExpiryTime = int64(60 * 60 * 24 * 31) // 31 days
|
||||||
|
|
||||||
|
MattermostAuthMod = "mattermost"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
@ -72,18 +74,18 @@ func New(cfg *config.Configuration, singleUserToken string, logger *mlog.Logger)
|
|||||||
logger.Error("Unable to start the database", mlog.Err(err))
|
logger.Error("Unable to start the database", mlog.Err(err))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if cfg.AuthMode == "mattermost" {
|
if cfg.AuthMode == MattermostAuthMod {
|
||||||
layeredStore, err := mattermostauthlayer.New(cfg.DBType, cfg.DBConfigString, db)
|
layeredStore, err2 := mattermostauthlayer.New(cfg.DBType, cfg.DBConfigString, db)
|
||||||
if err != nil {
|
if err2 != nil {
|
||||||
log.Print("Unable to start the database", err)
|
logger.Error("Unable to start the database", mlog.Err(err2))
|
||||||
return nil, err
|
return nil, err2
|
||||||
}
|
}
|
||||||
db = layeredStore
|
db = layeredStore
|
||||||
}
|
}
|
||||||
|
|
||||||
authenticator := auth.New(cfg, db)
|
authenticator := auth.New(cfg, db)
|
||||||
|
|
||||||
wsServer := ws.NewServer(authenticator, singleUserToken, cfg.AuthMode == "mattermost", logger)
|
wsServer := ws.NewServer(authenticator, singleUserToken, cfg.AuthMode == MattermostAuthMod, logger)
|
||||||
|
|
||||||
filesBackendSettings := filestore.FileBackendSettings{}
|
filesBackendSettings := filestore.FileBackendSettings{}
|
||||||
filesBackendSettings.DriverName = cfg.FilesDriver
|
filesBackendSettings.DriverName = cfg.FilesDriver
|
||||||
@ -119,8 +121,8 @@ func New(cfg *config.Configuration, singleUserToken string, logger *mlog.Logger)
|
|||||||
|
|
||||||
// Init audit
|
// Init audit
|
||||||
auditService := audit.NewAudit()
|
auditService := audit.NewAudit()
|
||||||
if err := auditService.Configure(cfg.AuditCfgFile, cfg.AuditCfgJSON); err != nil {
|
if err2 := auditService.Configure(cfg.AuditCfgFile, cfg.AuditCfgJSON); err2 != nil {
|
||||||
return nil, errors.New("unable to initialize the audit service")
|
return nil, fmt.Errorf("unable to initialize the audit service: %w", err2)
|
||||||
}
|
}
|
||||||
|
|
||||||
appServices := app.Services{
|
appServices := app.Services{
|
||||||
@ -250,7 +252,7 @@ func (s *Server) Start() error {
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}, func(error) {
|
}, func(error) {
|
||||||
s.metricsServer.Shutdown()
|
_ = s.metricsServer.Shutdown()
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := group.Run(); err != nil {
|
if err := group.Run(); err != nil {
|
||||||
|
@ -33,7 +33,7 @@ type Audit struct {
|
|||||||
auditLogger *mlog.Logger
|
auditLogger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewAudit creates a new Audit instance which can be configured via `(*Audit).Configure`
|
// NewAudit creates a new Audit instance which can be configured via `(*Audit).Configure`.
|
||||||
func NewAudit(options ...mlog.Option) *Audit {
|
func NewAudit(options ...mlog.Option) *Audit {
|
||||||
logger := mlog.NewLogger(options...)
|
logger := mlog.NewLogger(options...)
|
||||||
|
|
||||||
|
@ -2,6 +2,7 @@ package auth
|
|||||||
|
|
||||||
import "regexp"
|
import "regexp"
|
||||||
|
|
||||||
|
//nolint:lll
|
||||||
var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
var emailRegex = regexp.MustCompile("^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$")
|
||||||
|
|
||||||
// IsEmailValid checks if the email provided passes the required structure and length.
|
// IsEmailValid checks if the email provided passes the required structure and length.
|
||||||
|
@ -9,7 +9,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
PasswordMaximumLength = 64
|
PasswordMaximumLength = 64
|
||||||
PasswordSpecialChars = "!\"\\#$%&'()*+,-./:;<=>?@[]^_`|~"
|
PasswordSpecialChars = "!\"\\#$%&'()*+,-./:;<=>?@[]^_`|~" //nolint:gosec
|
||||||
PasswordNumbers = "0123456789"
|
PasswordNumbers = "0123456789"
|
||||||
PasswordUpperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
PasswordUpperCaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
PasswordLowerCaseLetters = "abcdefghijklmnopqrstuvwxyz"
|
PasswordLowerCaseLetters = "abcdefghijklmnopqrstuvwxyz"
|
||||||
@ -23,7 +23,7 @@ const (
|
|||||||
InvalidSymbolPassword = "symbol"
|
InvalidSymbolPassword = "symbol"
|
||||||
)
|
)
|
||||||
|
|
||||||
// HashPassword generates a hash using the bcrypt.GenerateFromPassword
|
// HashPassword generates a hash using the bcrypt.GenerateFromPassword.
|
||||||
func HashPassword(password string) string {
|
func HashPassword(password string) string {
|
||||||
hash, err := bcrypt.GenerateFromPassword([]byte(password), 10)
|
hash, err := bcrypt.GenerateFromPassword([]byte(password), 10)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -33,7 +33,7 @@ func HashPassword(password string) string {
|
|||||||
return string(hash)
|
return string(hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ComparePassword compares the hash
|
// ComparePassword compares the hash.
|
||||||
func ComparePassword(hash, password string) bool {
|
func ComparePassword(hash, password string) bool {
|
||||||
if len(password) == 0 || len(hash) == 0 {
|
if len(password) == 0 || len(hash) == 0 {
|
||||||
return false
|
return false
|
||||||
@ -48,7 +48,7 @@ type InvalidPasswordError struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ipe *InvalidPasswordError) Error() string {
|
func (ipe *InvalidPasswordError) Error() string {
|
||||||
return fmt.Sprintf("invalid password, failing criterias: %s", strings.Join(ipe.FailingCriterias, ", "))
|
return fmt.Sprintf("invalid password, failing criteria: %s", strings.Join(ipe.FailingCriterias, ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
type PasswordSettings struct {
|
type PasswordSettings struct {
|
||||||
|
@ -9,10 +9,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
HEADER_TOKEN = "token"
|
HeaderToken = "token"
|
||||||
HEADER_AUTH = "Authorization"
|
HeaderAuth = "Authorization"
|
||||||
HEADER_BEARER = "BEARER"
|
HeaderBearer = "BEARER"
|
||||||
SESSION_COOKIE_TOKEN = "FOCALBOARDAUTHTOKEN"
|
SessionCookieToken = "FOCALBOARDAUTHTOKEN"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TokenLocation int
|
type TokenLocation int
|
||||||
@ -40,20 +40,20 @@ func (tl TokenLocation) String() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func ParseAuthTokenFromRequest(r *http.Request) (string, TokenLocation) {
|
func ParseAuthTokenFromRequest(r *http.Request) (string, TokenLocation) {
|
||||||
authHeader := r.Header.Get(HEADER_AUTH)
|
authHeader := r.Header.Get(HeaderAuth)
|
||||||
|
|
||||||
// Attempt to parse the token from the cookie
|
// Attempt to parse the token from the cookie
|
||||||
if cookie, err := r.Cookie(SESSION_COOKIE_TOKEN); err == nil {
|
if cookie, err := r.Cookie(SessionCookieToken); err == nil {
|
||||||
return cookie.Value, TokenLocationCookie
|
return cookie.Value, TokenLocationCookie
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the token from the header
|
// Parse the token from the header
|
||||||
if len(authHeader) > 6 && strings.ToUpper(authHeader[0:6]) == HEADER_BEARER {
|
if len(authHeader) > 6 && strings.ToUpper(authHeader[0:6]) == HeaderBearer {
|
||||||
// Default session token
|
// Default session token
|
||||||
return authHeader[7:], TokenLocationHeader
|
return authHeader[7:], TokenLocationHeader
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(authHeader) > 5 && strings.ToLower(authHeader[0:5]) == HEADER_TOKEN {
|
if len(authHeader) > 5 && strings.ToLower(authHeader[0:5]) == HeaderToken {
|
||||||
// OAuth token
|
// OAuth token
|
||||||
return authHeader[6:], TokenLocationHeader
|
return authHeader[6:], TokenLocationHeader
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ func TestParseAuthTokenFromRequest(t *testing.T) {
|
|||||||
}
|
}
|
||||||
req := httptest.NewRequest("GET", pathname, nil)
|
req := httptest.NewRequest("GET", pathname, nil)
|
||||||
if tc.header != "" {
|
if tc.header != "" {
|
||||||
req.Header.Add(HEADER_AUTH, tc.header)
|
req.Header.Add(HeaderAuth, tc.header)
|
||||||
}
|
}
|
||||||
if tc.cookie != "" {
|
if tc.cookie != "" {
|
||||||
req.AddCookie(&http.Cookie{
|
req.AddCookie(&http.Cookie{
|
||||||
|
@ -22,7 +22,7 @@ type InstanceInfo struct {
|
|||||||
InstallationID string
|
InstallationID string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metrics used to instrumentate metrics in prometheus
|
// Metrics used to instrumentate metrics in prometheus.
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
registry *prometheus.Registry
|
registry *prometheus.Registry
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ type Metrics struct {
|
|||||||
blockLastActivity prometheus.Gauge
|
blockLastActivity prometheus.Gauge
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMetrics Factory method to create a new metrics collector
|
// NewMetrics Factory method to create a new metrics collector.
|
||||||
func NewMetrics(info InstanceInfo) *Metrics {
|
func NewMetrics(info InstanceInfo) *Metrics {
|
||||||
m := &Metrics{}
|
m := &Metrics{}
|
||||||
|
|
||||||
|
@ -8,12 +8,12 @@ import (
|
|||||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Service prometheus to run the server
|
// Service prometheus to run the server.
|
||||||
type Service struct {
|
type Service struct {
|
||||||
*http.Server
|
*http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewMetricsServer factory method to create a new prometheus server
|
// NewMetricsServer factory method to create a new prometheus server.
|
||||||
func NewMetricsServer(address string, metricsService *Metrics, logger *mlog.Logger) *Service {
|
func NewMetricsServer(address string, metricsService *Metrics, logger *mlog.Logger) *Service {
|
||||||
return &Service{
|
return &Service{
|
||||||
&http.Server{
|
&http.Server{
|
||||||
@ -25,12 +25,12 @@ func NewMetricsServer(address string, metricsService *Metrics, logger *mlog.Logg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run will start the prometheus server
|
// Run will start the prometheus server.
|
||||||
func (h *Service) Run() error {
|
func (h *Service) Run() error {
|
||||||
return errors.Wrap(h.Server.ListenAndServe(), "prometheus ListenAndServe")
|
return errors.Wrap(h.Server.ListenAndServe(), "prometheus ListenAndServe")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown will shutdown the prometheus server
|
// Shutdown will shutdown the prometheus server.
|
||||||
func (h *Service) Shutdown() error {
|
func (h *Service) Shutdown() error {
|
||||||
return errors.Wrap(h.Server.Close(), "prometheus Close")
|
return errors.Wrap(h.Server.Close(), "prometheus Close")
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||||||
// See LICENSE.txt for license information.
|
// See LICENSE.txt for license information.
|
||||||
|
|
||||||
|
//nolint:gomnd
|
||||||
package mlog
|
package mlog
|
||||||
|
|
||||||
import "github.com/mattermost/logr/v2"
|
import "github.com/mattermost/logr/v2"
|
||||||
|
|
||||||
// Standard levels
|
// Standard levels.
|
||||||
var (
|
var (
|
||||||
Panic = logr.Panic // ID = 0
|
Panic = logr.Panic // ID = 0
|
||||||
Fatal = logr.Fatal // ID = 1
|
Fatal = logr.Fatal // ID = 1
|
||||||
@ -33,7 +34,7 @@ var (
|
|||||||
Metrics = Level{ID: 501, Name: "metrics"}
|
Metrics = Level{ID: 501, Name: "metrics"}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Combinations for LogM (log multi)
|
// Combinations for LogM (log multi).
|
||||||
var (
|
var (
|
||||||
/* Example
|
/* Example
|
||||||
MAuditAll = []Level{AuditAPI, AuditContent, AuditPerms, AuditCLI}
|
MAuditAll = []Level{AuditAPI, AuditContent, AuditPerms, AuditCLI}
|
||||||
|
@ -106,7 +106,7 @@ type Logger struct {
|
|||||||
log *logr.Logger
|
log *logr.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewLogger creates a new Logger instance which can be configured via `(*Logger).Configure`
|
// NewLogger creates a new Logger instance which can be configured via `(*Logger).Configure`.
|
||||||
func NewLogger(options ...Option) *Logger {
|
func NewLogger(options ...Option) *Logger {
|
||||||
lgr, _ := logr.New(options...)
|
lgr, _ := logr.New(options...)
|
||||||
log := lgr.NewLogger()
|
log := lgr.NewLogger()
|
||||||
@ -129,15 +129,16 @@ func (l *Logger) Configure(cfgFile string, cfgEscaped string) error {
|
|||||||
|
|
||||||
// Add config from file
|
// Add config from file
|
||||||
if cfgFile != "" {
|
if cfgFile != "" {
|
||||||
if b, err := ioutil.ReadFile(string(cfgFile)); err != nil {
|
b, err := ioutil.ReadFile(cfgFile)
|
||||||
|
if err != nil {
|
||||||
return fmt.Errorf("error reading logger config file %s: %w", cfgFile, err)
|
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// Add config from escaped json string
|
||||||
@ -172,7 +173,7 @@ func (l *Logger) With(fields ...Field) *Logger {
|
|||||||
// Note, transformations and serializations done via fields are already
|
// Note, transformations and serializations done via fields are already
|
||||||
// lazily evaluated and don't require this check beforehand.
|
// lazily evaluated and don't require this check beforehand.
|
||||||
func (l *Logger) IsLevelEnabled(level Level) bool {
|
func (l *Logger) IsLevelEnabled(level Level) bool {
|
||||||
return l.IsLevelEnabled(level)
|
return l.log.IsLevelEnabled(level)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Log emits the log record for any targets configured for the specified level.
|
// Log emits the log record for any targets configured for the specified level.
|
||||||
@ -215,7 +216,7 @@ func (l *Logger) Error(msg string, fields ...Field) {
|
|||||||
// followed by `os.Exit(1)`.
|
// followed by `os.Exit(1)`.
|
||||||
func (l *Logger) Fatal(msg string, fields ...Field) {
|
func (l *Logger) Fatal(msg string, fields ...Field) {
|
||||||
l.log.Log(logr.Fatal, msg, fields...)
|
l.log.Log(logr.Fatal, msg, fields...)
|
||||||
l.Shutdown()
|
_ = l.Shutdown()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/mattermost/logr/v2/formatters"
|
"github.com/mattermost/logr/v2/formatters"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateTestLogger creates a logger for unit tests. Log records are output to `(*testing.T)Log`
|
// CreateTestLogger creates a logger for unit tests. Log records are output to `(*testing.T)Log`.
|
||||||
func CreateTestLogger(t *testing.T, levels ...Field) (logger *Logger) {
|
func CreateTestLogger(t *testing.T, levels ...Field) (logger *Logger) {
|
||||||
logger = NewLogger()
|
logger = NewLogger()
|
||||||
|
|
||||||
@ -16,7 +16,10 @@ func CreateTestLogger(t *testing.T, levels ...Field) (logger *Logger) {
|
|||||||
formatter := &formatters.Plain{}
|
formatter := &formatters.Plain{}
|
||||||
target := newTestingTarget(t)
|
target := newTestingTarget(t)
|
||||||
|
|
||||||
logger.log.Logr().AddTarget(target, "test", filter, formatter, 1000)
|
if err := logger.log.Logr().AddTarget(target, "test", filter, formatter, 1000); err != nil {
|
||||||
|
t.Fail()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return logger
|
return logger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,6 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
mysqlDBType = "mysql"
|
|
||||||
sqliteDBType = "sqlite3"
|
sqliteDBType = "sqlite3"
|
||||||
postgresDBType = "postgres"
|
postgresDBType = "postgres"
|
||||||
)
|
)
|
||||||
@ -56,12 +55,12 @@ func New(dbType, connectionString string, store store.Store) (*MattermostAuthLay
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Shutdown close the connection with the store.
|
// Shutdown close the connection with the store.
|
||||||
func (l *MattermostAuthLayer) Shutdown() error {
|
func (s *MattermostAuthLayer) Shutdown() error {
|
||||||
err := l.Store.Shutdown()
|
err := s.Store.Shutdown()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return l.mmDB.Close()
|
return s.mmDB.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MattermostAuthLayer) GetRegisteredUserCount() (int, error) {
|
func (s *MattermostAuthLayer) GetRegisteredUserCount() (int, error) {
|
||||||
@ -82,7 +81,8 @@ func (s *MattermostAuthLayer) GetRegisteredUserCount() (int, error) {
|
|||||||
|
|
||||||
func (s *MattermostAuthLayer) getUserByCondition(condition sq.Eq) (*model.User, error) {
|
func (s *MattermostAuthLayer) getUserByCondition(condition sq.Eq) (*model.User, error) {
|
||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select("id", "username", "email", "password", "MFASecret as mfa_secret", "AuthService as auth_service", "COALESCE(AuthData, '') as auth_data", "props", "CreateAt as create_at", "UpdateAt as update_at", "DeleteAt as delete_at").
|
Select("id", "username", "email", "password", "MFASecret as mfa_secret", "AuthService as auth_service", "COALESCE(AuthData, '') as auth_data",
|
||||||
|
"props", "CreateAt as create_at", "UpdateAt as update_at", "DeleteAt as delete_at").
|
||||||
From("Users").
|
From("Users").
|
||||||
Where(sq.Eq{"deleteAt": 0}).
|
Where(sq.Eq{"deleteAt": 0}).
|
||||||
Where(condition)
|
Where(condition)
|
||||||
@ -90,7 +90,8 @@ func (s *MattermostAuthLayer) getUserByCondition(condition sq.Eq) (*model.User,
|
|||||||
user := model.User{}
|
user := model.User{}
|
||||||
|
|
||||||
var propsBytes []byte
|
var propsBytes []byte
|
||||||
err := row.Scan(&user.ID, &user.Username, &user.Email, &user.Password, &user.MfaSecret, &user.AuthService, &user.AuthData, &propsBytes, &user.CreateAt, &user.UpdateAt, &user.DeleteAt)
|
err := row.Scan(&user.ID, &user.Username, &user.Email, &user.Password, &user.MfaSecret, &user.AuthService,
|
||||||
|
&user.AuthData, &propsBytes, &user.CreateAt, &user.UpdateAt, &user.DeleteAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -103,7 +104,7 @@ func (s *MattermostAuthLayer) getUserByCondition(condition sq.Eq) (*model.User,
|
|||||||
return &user, nil
|
return &user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MattermostAuthLayer) GetUserById(userID string) (*model.User, error) {
|
func (s *MattermostAuthLayer) GetUserByID(userID string) (*model.User, error) {
|
||||||
return s.getUserByCondition(sq.Eq{"id": userID})
|
return s.getUserByCondition(sq.Eq{"id": userID})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +132,7 @@ func (s *MattermostAuthLayer) UpdateUserPasswordByID(userID, password string) er
|
|||||||
return errors.New("no update allowed from focalboard, update it using mattermost")
|
return errors.New("no update allowed from focalboard, update it using mattermost")
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetActiveUserCount returns the number of users with active sessions within N seconds ago
|
// GetActiveUserCount returns the number of users with active sessions within N seconds ago.
|
||||||
func (s *MattermostAuthLayer) GetActiveUserCount(updatedSecondsAgo int64) (int, error) {
|
func (s *MattermostAuthLayer) GetActiveUserCount(updatedSecondsAgo int64) (int, error) {
|
||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select("count(distinct userId)").
|
Select("count(distinct userId)").
|
||||||
@ -165,7 +166,7 @@ func (s *MattermostAuthLayer) UpdateSession(session *model.Session) error {
|
|||||||
return errors.New("no update allowed from focalboard, update it using mattermost")
|
return errors.New("no update allowed from focalboard, update it using mattermost")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MattermostAuthLayer) DeleteSession(sessionId string) error {
|
func (s *MattermostAuthLayer) DeleteSession(sessionID string) error {
|
||||||
return errors.New("no update allowed from focalboard, update it using mattermost")
|
return errors.New("no update allowed from focalboard, update it using mattermost")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,10 +174,10 @@ func (s *MattermostAuthLayer) CleanUpSessions(expireTime int64) error {
|
|||||||
return errors.New("no update allowed from focalboard, update it using mattermost")
|
return errors.New("no update allowed from focalboard, update it using mattermost")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MattermostAuthLayer) GetWorkspace(ID string) (*model.Workspace, error) {
|
func (s *MattermostAuthLayer) GetWorkspace(id string) (*model.Workspace, error) {
|
||||||
if ID == "0" {
|
if id == "0" {
|
||||||
workspace := model.Workspace{
|
workspace := model.Workspace{
|
||||||
ID: ID,
|
ID: id,
|
||||||
Title: "",
|
Title: "",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -186,7 +187,7 @@ func (s *MattermostAuthLayer) GetWorkspace(ID string) (*model.Workspace, error)
|
|||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select("DisplayName, Type").
|
Select("DisplayName, Type").
|
||||||
From("Channels").
|
From("Channels").
|
||||||
Where(sq.Eq{"ID": ID})
|
Where(sq.Eq{"ID": id})
|
||||||
|
|
||||||
row := query.QueryRow()
|
row := query.QueryRow()
|
||||||
var displayName string
|
var displayName string
|
||||||
@ -197,14 +198,14 @@ func (s *MattermostAuthLayer) GetWorkspace(ID string) (*model.Workspace, error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if channelType != "D" && channelType != "G" {
|
if channelType != "D" && channelType != "G" {
|
||||||
return &model.Workspace{ID: ID, Title: displayName}, nil
|
return &model.Workspace{ID: id, Title: displayName}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
query = s.getQueryBuilder().
|
query = s.getQueryBuilder().
|
||||||
Select("Username").
|
Select("Username").
|
||||||
From("ChannelMembers").
|
From("ChannelMembers").
|
||||||
Join("Users ON Users.ID=ChannelMembers.UserID").
|
Join("Users ON Users.ID=ChannelMembers.UserID").
|
||||||
Where(sq.Eq{"ChannelID": ID})
|
Where(sq.Eq{"ChannelID": id})
|
||||||
|
|
||||||
var sb strings.Builder
|
var sb strings.Builder
|
||||||
rows, err := query.Query()
|
rows, err := query.Query()
|
||||||
@ -223,7 +224,7 @@ func (s *MattermostAuthLayer) GetWorkspace(ID string) (*model.Workspace, error)
|
|||||||
}
|
}
|
||||||
sb.WriteString(name)
|
sb.WriteString(name)
|
||||||
}
|
}
|
||||||
return &model.Workspace{ID: ID, Title: sb.String()}, nil
|
return &model.Workspace{ID: id, Title: sb.String()}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *MattermostAuthLayer) HasWorkspaceAccess(userID string, workspaceID string) (bool, error) {
|
func (s *MattermostAuthLayer) HasWorkspaceAccess(userID string, workspaceID string) (bool, error) {
|
||||||
@ -255,7 +256,8 @@ func (s *MattermostAuthLayer) getQueryBuilder() sq.StatementBuilderType {
|
|||||||
|
|
||||||
func (s *MattermostAuthLayer) GetUsersByWorkspace(workspaceID string) ([]*model.User, error) {
|
func (s *MattermostAuthLayer) GetUsersByWorkspace(workspaceID string) ([]*model.User, error) {
|
||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select("id", "username", "email", "password", "MFASecret as mfa_secret", "AuthService as auth_service", "COALESCE(AuthData, '') as auth_data", "props", "CreateAt as create_at", "UpdateAt as update_at", "DeleteAt as delete_at").
|
Select("id", "username", "email", "password", "MFASecret as mfa_secret", "AuthService as auth_service", "COALESCE(AuthData, '') as auth_data",
|
||||||
|
"props", "CreateAt as create_at", "UpdateAt as update_at", "DeleteAt as delete_at").
|
||||||
From("Users").
|
From("Users").
|
||||||
Join("ChannelMembers ON ChannelMembers.UserID = Users.ID").
|
Join("ChannelMembers ON ChannelMembers.UserID = Users.ID").
|
||||||
Where(sq.Eq{"deleteAt": 0}).
|
Where(sq.Eq{"deleteAt": 0}).
|
||||||
|
@ -345,19 +345,19 @@ func (mr *MockStoreMockRecorder) GetUserByEmail(email interface{}) *gomock.Call
|
|||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByEmail", reflect.TypeOf((*MockStore)(nil).GetUserByEmail), email)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByEmail", reflect.TypeOf((*MockStore)(nil).GetUserByEmail), email)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserById mocks base method.
|
// GetUserByID mocks base method.
|
||||||
func (m *MockStore) GetUserById(userID string) (*model.User, error) {
|
func (m *MockStore) GetUserByID(userID string) (*model.User, error) {
|
||||||
m.ctrl.T.Helper()
|
m.ctrl.T.Helper()
|
||||||
ret := m.ctrl.Call(m, "GetUserById", userID)
|
ret := m.ctrl.Call(m, "GetUserByID", userID)
|
||||||
ret0, _ := ret[0].(*model.User)
|
ret0, _ := ret[0].(*model.User)
|
||||||
ret1, _ := ret[1].(error)
|
ret1, _ := ret[1].(error)
|
||||||
return ret0, ret1
|
return ret0, ret1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserById indicates an expected call of GetUserById.
|
// GetUserByID indicates an expected call of GetUserByID.
|
||||||
func (mr *MockStoreMockRecorder) GetUserById(userID interface{}) *gomock.Call {
|
func (mr *MockStoreMockRecorder) GetUserByID(userID interface{}) *gomock.Call {
|
||||||
mr.mock.ctrl.T.Helper()
|
mr.mock.ctrl.T.Helper()
|
||||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserById", reflect.TypeOf((*MockStore)(nil).GetUserById), userID)
|
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetUserByID", reflect.TypeOf((*MockStore)(nil).GetUserByID), userID)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUserByUsername mocks base method.
|
// GetUserByUsername mocks base method.
|
||||||
|
@ -132,7 +132,7 @@ func (s *SQLStore) GetBlocksWithType(c store.Container, blockType string) ([]mod
|
|||||||
return s.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.
|
||||||
func (s *SQLStore) GetSubTree2(c store.Container, blockID string) ([]model.Block, error) {
|
func (s *SQLStore) GetSubTree2(c store.Container, blockID string) ([]model.Block, error) {
|
||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select(
|
Select(
|
||||||
@ -162,7 +162,7 @@ func (s *SQLStore) GetSubTree2(c store.Container, blockID string) ([]model.Block
|
|||||||
return s.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.
|
||||||
func (s *SQLStore) GetSubTree3(c store.Container, blockID string) ([]model.Block, error) {
|
func (s *SQLStore) GetSubTree3(c store.Container, blockID string) ([]model.Block, error) {
|
||||||
// This first subquery returns repeated blocks
|
// This first subquery returns repeated blocks
|
||||||
query := s.getQueryBuilder().Select(
|
query := s.getQueryBuilder().Select(
|
||||||
@ -363,19 +363,19 @@ func (s *SQLStore) InsertBlock(c store.Container, block model.Block) error {
|
|||||||
Where(sq.Eq{"COALESCE(workspace_id, '0')": c.WorkspaceID})
|
Where(sq.Eq{"COALESCE(workspace_id, '0')": c.WorkspaceID})
|
||||||
_, err = sq.ExecContextWith(ctx, tx, deleteQuery)
|
_, err = sq.ExecContextWith(ctx, tx, deleteQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = sq.ExecContextWith(ctx, tx, query.Into(s.tablePrefix+"blocks"))
|
_, err = sq.ExecContextWith(ctx, tx, query.Into(s.tablePrefix+"blocks"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = sq.ExecContextWith(ctx, tx, query.Into(s.tablePrefix+"blocks_history"))
|
_, err = sq.ExecContextWith(ctx, tx, query.Into(s.tablePrefix+"blocks_history"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +413,7 @@ func (s *SQLStore) DeleteBlock(c store.Container, blockID string, modifiedBy str
|
|||||||
|
|
||||||
_, err = sq.ExecContextWith(ctx, tx, insertQuery)
|
_, err = sq.ExecContextWith(ctx, tx, insertQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -424,7 +424,7 @@ func (s *SQLStore) DeleteBlock(c store.Container, blockID string, modifiedBy str
|
|||||||
|
|
||||||
_, err = sq.ExecContextWith(ctx, tx, deleteQuery)
|
_, err = sq.ExecContextWith(ctx, tx, deleteQuery)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tx.Rollback()
|
_ = tx.Rollback()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/mattermost/focalboard/server/services/store/sqlstore/initializations"
|
"github.com/mattermost/focalboard/server/services/store/sqlstore/initializations"
|
||||||
)
|
)
|
||||||
|
|
||||||
// InitializeTemplates imports default templates if the blocks table is empty
|
// InitializeTemplates imports default templates if the blocks table is empty.
|
||||||
func (s *SQLStore) InitializeTemplates() error {
|
func (s *SQLStore) InitializeTemplates() error {
|
||||||
isNeeded, err := s.isInitializationNeeded()
|
isNeeded, err := s.isInitializationNeeded()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -54,7 +54,7 @@ func (s *SQLStore) importInitialTemplates() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// isInitializationNeeded returns true if the blocks table is empty
|
// isInitializationNeeded returns true if the blocks table is empty.
|
||||||
func (s *SQLStore) isInitializationNeeded() (bool, error) {
|
func (s *SQLStore) isInitializationNeeded() (bool, error) {
|
||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select("count(*)").
|
Select("count(*)").
|
||||||
|
@ -84,7 +84,7 @@ func appendMultipleStatementsFlag(connectionString string) (string, error) {
|
|||||||
|
|
||||||
// migrations in MySQL need to run with the multiStatements flag
|
// migrations in MySQL need to run with the multiStatements flag
|
||||||
// enabled, so this method creates a new connection ensuring that it's
|
// enabled, so this method creates a new connection ensuring that it's
|
||||||
// enabled
|
// enabled.
|
||||||
func (s *SQLStore) getMySQLMigrationConnection() (*sql.DB, error) {
|
func (s *SQLStore) getMySQLMigrationConnection() (*sql.DB, error) {
|
||||||
connectionString, err := appendMultipleStatementsFlag(s.connectionString)
|
connectionString, err := appendMultipleStatementsFlag(s.connectionString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -123,9 +123,9 @@ func (s *SQLStore) Migrate() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.dbType == mysqlDBType {
|
if s.dbType == mysqlDBType {
|
||||||
db, err := s.getMySQLMigrationConnection()
|
db, err2 := s.getMySQLMigrationConnection()
|
||||||
if err != nil {
|
if err2 != nil {
|
||||||
return err
|
return err2
|
||||||
}
|
}
|
||||||
defer db.Close()
|
defer db.Close()
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import (
|
|||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetActiveUserCount returns the number of users with active sessions within N seconds ago
|
// GetActiveUserCount returns the number of users with active sessions within N seconds ago.
|
||||||
func (s *SQLStore) GetActiveUserCount(updatedSecondsAgo int64) (int, error) {
|
func (s *SQLStore) GetActiveUserCount(updatedSecondsAgo int64) (int, error) {
|
||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
Select("count(distinct user_id)").
|
Select("count(distinct user_id)").
|
||||||
@ -94,9 +94,9 @@ func (s *SQLStore) UpdateSession(session *model.Session) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) DeleteSession(sessionId string) error {
|
func (s *SQLStore) DeleteSession(sessionID string) error {
|
||||||
query := s.getQueryBuilder().Delete(s.tablePrefix+"sessions").
|
query := s.getQueryBuilder().Delete(s.tablePrefix+"sessions").
|
||||||
Where("id", sessionId)
|
Where("id", sessionID)
|
||||||
|
|
||||||
_, err := query.Exec()
|
_, err := query.Exec()
|
||||||
return err
|
return err
|
||||||
|
@ -29,9 +29,13 @@ func (s *SQLStore) UpsertSharing(c store.Container, sharing model.Sharing) error
|
|||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
if s.dbType == mysqlDBType {
|
if s.dbType == mysqlDBType {
|
||||||
query = query.Suffix("ON DUPLICATE KEY UPDATE enabled = ?, token = ?, modified_by = ?, update_at = ?", sharing.Enabled, sharing.Token, sharing.ModifiedBy, now)
|
query = query.Suffix("ON DUPLICATE KEY UPDATE enabled = ?, token = ?, modified_by = ?, update_at = ?",
|
||||||
|
sharing.Enabled, sharing.Token, sharing.ModifiedBy, now)
|
||||||
} else {
|
} else {
|
||||||
query = query.Suffix("ON CONFLICT (id) DO UPDATE SET enabled = EXCLUDED.enabled, token = EXCLUDED.token, modified_by = EXCLUDED.modified_by, update_at = EXCLUDED.update_at")
|
query = query.Suffix(
|
||||||
|
`ON CONFLICT (id)
|
||||||
|
DO UPDATE SET enabled = EXCLUDED.enabled, token = EXCLUDED.token, modified_by = EXCLUDED.modified_by, update_at = EXCLUDED.update_at`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := query.Exec()
|
_, err := query.Exec()
|
||||||
|
@ -27,7 +27,7 @@ func SetupTests(t *testing.T) (store.Store, func()) {
|
|||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
tearDown := func() {
|
tearDown := func() {
|
||||||
defer logger.Shutdown()
|
defer func() { _ = logger.Shutdown() }()
|
||||||
err = store.Shutdown()
|
err = store.Shutdown()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ func (s *SQLStore) getUsersByCondition(condition sq.Eq) ([]*model.User, error) {
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) GetUserById(userID string) (*model.User, error) {
|
func (s *SQLStore) GetUserByID(userID string) (*model.User, error) {
|
||||||
return s.getUserByCondition(sq.Eq{"id": userID})
|
return s.getUserByCondition(sq.Eq{"id": userID})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,9 +28,13 @@ func (s *SQLStore) UpsertWorkspaceSignupToken(workspace model.Workspace) error {
|
|||||||
now,
|
now,
|
||||||
)
|
)
|
||||||
if s.dbType == mysqlDBType {
|
if s.dbType == mysqlDBType {
|
||||||
query = query.Suffix("ON DUPLICATE KEY UPDATE signup_token = ?, modified_by = ?, update_at = ?", workspace.SignupToken, workspace.ModifiedBy, now)
|
query = query.Suffix("ON DUPLICATE KEY UPDATE signup_token = ?, modified_by = ?, update_at = ?",
|
||||||
|
workspace.SignupToken, workspace.ModifiedBy, now)
|
||||||
} else {
|
} else {
|
||||||
query = query.Suffix("ON CONFLICT (id) DO UPDATE SET signup_token = EXCLUDED.signup_token, modified_by = EXCLUDED.modified_by, update_at = EXCLUDED.update_at")
|
query = query.Suffix(
|
||||||
|
`ON CONFLICT (id)
|
||||||
|
DO UPDATE SET signup_token = EXCLUDED.signup_token, modified_by = EXCLUDED.modified_by, update_at = EXCLUDED.update_at`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := query.Exec()
|
_, err := query.Exec()
|
||||||
@ -62,14 +66,17 @@ func (s *SQLStore) UpsertWorkspaceSettings(workspace model.Workspace) error {
|
|||||||
if s.dbType == mysqlDBType {
|
if s.dbType == mysqlDBType {
|
||||||
query = query.Suffix("ON DUPLICATE KEY UPDATE settings = ?, modified_by = ?, update_at = ?", settingsJSON, workspace.ModifiedBy, now)
|
query = query.Suffix("ON DUPLICATE KEY UPDATE settings = ?, modified_by = ?, update_at = ?", settingsJSON, workspace.ModifiedBy, now)
|
||||||
} else {
|
} else {
|
||||||
query = query.Suffix("ON CONFLICT (id) DO UPDATE SET settings = EXCLUDED.settings, modified_by = EXCLUDED.modified_by, update_at = EXCLUDED.update_at")
|
query = query.Suffix(
|
||||||
|
`ON CONFLICT (id)
|
||||||
|
DO UPDATE SET settings = EXCLUDED.settings, modified_by = EXCLUDED.modified_by, update_at = EXCLUDED.update_at`,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = query.Exec()
|
_, err = query.Exec()
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SQLStore) GetWorkspace(ID string) (*model.Workspace, error) {
|
func (s *SQLStore) GetWorkspace(id string) (*model.Workspace, error) {
|
||||||
var settingsJSON string
|
var settingsJSON string
|
||||||
|
|
||||||
query := s.getQueryBuilder().
|
query := s.getQueryBuilder().
|
||||||
@ -81,7 +88,7 @@ func (s *SQLStore) GetWorkspace(ID string) (*model.Workspace, error) {
|
|||||||
"update_at",
|
"update_at",
|
||||||
).
|
).
|
||||||
From(s.tablePrefix + "workspaces").
|
From(s.tablePrefix + "workspaces").
|
||||||
Where(sq.Eq{"id": ID})
|
Where(sq.Eq{"id": id})
|
||||||
row := query.QueryRow()
|
row := query.QueryRow()
|
||||||
workspace := model.Workspace{}
|
workspace := model.Workspace{}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ package store
|
|||||||
import "github.com/mattermost/focalboard/server/model"
|
import "github.com/mattermost/focalboard/server/model"
|
||||||
|
|
||||||
// Conainer represents a container in a store
|
// Conainer represents a container in a store
|
||||||
// Using a struct to make extending this easier in the future
|
// Using a struct to make extending this easier in the future.
|
||||||
type Container struct {
|
type Container struct {
|
||||||
WorkspaceID string
|
WorkspaceID string
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ type Store interface {
|
|||||||
SetSystemSetting(key, value string) error
|
SetSystemSetting(key, value string) error
|
||||||
|
|
||||||
GetRegisteredUserCount() (int, error)
|
GetRegisteredUserCount() (int, error)
|
||||||
GetUserById(userID string) (*model.User, error)
|
GetUserByID(userID string) (*model.User, error)
|
||||||
GetUserByEmail(email string) (*model.User, error)
|
GetUserByEmail(email string) (*model.User, error)
|
||||||
GetUserByUsername(username string) (*model.User, error)
|
GetUserByUsername(username string) (*model.User, error)
|
||||||
CreateUser(user *model.User) error
|
CreateUser(user *model.User) error
|
||||||
@ -44,7 +44,7 @@ type Store interface {
|
|||||||
CreateSession(session *model.Session) error
|
CreateSession(session *model.Session) error
|
||||||
RefreshSession(session *model.Session) error
|
RefreshSession(session *model.Session) error
|
||||||
UpdateSession(session *model.Session) error
|
UpdateSession(session *model.Session) error
|
||||||
DeleteSession(sessionId string) error
|
DeleteSession(sessionID string) error
|
||||||
CleanUpSessions(expireTime int64) error
|
CleanUpSessions(expireTime int64) error
|
||||||
|
|
||||||
UpsertSharing(c Container, sharing model.Sharing) error
|
UpsertSharing(c Container, sharing model.Sharing) error
|
||||||
|
@ -19,7 +19,7 @@ func StoreTestSystemStore(t *testing.T, setup func(t *testing.T) (store.Store, f
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSetGetSystemSettings(t *testing.T, store store.Store, container store.Container) {
|
func testSetGetSystemSettings(t *testing.T, store store.Store, _ /*container*/ store.Container) {
|
||||||
t.Run("Get empty settings", func(t *testing.T) {
|
t.Run("Get empty settings", func(t *testing.T) {
|
||||||
settings, err := store.GetSystemSettings()
|
settings, err := store.GetSystemSettings()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
@ -39,10 +39,12 @@ func testGetWorkspaceUsers(t *testing.T, store store.Store) {
|
|||||||
})
|
})
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
|
|
||||||
defer store.UpdateUser(&model.User{
|
defer func() {
|
||||||
ID: userID,
|
_ = store.UpdateUser(&model.User{
|
||||||
DeleteAt: time.Now().Unix(),
|
ID: userID,
|
||||||
})
|
DeleteAt: time.Now().Unix(),
|
||||||
|
})
|
||||||
|
}()
|
||||||
|
|
||||||
users, err = store.GetUsersByWorkspace("workspace_1")
|
users, err = store.GetUsersByWorkspace("workspace_1")
|
||||||
require.Equal(t, 1, len(users))
|
require.Equal(t, 1, len(users))
|
||||||
|
@ -79,7 +79,7 @@ func (ts *Service) sendDailyTelemetry(override bool) {
|
|||||||
func (ts *Service) sendTelemetry(event string, properties map[string]interface{}) {
|
func (ts *Service) sendTelemetry(event string, properties map[string]interface{}) {
|
||||||
if ts.rudderClient != nil {
|
if ts.rudderClient != nil {
|
||||||
var context *rudder.Context
|
var context *rudder.Context
|
||||||
ts.rudderClient.Enqueue(rudder.Track{
|
_ = ts.rudderClient.Enqueue(rudder.Track{
|
||||||
Event: event,
|
Event: event,
|
||||||
UserId: ts.telemetryID,
|
UserId: ts.telemetryID,
|
||||||
Properties: properties,
|
Properties: properties,
|
||||||
@ -103,7 +103,7 @@ func (ts *Service) initRudder(endpoint, rudderKey string) {
|
|||||||
ts.logger.Fatal("Failed to create Rudder instance")
|
ts.logger.Fatal("Failed to create Rudder instance")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
client.Enqueue(rudder.Identify{
|
_ = client.Enqueue(rudder.Identify{
|
||||||
UserId: ts.telemetryID,
|
UserId: ts.telemetryID,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package webhook
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/mattermost/focalboard/server/model"
|
"github.com/mattermost/focalboard/server/model"
|
||||||
@ -10,7 +11,7 @@ import (
|
|||||||
"github.com/mattermost/focalboard/server/services/mlog"
|
"github.com/mattermost/focalboard/server/services/mlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NotifyUpdate calls webhooks
|
// NotifyUpdate calls webhooks.
|
||||||
func (wh *Client) NotifyUpdate(block model.Block) {
|
func (wh *Client) NotifyUpdate(block model.Block) {
|
||||||
if len(wh.config.WebhookUpdate) < 1 {
|
if len(wh.config.WebhookUpdate) < 1 {
|
||||||
return
|
return
|
||||||
@ -21,18 +22,21 @@ func (wh *Client) NotifyUpdate(block model.Block) {
|
|||||||
wh.logger.Fatal("NotifyUpdate: json.Marshal", mlog.Err(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))
|
resp, _ := http.Post(url, "application/json", bytes.NewBuffer(json)) //nolint:gosec
|
||||||
|
_, _ = ioutil.ReadAll(resp.Body)
|
||||||
|
resp.Body.Close()
|
||||||
|
|
||||||
wh.logger.Debug("webhook.NotifyUpdate", mlog.String("url", 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
|
logger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewClient creates a new Client
|
// NewClient creates a new Client.
|
||||||
func NewClient(config *config.Configuration, logger *mlog.Logger) *Client {
|
func NewClient(config *config.Configuration, logger *mlog.Logger) *Client {
|
||||||
return &Client{
|
return &Client{
|
||||||
config: config,
|
config: config,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package web
|
package web
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
@ -24,12 +25,11 @@ type RoutedService interface {
|
|||||||
type Server struct {
|
type Server struct {
|
||||||
http.Server
|
http.Server
|
||||||
|
|
||||||
baseURL string
|
baseURL string
|
||||||
rootPath string
|
rootPath string
|
||||||
port int
|
port int
|
||||||
ssl bool
|
ssl bool
|
||||||
localOnly bool
|
logger *mlog.Logger
|
||||||
logger *mlog.Logger
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServer creates a new instance of the webserver.
|
// NewServer creates a new instance of the webserver.
|
||||||
@ -81,13 +81,13 @@ func (ws *Server) registerRoutes() {
|
|||||||
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 {
|
||||||
ws.logger.Error("Unable to serve the index.html file", mlog.Err(err))
|
ws.logger.Error("Unable to serve the index.html file", mlog.Err(err))
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
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 {
|
||||||
ws.logger.Error("Unable to serve the index.html file", mlog.Err(err))
|
ws.logger.Error("Unable to serve the index.html file", mlog.Err(err))
|
||||||
w.WriteHeader(500)
|
w.WriteHeader(http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -115,7 +115,7 @@ func (ws *Server) Start() {
|
|||||||
|
|
||||||
ws.logger.Info("http server started", mlog.Int("port", 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(); !errors.Is(err, http.ErrServerClosed) {
|
||||||
ws.logger.Fatal("ListenAndServeTLS", mlog.Err(err))
|
ws.logger.Fatal("ListenAndServeTLS", mlog.Err(err))
|
||||||
}
|
}
|
||||||
ws.logger.Info("http server stopped")
|
ws.logger.Info("http server stopped")
|
||||||
|
@ -16,7 +16,7 @@ import (
|
|||||||
"github.com/mattermost/focalboard/server/services/store"
|
"github.com/mattermost/focalboard/server/services/store"
|
||||||
)
|
)
|
||||||
|
|
||||||
// IsValidSessionToken authenticates session tokens
|
// IsValidSessionToken authenticates session tokens.
|
||||||
type IsValidSessionToken func(token string) bool
|
type IsValidSessionToken func(token string) bool
|
||||||
|
|
||||||
type Hub interface {
|
type Hub interface {
|
||||||
@ -36,20 +36,20 @@ type Server struct {
|
|||||||
logger *mlog.Logger
|
logger *mlog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateMsg is sent on block updates
|
// UpdateMsg is sent on block updates.
|
||||||
type UpdateMsg struct {
|
type UpdateMsg struct {
|
||||||
Action string `json:"action"`
|
Action string `json:"action"`
|
||||||
Block model.Block `json:"block"`
|
Block model.Block `json:"block"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// clusterUpdateMsg is sent on block updates
|
// clusterUpdateMsg is sent on block updates.
|
||||||
type clusterUpdateMsg struct {
|
type clusterUpdateMsg struct {
|
||||||
UpdateMsg
|
UpdateMsg
|
||||||
BlockID string `json:"block_id"`
|
BlockID string `json:"block_id"`
|
||||||
WorkspaceID string `json:"workspace_id"`
|
WorkspaceID string `json:"workspace_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// ErrorMsg is sent on errors
|
// ErrorMsg is sent on errors.
|
||||||
type ErrorMsg struct {
|
type ErrorMsg struct {
|
||||||
Error string `json:"error"`
|
Error string `json:"error"`
|
||||||
}
|
}
|
||||||
@ -219,7 +219,7 @@ func (ws *Server) getAuthenticatedWorkspaceID(wsSession *websocketSession, comma
|
|||||||
workspaceID := command.WorkspaceID
|
workspaceID := command.WorkspaceID
|
||||||
if len(workspaceID) == 0 {
|
if len(workspaceID) == 0 {
|
||||||
ws.logger.Error("getAuthenticatedWorkspaceID: No workspace")
|
ws.logger.Error("getAuthenticatedWorkspaceID: No workspace")
|
||||||
return "", errors.New("No workspace")
|
return "", errors.New("no workspace")
|
||||||
}
|
}
|
||||||
|
|
||||||
container := store.Container{
|
container := store.Container{
|
||||||
@ -231,16 +231,16 @@ func (ws *Server) getAuthenticatedWorkspaceID(wsSession *websocketSession, comma
|
|||||||
for _, blockID := range command.BlockIDs {
|
for _, blockID := range command.BlockIDs {
|
||||||
isValid, _ := ws.auth.IsValidReadToken(container, blockID, command.ReadToken)
|
isValid, _ := ws.auth.IsValidReadToken(container, blockID, command.ReadToken)
|
||||||
if !isValid {
|
if !isValid {
|
||||||
return "", errors.New("Invalid read token for workspace")
|
return "", errors.New("invalid read token for workspace")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return workspaceID, nil
|
return workspaceID, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", errors.New("No read token")
|
return "", errors.New("no read token")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Refactor workspace hashing
|
// TODO: Refactor workspace hashing.
|
||||||
func makeItemID(workspaceID, blockID string) string {
|
func makeItemID(workspaceID, blockID string) string {
|
||||||
return workspaceID + "-" + blockID
|
return workspaceID + "-" + blockID
|
||||||
}
|
}
|
||||||
@ -283,7 +283,7 @@ func (ws *Server) removeListener(client *websocket.Conn) {
|
|||||||
ws.mu.Unlock()
|
ws.mu.Unlock()
|
||||||
}
|
}
|
||||||
|
|
||||||
// removeListenerFromBlocks removes a webSocket listener from a set of block.
|
// removeListenerFromBlocks removes a webSocket listener from a set of blocks.
|
||||||
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 {
|
||||||
@ -345,15 +345,13 @@ func (ws *Server) SetHub(hub Hub) {
|
|||||||
Block: msg.Block,
|
Block: msg.Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
if listeners != nil {
|
for _, listener := range listeners {
|
||||||
for _, listener := range listeners {
|
log.Printf("Broadcast change, workspaceID: %s, blockID: %s, remoteAddr: %s", msg.WorkspaceID, msg.BlockID, listener.RemoteAddr())
|
||||||
log.Printf("Broadcast change, workspaceID: %s, blockID: %s, remoteAddr: %s", msg.WorkspaceID, msg.BlockID, listener.RemoteAddr())
|
|
||||||
|
|
||||||
err := listener.WriteJSON(message)
|
err := listener.WriteJSON(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("broadcast error: %v", err)
|
log.Printf("broadcast error: %v", err)
|
||||||
listener.Close()
|
listener.Close()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -369,7 +367,7 @@ func (ws *Server) getListeners(workspaceID string, blockID string) []*websocket.
|
|||||||
return listeners
|
return listeners
|
||||||
}
|
}
|
||||||
|
|
||||||
// BroadcastBlockDelete broadcasts delete messages to clients
|
// BroadcastBlockDelete broadcasts delete messages to clients.
|
||||||
func (ws *Server) BroadcastBlockDelete(workspaceID, blockID, parentID string) {
|
func (ws *Server) BroadcastBlockDelete(workspaceID, blockID, parentID string) {
|
||||||
now := time.Now().Unix()
|
now := time.Now().Unix()
|
||||||
block := model.Block{}
|
block := model.Block{}
|
||||||
@ -381,7 +379,7 @@ func (ws *Server) BroadcastBlockDelete(workspaceID, blockID, parentID string) {
|
|||||||
ws.BroadcastBlockChange(workspaceID, block)
|
ws.BroadcastBlockChange(workspaceID, block)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BroadcastBlockChange broadcasts update messages to clients
|
// BroadcastBlockChange broadcasts update messages to clients.
|
||||||
func (ws *Server) BroadcastBlockChange(workspaceID string, block model.Block) {
|
func (ws *Server) BroadcastBlockChange(workspaceID string, block model.Block) {
|
||||||
blockIDsToNotify := []string{block.ID, block.ParentID}
|
blockIDsToNotify := []string{block.ID, block.ParentID}
|
||||||
|
|
||||||
@ -404,19 +402,17 @@ func (ws *Server) BroadcastBlockChange(workspaceID string, block model.Block) {
|
|||||||
ws.hub.SendWSMessage(data)
|
ws.hub.SendWSMessage(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if listeners != nil {
|
for _, listener := range listeners {
|
||||||
for _, listener := range listeners {
|
ws.logger.Debug("Broadcast change",
|
||||||
ws.logger.Debug("Broadcast change",
|
mlog.String("workspaceID", workspaceID),
|
||||||
mlog.String("workspaceID", workspaceID),
|
mlog.String("blockID", blockID),
|
||||||
mlog.String("blockID", blockID),
|
mlog.Stringer("remoteAddr", listener.RemoteAddr()),
|
||||||
mlog.Stringer("remoteAddr", listener.RemoteAddr()),
|
)
|
||||||
)
|
|
||||||
|
|
||||||
err := listener.WriteJSON(message)
|
err := listener.WriteJSON(message)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ws.logger.Error("broadcast error", mlog.Err(err))
|
ws.logger.Error("broadcast error", mlog.Err(err))
|
||||||
listener.Close()
|
listener.Close()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user