You've already forked focalboard
							
							
				mirror of
				https://github.com/mattermost/focalboard.git
				synced 2025-10-31 00:17:42 +02:00 
			
		
		
		
	wip
This commit is contained in:
		| @@ -1,23 +1,37 @@ | ||||
| package api | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
|  | ||||
| 	"github.com/gorilla/mux" | ||||
| 	"github.com/mattermost/focalboard/server/model" | ||||
| 	mmModel "github.com/mattermost/mattermost-server/v6/model" | ||||
| 	"github.com/mattermost/mattermost-server/v6/shared/mlog" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	complianceDefaultPage    = "0" | ||||
| 	complianceDefaultPerPage = "60" | ||||
| ) | ||||
|  | ||||
| func (a *API) registerComplianceRoutes(r *mux.Router) { | ||||
| 	// Compliance APIs | ||||
| 	r.HandleFunc("/admin/boards", a.sessionRequired(a.handleGetAllBoards)).Methods("GET") | ||||
| 	r.HandleFunc("/admin/boards_history", a.sessionRequired(a.handleGetBoardsHistory)).Methods("POST") | ||||
| 	r.HandleFunc("/admin/boards_history", a.sessionRequired(a.handleGetBoardsHistory)).Methods("GET") | ||||
| 	r.HandleFunc("/admin/blocks_history", a.sessionRequired(a.handleGetBlocksHistory)).Methods("GET") | ||||
| } | ||||
|  | ||||
| func (a *API) handleGetAllBoards(w http.ResponseWriter, r *http.Request) { | ||||
| 	// TODO(@pinjasaur): swagger | ||||
|  | ||||
| 	query := r.URL.Query() | ||||
| 	teamID := query.Get("team_id") | ||||
| 	strPage := query.Get("page") | ||||
| 	strPerPage := query.Get("per_page") | ||||
|  | ||||
| 	// Valid authorization (`manage_system`)? | ||||
| 	userID := getUserID(r) | ||||
| 	if !a.permissions.HasPermissionTo(userID, mmModel.PermissionManageSystem) { | ||||
| @@ -32,12 +46,78 @@ func (a *API) handleGetAllBoards(w http.ResponseWriter, r *http.Request) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	stringResponse(w, "OK") | ||||
| 	if strPage == "" { | ||||
| 		strPage = complianceDefaultPage | ||||
| 	} | ||||
| 	if strPerPage == "" { | ||||
| 		strPerPage = complianceDefaultPerPage | ||||
| 	} | ||||
| 	page, err := strconv.Atoi(strPage) | ||||
| 	if err != nil { | ||||
| 		message := fmt.Sprintf("invalid `page` parameter: %s", err) | ||||
| 		a.errorResponse(w, r, model.NewErrBadRequest(message)) | ||||
| 		return | ||||
| 	} | ||||
| 	perPage, err := strconv.Atoi(strPerPage) | ||||
| 	if err != nil { | ||||
| 		message := fmt.Sprintf("invalid `per_page` parameter: %s", err) | ||||
| 		a.errorResponse(w, r, model.NewErrBadRequest(message)) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	boards, err := a.app.GetBoardsForTeam(teamID, page, perPage) | ||||
| 	if err != nil { | ||||
| 		a.errorResponse(w, r, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// N+1; is there more? | ||||
| 	// TODO: potentially fragile if len(boards) == 0 or perPage < 0 | ||||
| 	hasNext := len(boards) > perPage | ||||
| 	if hasNext { | ||||
| 		boards = boards[:len(boards)-1] | ||||
| 	} | ||||
|  | ||||
| 	a.logger.Debug("GetAllBoards", | ||||
| 		mlog.String("teamID", teamID), | ||||
| 		mlog.Int("boardsCount", len(boards)), | ||||
| 	) | ||||
|  | ||||
| 	response := struct { | ||||
| 		hasNext bool | ||||
| 		results []*model.Board | ||||
| 	}{ | ||||
| 		hasNext: hasNext, | ||||
| 		results: boards, | ||||
| 	} | ||||
| 	// response := model.ComplianceResponse{ | ||||
| 	// 	HasNext: hasNext, | ||||
| 	// 	Results: boards, | ||||
| 	// } | ||||
| 	data, err := json.Marshal(response) | ||||
| 	if err != nil { | ||||
| 		a.errorResponse(w, r, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	jsonBytesResponse(w, http.StatusOK, data) | ||||
| } | ||||
|  | ||||
| func (a *API) handleGetBoardsHistory(w http.ResponseWriter, r *http.Request) { | ||||
| 	// TODO(@pinjasaur): swagger | ||||
|  | ||||
| 	query := r.URL.Query() | ||||
| 	strModifiedSince := query.Get("modified_since") // required, everything else optional | ||||
| 	includeDeleted := query.Get("include_deleted") == "true" | ||||
| 	teamID := query.Get("team_id") | ||||
| 	strPage := query.Get("page") | ||||
| 	strPerPage := query.Get("per_page") | ||||
|  | ||||
| 	if strModifiedSince == "" { | ||||
| 		a.errorResponse(w, r, model.NewErrBadRequest("`modified_since` parameter required")) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Valid authorization (`manage_system`)? | ||||
| 	userID := getUserID(r) | ||||
| 	if !a.permissions.HasPermissionTo(userID, mmModel.PermissionManageSystem) { | ||||
| @@ -52,12 +132,81 @@ func (a *API) handleGetBoardsHistory(w http.ResponseWriter, r *http.Request) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	stringResponse(w, "OK") | ||||
| 	if strPage == "" { | ||||
| 		strPage = complianceDefaultPage | ||||
| 	} | ||||
| 	if strPerPage == "" { | ||||
| 		strPerPage = complianceDefaultPerPage | ||||
| 	} | ||||
| 	page, err := strconv.Atoi(strPage) | ||||
| 	if err != nil { | ||||
| 		message := fmt.Sprintf("invalid `page` parameter: %s", err) | ||||
| 		a.errorResponse(w, r, model.NewErrBadRequest(message)) | ||||
| 		return | ||||
| 	} | ||||
| 	perPage, err := strconv.Atoi(strPerPage) | ||||
| 	if err != nil { | ||||
| 		message := fmt.Sprintf("invalid `per_page` parameter: %s", err) | ||||
| 		a.errorResponse(w, r, model.NewErrBadRequest(message)) | ||||
| 		return | ||||
| 	} | ||||
| 	modifiedSince, err := strconv.Atoi(strModifiedSince) | ||||
| 	if err != nil { | ||||
| 		message := fmt.Sprintf("invalid `modified_since` parameter: %s", err) | ||||
| 		a.errorResponse(w, r, model.NewErrBadRequest(message)) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	boards, err := a.app.GetBoardsHistory(modifiedSince, includeDeleted, teamID, page, perPage) | ||||
| 	if err != nil { | ||||
| 		a.errorResponse(w, r, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// N+1; is there more? | ||||
| 	// TODO: potentially fragile if len(boards) == 0 or perPage < 0 | ||||
| 	hasNext := len(boards) > perPage | ||||
| 	if hasNext { | ||||
| 		boards = boards[:len(boards)-1] | ||||
| 	} | ||||
|  | ||||
| 	a.logger.Debug("GetBoardsHistory", | ||||
| 		mlog.String("teamID", teamID), | ||||
| 		mlog.Int("boardsCount", len(boards)), | ||||
| 	) | ||||
|  | ||||
| 	response := struct { | ||||
| 		hasNext bool | ||||
| 		results []*model.Board // ish | ||||
| 	}{ | ||||
| 		hasNext: hasNext, | ||||
| 		results: boards, | ||||
| 	} | ||||
| 	data, err := json.Marshal(response) | ||||
| 	if err != nil { | ||||
| 		a.errorResponse(w, r, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	jsonBytesResponse(w, http.StatusOK, data) | ||||
| } | ||||
|  | ||||
| func (a *API) handleGetBlocksHistory(w http.ResponseWriter, r *http.Request) { | ||||
| 	// TODO(@pinjasaur): swagger | ||||
|  | ||||
| 	query := r.URL.Query() | ||||
| 	strModifiedSince := query.Get("modified_since") // required, everything else optional | ||||
| 	includeDeleted := query.Get("include_deleted") == "true" | ||||
| 	teamID := query.Get("team_id") | ||||
| 	boardID := query.Get("board_id") | ||||
| 	strPage := query.Get("page") | ||||
| 	strPerPage := query.Get("per_page") | ||||
|  | ||||
| 	if strModifiedSince == "" { | ||||
| 		a.errorResponse(w, r, model.NewErrBadRequest("`modified_since` parameter required")) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// Valid authorization (`manage_system`)? | ||||
| 	userID := getUserID(r) | ||||
| 	if !a.permissions.HasPermissionTo(userID, mmModel.PermissionManageSystem) { | ||||
| @@ -72,5 +221,62 @@ func (a *API) handleGetBlocksHistory(w http.ResponseWriter, r *http.Request) { | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	stringResponse(w, "OK") | ||||
| 	if strPage == "" { | ||||
| 		strPage = complianceDefaultPage | ||||
| 	} | ||||
| 	if strPerPage == "" { | ||||
| 		strPerPage = complianceDefaultPerPage | ||||
| 	} | ||||
| 	page, err := strconv.Atoi(strPage) | ||||
| 	if err != nil { | ||||
| 		message := fmt.Sprintf("invalid `page` parameter: %s", err) | ||||
| 		a.errorResponse(w, r, model.NewErrBadRequest(message)) | ||||
| 		return | ||||
| 	} | ||||
| 	perPage, err := strconv.Atoi(strPerPage) | ||||
| 	if err != nil { | ||||
| 		message := fmt.Sprintf("invalid `per_page` parameter: %s", err) | ||||
| 		a.errorResponse(w, r, model.NewErrBadRequest(message)) | ||||
| 		return | ||||
| 	} | ||||
| 	modifiedSince, err := strconv.Atoi(strModifiedSince) | ||||
| 	if err != nil { | ||||
| 		message := fmt.Sprintf("invalid `modified_since` parameter: %s", err) | ||||
| 		a.errorResponse(w, r, model.NewErrBadRequest(message)) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	blocks, err := a.app.GetBlocksHistory(modifiedSince, includeDeleted, teamID, boardID, page, perPage) | ||||
| 	if err != nil { | ||||
| 		a.errorResponse(w, r, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// N+1; is there more? | ||||
| 	// TODO: potentially fragile if len(blocks) == 0 or perPage < 0 | ||||
| 	hasNext := len(blocks) > perPage | ||||
| 	if hasNext { | ||||
| 		blocks = blocks[:len(blocks)-1] | ||||
| 	} | ||||
|  | ||||
| 	a.logger.Debug("GetBlocksHistory", | ||||
| 		mlog.String("teamID", teamID), | ||||
| 		mlog.String("boardID", boardID), | ||||
| 		mlog.Int("blocksCount", len(blocks)), | ||||
| 	) | ||||
|  | ||||
| 	response := struct { | ||||
| 		hasNext bool | ||||
| 		results []*model.Block // ish | ||||
| 	}{ | ||||
| 		hasNext: hasNext, | ||||
| 		results: blocks, | ||||
| 	} | ||||
| 	data, err := json.Marshal(response) | ||||
| 	if err != nil { | ||||
| 		a.errorResponse(w, r, err) | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	jsonBytesResponse(w, http.StatusOK, data) | ||||
| } | ||||
|   | ||||
| @@ -256,6 +256,18 @@ func (a *App) DuplicateBoard(boardID, userID, toTeam string, asTemplate bool) (* | ||||
| 	return bab, members, err | ||||
| } | ||||
|  | ||||
| func (a *App) GetBoardsForTeam(teamID string, page, perPage int) ([]*model.Board, error) { | ||||
| 	return a.store.GetBoardsForTeam(teamID, page, perPage) | ||||
| } | ||||
|  | ||||
| func (a *App) GetBoardsHistory(modifiedSince int, includeDeleted bool, teamID string, page, perPage int) ([]*model.Board, error) { | ||||
| 	return a.store.GetBoardsHistory(modifiedSince, includeDeleted, teamID, page, perPage) | ||||
| } | ||||
|  | ||||
| func (a *App) GetBlocksHistory(modifiedSince int, includeDeleted bool, teamID, boardID string, page, perPage int) ([]*model.Block, error) { | ||||
| 	return a.store.GetBlocksHistory(modifiedSince, includeDeleted, teamID, boardID, page, perPage) | ||||
| } | ||||
|  | ||||
| func (a *App) GetBoardsForUserAndTeam(userID, teamID string, includePublicBoards bool) ([]*model.Board, error) { | ||||
| 	return a.store.GetBoardsForUserAndTeam(userID, teamID, includePublicBoards) | ||||
| } | ||||
|   | ||||
							
								
								
									
										15
									
								
								server/model/compliance.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								server/model/compliance.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. | ||||
| // See LICENSE.txt for license information. | ||||
| package model | ||||
|  | ||||
| // ComplianceResponse is the generic response body to a compliance API | ||||
| // swagger:model | ||||
| type ComplianceResponse struct { | ||||
| 	// Is there a next page for pagination? | ||||
| 	// required: true | ||||
| 	HasNext bool `json:"hasNext"` | ||||
|  | ||||
| 	// The array of results | ||||
| 	// required: true | ||||
| 	Results []interface{} `json:"results"` | ||||
| } | ||||
| @@ -1041,6 +1041,105 @@ func (s *MattermostAuthLayer) GetMembersForBoard(boardID string) ([]*model.Board | ||||
| 	return members, nil | ||||
| } | ||||
|  | ||||
| func (s *MattermostAuthLayer) GetBoardsForTeam(teamID string, page, perPage int) ([]*model.Board, error) { | ||||
| 	query := s.getQueryBuilder(). | ||||
| 		Select(boardFields("b.")...). | ||||
| 		From(s.tablePrefix + "boards as b") | ||||
|  | ||||
| 	if teamID != "" { | ||||
| 		query = query.Where(sq.Eq{"b.team_id": teamID}) | ||||
| 	} | ||||
|  | ||||
| 	if page != 0 { | ||||
| 		query = query.Offset(uint64(page * perPage)) | ||||
| 	} | ||||
|  | ||||
| 	if perPage > 0 { | ||||
| 		// N+1 to check if there's a next page for pagination | ||||
| 		query = query.Limit(uint64(perPage) + 1) | ||||
| 	} | ||||
|  | ||||
| 	rows, err := query.Query() | ||||
| 	if err != nil { | ||||
| 		s.logger.Error(`GetBoardsForTeam ERROR`, mlog.Err(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer s.CloseRows(rows) | ||||
|  | ||||
| 	return s.boardsFromRows(rows, true) | ||||
| } | ||||
|  | ||||
| func (s *MattermostAuthLayer) GetBoardsHistory(modifiedSince int, includeDeleted bool, teamID string, page, perPage int) ([]*model.Board, error) { | ||||
| 	query := s.getQueryBuilder(). | ||||
| 		Select("*"). // TODO: specify | ||||
| 		From(s.tablePrefix + "boards_history as bh"). | ||||
| 		Where(sq.Gt{"bh.update_at": modifiedSince}) | ||||
|  | ||||
| 	if !includeDeleted { | ||||
| 		query = query.Where(sq.Eq{"bh.delete_at": 0}) | ||||
| 	} | ||||
|  | ||||
| 	if teamID != "" { | ||||
| 		query = query.Where(sq.Eq{"bh.team_id": teamID}) | ||||
| 	} | ||||
|  | ||||
| 	if page != 0 { | ||||
| 		query = query.Offset(uint64(page * perPage)) | ||||
| 	} | ||||
|  | ||||
| 	if perPage > 0 { | ||||
| 		// N+1 to check if there's a next page for pagination | ||||
| 		query = query.Limit(uint64(perPage) + 1) | ||||
| 	} | ||||
|  | ||||
| 	rows, err := query.Query() | ||||
| 	if err != nil { | ||||
| 		s.logger.Error(`GetBoardsHistory ERROR`, mlog.Err(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer s.CloseRows(rows) | ||||
|  | ||||
| 	return s.boardsFromRows(rows, true) | ||||
| } | ||||
|  | ||||
| func (s *MattermostAuthLayer) GetBlocksHistory(modifiedSince int, includeDeleted bool, teamID, boardID string, page, perPage int) ([]*model.Block, error) { | ||||
| 	query := s.getQueryBuilder(). | ||||
| 		Select("*"). // TODO: specify | ||||
| 		From(s.tablePrefix + "blocks_history as bh"). | ||||
| 		Where(sq.Gt{"bh.update_at": modifiedSince}) | ||||
|  | ||||
| 	if !includeDeleted { | ||||
| 		query = query.Where(sq.Eq{"bh.delete_at": 0}) | ||||
| 	} | ||||
|  | ||||
| 	if teamID != "" { | ||||
| 		query = query.Where(sq.Eq{"bh.team_id": teamID}) | ||||
| 	} | ||||
|  | ||||
| 	if boardID != "" { | ||||
| 		query = query.Where(sq.Eq{"bh.board_id": boardID}) | ||||
| 	} | ||||
|  | ||||
| 	if page != 0 { | ||||
| 		query = query.Offset(uint64(page * perPage)) | ||||
| 	} | ||||
|  | ||||
| 	if perPage > 0 { | ||||
| 		// N+1 to check if there's a next page for pagination | ||||
| 		query = query.Limit(uint64(perPage) + 1) | ||||
| 	} | ||||
|  | ||||
| 	rows, err := query.Query() | ||||
| 	if err != nil { | ||||
| 		s.logger.Error(`GetBoardsHistory ERROR`, mlog.Err(err)) | ||||
| 		return nil, err | ||||
| 	} | ||||
| 	defer s.CloseRows(rows) | ||||
|  | ||||
| 	// TODO: copy implementation from SQLStore? | ||||
| 	return s.blocksFromRows(rows) | ||||
| } | ||||
|  | ||||
| func (s *MattermostAuthLayer) GetBoardsForUserAndTeam(userID, teamID string, includePublicBoards bool) ([]*model.Board, error) { | ||||
| 	if includePublicBoards { | ||||
| 		boards, err := s.SearchBoardsForUserInTeam(teamID, "", userID) | ||||
|   | ||||
| @@ -92,6 +92,9 @@ type Store interface { | ||||
| 	// @withTransaction | ||||
| 	PatchBoard(boardID string, boardPatch *model.BoardPatch, userID string) (*model.Board, error) | ||||
| 	GetBoard(id string) (*model.Board, error) | ||||
| 	GetBoardsForTeam(teamID string, page, perPage int) ([]*model.Board, error) | ||||
| 	GetBoardsHistory(modifiedSince int, includeDeleted bool, teamID string, page, perPage int) ([]*model.Board, error) | ||||
| 	GetBlocksHistory(modifiedSince int, includeDeleted bool, teamID, boardID string, page, perPage int) ([]*model.Block, error) | ||||
| 	GetBoardsForUserAndTeam(userID, teamID string, includePublicBoards bool) ([]*model.Board, error) | ||||
| 	GetBoardsInTeamByIds(boardIDs []string, teamID string) ([]*model.Board, error) | ||||
| 	// @withTransaction | ||||
|   | ||||
		Reference in New Issue
	
	Block a user