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