mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-12 20:29:04 +08:00
chore: dashboard locking ee query-service (#3856)
* chore: dashboard locking ee query-service * chore: remove print statements * chore: remove unused imports * chore: no one is allowed to edit/delete the locked dashboard --------- Co-authored-by: Srikanth Chekuri <srikanth@Srikanths-MacBook-Pro.local>
This commit is contained in:
parent
b5654c8bfa
commit
5e349d8294
@ -160,6 +160,9 @@ func (ah *APIHandler) RegisterRoutes(router *mux.Router, am *baseapp.AuthMiddlew
|
|||||||
router.HandleFunc("/api/v1/billing", am.AdminAccess(ah.getBilling)).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/billing", am.AdminAccess(ah.getBilling)).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/api/v1/portal", am.AdminAccess(ah.portalSession)).Methods(http.MethodPost)
|
router.HandleFunc("/api/v1/portal", am.AdminAccess(ah.portalSession)).Methods(http.MethodPost)
|
||||||
|
|
||||||
|
router.HandleFunc("/api/v1/dashboards/{uuid}/lock", am.EditAccess(ah.lockDashboard)).Methods(http.MethodPut)
|
||||||
|
router.HandleFunc("/api/v1/dashboards/{uuid}/unlock", am.EditAccess(ah.unlockDashboard)).Methods(http.MethodPut)
|
||||||
|
|
||||||
router.HandleFunc("/api/v2/licenses",
|
router.HandleFunc("/api/v2/licenses",
|
||||||
am.ViewAccess(ah.listLicensesV2)).
|
am.ViewAccess(ah.listLicensesV2)).
|
||||||
Methods(http.MethodGet)
|
Methods(http.MethodGet)
|
||||||
|
51
ee/query-service/app/api/dashboard.go
Normal file
51
ee/query-service/app/api/dashboard.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gorilla/mux"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/app/dashboards"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/auth"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/common"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (ah *APIHandler) lockDashboard(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ah.lockUnlockDashboard(w, r, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ah *APIHandler) unlockDashboard(w http.ResponseWriter, r *http.Request) {
|
||||||
|
ah.lockUnlockDashboard(w, r, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ah *APIHandler) lockUnlockDashboard(w http.ResponseWriter, r *http.Request, lock bool) {
|
||||||
|
// Locking can only be done by the owner of the dashboard
|
||||||
|
// or an admin
|
||||||
|
|
||||||
|
// - Fetch the dashboard
|
||||||
|
// - Check if the user is the owner or an admin
|
||||||
|
// - If yes, lock/unlock the dashboard
|
||||||
|
// - If no, return 403
|
||||||
|
|
||||||
|
// Get the dashboard UUID from the request
|
||||||
|
uuid := mux.Vars(r)["uuid"]
|
||||||
|
dashboard, err := dashboards.GetDashboard(r.Context(), uuid)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user := common.GetUserFromContext(r.Context())
|
||||||
|
if !auth.IsAdmin(user) || (dashboard.CreateBy != nil && *dashboard.CreateBy != user.Email) {
|
||||||
|
RespondError(w, &model.ApiError{Typ: model.ErrorForbidden, Err: err}, "You are not authorized to lock/unlock this dashboard")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lock/Unlock the dashboard
|
||||||
|
err = dashboards.LockUnlockDashboard(r.Context(), uuid, lock)
|
||||||
|
if err != nil {
|
||||||
|
RespondError(w, &model.ApiError{Typ: model.ErrorInternal, Err: err}, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ah.Respond(w, "Dashboard updated successfully")
|
||||||
|
}
|
@ -128,6 +128,12 @@ func InitDB(dataSourceName string) (*sqlx.DB, error) {
|
|||||||
return nil, fmt.Errorf("error in adding column updated_by to dashboards table: %s", err.Error())
|
return nil, fmt.Errorf("error in adding column updated_by to dashboards table: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
locked := `ALTER TABLE dashboards ADD COLUMN locked INTEGER DEFAULT 0;`
|
||||||
|
_, err = db.Exec(locked)
|
||||||
|
if err != nil && !strings.Contains(err.Error(), "duplicate column name") {
|
||||||
|
return nil, fmt.Errorf("error in adding column locked to dashboards table: %s", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
return db, nil
|
return db, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +147,7 @@ type Dashboard struct {
|
|||||||
UpdateBy *string `json:"updated_by" db:"updated_by"`
|
UpdateBy *string `json:"updated_by" db:"updated_by"`
|
||||||
Title string `json:"-" db:"-"`
|
Title string `json:"-" db:"-"`
|
||||||
Data Data `json:"data" db:"data"`
|
Data Data `json:"data" db:"data"`
|
||||||
|
Locked *int `json:"isLocked" db:"locked"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Data map[string]interface{}
|
type Data map[string]interface{}
|
||||||
@ -239,6 +246,12 @@ func DeleteDashboard(ctx context.Context, uuid string, fm interfaces.FeatureLook
|
|||||||
return dErr
|
return dErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user := common.GetUserFromContext(ctx); user != nil {
|
||||||
|
if dashboard.Locked != nil && *dashboard.Locked == 1 {
|
||||||
|
return model.BadRequest(fmt.Errorf("dashboard is locked, please unlock the dashboard to be able to delete it"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
query := `DELETE FROM dashboards WHERE uuid=?`
|
query := `DELETE FROM dashboards WHERE uuid=?`
|
||||||
|
|
||||||
result, err := db.Exec(query, uuid)
|
result, err := db.Exec(query, uuid)
|
||||||
@ -289,6 +302,14 @@ func UpdateDashboard(ctx context.Context, uuid string, data map[string]interface
|
|||||||
return nil, apiErr
|
return nil, apiErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var userEmail string
|
||||||
|
if user := common.GetUserFromContext(ctx); user != nil {
|
||||||
|
userEmail = user.Email
|
||||||
|
if dashboard.Locked != nil && *dashboard.Locked == 1 {
|
||||||
|
return nil, model.BadRequest(fmt.Errorf("dashboard is locked, please unlock the dashboard to be able to edit it"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// check if the count of trace and logs QB panel has changed, if yes, then check feature flag count
|
// check if the count of trace and logs QB panel has changed, if yes, then check feature flag count
|
||||||
existingCount, existingTotal := countTraceAndLogsPanel(dashboard.Data)
|
existingCount, existingTotal := countTraceAndLogsPanel(dashboard.Data)
|
||||||
newCount, newTotal := countTraceAndLogsPanel(data)
|
newCount, newTotal := countTraceAndLogsPanel(data)
|
||||||
@ -306,10 +327,6 @@ func UpdateDashboard(ctx context.Context, uuid string, data map[string]interface
|
|||||||
}
|
}
|
||||||
|
|
||||||
dashboard.UpdatedAt = time.Now()
|
dashboard.UpdatedAt = time.Now()
|
||||||
var userEmail string
|
|
||||||
if user := common.GetUserFromContext(ctx); user != nil {
|
|
||||||
userEmail = user.Email
|
|
||||||
}
|
|
||||||
dashboard.UpdateBy = &userEmail
|
dashboard.UpdateBy = &userEmail
|
||||||
dashboard.Data = data
|
dashboard.Data = data
|
||||||
|
|
||||||
@ -327,6 +344,24 @@ func UpdateDashboard(ctx context.Context, uuid string, data map[string]interface
|
|||||||
return dashboard, nil
|
return dashboard, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LockUnlockDashboard(ctx context.Context, uuid string, lock bool) *model.ApiError {
|
||||||
|
var query string
|
||||||
|
if lock {
|
||||||
|
query = `UPDATE dashboards SET locked=1 WHERE uuid=?;`
|
||||||
|
} else {
|
||||||
|
query = `UPDATE dashboards SET locked=0 WHERE uuid=?;`
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := db.Exec(query, uuid)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
zap.S().Errorf("Error in updating dashboard: ", uuid, err)
|
||||||
|
return &model.ApiError{Typ: model.ErrorExec, Err: err}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func updateFeatureUsage(fm interfaces.FeatureLookup, usage int64) *model.ApiError {
|
func updateFeatureUsage(fm interfaces.FeatureLookup, usage int64) *model.ApiError {
|
||||||
feature, err := fm.GetFeatureFlag(model.QueryBuilderPanels)
|
feature, err := fm.GetFeatureFlag(model.QueryBuilderPanels)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -91,7 +91,6 @@ func ParseMetricAutocompleteTagParams(r *http.Request) (*model.MetricAutocomplet
|
|||||||
}
|
}
|
||||||
|
|
||||||
tagsStr := r.URL.Query().Get("tags")
|
tagsStr := r.URL.Query().Get("tags")
|
||||||
// fmt.Println(tagsStr)
|
|
||||||
|
|
||||||
// parsing tags
|
// parsing tags
|
||||||
var tags map[string]string
|
var tags map[string]string
|
||||||
|
@ -2,7 +2,6 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
@ -51,7 +50,6 @@ func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, services []stri
|
|||||||
|
|
||||||
func (mds *ModelDaoSqlite) SetApdexSettings(ctx context.Context, apdexSettings *model.ApdexSettings) *model.ApiError {
|
func (mds *ModelDaoSqlite) SetApdexSettings(ctx context.Context, apdexSettings *model.ApdexSettings) *model.ApiError {
|
||||||
|
|
||||||
fmt.Println("apdexSettings:", apdexSettings)
|
|
||||||
_, err := mds.db.NamedExec(`
|
_, err := mds.db.NamedExec(`
|
||||||
INSERT OR REPLACE INTO apdex_settings (
|
INSERT OR REPLACE INTO apdex_settings (
|
||||||
service_name,
|
service_name,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user