mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-29 19:12:02 +08:00
Fix: Multitenancy support for ORG (#7155)
* fix: support multitenancy in org * fix: register and login working now * fix: changes to migration * fix: migrations run both on sqlite and postgres * fix: remove user flags from fe and be * fix: remove ingestion keys from update * fix: multitenancy support for apdex settings * fix: render ts for users correctly * fix: fix migration to run for new tenants * fix: clean up migrations * fix: address comments * Update pkg/sqlmigration/013_update_organization.go Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * fix: fix build * fix: force invites with org id * Update pkg/query-service/auth/auth.go Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * fix: address comments * fix: address comments * fix: provier with their own dialect * fix: update dialects * fix: remove unwanted change * Update pkg/query-service/app/http_handler.go Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * fix: different files for types --------- Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
This commit is contained in:
parent
296a444bd8
commit
2d73f91380
@ -18,6 +18,7 @@ import (
|
|||||||
baseconstants "go.signoz.io/signoz/pkg/query-service/constants"
|
baseconstants "go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -45,7 +46,7 @@ func (ah *APIHandler) CloudIntegrationsGenerateConnectionParams(w http.ResponseW
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
apiKey, apiErr := ah.getOrCreateCloudIntegrationPAT(r.Context(), currentUser.OrgId, cloudProvider)
|
apiKey, apiErr := ah.getOrCreateCloudIntegrationPAT(r.Context(), currentUser.OrgID, cloudProvider)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, basemodel.WrapApiError(
|
RespondError(w, basemodel.WrapApiError(
|
||||||
apiErr, "couldn't provision PAT for cloud integration:",
|
apiErr, "couldn't provision PAT for cloud integration:",
|
||||||
@ -124,7 +125,7 @@ func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
for _, p := range allPats {
|
for _, p := range allPats {
|
||||||
if p.UserID == integrationUser.Id && p.Name == integrationPATName {
|
if p.UserID == integrationUser.ID && p.Name == integrationPATName {
|
||||||
return p.Token, nil
|
return p.Token, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -136,7 +137,7 @@ func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId
|
|||||||
|
|
||||||
newPAT := model.PAT{
|
newPAT := model.PAT{
|
||||||
Token: generatePATToken(),
|
Token: generatePATToken(),
|
||||||
UserID: integrationUser.Id,
|
UserID: integrationUser.ID,
|
||||||
Name: integrationPATName,
|
Name: integrationPATName,
|
||||||
Role: baseconstants.ViewerGroup,
|
Role: baseconstants.ViewerGroup,
|
||||||
ExpiresAt: 0,
|
ExpiresAt: 0,
|
||||||
@ -154,7 +155,7 @@ func (ah *APIHandler) getOrCreateCloudIntegrationPAT(ctx context.Context, orgId
|
|||||||
|
|
||||||
func (ah *APIHandler) getOrCreateCloudIntegrationUser(
|
func (ah *APIHandler) getOrCreateCloudIntegrationUser(
|
||||||
ctx context.Context, orgId string, cloudProvider string,
|
ctx context.Context, orgId string, cloudProvider string,
|
||||||
) (*basemodel.User, *basemodel.ApiError) {
|
) (*types.User, *basemodel.ApiError) {
|
||||||
cloudIntegrationUserId := fmt.Sprintf("%s-integration", cloudProvider)
|
cloudIntegrationUserId := fmt.Sprintf("%s-integration", cloudProvider)
|
||||||
|
|
||||||
integrationUserResult, apiErr := ah.AppDao().GetUser(ctx, cloudIntegrationUserId)
|
integrationUserResult, apiErr := ah.AppDao().GetUser(ctx, cloudIntegrationUserId)
|
||||||
@ -171,19 +172,21 @@ func (ah *APIHandler) getOrCreateCloudIntegrationUser(
|
|||||||
zap.String("cloudProvider", cloudProvider),
|
zap.String("cloudProvider", cloudProvider),
|
||||||
)
|
)
|
||||||
|
|
||||||
newUser := &basemodel.User{
|
newUser := &types.User{
|
||||||
Id: cloudIntegrationUserId,
|
ID: cloudIntegrationUserId,
|
||||||
Name: fmt.Sprintf("%s integration", cloudProvider),
|
Name: fmt.Sprintf("%s integration", cloudProvider),
|
||||||
Email: fmt.Sprintf("%s@signoz.io", cloudIntegrationUserId),
|
Email: fmt.Sprintf("%s@signoz.io", cloudIntegrationUserId),
|
||||||
CreatedAt: time.Now().Unix(),
|
TimeAuditable: types.TimeAuditable{
|
||||||
OrgId: orgId,
|
CreatedAt: time.Now(),
|
||||||
|
},
|
||||||
|
OrgID: orgId,
|
||||||
}
|
}
|
||||||
|
|
||||||
viewerGroup, apiErr := dao.DB().GetGroupByName(ctx, baseconstants.ViewerGroup)
|
viewerGroup, apiErr := dao.DB().GetGroupByName(ctx, baseconstants.ViewerGroup)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
return nil, basemodel.WrapApiError(apiErr, "couldn't get viewer group for creating integration user")
|
return nil, basemodel.WrapApiError(apiErr, "couldn't get viewer group for creating integration user")
|
||||||
}
|
}
|
||||||
newUser.GroupId = viewerGroup.ID
|
newUser.GroupID = viewerGroup.ID
|
||||||
|
|
||||||
passwordHash, err := auth.PasswordHash(uuid.NewString())
|
passwordHash, err := auth.PasswordHash(uuid.NewString())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -54,7 +54,7 @@ func (ah *APIHandler) createPAT(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// All the PATs are associated with the user creating the PAT.
|
// All the PATs are associated with the user creating the PAT.
|
||||||
pat.UserID = user.Id
|
pat.UserID = user.ID
|
||||||
pat.CreatedAt = time.Now().Unix()
|
pat.CreatedAt = time.Now().Unix()
|
||||||
pat.UpdatedAt = time.Now().Unix()
|
pat.UpdatedAt = time.Now().Unix()
|
||||||
pat.LastUsed = 0
|
pat.LastUsed = 0
|
||||||
@ -112,7 +112,7 @@ func (ah *APIHandler) updatePAT(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.UpdatedByUserID = user.Id
|
req.UpdatedByUserID = user.ID
|
||||||
id := mux.Vars(r)["id"]
|
id := mux.Vars(r)["id"]
|
||||||
req.UpdatedAt = time.Now().Unix()
|
req.UpdatedAt = time.Now().Unix()
|
||||||
zap.L().Info("Got Update PAT request", zap.Any("pat", req))
|
zap.L().Info("Got Update PAT request", zap.Any("pat", req))
|
||||||
@ -135,7 +135,7 @@ func (ah *APIHandler) getPATs(w http.ResponseWriter, r *http.Request) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
zap.L().Info("Get PATs for user", zap.String("user_id", user.Id))
|
zap.L().Info("Get PATs for user", zap.String("user_id", user.ID))
|
||||||
pats, apierr := ah.AppDao().ListPATs(ctx)
|
pats, apierr := ah.AppDao().ListPATs(ctx)
|
||||||
if apierr != nil {
|
if apierr != nil {
|
||||||
RespondError(w, apierr, nil)
|
RespondError(w, apierr, nil)
|
||||||
@ -157,7 +157,7 @@ func (ah *APIHandler) revokePAT(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
zap.L().Info("Revoke PAT with id", zap.String("id", id))
|
zap.L().Info("Revoke PAT with id", zap.String("id", id))
|
||||||
if apierr := ah.AppDao().RevokePAT(ctx, id, user.Id); apierr != nil {
|
if apierr := ah.AppDao().RevokePAT(ctx, id, user.ID); apierr != nil {
|
||||||
RespondError(w, apierr, nil)
|
RespondError(w, apierr, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"go.signoz.io/signoz/ee/query-service/rules"
|
"go.signoz.io/signoz/ee/query-service/rules"
|
||||||
"go.signoz.io/signoz/pkg/http/middleware"
|
"go.signoz.io/signoz/pkg/http/middleware"
|
||||||
"go.signoz.io/signoz/pkg/signoz"
|
"go.signoz.io/signoz/pkg/signoz"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||||
"go.signoz.io/signoz/pkg/web"
|
"go.signoz.io/signoz/pkg/web"
|
||||||
|
|
||||||
@ -340,14 +341,14 @@ func (s *Server) createPublicServer(apiHandler *api.APIHandler, web web.Web) (*h
|
|||||||
r := baseapp.NewRouter()
|
r := baseapp.NewRouter()
|
||||||
|
|
||||||
// add auth middleware
|
// add auth middleware
|
||||||
getUserFromRequest := func(ctx context.Context) (*basemodel.UserPayload, error) {
|
getUserFromRequest := func(ctx context.Context) (*types.GettableUser, error) {
|
||||||
user, err := auth.GetUserFromRequestContext(ctx, apiHandler)
|
user, err := auth.GetUserFromRequestContext(ctx, apiHandler)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.User.OrgId == "" {
|
if user.User.OrgID == "" {
|
||||||
return nil, basemodel.UnauthorizedError(errors.New("orgId is missing in the claims"))
|
return nil, basemodel.UnauthorizedError(errors.New("orgId is missing in the claims"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,14 +7,14 @@ import (
|
|||||||
|
|
||||||
"go.signoz.io/signoz/ee/query-service/app/api"
|
"go.signoz.io/signoz/ee/query-service/app/api"
|
||||||
baseauth "go.signoz.io/signoz/pkg/query-service/auth"
|
baseauth "go.signoz.io/signoz/pkg/query-service/auth"
|
||||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
|
||||||
"go.signoz.io/signoz/pkg/query-service/telemetry"
|
"go.signoz.io/signoz/pkg/query-service/telemetry"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetUserFromRequestContext(ctx context.Context, apiHandler *api.APIHandler) (*basemodel.UserPayload, error) {
|
func GetUserFromRequestContext(ctx context.Context, apiHandler *api.APIHandler) (*types.GettableUser, error) {
|
||||||
patToken, ok := authtypes.UUIDFromContext(ctx)
|
patToken, ok := authtypes.UUIDFromContext(ctx)
|
||||||
if ok && patToken != "" {
|
if ok && patToken != "" {
|
||||||
zap.L().Debug("Received a non-zero length PAT token")
|
zap.L().Debug("Received a non-zero length PAT token")
|
||||||
@ -40,9 +40,9 @@ func GetUserFromRequestContext(ctx context.Context, apiHandler *api.APIHandler)
|
|||||||
}
|
}
|
||||||
telemetry.GetInstance().SetPatTokenUser()
|
telemetry.GetInstance().SetPatTokenUser()
|
||||||
dao.UpdatePATLastUsed(ctx, patToken, time.Now().Unix())
|
dao.UpdatePATLastUsed(ctx, patToken, time.Now().Unix())
|
||||||
user.User.GroupId = group.ID
|
user.User.GroupID = group.ID
|
||||||
user.User.Id = pat.Id
|
user.User.ID = pat.Id
|
||||||
return &basemodel.UserPayload{
|
return &types.GettableUser{
|
||||||
User: user.User,
|
User: user.User,
|
||||||
Role: pat.Role,
|
Role: pat.Role,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
basedao "go.signoz.io/signoz/pkg/query-service/dao"
|
basedao "go.signoz.io/signoz/pkg/query-service/dao"
|
||||||
baseint "go.signoz.io/signoz/pkg/query-service/interfaces"
|
baseint "go.signoz.io/signoz/pkg/query-service/interfaces"
|
||||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,7 +40,7 @@ type ModelDao interface {
|
|||||||
GetPAT(ctx context.Context, pat string) (*model.PAT, basemodel.BaseApiError)
|
GetPAT(ctx context.Context, pat string) (*model.PAT, basemodel.BaseApiError)
|
||||||
UpdatePATLastUsed(ctx context.Context, pat string, lastUsed int64) basemodel.BaseApiError
|
UpdatePATLastUsed(ctx context.Context, pat string, lastUsed int64) basemodel.BaseApiError
|
||||||
GetPATByID(ctx context.Context, id string) (*model.PAT, basemodel.BaseApiError)
|
GetPATByID(ctx context.Context, id string) (*model.PAT, basemodel.BaseApiError)
|
||||||
GetUserByPAT(ctx context.Context, token string) (*basemodel.UserPayload, basemodel.BaseApiError)
|
GetUserByPAT(ctx context.Context, token string) (*types.GettableUser, basemodel.BaseApiError)
|
||||||
ListPATs(ctx context.Context) ([]model.PAT, basemodel.BaseApiError)
|
ListPATs(ctx context.Context) ([]model.PAT, basemodel.BaseApiError)
|
||||||
RevokePAT(ctx context.Context, id string, userID string) basemodel.BaseApiError
|
RevokePAT(ctx context.Context, id string, userID string) basemodel.BaseApiError
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,12 @@ import (
|
|||||||
baseconst "go.signoz.io/signoz/pkg/query-service/constants"
|
baseconst "go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (*basemodel.User, basemodel.BaseApiError) {
|
func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (*types.User, basemodel.BaseApiError) {
|
||||||
// get auth domain from email domain
|
// get auth domain from email domain
|
||||||
domain, apierr := m.GetDomainByEmail(ctx, email)
|
domain, apierr := m.GetDomainByEmail(ctx, email)
|
||||||
if apierr != nil {
|
if apierr != nil {
|
||||||
@ -42,15 +43,17 @@ func (m *modelDao) createUserForSAMLRequest(ctx context.Context, email string) (
|
|||||||
return nil, apiErr
|
return nil, apiErr
|
||||||
}
|
}
|
||||||
|
|
||||||
user := &basemodel.User{
|
user := &types.User{
|
||||||
Id: uuid.NewString(),
|
ID: uuid.NewString(),
|
||||||
Name: "",
|
Name: "",
|
||||||
Email: email,
|
Email: email,
|
||||||
Password: hash,
|
Password: hash,
|
||||||
CreatedAt: time.Now().Unix(),
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
},
|
||||||
ProfilePictureURL: "", // Currently unused
|
ProfilePictureURL: "", // Currently unused
|
||||||
GroupId: group.ID,
|
GroupID: group.ID,
|
||||||
OrgId: domain.OrgId,
|
OrgID: domain.OrgId,
|
||||||
}
|
}
|
||||||
|
|
||||||
user, apiErr = m.CreateUser(ctx, user, false)
|
user, apiErr = m.CreateUser(ctx, user, false)
|
||||||
@ -73,7 +76,7 @@ func (m *modelDao) PrepareSsoRedirect(ctx context.Context, redirectUri, email st
|
|||||||
return "", model.BadRequestStr("invalid user email received from the auth provider")
|
return "", model.BadRequestStr("invalid user email received from the auth provider")
|
||||||
}
|
}
|
||||||
|
|
||||||
user := &basemodel.User{}
|
user := &types.User{}
|
||||||
|
|
||||||
if userPayload == nil {
|
if userPayload == nil {
|
||||||
newUser, apiErr := m.createUserForSAMLRequest(ctx, email)
|
newUser, apiErr := m.createUserForSAMLRequest(ctx, email)
|
||||||
@ -95,7 +98,7 @@ func (m *modelDao) PrepareSsoRedirect(ctx context.Context, redirectUri, email st
|
|||||||
return fmt.Sprintf("%s?jwt=%s&usr=%s&refreshjwt=%s",
|
return fmt.Sprintf("%s?jwt=%s&usr=%s&refreshjwt=%s",
|
||||||
redirectUri,
|
redirectUri,
|
||||||
tokenStore.AccessJwt,
|
tokenStore.AccessJwt,
|
||||||
user.Id,
|
user.ID,
|
||||||
tokenStore.RefreshJwt), nil
|
tokenStore.RefreshJwt), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
|
|
||||||
"go.signoz.io/signoz/ee/query-service/model"
|
"go.signoz.io/signoz/ee/query-service/model"
|
||||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -42,10 +43,10 @@ func (m *modelDao) CreatePAT(ctx context.Context, p model.PAT) (model.PAT, basem
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
p.CreatedByUser = model.User{
|
p.CreatedByUser = model.User{
|
||||||
Id: createdByUser.Id,
|
Id: createdByUser.ID,
|
||||||
Name: createdByUser.Name,
|
Name: createdByUser.Name,
|
||||||
Email: createdByUser.Email,
|
Email: createdByUser.Email,
|
||||||
CreatedAt: createdByUser.CreatedAt,
|
CreatedAt: createdByUser.CreatedAt.Unix(),
|
||||||
ProfilePictureURL: createdByUser.ProfilePictureURL,
|
ProfilePictureURL: createdByUser.ProfilePictureURL,
|
||||||
NotFound: false,
|
NotFound: false,
|
||||||
}
|
}
|
||||||
@ -95,10 +96,10 @@ func (m *modelDao) ListPATs(ctx context.Context) ([]model.PAT, basemodel.BaseApi
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pats[i].CreatedByUser = model.User{
|
pats[i].CreatedByUser = model.User{
|
||||||
Id: createdByUser.Id,
|
Id: createdByUser.ID,
|
||||||
Name: createdByUser.Name,
|
Name: createdByUser.Name,
|
||||||
Email: createdByUser.Email,
|
Email: createdByUser.Email,
|
||||||
CreatedAt: createdByUser.CreatedAt,
|
CreatedAt: createdByUser.CreatedAt.Unix(),
|
||||||
ProfilePictureURL: createdByUser.ProfilePictureURL,
|
ProfilePictureURL: createdByUser.ProfilePictureURL,
|
||||||
NotFound: false,
|
NotFound: false,
|
||||||
}
|
}
|
||||||
@ -111,10 +112,10 @@ func (m *modelDao) ListPATs(ctx context.Context) ([]model.PAT, basemodel.BaseApi
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
pats[i].UpdatedByUser = model.User{
|
pats[i].UpdatedByUser = model.User{
|
||||||
Id: updatedByUser.Id,
|
Id: updatedByUser.ID,
|
||||||
Name: updatedByUser.Name,
|
Name: updatedByUser.Name,
|
||||||
Email: updatedByUser.Email,
|
Email: updatedByUser.Email,
|
||||||
CreatedAt: updatedByUser.CreatedAt,
|
CreatedAt: updatedByUser.CreatedAt.Unix(),
|
||||||
ProfilePictureURL: updatedByUser.ProfilePictureURL,
|
ProfilePictureURL: updatedByUser.ProfilePictureURL,
|
||||||
NotFound: false,
|
NotFound: false,
|
||||||
}
|
}
|
||||||
@ -170,8 +171,8 @@ func (m *modelDao) GetPATByID(ctx context.Context, id string) (*model.PAT, basem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// deprecated
|
// deprecated
|
||||||
func (m *modelDao) GetUserByPAT(ctx context.Context, token string) (*basemodel.UserPayload, basemodel.BaseApiError) {
|
func (m *modelDao) GetUserByPAT(ctx context.Context, token string) (*types.GettableUser, basemodel.BaseApiError) {
|
||||||
users := []basemodel.UserPayload{}
|
users := []types.GettableUser{}
|
||||||
|
|
||||||
query := `SELECT
|
query := `SELECT
|
||||||
u.id,
|
u.id,
|
||||||
|
@ -11,7 +11,7 @@ import (
|
|||||||
saml2 "github.com/russellhaering/gosaml2"
|
saml2 "github.com/russellhaering/gosaml2"
|
||||||
"go.signoz.io/signoz/ee/query-service/sso"
|
"go.signoz.io/signoz/ee/query-service/sso"
|
||||||
"go.signoz.io/signoz/ee/query-service/sso/saml"
|
"go.signoz.io/signoz/ee/query-service/sso/saml"
|
||||||
basemodel "go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ type OrgDomain struct {
|
|||||||
SamlConfig *SamlConfig `json:"samlConfig"`
|
SamlConfig *SamlConfig `json:"samlConfig"`
|
||||||
GoogleAuthConfig *GoogleOAuthConfig `json:"googleAuthConfig"`
|
GoogleAuthConfig *GoogleOAuthConfig `json:"googleAuthConfig"`
|
||||||
|
|
||||||
Org *basemodel.Organization
|
Org *types.Organization
|
||||||
}
|
}
|
||||||
|
|
||||||
func (od *OrgDomain) String() string {
|
func (od *OrgDomain) String() string {
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
import axios from 'api';
|
|
||||||
import { ErrorResponseHandler } from 'api/ErrorResponseHandler';
|
|
||||||
import { AxiosError } from 'axios';
|
|
||||||
import { ErrorResponse, SuccessResponse } from 'types/api';
|
|
||||||
import { PayloadProps, Props } from 'types/api/user/setFlags';
|
|
||||||
|
|
||||||
const setFlags = async (
|
|
||||||
props: Props,
|
|
||||||
): Promise<SuccessResponse<PayloadProps> | ErrorResponse> => {
|
|
||||||
try {
|
|
||||||
const response = await axios.patch(`/user/${props.userId}/flags`, {
|
|
||||||
...props.flags,
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
error: null,
|
|
||||||
message: response.data?.status,
|
|
||||||
payload: response.data,
|
|
||||||
};
|
|
||||||
} catch (error) {
|
|
||||||
return ErrorResponseHandler(error as AxiosError);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default setFlags;
|
|
@ -1,4 +0,0 @@
|
|||||||
export default interface ReleaseNoteProps {
|
|
||||||
path?: string;
|
|
||||||
release?: string;
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
import { Button, Space } from 'antd';
|
|
||||||
import setFlags from 'api/user/setFlags';
|
|
||||||
import MessageTip from 'components/MessageTip';
|
|
||||||
import { useAppContext } from 'providers/App/App';
|
|
||||||
import { useCallback } from 'react';
|
|
||||||
import { UserFlags } from 'types/api/user/setFlags';
|
|
||||||
|
|
||||||
import ReleaseNoteProps from '../ReleaseNoteProps';
|
|
||||||
|
|
||||||
export default function ReleaseNote0120({
|
|
||||||
release,
|
|
||||||
}: ReleaseNoteProps): JSX.Element | null {
|
|
||||||
const { user, setUserFlags } = useAppContext();
|
|
||||||
|
|
||||||
const handleDontShow = useCallback(async (): Promise<void> => {
|
|
||||||
const flags: UserFlags = { ReleaseNote0120Hide: 'Y' };
|
|
||||||
|
|
||||||
try {
|
|
||||||
setUserFlags(flags);
|
|
||||||
if (!user) {
|
|
||||||
// no user is set, so escape the routine
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await setFlags({ userId: user.id, flags });
|
|
||||||
|
|
||||||
if (response.statusCode !== 200) {
|
|
||||||
console.log('failed to complete do not show status', response.error);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
// here we do not nothing as the cost of error is minor,
|
|
||||||
// the user can switch the do no show option again in the further.
|
|
||||||
console.log('unexpected error: failed to complete do not show status', e);
|
|
||||||
}
|
|
||||||
}, [setUserFlags, user]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MessageTip
|
|
||||||
show
|
|
||||||
message={
|
|
||||||
<div>
|
|
||||||
You are using {release} of SigNoz. We have introduced distributed setup in
|
|
||||||
v0.12.0 release. If you use or plan to use clickhouse queries in dashboard
|
|
||||||
or alerts, you might want to read about querying the new distributed tables{' '}
|
|
||||||
<a
|
|
||||||
href="https://signoz.io/docs/operate/migration/upgrade-0.12/#querying-distributed-tables"
|
|
||||||
target="_blank"
|
|
||||||
rel="noreferrer"
|
|
||||||
>
|
|
||||||
here
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
action={
|
|
||||||
<Space>
|
|
||||||
<Button onClick={handleDontShow}>Do not show again</Button>
|
|
||||||
</Space>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
import ReleaseNoteProps from 'components/ReleaseNote/ReleaseNoteProps';
|
|
||||||
import ReleaseNote0120 from 'components/ReleaseNote/Releases/ReleaseNote0120';
|
|
||||||
import ROUTES from 'constants/routes';
|
|
||||||
import { useAppContext } from 'providers/App/App';
|
|
||||||
import { useSelector } from 'react-redux';
|
|
||||||
import { AppState } from 'store/reducers';
|
|
||||||
import { UserFlags } from 'types/api/user/setFlags';
|
|
||||||
import AppReducer from 'types/reducer/app';
|
|
||||||
|
|
||||||
interface ComponentMapType {
|
|
||||||
match: (
|
|
||||||
path: string | undefined,
|
|
||||||
version: string,
|
|
||||||
userFlags: UserFlags | null,
|
|
||||||
) => boolean;
|
|
||||||
component: ({ path, release }: ReleaseNoteProps) => JSX.Element | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const allComponentMap: ComponentMapType[] = [
|
|
||||||
{
|
|
||||||
match: (
|
|
||||||
path: string | undefined,
|
|
||||||
version: string,
|
|
||||||
userFlags: UserFlags | null,
|
|
||||||
): boolean => {
|
|
||||||
if (!path) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const allowedPaths: string[] = [
|
|
||||||
ROUTES.LIST_ALL_ALERT,
|
|
||||||
ROUTES.APPLICATION,
|
|
||||||
ROUTES.ALL_DASHBOARD,
|
|
||||||
];
|
|
||||||
|
|
||||||
return (
|
|
||||||
userFlags?.ReleaseNote0120Hide !== 'Y' &&
|
|
||||||
allowedPaths.includes(path) &&
|
|
||||||
version.startsWith('v0.12')
|
|
||||||
);
|
|
||||||
},
|
|
||||||
component: ReleaseNote0120,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
// ReleaseNote prints release specific warnings and notes that
|
|
||||||
// user needs to be aware of before using the upgraded version.
|
|
||||||
function ReleaseNote({ path }: ReleaseNoteProps): JSX.Element | null {
|
|
||||||
const { user } = useAppContext();
|
|
||||||
const { currentVersion } = useSelector<AppState, AppReducer>(
|
|
||||||
(state) => state.app,
|
|
||||||
);
|
|
||||||
|
|
||||||
const c = allComponentMap.find((item) =>
|
|
||||||
item.match(path, currentVersion, user.flags),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!c) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <c.component path={path} release={currentVersion} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
ReleaseNote.defaultProps = {
|
|
||||||
path: '',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default ReleaseNote;
|
|
@ -1,21 +1,18 @@
|
|||||||
import { Space } from 'antd';
|
import { Space } from 'antd';
|
||||||
import getAll from 'api/alerts/getAll';
|
import getAll from 'api/alerts/getAll';
|
||||||
import logEvent from 'api/common/logEvent';
|
import logEvent from 'api/common/logEvent';
|
||||||
import ReleaseNote from 'components/ReleaseNote';
|
|
||||||
import Spinner from 'components/Spinner';
|
import Spinner from 'components/Spinner';
|
||||||
import { useNotifications } from 'hooks/useNotifications';
|
import { useNotifications } from 'hooks/useNotifications';
|
||||||
import { isUndefined } from 'lodash-es';
|
import { isUndefined } from 'lodash-es';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useQuery } from 'react-query';
|
import { useQuery } from 'react-query';
|
||||||
import { useLocation } from 'react-router-dom';
|
|
||||||
|
|
||||||
import { AlertsEmptyState } from './AlertsEmptyState/AlertsEmptyState';
|
import { AlertsEmptyState } from './AlertsEmptyState/AlertsEmptyState';
|
||||||
import ListAlert from './ListAlert';
|
import ListAlert from './ListAlert';
|
||||||
|
|
||||||
function ListAlertRules(): JSX.Element {
|
function ListAlertRules(): JSX.Element {
|
||||||
const { t } = useTranslation('common');
|
const { t } = useTranslation('common');
|
||||||
const location = useLocation();
|
|
||||||
const { data, isError, isLoading, refetch, status } = useQuery('allAlerts', {
|
const { data, isError, isLoading, refetch, status } = useQuery('allAlerts', {
|
||||||
queryFn: getAll,
|
queryFn: getAll,
|
||||||
cacheTime: 0,
|
cacheTime: 0,
|
||||||
@ -70,7 +67,6 @@ function ListAlertRules(): JSX.Element {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" size="large" style={{ width: '100%' }}>
|
<Space direction="vertical" size="large" style={{ width: '100%' }}>
|
||||||
<ReleaseNote path={location.pathname} />
|
|
||||||
<ListAlert
|
<ListAlert
|
||||||
{...{
|
{...{
|
||||||
allAlertRules: data.payload,
|
allAlertRules: data.payload,
|
||||||
|
@ -281,7 +281,7 @@ function Members(): JSX.Element {
|
|||||||
const { joinedOn } = record;
|
const { joinedOn } = record;
|
||||||
return (
|
return (
|
||||||
<Typography>
|
<Typography>
|
||||||
{dayjs.unix(Number(joinedOn)).format(DATE_TIME_FORMATS.MONTH_DATE_FULL)}
|
{dayjs(joinedOn).format(DATE_TIME_FORMATS.MONTH_DATE_FULL)}
|
||||||
</Typography>
|
</Typography>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,14 +1,10 @@
|
|||||||
import './DashboardsListPage.styles.scss';
|
import './DashboardsListPage.styles.scss';
|
||||||
|
|
||||||
import { Space, Typography } from 'antd';
|
import { Space, Typography } from 'antd';
|
||||||
import ReleaseNote from 'components/ReleaseNote';
|
|
||||||
import ListOfAllDashboard from 'container/ListOfDashboard';
|
import ListOfAllDashboard from 'container/ListOfDashboard';
|
||||||
import { LayoutGrid } from 'lucide-react';
|
import { LayoutGrid } from 'lucide-react';
|
||||||
import { useLocation } from 'react-router-dom';
|
|
||||||
|
|
||||||
function DashboardsListPage(): JSX.Element {
|
function DashboardsListPage(): JSX.Element {
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space
|
<Space
|
||||||
direction="vertical"
|
direction="vertical"
|
||||||
@ -16,7 +12,6 @@ function DashboardsListPage(): JSX.Element {
|
|||||||
style={{ width: '100%' }}
|
style={{ width: '100%' }}
|
||||||
className="dashboard-list-page"
|
className="dashboard-list-page"
|
||||||
>
|
>
|
||||||
<ReleaseNote path={location.pathname} />
|
|
||||||
<div className="dashboard-header">
|
<div className="dashboard-header">
|
||||||
<LayoutGrid size={14} className="icon" />
|
<LayoutGrid size={14} className="icon" />
|
||||||
<Typography.Text className="text">Dashboards</Typography.Text>
|
<Typography.Text className="text">Dashboards</Typography.Text>
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
import { Space } from 'antd';
|
import { Space } from 'antd';
|
||||||
import ReleaseNote from 'components/ReleaseNote';
|
|
||||||
import ServicesApplication from 'container/ServiceApplication';
|
import ServicesApplication from 'container/ServiceApplication';
|
||||||
import { useLocation } from 'react-router-dom';
|
|
||||||
|
|
||||||
function Metrics(): JSX.Element {
|
function Metrics(): JSX.Element {
|
||||||
const location = useLocation();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Space direction="vertical" style={{ width: '100%' }}>
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
<ReleaseNote path={location.pathname} />
|
|
||||||
|
|
||||||
<ServicesApplication />
|
<ServicesApplication />
|
||||||
</Space>
|
</Space>
|
||||||
);
|
);
|
||||||
|
@ -21,7 +21,6 @@ import { FeatureFlagProps as FeatureFlags } from 'types/api/features/getFeatures
|
|||||||
import { PayloadProps as LicensesResModel } from 'types/api/licenses/getAll';
|
import { PayloadProps as LicensesResModel } from 'types/api/licenses/getAll';
|
||||||
import { LicenseV3ResModel } from 'types/api/licensesV3/getActive';
|
import { LicenseV3ResModel } from 'types/api/licensesV3/getActive';
|
||||||
import { Organization } from 'types/api/user/getOrganization';
|
import { Organization } from 'types/api/user/getOrganization';
|
||||||
import { UserFlags } from 'types/api/user/setFlags';
|
|
||||||
import { OrgPreference } from 'types/reducer/app';
|
import { OrgPreference } from 'types/reducer/app';
|
||||||
import { USER_ROLES } from 'types/roles';
|
import { USER_ROLES } from 'types/roles';
|
||||||
|
|
||||||
@ -158,13 +157,6 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
|||||||
}
|
}
|
||||||
}, [orgPreferencesData, isFetchingOrgPreferences]);
|
}, [orgPreferencesData, isFetchingOrgPreferences]);
|
||||||
|
|
||||||
function setUserFlags(userflags: UserFlags): void {
|
|
||||||
setUser((prev) => ({
|
|
||||||
...prev,
|
|
||||||
flags: userflags,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateUser(user: IUser): void {
|
function updateUser(user: IUser): void {
|
||||||
setUser((prev) => ({
|
setUser((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
@ -252,7 +244,6 @@ export function AppProvider({ children }: PropsWithChildren): JSX.Element {
|
|||||||
orgPreferencesFetchError,
|
orgPreferencesFetchError,
|
||||||
licensesRefetch,
|
licensesRefetch,
|
||||||
updateUser,
|
updateUser,
|
||||||
setUserFlags,
|
|
||||||
updateOrgPreferences,
|
updateOrgPreferences,
|
||||||
updateOrg,
|
updateOrg,
|
||||||
}),
|
}),
|
||||||
|
@ -3,7 +3,6 @@ import { PayloadProps as LicensesResModel } from 'types/api/licenses/getAll';
|
|||||||
import { LicenseV3ResModel } from 'types/api/licensesV3/getActive';
|
import { LicenseV3ResModel } from 'types/api/licensesV3/getActive';
|
||||||
import { Organization } from 'types/api/user/getOrganization';
|
import { Organization } from 'types/api/user/getOrganization';
|
||||||
import { PayloadProps as User } from 'types/api/user/getUser';
|
import { PayloadProps as User } from 'types/api/user/getUser';
|
||||||
import { UserFlags } from 'types/api/user/setFlags';
|
|
||||||
import { OrgPreference } from 'types/reducer/app';
|
import { OrgPreference } from 'types/reducer/app';
|
||||||
|
|
||||||
export interface IAppContext {
|
export interface IAppContext {
|
||||||
@ -26,7 +25,6 @@ export interface IAppContext {
|
|||||||
orgPreferencesFetchError: unknown;
|
orgPreferencesFetchError: unknown;
|
||||||
licensesRefetch: () => void;
|
licensesRefetch: () => void;
|
||||||
updateUser: (user: IUser) => void;
|
updateUser: (user: IUser) => void;
|
||||||
setUserFlags: (flags: UserFlags) => void;
|
|
||||||
updateOrgPreferences: (orgPreferences: OrgPreference[]) => void;
|
updateOrgPreferences: (orgPreferences: OrgPreference[]) => void;
|
||||||
updateOrg(orgId: string, updatedOrgName: string): void;
|
updateOrg(orgId: string, updatedOrgName: string): void;
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ function getUserDefaults(): IUser {
|
|||||||
name: '',
|
name: '',
|
||||||
profilePictureURL: '',
|
profilePictureURL: '',
|
||||||
createdAt: 0,
|
createdAt: 0,
|
||||||
flags: {},
|
|
||||||
organization: '',
|
organization: '',
|
||||||
orgId: '',
|
orgId: '',
|
||||||
role: 'VIEWER',
|
role: 'VIEWER',
|
||||||
|
@ -128,7 +128,6 @@ export function getAppContextMock(
|
|||||||
name: 'John Doe',
|
name: 'John Doe',
|
||||||
profilePictureURL: '',
|
profilePictureURL: '',
|
||||||
createdAt: 1732544623,
|
createdAt: 1732544623,
|
||||||
flags: {},
|
|
||||||
organization: 'Nightswatch',
|
organization: 'Nightswatch',
|
||||||
orgId: 'does-not-matter-id',
|
orgId: 'does-not-matter-id',
|
||||||
role: role as ROLES,
|
role: role as ROLES,
|
||||||
@ -326,7 +325,6 @@ export function getAppContextMock(
|
|||||||
orgPreferencesFetchError: null,
|
orgPreferencesFetchError: null,
|
||||||
isLoggedIn: true,
|
isLoggedIn: true,
|
||||||
updateUser: jest.fn(),
|
updateUser: jest.fn(),
|
||||||
setUserFlags: jest.fn(),
|
|
||||||
updateOrg: jest.fn(),
|
updateOrg: jest.fn(),
|
||||||
updateOrgPreferences: jest.fn(),
|
updateOrgPreferences: jest.fn(),
|
||||||
licensesRefetch: jest.fn(),
|
licensesRefetch: jest.fn(),
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { UserFlags } from 'types/api/user/setFlags';
|
|
||||||
import { User } from 'types/reducer/app';
|
import { User } from 'types/reducer/app';
|
||||||
import { ROLES } from 'types/roles';
|
import { ROLES } from 'types/roles';
|
||||||
|
|
||||||
@ -16,6 +15,5 @@ export interface PayloadProps {
|
|||||||
profilePictureURL: string;
|
profilePictureURL: string;
|
||||||
organization: string;
|
organization: string;
|
||||||
role: ROLES;
|
role: ROLES;
|
||||||
flags: UserFlags;
|
|
||||||
groupId: string;
|
groupId: string;
|
||||||
}
|
}
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import { User } from 'types/reducer/app';
|
|
||||||
|
|
||||||
export interface UserFlags {
|
|
||||||
ReleaseNote0120Hide?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type PayloadProps = UserFlags;
|
|
||||||
|
|
||||||
export interface Props {
|
|
||||||
userId: User['userId'];
|
|
||||||
flags: UserFlags;
|
|
||||||
}
|
|
@ -1,21 +1,27 @@
|
|||||||
package app
|
package app
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
|
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (aH *APIHandler) setApdexSettings(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) setApdexSettings(w http.ResponseWriter, r *http.Request) {
|
||||||
|
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
RespondError(w, &model.ApiError{Err: errors.New("unauthorized"), Typ: model.ErrorUnauthorized}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
req, err := parseSetApdexScoreRequest(r)
|
req, err := parseSetApdexScoreRequest(r)
|
||||||
if aH.HandleError(w, err, http.StatusBadRequest) {
|
if aH.HandleError(w, err, http.StatusBadRequest) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dao.DB().SetApdexSettings(context.Background(), req); err != nil {
|
if err := dao.DB().SetApdexSettings(r.Context(), claims.OrgID, req); err != nil {
|
||||||
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
|
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -25,7 +31,12 @@ func (aH *APIHandler) setApdexSettings(w http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (aH *APIHandler) getApdexSettings(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getApdexSettings(w http.ResponseWriter, r *http.Request) {
|
||||||
services := r.URL.Query().Get("services")
|
services := r.URL.Query().Get("services")
|
||||||
apdexSet, err := dao.DB().GetApdexSettings(context.Background(), strings.Split(strings.TrimSpace(services), ","))
|
claims, ok := authtypes.ClaimsFromContext(r.Context())
|
||||||
|
if !ok {
|
||||||
|
RespondError(w, &model.ApiError{Err: errors.New("unauthorized"), Typ: model.ErrorUnauthorized}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
apdexSet, err := dao.DB().GetApdexSettings(r.Context(), claims.OrgID, strings.Split(strings.TrimSpace(services), ","))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
|
RespondError(w, &model.ApiError{Err: err, Typ: model.ErrorInternal}, nil)
|
||||||
return
|
return
|
||||||
|
@ -9,13 +9,14 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/query-service/auth"
|
"go.signoz.io/signoz/pkg/query-service/auth"
|
||||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthMiddleware struct {
|
type AuthMiddleware struct {
|
||||||
GetUserFromRequest func(r context.Context) (*model.UserPayload, error)
|
GetUserFromRequest func(r context.Context) (*types.GettableUser, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthMiddleware(f func(ctx context.Context) (*model.UserPayload, error)) *AuthMiddleware {
|
func NewAuthMiddleware(f func(ctx context.Context) (*types.GettableUser, error)) *AuthMiddleware {
|
||||||
return &AuthMiddleware{
|
return &AuthMiddleware{
|
||||||
GetUserFromRequest: f,
|
GetUserFromRequest: f,
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go.signoz.io/signoz/pkg/query-service/app/metricsexplorer"
|
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
@ -19,6 +18,8 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/app/metricsexplorer"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/websocket"
|
"github.com/gorilla/websocket"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
@ -51,6 +52,7 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/query-service/contextlinks"
|
"go.signoz.io/signoz/pkg/query-service/contextlinks"
|
||||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||||
"go.signoz.io/signoz/pkg/query-service/postprocess"
|
"go.signoz.io/signoz/pkg/query-service/postprocess"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
@ -597,8 +599,6 @@ func (aH *APIHandler) RegisterRoutes(router *mux.Router, am *AuthMiddleware) {
|
|||||||
router.HandleFunc("/api/v1/user/{id}", am.SelfAccess(aH.editUser)).Methods(http.MethodPut)
|
router.HandleFunc("/api/v1/user/{id}", am.SelfAccess(aH.editUser)).Methods(http.MethodPut)
|
||||||
router.HandleFunc("/api/v1/user/{id}", am.AdminAccess(aH.deleteUser)).Methods(http.MethodDelete)
|
router.HandleFunc("/api/v1/user/{id}", am.AdminAccess(aH.deleteUser)).Methods(http.MethodDelete)
|
||||||
|
|
||||||
router.HandleFunc("/api/v1/user/{id}/flags", am.SelfAccess(aH.patchUserFlag)).Methods(http.MethodPatch)
|
|
||||||
|
|
||||||
router.HandleFunc("/api/v1/rbac/role/{id}", am.SelfAccess(aH.getRole)).Methods(http.MethodGet)
|
router.HandleFunc("/api/v1/rbac/role/{id}", am.SelfAccess(aH.getRole)).Methods(http.MethodGet)
|
||||||
router.HandleFunc("/api/v1/rbac/role/{id}", am.AdminAccess(aH.editRole)).Methods(http.MethodPut)
|
router.HandleFunc("/api/v1/rbac/role/{id}", am.AdminAccess(aH.editRole)).Methods(http.MethodPut)
|
||||||
|
|
||||||
@ -2108,8 +2108,13 @@ func (aH *APIHandler) revokeInvite(w http.ResponseWriter, r *http.Request) {
|
|||||||
// listPendingInvites is used to list the pending invites.
|
// listPendingInvites is used to list the pending invites.
|
||||||
func (aH *APIHandler) listPendingInvites(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) listPendingInvites(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := r.Context()
|
||||||
invites, err := dao.DB().GetInvites(ctx)
|
claims, ok := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
RespondError(w, &model.ApiError{Err: errors.New("failed to get org id from context"), Typ: model.ErrorInternal}, nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
invites, err := dao.DB().GetInvites(ctx, claims.OrgID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
RespondError(w, err, nil)
|
RespondError(w, err, nil)
|
||||||
return
|
return
|
||||||
@ -2120,7 +2125,7 @@ func (aH *APIHandler) listPendingInvites(w http.ResponseWriter, r *http.Request)
|
|||||||
var resp []*model.InvitationResponseObject
|
var resp []*model.InvitationResponseObject
|
||||||
for _, inv := range invites {
|
for _, inv := range invites {
|
||||||
|
|
||||||
org, apiErr := dao.DB().GetOrg(ctx, inv.OrgId)
|
org, apiErr := dao.DB().GetOrg(ctx, inv.OrgID)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, apiErr, nil)
|
RespondError(w, apiErr, nil)
|
||||||
}
|
}
|
||||||
@ -2128,7 +2133,7 @@ func (aH *APIHandler) listPendingInvites(w http.ResponseWriter, r *http.Request)
|
|||||||
Name: inv.Name,
|
Name: inv.Name,
|
||||||
Email: inv.Email,
|
Email: inv.Email,
|
||||||
Token: inv.Token,
|
Token: inv.Token,
|
||||||
CreatedAt: inv.CreatedAt,
|
CreatedAt: inv.CreatedAt.Unix(),
|
||||||
Role: inv.Role,
|
Role: inv.Role,
|
||||||
Organization: org.Name,
|
Organization: org.Name,
|
||||||
})
|
})
|
||||||
@ -2271,13 +2276,15 @@ func (aH *APIHandler) editUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
old.ProfilePictureURL = update.ProfilePictureURL
|
old.ProfilePictureURL = update.ProfilePictureURL
|
||||||
}
|
}
|
||||||
|
|
||||||
_, apiErr = dao.DB().EditUser(ctx, &model.User{
|
_, apiErr = dao.DB().EditUser(ctx, &types.User{
|
||||||
Id: old.Id,
|
ID: old.ID,
|
||||||
Name: old.Name,
|
Name: old.Name,
|
||||||
OrgId: old.OrgId,
|
OrgID: old.OrgID,
|
||||||
Email: old.Email,
|
Email: old.Email,
|
||||||
Password: old.Password,
|
Password: old.Password,
|
||||||
CreatedAt: old.CreatedAt,
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: old.CreatedAt,
|
||||||
|
},
|
||||||
ProfilePictureURL: old.ProfilePictureURL,
|
ProfilePictureURL: old.ProfilePictureURL,
|
||||||
})
|
})
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
@ -2319,7 +2326,7 @@ func (aH *APIHandler) deleteUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.GroupId == adminGroup.ID && len(adminUsers) == 1 {
|
if user.GroupID == adminGroup.ID && len(adminUsers) == 1 {
|
||||||
RespondError(w, &model.ApiError{
|
RespondError(w, &model.ApiError{
|
||||||
Typ: model.ErrorInternal,
|
Typ: model.ErrorInternal,
|
||||||
Err: errors.New("cannot delete the last admin user")}, nil)
|
Err: errors.New("cannot delete the last admin user")}, nil)
|
||||||
@ -2334,37 +2341,6 @@ func (aH *APIHandler) deleteUser(w http.ResponseWriter, r *http.Request) {
|
|||||||
aH.WriteJSON(w, r, map[string]string{"data": "user deleted successfully"})
|
aH.WriteJSON(w, r, map[string]string{"data": "user deleted successfully"})
|
||||||
}
|
}
|
||||||
|
|
||||||
// addUserFlag patches a user flags with the changes
|
|
||||||
func (aH *APIHandler) patchUserFlag(w http.ResponseWriter, r *http.Request) {
|
|
||||||
// read user id from path var
|
|
||||||
userId := mux.Vars(r)["id"]
|
|
||||||
|
|
||||||
// read input into user flag
|
|
||||||
defer r.Body.Close()
|
|
||||||
b, err := io.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("failed read user flags from http request for userId ", zap.String("userId", userId), zap.Error(err))
|
|
||||||
RespondError(w, model.BadRequestStr("received user flags in invalid format"), nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
flags := make(map[string]string, 0)
|
|
||||||
|
|
||||||
err = json.Unmarshal(b, &flags)
|
|
||||||
if err != nil {
|
|
||||||
zap.L().Error("failed parsing user flags for userId ", zap.String("userId", userId), zap.Error(err))
|
|
||||||
RespondError(w, model.BadRequestStr("received user flags in invalid format"), nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
newflags, apiError := dao.DB().UpdateUserFlags(r.Context(), userId, flags)
|
|
||||||
if !apiError.IsNil() {
|
|
||||||
RespondError(w, apiError, nil)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
aH.Respond(w, newflags)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (aH *APIHandler) getRole(w http.ResponseWriter, r *http.Request) {
|
func (aH *APIHandler) getRole(w http.ResponseWriter, r *http.Request) {
|
||||||
id := mux.Vars(r)["id"]
|
id := mux.Vars(r)["id"]
|
||||||
|
|
||||||
@ -2380,7 +2356,7 @@ func (aH *APIHandler) getRole(w http.ResponseWriter, r *http.Request) {
|
|||||||
}, nil)
|
}, nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
group, err := dao.DB().GetGroup(context.Background(), user.GroupId)
|
group, err := dao.DB().GetGroup(context.Background(), user.GroupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
RespondError(w, err, "Failed to get group")
|
RespondError(w, err, "Failed to get group")
|
||||||
return
|
return
|
||||||
@ -2416,7 +2392,7 @@ func (aH *APIHandler) editRole(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure that the request is not demoting the last admin user.
|
// Make sure that the request is not demoting the last admin user.
|
||||||
if user.GroupId == auth.AuthCacheObj.AdminGroupId {
|
if user.GroupID == auth.AuthCacheObj.AdminGroupId {
|
||||||
adminUsers, apiErr := dao.DB().GetUsersByGroup(ctx, auth.AuthCacheObj.AdminGroupId)
|
adminUsers, apiErr := dao.DB().GetUsersByGroup(ctx, auth.AuthCacheObj.AdminGroupId)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, apiErr, "Failed to fetch adminUsers")
|
RespondError(w, apiErr, "Failed to fetch adminUsers")
|
||||||
@ -2431,7 +2407,7 @@ func (aH *APIHandler) editRole(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apiErr = dao.DB().UpdateUserGroup(context.Background(), user.Id, newGroup.ID)
|
apiErr = dao.DB().UpdateUserGroup(context.Background(), user.ID, newGroup.ID)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, apiErr, "Failed to add user to group")
|
RespondError(w, apiErr, "Failed to add user to group")
|
||||||
return
|
return
|
||||||
@ -2465,7 +2441,7 @@ func (aH *APIHandler) editOrg(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
req.Id = id
|
req.ID = id
|
||||||
if apiErr := dao.DB().EditOrg(context.Background(), req); apiErr != nil {
|
if apiErr := dao.DB().EditOrg(context.Background(), req); apiErr != nil {
|
||||||
RespondError(w, apiErr, "Failed to update org in the DB")
|
RespondError(w, apiErr, "Failed to update org in the DB")
|
||||||
return
|
return
|
||||||
@ -3528,7 +3504,7 @@ func (aH *APIHandler) getUserPreference(
|
|||||||
user := common.GetUserFromContext(r.Context())
|
user := common.GetUserFromContext(r.Context())
|
||||||
|
|
||||||
preference, apiErr := preferences.GetUserPreference(
|
preference, apiErr := preferences.GetUserPreference(
|
||||||
r.Context(), preferenceId, user.User.OrgId, user.User.Id,
|
r.Context(), preferenceId, user.User.OrgID, user.User.ID,
|
||||||
)
|
)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, apiErr, nil)
|
RespondError(w, apiErr, nil)
|
||||||
@ -3551,7 +3527,7 @@ func (aH *APIHandler) updateUserPreference(
|
|||||||
RespondError(w, model.BadRequest(err), nil)
|
RespondError(w, model.BadRequest(err), nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
preference, apiErr := preferences.UpdateUserPreference(r.Context(), preferenceId, req.PreferenceValue, user.User.Id)
|
preference, apiErr := preferences.UpdateUserPreference(r.Context(), preferenceId, req.PreferenceValue, user.User.ID)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, apiErr, nil)
|
RespondError(w, apiErr, nil)
|
||||||
return
|
return
|
||||||
@ -3565,7 +3541,7 @@ func (aH *APIHandler) getAllUserPreferences(
|
|||||||
) {
|
) {
|
||||||
user := common.GetUserFromContext(r.Context())
|
user := common.GetUserFromContext(r.Context())
|
||||||
preference, apiErr := preferences.GetAllUserPreferences(
|
preference, apiErr := preferences.GetAllUserPreferences(
|
||||||
r.Context(), user.User.OrgId, user.User.Id,
|
r.Context(), user.User.OrgID, user.User.ID,
|
||||||
)
|
)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, apiErr, nil)
|
RespondError(w, apiErr, nil)
|
||||||
@ -3581,7 +3557,7 @@ func (aH *APIHandler) getOrgPreference(
|
|||||||
preferenceId := mux.Vars(r)["preferenceId"]
|
preferenceId := mux.Vars(r)["preferenceId"]
|
||||||
user := common.GetUserFromContext(r.Context())
|
user := common.GetUserFromContext(r.Context())
|
||||||
preference, apiErr := preferences.GetOrgPreference(
|
preference, apiErr := preferences.GetOrgPreference(
|
||||||
r.Context(), preferenceId, user.User.OrgId,
|
r.Context(), preferenceId, user.User.OrgID,
|
||||||
)
|
)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, apiErr, nil)
|
RespondError(w, apiErr, nil)
|
||||||
@ -3604,7 +3580,7 @@ func (aH *APIHandler) updateOrgPreference(
|
|||||||
RespondError(w, model.BadRequest(err), nil)
|
RespondError(w, model.BadRequest(err), nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
preference, apiErr := preferences.UpdateOrgPreference(r.Context(), preferenceId, req.PreferenceValue, user.User.OrgId)
|
preference, apiErr := preferences.UpdateOrgPreference(r.Context(), preferenceId, req.PreferenceValue, user.User.OrgID)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, apiErr, nil)
|
RespondError(w, apiErr, nil)
|
||||||
return
|
return
|
||||||
@ -3618,7 +3594,7 @@ func (aH *APIHandler) getAllOrgPreferences(
|
|||||||
) {
|
) {
|
||||||
user := common.GetUserFromContext(r.Context())
|
user := common.GetUserFromContext(r.Context())
|
||||||
preference, apiErr := preferences.GetAllOrgPreferences(
|
preference, apiErr := preferences.GetAllOrgPreferences(
|
||||||
r.Context(), user.User.OrgId,
|
r.Context(), user.User.OrgID,
|
||||||
)
|
)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
RespondError(w, apiErr, nil)
|
RespondError(w, apiErr, nil)
|
||||||
|
@ -6,9 +6,6 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"go.signoz.io/signoz/pkg/query-service/app/integrations/messagingQueues/kafka"
|
|
||||||
queues2 "go.signoz.io/signoz/pkg/query-service/app/integrations/messagingQueues/queues"
|
|
||||||
"go.signoz.io/signoz/pkg/query-service/app/integrations/thirdPartyApi"
|
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
@ -16,6 +13,10 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/app/integrations/messagingQueues/kafka"
|
||||||
|
queues2 "go.signoz.io/signoz/pkg/query-service/app/integrations/messagingQueues/queues"
|
||||||
|
"go.signoz.io/signoz/pkg/query-service/app/integrations/thirdPartyApi"
|
||||||
|
|
||||||
"github.com/SigNoz/govaluate"
|
"github.com/SigNoz/govaluate"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
promModel "github.com/prometheus/common/model"
|
promModel "github.com/prometheus/common/model"
|
||||||
@ -32,6 +33,7 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/query-service/postprocess"
|
"go.signoz.io/signoz/pkg/query-service/postprocess"
|
||||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||||
querytemplate "go.signoz.io/signoz/pkg/query-service/utils/queryTemplate"
|
querytemplate "go.signoz.io/signoz/pkg/query-service/utils/queryTemplate"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
var allowedFunctions = []string{"count", "ratePerSec", "sum", "avg", "min", "max", "p50", "p90", "p95", "p99"}
|
var allowedFunctions = []string{"count", "ratePerSec", "sum", "avg", "min", "max", "p50", "p90", "p95", "p99"}
|
||||||
@ -465,8 +467,8 @@ func parseGetTTL(r *http.Request) (*model.GetTTLParams, error) {
|
|||||||
return &model.GetTTLParams{Type: typeTTL}, nil
|
return &model.GetTTLParams{Type: typeTTL}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseUserRequest(r *http.Request) (*model.User, error) {
|
func parseUserRequest(r *http.Request) (*types.User, error) {
|
||||||
var req model.User
|
var req types.User
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -519,8 +521,8 @@ func parseInviteUsersRequest(r *http.Request) (*model.BulkInviteRequest, error)
|
|||||||
return &req, nil
|
return &req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSetApdexScoreRequest(r *http.Request) (*model.ApdexSettings, error) {
|
func parseSetApdexScoreRequest(r *http.Request) (*types.ApdexSettings, error) {
|
||||||
var req model.ApdexSettings
|
var req types.ApdexSettings
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -566,8 +568,8 @@ func parseUserRoleRequest(r *http.Request) (*model.UserRole, error) {
|
|||||||
return &req, nil
|
return &req, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseEditOrgRequest(r *http.Request) (*model.Organization, error) {
|
func parseEditOrgRequest(r *http.Request) (*types.Organization, error) {
|
||||||
var req model.Organization
|
var req types.Organization
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
opAmpModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model"
|
opAmpModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model"
|
||||||
"go.signoz.io/signoz/pkg/query-service/app/preferences"
|
"go.signoz.io/signoz/pkg/query-service/app/preferences"
|
||||||
"go.signoz.io/signoz/pkg/signoz"
|
"go.signoz.io/signoz/pkg/signoz"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||||
"go.signoz.io/signoz/pkg/web"
|
"go.signoz.io/signoz/pkg/web"
|
||||||
|
|
||||||
@ -291,14 +292,14 @@ func (s *Server) createPublicServer(api *APIHandler, web web.Web) (*http.Server,
|
|||||||
r.Use(middleware.NewLogging(zap.L(), s.serverOptions.Config.APIServer.Logging.ExcludedRoutes).Wrap)
|
r.Use(middleware.NewLogging(zap.L(), s.serverOptions.Config.APIServer.Logging.ExcludedRoutes).Wrap)
|
||||||
|
|
||||||
// add auth middleware
|
// add auth middleware
|
||||||
getUserFromRequest := func(ctx context.Context) (*model.UserPayload, error) {
|
getUserFromRequest := func(ctx context.Context) (*types.GettableUser, error) {
|
||||||
user, err := auth.GetUserFromReqContext(ctx)
|
user, err := auth.GetUserFromReqContext(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if user.User.OrgId == "" {
|
if user.User.OrgID == "" {
|
||||||
return nil, model.UnauthorizedError(errors.New("orgId is missing in the claims"))
|
return nil, model.UnauthorizedError(errors.New("orgId is missing in the claims"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/query-service/telemetry"
|
"go.signoz.io/signoz/pkg/query-service/telemetry"
|
||||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||||
smtpservice "go.signoz.io/signoz/pkg/query-service/utils/smtpService"
|
smtpservice "go.signoz.io/signoz/pkg/query-service/utils/smtpService"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
@ -61,6 +62,10 @@ func Invite(ctx context.Context, req *model.InviteRequest) (*model.InviteRespons
|
|||||||
return nil, errors.New("User already exists with the same email")
|
return nil, errors.New("User already exists with the same email")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
claims, ok := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("failed to extract OrgID from context")
|
||||||
|
}
|
||||||
// Check if an invite already exists
|
// Check if an invite already exists
|
||||||
invite, apiErr := dao.DB().GetInviteFromEmail(ctx, req.Email)
|
invite, apiErr := dao.DB().GetInviteFromEmail(ctx, req.Email)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
@ -75,23 +80,18 @@ func Invite(ctx context.Context, req *model.InviteRequest) (*model.InviteRespons
|
|||||||
return nil, errors.Wrap(err, "invalid invite request")
|
return nil, errors.Wrap(err, "invalid invite request")
|
||||||
}
|
}
|
||||||
|
|
||||||
claims, ok := authtypes.ClaimsFromContext(ctx)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.Wrap(err, "failed to extract admin user id")
|
|
||||||
}
|
|
||||||
|
|
||||||
au, apiErr := dao.DB().GetUser(ctx, claims.UserID)
|
au, apiErr := dao.DB().GetUser(ctx, claims.UserID)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
return nil, errors.Wrap(err, "failed to query admin user from the DB")
|
return nil, errors.Wrap(err, "failed to query admin user from the DB")
|
||||||
}
|
}
|
||||||
|
|
||||||
inv := &model.InvitationObject{
|
inv := &types.Invite{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Email: req.Email,
|
Email: req.Email,
|
||||||
Token: token,
|
Token: token,
|
||||||
CreatedAt: time.Now().Unix(),
|
CreatedAt: time.Now(),
|
||||||
Role: req.Role,
|
Role: req.Role,
|
||||||
OrgId: au.OrgId,
|
OrgID: au.OrgID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dao.DB().CreateInviteEntry(ctx, inv); err != nil {
|
if err := dao.DB().CreateInviteEntry(ctx, inv); err != nil {
|
||||||
@ -157,7 +157,7 @@ func InviteUsers(ctx context.Context, req *model.BulkInviteRequest) (*model.Bulk
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Helper function to handle individual invites
|
// Helper function to handle individual invites
|
||||||
func inviteUser(ctx context.Context, req *model.InviteRequest, au *model.UserPayload) (*model.InviteResponse, error) {
|
func inviteUser(ctx context.Context, req *model.InviteRequest, au *types.GettableUser) (*model.InviteResponse, error) {
|
||||||
token, err := utils.RandomHex(opaqueTokenSize)
|
token, err := utils.RandomHex(opaqueTokenSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to generate invite token")
|
return nil, errors.Wrap(err, "failed to generate invite token")
|
||||||
@ -186,13 +186,13 @@ func inviteUser(ctx context.Context, req *model.InviteRequest, au *model.UserPay
|
|||||||
return nil, errors.Wrap(err, "invalid invite request")
|
return nil, errors.Wrap(err, "invalid invite request")
|
||||||
}
|
}
|
||||||
|
|
||||||
inv := &model.InvitationObject{
|
inv := &types.Invite{
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Email: req.Email,
|
Email: req.Email,
|
||||||
Token: token,
|
Token: token,
|
||||||
CreatedAt: time.Now().Unix(),
|
CreatedAt: time.Now(),
|
||||||
Role: req.Role,
|
Role: req.Role,
|
||||||
OrgId: au.OrgId,
|
OrgID: au.OrgID,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dao.DB().CreateInviteEntry(ctx, inv); err != nil {
|
if err := dao.DB().CreateInviteEntry(ctx, inv); err != nil {
|
||||||
@ -211,7 +211,7 @@ func inviteUser(ctx context.Context, req *model.InviteRequest, au *model.UserPay
|
|||||||
return &model.InviteResponse{Email: inv.Email, InviteToken: inv.Token}, nil
|
return &model.InviteResponse{Email: inv.Email, InviteToken: inv.Token}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func inviteEmail(req *model.InviteRequest, au *model.UserPayload, token string) {
|
func inviteEmail(req *model.InviteRequest, au *types.GettableUser, token string) {
|
||||||
smtp := smtpservice.GetInstance()
|
smtp := smtpservice.GetInstance()
|
||||||
data := InviteEmailData{
|
data := InviteEmailData{
|
||||||
CustomerName: req.Name,
|
CustomerName: req.Name,
|
||||||
@ -251,7 +251,12 @@ func RevokeInvite(ctx context.Context, email string) error {
|
|||||||
return ErrorInvalidInviteToken
|
return ErrorInvalidInviteToken
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := dao.DB().DeleteInvitation(ctx, email); err != nil {
|
claims, ok := authtypes.ClaimsFromContext(ctx)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("failed to org id from context")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dao.DB().DeleteInvitation(ctx, claims.OrgID, email); err != nil {
|
||||||
return errors.Wrap(err.Err, "failed to write to DB")
|
return errors.Wrap(err.Err, "failed to write to DB")
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -272,7 +277,7 @@ func GetInvite(ctx context.Context, token string) (*model.InvitationResponseObje
|
|||||||
|
|
||||||
// TODO(Ahsan): This is not the best way to add org name in the invite response. We should
|
// TODO(Ahsan): This is not the best way to add org name in the invite response. We should
|
||||||
// either include org name in the invite table or do a join query.
|
// either include org name in the invite table or do a join query.
|
||||||
org, apiErr := dao.DB().GetOrg(ctx, inv.OrgId)
|
org, apiErr := dao.DB().GetOrg(ctx, inv.OrgID)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
return nil, errors.Wrap(apiErr.Err, "failed to query the DB")
|
return nil, errors.Wrap(apiErr.Err, "failed to query the DB")
|
||||||
}
|
}
|
||||||
@ -280,13 +285,13 @@ func GetInvite(ctx context.Context, token string) (*model.InvitationResponseObje
|
|||||||
Name: inv.Name,
|
Name: inv.Name,
|
||||||
Email: inv.Email,
|
Email: inv.Email,
|
||||||
Token: inv.Token,
|
Token: inv.Token,
|
||||||
CreatedAt: inv.CreatedAt,
|
CreatedAt: inv.CreatedAt.Unix(),
|
||||||
Role: inv.Role,
|
Role: inv.Role,
|
||||||
Organization: org.Name,
|
Organization: org.Name,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ValidateInvite(ctx context.Context, req *RegisterRequest) (*model.InvitationObject, error) {
|
func ValidateInvite(ctx context.Context, req *RegisterRequest) (*types.Invite, error) {
|
||||||
invitation, err := dao.DB().GetInviteFromEmail(ctx, req.Email)
|
invitation, err := dao.DB().GetInviteFromEmail(ctx, req.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err.Err, "Failed to read from DB")
|
return nil, errors.Wrap(err.Err, "Failed to read from DB")
|
||||||
@ -303,14 +308,14 @@ func ValidateInvite(ctx context.Context, req *RegisterRequest) (*model.Invitatio
|
|||||||
return invitation, nil
|
return invitation, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateResetPasswordToken(ctx context.Context, userId string) (*model.ResetPasswordEntry, error) {
|
func CreateResetPasswordToken(ctx context.Context, userId string) (*types.ResetPasswordRequest, error) {
|
||||||
token, err := utils.RandomHex(opaqueTokenSize)
|
token, err := utils.RandomHex(opaqueTokenSize)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "failed to generate reset password token")
|
return nil, errors.Wrap(err, "failed to generate reset password token")
|
||||||
}
|
}
|
||||||
|
|
||||||
req := &model.ResetPasswordEntry{
|
req := &types.ResetPasswordRequest{
|
||||||
UserId: userId,
|
UserID: userId,
|
||||||
Token: token,
|
Token: token,
|
||||||
}
|
}
|
||||||
if apiErr := dao.DB().CreateResetPasswordEntry(ctx, req); err != nil {
|
if apiErr := dao.DB().CreateResetPasswordEntry(ctx, req); err != nil {
|
||||||
@ -334,7 +339,7 @@ func ResetPassword(ctx context.Context, req *model.ResetPasswordRequest) error {
|
|||||||
return errors.Wrap(err, "Failed to generate password hash")
|
return errors.Wrap(err, "Failed to generate password hash")
|
||||||
}
|
}
|
||||||
|
|
||||||
if apiErr := dao.DB().UpdateUserPassword(ctx, hash, entry.UserId); apiErr != nil {
|
if apiErr := dao.DB().UpdateUserPassword(ctx, hash, entry.UserID); apiErr != nil {
|
||||||
return apiErr.Err
|
return apiErr.Err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,7 +365,7 @@ func ChangePassword(ctx context.Context, req *model.ChangePasswordRequest) *mode
|
|||||||
return model.InternalError(errors.New("Failed to generate password hash"))
|
return model.InternalError(errors.New("Failed to generate password hash"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if apiErr := dao.DB().UpdateUserPassword(ctx, hash, user.Id); apiErr != nil {
|
if apiErr := dao.DB().UpdateUserPassword(ctx, hash, user.ID); apiErr != nil {
|
||||||
return apiErr
|
return apiErr
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -369,6 +374,7 @@ func ChangePassword(ctx context.Context, req *model.ChangePasswordRequest) *mode
|
|||||||
|
|
||||||
type RegisterRequest struct {
|
type RegisterRequest struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
OrgID string `json:"orgId"`
|
||||||
OrgName string `json:"orgName"`
|
OrgName string `json:"orgName"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
@ -380,7 +386,7 @@ type RegisterRequest struct {
|
|||||||
SourceUrl string `json:"sourceUrl"`
|
SourceUrl string `json:"sourceUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*model.User, *model.ApiError) {
|
func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*types.User, *model.ApiError) {
|
||||||
|
|
||||||
if req.Email == "" {
|
if req.Email == "" {
|
||||||
return nil, model.BadRequest(model.ErrEmailRequired{})
|
return nil, model.BadRequest(model.ErrEmailRequired{})
|
||||||
@ -392,8 +398,9 @@ func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*model.User,
|
|||||||
|
|
||||||
groupName := constants.AdminGroup
|
groupName := constants.AdminGroup
|
||||||
|
|
||||||
|
// modify this to use bun
|
||||||
org, apierr := dao.DB().CreateOrg(ctx,
|
org, apierr := dao.DB().CreateOrg(ctx,
|
||||||
&model.Organization{Name: req.OrgName, IsAnonymous: req.IsAnonymous, HasOptedUpdates: req.HasOptedUpdates})
|
&types.Organization{Name: req.OrgName, IsAnonymous: req.IsAnonymous, HasOptedUpdates: req.HasOptedUpdates})
|
||||||
if apierr != nil {
|
if apierr != nil {
|
||||||
zap.L().Error("CreateOrg failed", zap.Error(apierr.ToError()))
|
zap.L().Error("CreateOrg failed", zap.Error(apierr.ToError()))
|
||||||
return nil, apierr
|
return nil, apierr
|
||||||
@ -414,22 +421,24 @@ func RegisterFirstUser(ctx context.Context, req *RegisterRequest) (*model.User,
|
|||||||
return nil, model.InternalError(model.ErrSignupFailed{})
|
return nil, model.InternalError(model.ErrSignupFailed{})
|
||||||
}
|
}
|
||||||
|
|
||||||
user := &model.User{
|
user := &types.User{
|
||||||
Id: uuid.NewString(),
|
ID: uuid.NewString(),
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Email: req.Email,
|
Email: req.Email,
|
||||||
Password: hash,
|
Password: hash,
|
||||||
CreatedAt: time.Now().Unix(),
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
},
|
||||||
ProfilePictureURL: "", // Currently unused
|
ProfilePictureURL: "", // Currently unused
|
||||||
GroupId: group.ID,
|
GroupID: group.ID,
|
||||||
OrgId: org.Id,
|
OrgID: org.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
return dao.DB().CreateUser(ctx, user, true)
|
return dao.DB().CreateUser(ctx, user, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
// RegisterInvitedUser handles registering a invited user
|
// RegisterInvitedUser handles registering a invited user
|
||||||
func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword bool) (*model.User, *model.ApiError) {
|
func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword bool) (*types.User, *model.ApiError) {
|
||||||
|
|
||||||
if req.InviteToken == "" {
|
if req.InviteToken == "" {
|
||||||
return nil, model.BadRequest(ErrorAskAdmin)
|
return nil, model.BadRequest(ErrorAskAdmin)
|
||||||
@ -459,7 +468,7 @@ func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword b
|
|||||||
return &userPayload.User, nil
|
return &userPayload.User, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if invite.OrgId == "" {
|
if invite.OrgID == "" {
|
||||||
zap.L().Error("failed to find org in the invite")
|
zap.L().Error("failed to find org in the invite")
|
||||||
return nil, model.InternalError(fmt.Errorf("invalid invite, org not found"))
|
return nil, model.InternalError(fmt.Errorf("invalid invite, org not found"))
|
||||||
}
|
}
|
||||||
@ -492,15 +501,17 @@ func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword b
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
user := &model.User{
|
user := &types.User{
|
||||||
Id: uuid.NewString(),
|
ID: uuid.NewString(),
|
||||||
Name: req.Name,
|
Name: req.Name,
|
||||||
Email: req.Email,
|
Email: req.Email,
|
||||||
Password: hash,
|
Password: hash,
|
||||||
CreatedAt: time.Now().Unix(),
|
TimeAuditable: types.TimeAuditable{
|
||||||
|
CreatedAt: time.Now(),
|
||||||
|
},
|
||||||
ProfilePictureURL: "", // Currently unused
|
ProfilePictureURL: "", // Currently unused
|
||||||
GroupId: group.ID,
|
GroupID: group.ID,
|
||||||
OrgId: invite.OrgId,
|
OrgID: invite.OrgID,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(Ahsan): Ideally create user and delete invitation should happen in a txn.
|
// TODO(Ahsan): Ideally create user and delete invitation should happen in a txn.
|
||||||
@ -510,7 +521,7 @@ func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword b
|
|||||||
return nil, apiErr
|
return nil, apiErr
|
||||||
}
|
}
|
||||||
|
|
||||||
apiErr = dao.DB().DeleteInvitation(ctx, user.Email)
|
apiErr = dao.DB().DeleteInvitation(ctx, user.OrgID, user.Email)
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
zap.L().Error("delete invitation failed", zap.Error(apiErr.Err))
|
zap.L().Error("delete invitation failed", zap.Error(apiErr.Err))
|
||||||
return nil, apiErr
|
return nil, apiErr
|
||||||
@ -525,7 +536,7 @@ func RegisterInvitedUser(ctx context.Context, req *RegisterRequest, nopassword b
|
|||||||
// Register registers a new user. For the first register request, it doesn't need an invite token
|
// Register registers a new user. For the first register request, it doesn't need an invite token
|
||||||
// and also the first registration is an enforced ADMIN registration. Every subsequent request will
|
// and also the first registration is an enforced ADMIN registration. Every subsequent request will
|
||||||
// need an invite token to go through.
|
// need an invite token to go through.
|
||||||
func Register(ctx context.Context, req *RegisterRequest) (*model.User, *model.ApiError) {
|
func Register(ctx context.Context, req *RegisterRequest) (*types.User, *model.ApiError) {
|
||||||
users, err := dao.DB().GetUsers(ctx)
|
users, err := dao.DB().GetUsers(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, model.InternalError(fmt.Errorf("failed to get user count"))
|
return nil, model.InternalError(fmt.Errorf("failed to get user count"))
|
||||||
@ -562,24 +573,24 @@ func Login(ctx context.Context, request *model.LoginRequest, jwt *authtypes.JWT)
|
|||||||
|
|
||||||
return &model.LoginResponse{
|
return &model.LoginResponse{
|
||||||
UserJwtObject: userjwt,
|
UserJwtObject: userjwt,
|
||||||
UserId: user.User.Id,
|
UserId: user.User.ID,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func claimsToUserPayload(claims authtypes.Claims) (*model.UserPayload, error) {
|
func claimsToUserPayload(claims authtypes.Claims) (*types.GettableUser, error) {
|
||||||
user := &model.UserPayload{
|
user := &types.GettableUser{
|
||||||
User: model.User{
|
User: types.User{
|
||||||
Id: claims.UserID,
|
ID: claims.UserID,
|
||||||
GroupId: claims.GroupID,
|
GroupID: claims.GroupID,
|
||||||
Email: claims.Email,
|
Email: claims.Email,
|
||||||
OrgId: claims.OrgID,
|
OrgID: claims.OrgID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// authenticateLogin is responsible for querying the DB and validating the credentials.
|
// authenticateLogin is responsible for querying the DB and validating the credentials.
|
||||||
func authenticateLogin(ctx context.Context, req *model.LoginRequest, jwt *authtypes.JWT) (*model.UserPayload, error) {
|
func authenticateLogin(ctx context.Context, req *model.LoginRequest, jwt *authtypes.JWT) (*types.GettableUser, error) {
|
||||||
// If refresh token is valid, then simply authorize the login request.
|
// If refresh token is valid, then simply authorize the login request.
|
||||||
if len(req.RefreshToken) > 0 {
|
if len(req.RefreshToken) > 0 {
|
||||||
// parse the refresh token
|
// parse the refresh token
|
||||||
@ -624,17 +635,17 @@ func passwordMatch(hash, password string) bool {
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GenerateJWTForUser(user *model.User, jwt *authtypes.JWT) (model.UserJwtObject, error) {
|
func GenerateJWTForUser(user *types.User, jwt *authtypes.JWT) (model.UserJwtObject, error) {
|
||||||
j := model.UserJwtObject{}
|
j := model.UserJwtObject{}
|
||||||
var err error
|
var err error
|
||||||
j.AccessJwtExpiry = time.Now().Add(jwt.JwtExpiry).Unix()
|
j.AccessJwtExpiry = time.Now().Add(jwt.JwtExpiry).Unix()
|
||||||
j.AccessJwt, err = jwt.AccessToken(user.OrgId, user.Id, user.GroupId, user.Email)
|
j.AccessJwt, err = jwt.AccessToken(user.OrgID, user.ID, user.GroupID, user.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return j, errors.Errorf("failed to encode jwt: %v", err)
|
return j, errors.Errorf("failed to encode jwt: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
j.RefreshJwtExpiry = time.Now().Add(jwt.JwtRefresh).Unix()
|
j.RefreshJwtExpiry = time.Now().Add(jwt.JwtRefresh).Unix()
|
||||||
j.RefreshJwt, err = jwt.RefreshToken(user.OrgId, user.Id, user.GroupId, user.Email)
|
j.RefreshJwt, err = jwt.RefreshToken(user.OrgID, user.ID, user.GroupID, user.Email)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return j, errors.Errorf("failed to encode jwt: %v", err)
|
return j, errors.Errorf("failed to encode jwt: %v", err)
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -48,28 +48,28 @@ func InitAuthCache(ctx context.Context) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserFromReqContext(ctx context.Context) (*model.UserPayload, error) {
|
func GetUserFromReqContext(ctx context.Context) (*types.GettableUser, error) {
|
||||||
claims, ok := authtypes.ClaimsFromContext(ctx)
|
claims, ok := authtypes.ClaimsFromContext(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, errors.New("no claims found in context")
|
return nil, errors.New("no claims found in context")
|
||||||
}
|
}
|
||||||
|
|
||||||
user := &model.UserPayload{
|
user := &types.GettableUser{
|
||||||
User: model.User{
|
User: types.User{
|
||||||
Id: claims.UserID,
|
ID: claims.UserID,
|
||||||
GroupId: claims.GroupID,
|
GroupID: claims.GroupID,
|
||||||
Email: claims.Email,
|
Email: claims.Email,
|
||||||
OrgId: claims.OrgID,
|
OrgID: claims.OrgID,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return user, nil
|
return user, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func IsSelfAccessRequest(user *model.UserPayload, id string) bool { return user.Id == id }
|
func IsSelfAccessRequest(user *types.GettableUser, id string) bool { return user.ID == id }
|
||||||
|
|
||||||
func IsViewer(user *model.UserPayload) bool { return user.GroupId == AuthCacheObj.ViewerGroupId }
|
func IsViewer(user *types.GettableUser) bool { return user.GroupID == AuthCacheObj.ViewerGroupId }
|
||||||
func IsEditor(user *model.UserPayload) bool { return user.GroupId == AuthCacheObj.EditorGroupId }
|
func IsEditor(user *types.GettableUser) bool { return user.GroupID == AuthCacheObj.EditorGroupId }
|
||||||
func IsAdmin(user *model.UserPayload) bool { return user.GroupId == AuthCacheObj.AdminGroupId }
|
func IsAdmin(user *types.GettableUser) bool { return user.GroupID == AuthCacheObj.AdminGroupId }
|
||||||
|
|
||||||
func ValidatePassword(password string) error {
|
func ValidatePassword(password string) error {
|
||||||
if len(password) < minimumPasswordLength {
|
if len(password) < minimumPasswordLength {
|
||||||
|
@ -4,11 +4,11 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetUserFromContext(ctx context.Context) *model.UserPayload {
|
func GetUserFromContext(ctx context.Context) *types.GettableUser {
|
||||||
user, ok := ctx.Value(constants.ContextUserKey).(*model.UserPayload)
|
user, ok := ctx.Value(constants.ContextUserKey).(*types.GettableUser)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -13,28 +13,28 @@ type ModelDao interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Queries interface {
|
type Queries interface {
|
||||||
GetInviteFromEmail(ctx context.Context, email string) (*model.InvitationObject, *model.ApiError)
|
GetInviteFromEmail(ctx context.Context, email string) (*types.Invite, *model.ApiError)
|
||||||
GetInviteFromToken(ctx context.Context, token string) (*model.InvitationObject, *model.ApiError)
|
GetInviteFromToken(ctx context.Context, token string) (*types.Invite, *model.ApiError)
|
||||||
GetInvites(ctx context.Context) ([]model.InvitationObject, *model.ApiError)
|
GetInvites(ctx context.Context, orgID string) ([]types.Invite, *model.ApiError)
|
||||||
|
|
||||||
GetUser(ctx context.Context, id string) (*model.UserPayload, *model.ApiError)
|
GetUser(ctx context.Context, id string) (*types.GettableUser, *model.ApiError)
|
||||||
GetUserByEmail(ctx context.Context, email string) (*model.UserPayload, *model.ApiError)
|
GetUserByEmail(ctx context.Context, email string) (*types.GettableUser, *model.ApiError)
|
||||||
GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError)
|
GetUsers(ctx context.Context) ([]types.GettableUser, *model.ApiError)
|
||||||
GetUsersWithOpts(ctx context.Context, limit int) ([]model.UserPayload, *model.ApiError)
|
GetUsersWithOpts(ctx context.Context, limit int) ([]types.GettableUser, *model.ApiError)
|
||||||
|
|
||||||
GetGroup(ctx context.Context, id string) (*model.Group, *model.ApiError)
|
GetGroup(ctx context.Context, id string) (*types.Group, *model.ApiError)
|
||||||
GetGroupByName(ctx context.Context, name string) (*types.Group, *model.ApiError)
|
GetGroupByName(ctx context.Context, name string) (*types.Group, *model.ApiError)
|
||||||
GetGroups(ctx context.Context) ([]model.Group, *model.ApiError)
|
GetGroups(ctx context.Context) ([]types.Group, *model.ApiError)
|
||||||
|
|
||||||
GetOrgs(ctx context.Context) ([]model.Organization, *model.ApiError)
|
GetOrgs(ctx context.Context) ([]types.Organization, *model.ApiError)
|
||||||
GetOrgByName(ctx context.Context, name string) (*model.Organization, *model.ApiError)
|
GetOrgByName(ctx context.Context, name string) (*types.Organization, *model.ApiError)
|
||||||
GetOrg(ctx context.Context, id string) (*model.Organization, *model.ApiError)
|
GetOrg(ctx context.Context, id string) (*types.Organization, *model.ApiError)
|
||||||
|
|
||||||
GetResetPasswordEntry(ctx context.Context, token string) (*model.ResetPasswordEntry, *model.ApiError)
|
GetResetPasswordEntry(ctx context.Context, token string) (*types.ResetPasswordRequest, *model.ApiError)
|
||||||
GetUsersByOrg(ctx context.Context, orgId string) ([]model.UserPayload, *model.ApiError)
|
GetUsersByOrg(ctx context.Context, orgId string) ([]types.GettableUser, *model.ApiError)
|
||||||
GetUsersByGroup(ctx context.Context, groupId string) ([]model.UserPayload, *model.ApiError)
|
GetUsersByGroup(ctx context.Context, groupId string) ([]types.GettableUser, *model.ApiError)
|
||||||
|
|
||||||
GetApdexSettings(ctx context.Context, services []string) ([]model.ApdexSettings, *model.ApiError)
|
GetApdexSettings(ctx context.Context, orgID string, services []string) ([]types.ApdexSettings, *model.ApiError)
|
||||||
|
|
||||||
GetIngestionKeys(ctx context.Context) ([]model.IngestionKey, *model.ApiError)
|
GetIngestionKeys(ctx context.Context) ([]model.IngestionKey, *model.ApiError)
|
||||||
|
|
||||||
@ -42,29 +42,27 @@ type Queries interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Mutations interface {
|
type Mutations interface {
|
||||||
CreateInviteEntry(ctx context.Context, req *model.InvitationObject) *model.ApiError
|
CreateInviteEntry(ctx context.Context, req *types.Invite) *model.ApiError
|
||||||
DeleteInvitation(ctx context.Context, email string) *model.ApiError
|
DeleteInvitation(ctx context.Context, orgID string, email string) *model.ApiError
|
||||||
|
|
||||||
CreateUser(ctx context.Context, user *model.User, isFirstUser bool) (*model.User, *model.ApiError)
|
CreateUser(ctx context.Context, user *types.User, isFirstUser bool) (*types.User, *model.ApiError)
|
||||||
EditUser(ctx context.Context, update *model.User) (*model.User, *model.ApiError)
|
EditUser(ctx context.Context, update *types.User) (*types.User, *model.ApiError)
|
||||||
DeleteUser(ctx context.Context, id string) *model.ApiError
|
DeleteUser(ctx context.Context, id string) *model.ApiError
|
||||||
|
|
||||||
UpdateUserFlags(ctx context.Context, userId string, flags map[string]string) (model.UserFlag, *model.ApiError)
|
|
||||||
|
|
||||||
CreateGroup(ctx context.Context, group *types.Group) (*types.Group, *model.ApiError)
|
CreateGroup(ctx context.Context, group *types.Group) (*types.Group, *model.ApiError)
|
||||||
DeleteGroup(ctx context.Context, id string) *model.ApiError
|
DeleteGroup(ctx context.Context, id string) *model.ApiError
|
||||||
|
|
||||||
CreateOrg(ctx context.Context, org *model.Organization) (*model.Organization, *model.ApiError)
|
CreateOrg(ctx context.Context, org *types.Organization) (*types.Organization, *model.ApiError)
|
||||||
EditOrg(ctx context.Context, org *model.Organization) *model.ApiError
|
EditOrg(ctx context.Context, org *types.Organization) *model.ApiError
|
||||||
DeleteOrg(ctx context.Context, id string) *model.ApiError
|
DeleteOrg(ctx context.Context, id string) *model.ApiError
|
||||||
|
|
||||||
CreateResetPasswordEntry(ctx context.Context, req *model.ResetPasswordEntry) *model.ApiError
|
CreateResetPasswordEntry(ctx context.Context, req *types.ResetPasswordRequest) *model.ApiError
|
||||||
DeleteResetPasswordEntry(ctx context.Context, token string) *model.ApiError
|
DeleteResetPasswordEntry(ctx context.Context, token string) *model.ApiError
|
||||||
|
|
||||||
UpdateUserPassword(ctx context.Context, hash, userId string) *model.ApiError
|
UpdateUserPassword(ctx context.Context, hash, userId string) *model.ApiError
|
||||||
UpdateUserGroup(ctx context.Context, userId, groupId string) *model.ApiError
|
UpdateUserGroup(ctx context.Context, userId, groupId string) *model.ApiError
|
||||||
|
|
||||||
SetApdexSettings(ctx context.Context, set *model.ApdexSettings) *model.ApiError
|
SetApdexSettings(ctx context.Context, orgID string, set *types.ApdexSettings) *model.ApiError
|
||||||
|
|
||||||
InsertIngestionKey(ctx context.Context, ingestionKey *model.IngestionKey) *model.ApiError
|
InsertIngestionKey(ctx context.Context, ingestionKey *model.IngestionKey) *model.ApiError
|
||||||
}
|
}
|
||||||
|
@ -3,24 +3,21 @@ package sqlite
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
"github.com/uptrace/bun"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const defaultApdexThreshold = 0.5
|
const defaultApdexThreshold = 0.5
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, services []string) ([]model.ApdexSettings, *model.ApiError) {
|
func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, orgID string, services []string) ([]types.ApdexSettings, *model.ApiError) {
|
||||||
var apdexSettings []model.ApdexSettings
|
var apdexSettings []types.ApdexSettings
|
||||||
|
|
||||||
query, args, err := sqlx.In("SELECT * FROM apdex_settings WHERE service_name IN (?)", services)
|
err := mds.bundb.NewSelect().
|
||||||
if err != nil {
|
Model(&apdexSettings).
|
||||||
return nil, &model.ApiError{
|
Where("org_id = ?", orgID).
|
||||||
Err: err,
|
Where("service_name IN (?)", bun.In(services)).
|
||||||
}
|
Scan(ctx)
|
||||||
}
|
|
||||||
query = mds.db.Rebind(query)
|
|
||||||
|
|
||||||
err = mds.db.Select(&apdexSettings, query, args...)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &model.ApiError{
|
return nil, &model.ApiError{
|
||||||
Err: err,
|
Err: err,
|
||||||
@ -38,7 +35,7 @@ func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, services []stri
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
apdexSettings = append(apdexSettings, model.ApdexSettings{
|
apdexSettings = append(apdexSettings, types.ApdexSettings{
|
||||||
ServiceName: service,
|
ServiceName: service,
|
||||||
Threshold: defaultApdexThreshold,
|
Threshold: defaultApdexThreshold,
|
||||||
})
|
})
|
||||||
@ -48,18 +45,16 @@ func (mds *ModelDaoSqlite) GetApdexSettings(ctx context.Context, services []stri
|
|||||||
return apdexSettings, nil
|
return apdexSettings, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) SetApdexSettings(ctx context.Context, apdexSettings *model.ApdexSettings) *model.ApiError {
|
func (mds *ModelDaoSqlite) SetApdexSettings(ctx context.Context, orgID string, apdexSettings *types.ApdexSettings) *model.ApiError {
|
||||||
|
// Set the org_id from the parameter since it's required for the foreign key constraint
|
||||||
|
apdexSettings.OrgID = orgID
|
||||||
|
|
||||||
_, err := mds.db.NamedExec(`
|
_, err := mds.bundb.NewInsert().
|
||||||
INSERT OR REPLACE INTO apdex_settings (
|
Model(apdexSettings).
|
||||||
service_name,
|
On("CONFLICT (org_id, service_name) DO UPDATE").
|
||||||
threshold,
|
Set("threshold = EXCLUDED.threshold").
|
||||||
exclude_status_codes
|
Set("exclude_status_codes = EXCLUDED.exclude_status_codes").
|
||||||
) VALUES (
|
Exec(ctx)
|
||||||
:service_name,
|
|
||||||
:threshold,
|
|
||||||
:exclude_status_codes
|
|
||||||
)`, apdexSettings)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.ApiError{
|
return &model.ApiError{
|
||||||
Err: err,
|
Err: err,
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
|
||||||
"go.signoz.io/signoz/pkg/query-service/telemetry"
|
"go.signoz.io/signoz/pkg/query-service/telemetry"
|
||||||
"go.signoz.io/signoz/pkg/sqlstore"
|
"go.signoz.io/signoz/pkg/sqlstore"
|
||||||
"go.signoz.io/signoz/pkg/types"
|
"go.signoz.io/signoz/pkg/types"
|
||||||
@ -62,13 +61,13 @@ func (mds *ModelDaoSqlite) initializeOrgPreferences(ctx context.Context) error {
|
|||||||
return errors.Errorf("Found %d organizations, expected one or none.", len(orgs))
|
return errors.Errorf("Found %d organizations, expected one or none.", len(orgs))
|
||||||
}
|
}
|
||||||
|
|
||||||
var org model.Organization
|
var org types.Organization
|
||||||
if len(orgs) == 1 {
|
if len(orgs) == 1 {
|
||||||
org = orgs[0]
|
org = orgs[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// set telemetry fields from userPreferences
|
// set telemetry fields from userPreferences
|
||||||
telemetry.GetInstance().SetDistinctId(org.Id)
|
telemetry.GetInstance().SetDistinctId(org.ID)
|
||||||
|
|
||||||
users, _ := mds.GetUsers(ctx)
|
users, _ := mds.GetUsers(ctx)
|
||||||
countUsers := len(users)
|
countUsers := len(users)
|
||||||
|
@ -2,7 +2,6 @@ package sqlite
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -13,33 +12,38 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/types"
|
"go.signoz.io/signoz/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) CreateInviteEntry(ctx context.Context,
|
func (mds *ModelDaoSqlite) CreateInviteEntry(ctx context.Context, req *types.Invite) *model.ApiError {
|
||||||
req *model.InvitationObject) *model.ApiError {
|
_, err := mds.bundb.NewInsert().
|
||||||
|
Model(req).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
_, err := mds.db.ExecContext(ctx,
|
|
||||||
`INSERT INTO invites (email, name, token, role, created_at, org_id)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?);`,
|
|
||||||
req.Email, req.Name, req.Token, req.Role, req.CreatedAt, req.OrgId)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) DeleteInvitation(ctx context.Context, email string) *model.ApiError {
|
func (mds *ModelDaoSqlite) DeleteInvitation(ctx context.Context, orgID string, email string) *model.ApiError {
|
||||||
_, err := mds.db.ExecContext(ctx, `DELETE from invites where email=?;`, email)
|
_, err := mds.bundb.NewDelete().
|
||||||
|
Model(&types.Invite{}).
|
||||||
|
Where("org_id = ?", orgID).
|
||||||
|
Where("email = ?", email).
|
||||||
|
Exec(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Make this work with org id
|
||||||
func (mds *ModelDaoSqlite) GetInviteFromEmail(ctx context.Context, email string,
|
func (mds *ModelDaoSqlite) GetInviteFromEmail(ctx context.Context, email string,
|
||||||
) (*model.InvitationObject, *model.ApiError) {
|
) (*types.Invite, *model.ApiError) {
|
||||||
|
|
||||||
invites := []model.InvitationObject{}
|
invites := []types.Invite{}
|
||||||
err := mds.db.Select(&invites,
|
err := mds.bundb.NewSelect().
|
||||||
`SELECT * FROM invites WHERE email=?;`, email)
|
Model(&invites).
|
||||||
|
Where("email = ?", email).
|
||||||
|
Scan(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
@ -57,11 +61,14 @@ func (mds *ModelDaoSqlite) GetInviteFromEmail(ctx context.Context, email string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetInviteFromToken(ctx context.Context, token string,
|
func (mds *ModelDaoSqlite) GetInviteFromToken(ctx context.Context, token string,
|
||||||
) (*model.InvitationObject, *model.ApiError) {
|
) (*types.Invite, *model.ApiError) {
|
||||||
|
// This won't take org id because it's a public facing API
|
||||||
|
|
||||||
invites := []model.InvitationObject{}
|
invites := []types.Invite{}
|
||||||
err := mds.db.Select(&invites,
|
err := mds.bundb.NewSelect().
|
||||||
`SELECT * FROM invites WHERE token=?;`, token)
|
Model(&invites).
|
||||||
|
Where("token = ?", token).
|
||||||
|
Scan(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
@ -76,11 +83,12 @@ func (mds *ModelDaoSqlite) GetInviteFromToken(ctx context.Context, token string,
|
|||||||
return &invites[0], nil
|
return &invites[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetInvites(ctx context.Context,
|
func (mds *ModelDaoSqlite) GetInvites(ctx context.Context, orgID string) ([]types.Invite, *model.ApiError) {
|
||||||
) ([]model.InvitationObject, *model.ApiError) {
|
invites := []types.Invite{}
|
||||||
|
err := mds.bundb.NewSelect().
|
||||||
invites := []model.InvitationObject{}
|
Model(&invites).
|
||||||
err := mds.db.Select(&invites, "SELECT * FROM invites")
|
Where("org_id = ?", orgID).
|
||||||
|
Scan(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
@ -88,13 +96,13 @@ func (mds *ModelDaoSqlite) GetInvites(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) CreateOrg(ctx context.Context,
|
func (mds *ModelDaoSqlite) CreateOrg(ctx context.Context,
|
||||||
org *model.Organization) (*model.Organization, *model.ApiError) {
|
org *types.Organization) (*types.Organization, *model.ApiError) {
|
||||||
|
|
||||||
org.Id = uuid.NewString()
|
org.ID = uuid.NewString()
|
||||||
org.CreatedAt = time.Now().Unix()
|
org.CreatedAt = time.Now()
|
||||||
_, err := mds.db.ExecContext(ctx,
|
_, err := mds.bundb.NewInsert().
|
||||||
`INSERT INTO organizations (id, name, created_at,is_anonymous,has_opted_updates) VALUES (?, ?, ?, ?, ?);`,
|
Model(org).
|
||||||
org.Id, org.Name, org.CreatedAt, org.IsAnonymous, org.HasOptedUpdates)
|
Exec(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
@ -103,14 +111,18 @@ func (mds *ModelDaoSqlite) CreateOrg(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetOrg(ctx context.Context,
|
func (mds *ModelDaoSqlite) GetOrg(ctx context.Context,
|
||||||
id string) (*model.Organization, *model.ApiError) {
|
id string) (*types.Organization, *model.ApiError) {
|
||||||
|
|
||||||
orgs := []model.Organization{}
|
orgs := []types.Organization{}
|
||||||
err := mds.db.Select(&orgs, `SELECT * FROM organizations WHERE id=?;`, id)
|
|
||||||
|
|
||||||
if err != nil {
|
if err := mds.bundb.NewSelect().
|
||||||
|
Model(&orgs).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Scan(ctx); err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(nitya): remove for multitenancy
|
||||||
if len(orgs) > 1 {
|
if len(orgs) > 1 {
|
||||||
return nil, &model.ApiError{
|
return nil, &model.ApiError{
|
||||||
Typ: model.ErrorInternal,
|
Typ: model.ErrorInternal,
|
||||||
@ -125,11 +137,14 @@ func (mds *ModelDaoSqlite) GetOrg(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetOrgByName(ctx context.Context,
|
func (mds *ModelDaoSqlite) GetOrgByName(ctx context.Context,
|
||||||
name string) (*model.Organization, *model.ApiError) {
|
name string) (*types.Organization, *model.ApiError) {
|
||||||
|
|
||||||
orgs := []model.Organization{}
|
orgs := []types.Organization{}
|
||||||
|
|
||||||
if err := mds.db.Select(&orgs, `SELECT * FROM organizations WHERE name=?;`, name); err != nil {
|
if err := mds.bundb.NewSelect().
|
||||||
|
Model(&orgs).
|
||||||
|
Where("name = ?", name).
|
||||||
|
Scan(ctx); err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,9 +160,11 @@ func (mds *ModelDaoSqlite) GetOrgByName(ctx context.Context,
|
|||||||
return &orgs[0], nil
|
return &orgs[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]model.Organization, *model.ApiError) {
|
func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]types.Organization, *model.ApiError) {
|
||||||
orgs := []model.Organization{}
|
var orgs []types.Organization
|
||||||
err := mds.db.Select(&orgs, `SELECT * FROM organizations`)
|
err := mds.bundb.NewSelect().
|
||||||
|
Model(&orgs).
|
||||||
|
Scan(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
@ -155,24 +172,31 @@ func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]model.Organization, *
|
|||||||
return orgs, nil
|
return orgs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) EditOrg(ctx context.Context, org *model.Organization) *model.ApiError {
|
func (mds *ModelDaoSqlite) EditOrg(ctx context.Context, org *types.Organization) *model.ApiError {
|
||||||
|
_, err := mds.bundb.NewUpdate().
|
||||||
|
Model(org).
|
||||||
|
Column("name").
|
||||||
|
Column("has_opted_updates").
|
||||||
|
Column("is_anonymous").
|
||||||
|
Where("id = ?", org.ID).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
q := `UPDATE organizations SET name=?,has_opted_updates=?,is_anonymous=? WHERE id=?;`
|
|
||||||
|
|
||||||
_, err := mds.db.ExecContext(ctx, q, org.Name, org.HasOptedUpdates, org.IsAnonymous, org.Id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
telemetry.GetInstance().SetTelemetryAnonymous(org.IsAnonymous)
|
telemetry.GetInstance().SetTelemetryAnonymous(org.IsAnonymous)
|
||||||
telemetry.GetInstance().SetDistinctId(org.Id)
|
telemetry.GetInstance().SetDistinctId(org.ID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) DeleteOrg(ctx context.Context, id string) *model.ApiError {
|
func (mds *ModelDaoSqlite) DeleteOrg(ctx context.Context, id string) *model.ApiError {
|
||||||
|
_, err := mds.bundb.NewDelete().
|
||||||
|
Model(&types.Organization{}).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
_, err := mds.db.ExecContext(ctx, `DELETE from organizations where id=?;`, id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
@ -180,14 +204,10 @@ func (mds *ModelDaoSqlite) DeleteOrg(ctx context.Context, id string) *model.ApiE
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) CreateUser(ctx context.Context,
|
func (mds *ModelDaoSqlite) CreateUser(ctx context.Context,
|
||||||
user *model.User, isFirstUser bool) (*model.User, *model.ApiError) {
|
user *types.User, isFirstUser bool) (*types.User, *model.ApiError) {
|
||||||
|
_, err := mds.bundb.NewInsert().
|
||||||
_, err := mds.db.ExecContext(ctx,
|
Model(user).
|
||||||
`INSERT INTO users (id, name, email, password, created_at, profile_picture_url, group_id, org_id)
|
Exec(ctx)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?,?);`,
|
|
||||||
user.Id, user.Name, user.Email, user.Password, user.CreatedAt,
|
|
||||||
user.ProfilePictureURL, user.GroupId, user.OrgId,
|
|
||||||
)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
@ -210,11 +230,15 @@ func (mds *ModelDaoSqlite) CreateUser(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) EditUser(ctx context.Context,
|
func (mds *ModelDaoSqlite) EditUser(ctx context.Context,
|
||||||
update *model.User) (*model.User, *model.ApiError) {
|
update *types.User) (*types.User, *model.ApiError) {
|
||||||
|
_, err := mds.bundb.NewUpdate().
|
||||||
|
Model(update).
|
||||||
|
Column("name").
|
||||||
|
Column("org_id").
|
||||||
|
Column("email").
|
||||||
|
Where("id = ?", update.ID).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
_, err := mds.db.ExecContext(ctx,
|
|
||||||
`UPDATE users SET name=?,org_id=?,email=? WHERE id=?;`, update.Name,
|
|
||||||
update.OrgId, update.Email, update.Id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
@ -224,8 +248,13 @@ func (mds *ModelDaoSqlite) EditUser(ctx context.Context,
|
|||||||
func (mds *ModelDaoSqlite) UpdateUserPassword(ctx context.Context, passwordHash,
|
func (mds *ModelDaoSqlite) UpdateUserPassword(ctx context.Context, passwordHash,
|
||||||
userId string) *model.ApiError {
|
userId string) *model.ApiError {
|
||||||
|
|
||||||
q := `UPDATE users SET password=? WHERE id=?;`
|
_, err := mds.bundb.NewUpdate().
|
||||||
if _, err := mds.db.ExecContext(ctx, q, passwordHash, userId); err != nil {
|
Model(&types.User{}).
|
||||||
|
Set("password = ?", passwordHash).
|
||||||
|
Where("id = ?", userId).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -233,16 +262,24 @@ func (mds *ModelDaoSqlite) UpdateUserPassword(ctx context.Context, passwordHash,
|
|||||||
|
|
||||||
func (mds *ModelDaoSqlite) UpdateUserGroup(ctx context.Context, userId, groupId string) *model.ApiError {
|
func (mds *ModelDaoSqlite) UpdateUserGroup(ctx context.Context, userId, groupId string) *model.ApiError {
|
||||||
|
|
||||||
q := `UPDATE users SET group_id=? WHERE id=?;`
|
_, err := mds.bundb.NewUpdate().
|
||||||
if _, err := mds.db.ExecContext(ctx, q, groupId, userId); err != nil {
|
Model(&types.User{}).
|
||||||
|
Set("group_id = ?", groupId).
|
||||||
|
Where("id = ?", userId).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) DeleteUser(ctx context.Context, id string) *model.ApiError {
|
func (mds *ModelDaoSqlite) DeleteUser(ctx context.Context, id string) *model.ApiError {
|
||||||
|
result, err := mds.bundb.NewDelete().
|
||||||
|
Model(&types.User{}).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
result, err := mds.db.ExecContext(ctx, `DELETE from users where id=?;`, id)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
@ -262,30 +299,19 @@ func (mds *ModelDaoSqlite) DeleteUser(ctx context.Context, id string) *model.Api
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetUser(ctx context.Context,
|
func (mds *ModelDaoSqlite) GetUser(ctx context.Context,
|
||||||
id string) (*model.UserPayload, *model.ApiError) {
|
id string) (*types.GettableUser, *model.ApiError) {
|
||||||
|
|
||||||
users := []model.UserPayload{}
|
users := []types.GettableUser{}
|
||||||
query := `select
|
query := mds.bundb.NewSelect().
|
||||||
u.id,
|
Table("users").
|
||||||
u.name,
|
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
||||||
u.email,
|
ColumnExpr("g.name as role").
|
||||||
u.password,
|
ColumnExpr("o.name as organization").
|
||||||
u.created_at,
|
Join("JOIN groups g ON g.id = users.group_id").
|
||||||
u.profile_picture_url,
|
Join("JOIN organizations o ON o.id = users.org_id").
|
||||||
u.org_id,
|
Where("users.id = ?", id)
|
||||||
u.group_id,
|
|
||||||
g.name as role,
|
|
||||||
o.name as organization,
|
|
||||||
COALESCE((select uf.flags
|
|
||||||
from user_flags uf
|
|
||||||
where u.id = uf.user_id), '') as flags
|
|
||||||
from users u, groups g, organizations o
|
|
||||||
where
|
|
||||||
g.id=u.group_id and
|
|
||||||
o.id = u.org_id and
|
|
||||||
u.id=?;`
|
|
||||||
|
|
||||||
if err := mds.db.Select(&users, query, id); err != nil {
|
if err := query.Scan(ctx, &users); err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
if len(users) > 1 {
|
if len(users) > 1 {
|
||||||
@ -303,7 +329,7 @@ func (mds *ModelDaoSqlite) GetUser(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context,
|
func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context,
|
||||||
email string) (*model.UserPayload, *model.ApiError) {
|
email string) (*types.GettableUser, *model.ApiError) {
|
||||||
|
|
||||||
if email == "" {
|
if email == "" {
|
||||||
return nil, &model.ApiError{
|
return nil, &model.ApiError{
|
||||||
@ -312,27 +338,20 @@ func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
users := []model.UserPayload{}
|
users := []types.GettableUser{}
|
||||||
query := `select
|
query := mds.bundb.NewSelect().
|
||||||
u.id,
|
Table("users").
|
||||||
u.name,
|
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
||||||
u.email,
|
ColumnExpr("g.name as role").
|
||||||
u.password,
|
ColumnExpr("o.name as organization").
|
||||||
u.created_at,
|
Join("JOIN groups g ON g.id = users.group_id").
|
||||||
u.profile_picture_url,
|
Join("JOIN organizations o ON o.id = users.org_id").
|
||||||
u.org_id,
|
Where("users.email = ?", email)
|
||||||
u.group_id,
|
|
||||||
g.name as role,
|
|
||||||
o.name as organization
|
|
||||||
from users u, groups g, organizations o
|
|
||||||
where
|
|
||||||
g.id=u.group_id and
|
|
||||||
o.id = u.org_id and
|
|
||||||
u.email=?;`
|
|
||||||
|
|
||||||
if err := mds.db.Select(&users, query, email); err != nil {
|
if err := query.Scan(ctx, &users); err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(users) > 1 {
|
if len(users) > 1 {
|
||||||
return nil, &model.ApiError{
|
return nil, &model.ApiError{
|
||||||
Typ: model.ErrorInternal,
|
Typ: model.ErrorInternal,
|
||||||
@ -347,34 +366,26 @@ func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetUsers fetches total user count
|
// GetUsers fetches total user count
|
||||||
func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError) {
|
func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]types.GettableUser, *model.ApiError) {
|
||||||
return mds.GetUsersWithOpts(ctx, 0)
|
return mds.GetUsersWithOpts(ctx, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetUsersWithOpts fetches users and supports additional search options
|
// GetUsersWithOpts fetches users and supports additional search options
|
||||||
func (mds *ModelDaoSqlite) GetUsersWithOpts(ctx context.Context, limit int) ([]model.UserPayload, *model.ApiError) {
|
func (mds *ModelDaoSqlite) GetUsersWithOpts(ctx context.Context, limit int) ([]types.GettableUser, *model.ApiError) {
|
||||||
users := []model.UserPayload{}
|
users := []types.GettableUser{}
|
||||||
|
|
||||||
query := `select
|
query := mds.bundb.NewSelect().
|
||||||
u.id,
|
Table("users").
|
||||||
u.name,
|
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
||||||
u.email,
|
ColumnExpr("g.name as role").
|
||||||
u.password,
|
ColumnExpr("o.name as organization").
|
||||||
u.created_at,
|
Join("JOIN groups g ON g.id = users.group_id").
|
||||||
u.profile_picture_url,
|
Join("JOIN organizations o ON o.id = users.org_id")
|
||||||
u.org_id,
|
|
||||||
u.group_id,
|
|
||||||
g.name as role,
|
|
||||||
o.name as organization
|
|
||||||
from users u, groups g, organizations o
|
|
||||||
where
|
|
||||||
g.id = u.group_id and
|
|
||||||
o.id = u.org_id`
|
|
||||||
|
|
||||||
if limit > 0 {
|
if limit > 0 {
|
||||||
query = fmt.Sprintf("%s LIMIT %d", query, limit)
|
query.Limit(limit)
|
||||||
}
|
}
|
||||||
err := mds.db.Select(&users, query)
|
err := query.Scan(ctx, &users)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
@ -383,54 +394,42 @@ func (mds *ModelDaoSqlite) GetUsersWithOpts(ctx context.Context, limit int) ([]m
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetUsersByOrg(ctx context.Context,
|
func (mds *ModelDaoSqlite) GetUsersByOrg(ctx context.Context,
|
||||||
orgId string) ([]model.UserPayload, *model.ApiError) {
|
orgId string) ([]types.GettableUser, *model.ApiError) {
|
||||||
|
|
||||||
users := []model.UserPayload{}
|
users := []types.GettableUser{}
|
||||||
query := `select
|
|
||||||
u.id,
|
|
||||||
u.name,
|
|
||||||
u.email,
|
|
||||||
u.password,
|
|
||||||
u.created_at,
|
|
||||||
u.profile_picture_url,
|
|
||||||
u.org_id,
|
|
||||||
u.group_id,
|
|
||||||
g.name as role,
|
|
||||||
o.name as organization
|
|
||||||
from users u, groups g, organizations o
|
|
||||||
where
|
|
||||||
u.group_id = g.id and
|
|
||||||
u.org_id = o.id and
|
|
||||||
u.org_id=?;`
|
|
||||||
|
|
||||||
if err := mds.db.Select(&users, query, orgId); err != nil {
|
query := mds.bundb.NewSelect().
|
||||||
|
Table("users").
|
||||||
|
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
||||||
|
ColumnExpr("g.name as role").
|
||||||
|
ColumnExpr("o.name as organization").
|
||||||
|
Join("JOIN groups g ON g.id = users.group_id").
|
||||||
|
Join("JOIN organizations o ON o.id = users.org_id").
|
||||||
|
Where("users.org_id = ?", orgId)
|
||||||
|
|
||||||
|
err := query.Scan(ctx, &users)
|
||||||
|
if err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetUsersByGroup(ctx context.Context,
|
func (mds *ModelDaoSqlite) GetUsersByGroup(ctx context.Context,
|
||||||
groupId string) ([]model.UserPayload, *model.ApiError) {
|
groupId string) ([]types.GettableUser, *model.ApiError) {
|
||||||
|
|
||||||
users := []model.UserPayload{}
|
users := []types.GettableUser{}
|
||||||
query := `select
|
|
||||||
u.id,
|
|
||||||
u.name,
|
|
||||||
u.email,
|
|
||||||
u.password,
|
|
||||||
u.created_at,
|
|
||||||
u.profile_picture_url,
|
|
||||||
u.org_id,
|
|
||||||
u.group_id,
|
|
||||||
g.name as role,
|
|
||||||
o.name as organization
|
|
||||||
from users u, groups g, organizations o
|
|
||||||
where
|
|
||||||
u.group_id = g.id and
|
|
||||||
o.id = u.org_id and
|
|
||||||
u.group_id=?;`
|
|
||||||
|
|
||||||
if err := mds.db.Select(&users, query, groupId); err != nil {
|
query := mds.bundb.NewSelect().
|
||||||
|
Table("users").
|
||||||
|
Column("users.id", "users.name", "users.email", "users.password", "users.created_at", "users.profile_picture_url", "users.org_id", "users.group_id").
|
||||||
|
ColumnExpr("g.name as role").
|
||||||
|
ColumnExpr("o.name as organization").
|
||||||
|
Join("JOIN groups g ON g.id = users.group_id").
|
||||||
|
Join("JOIN organizations o ON o.id = users.org_id").
|
||||||
|
Where("users.group_id = ?", groupId)
|
||||||
|
|
||||||
|
err := query.Scan(ctx, &users)
|
||||||
|
if err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
return users, nil
|
return users, nil
|
||||||
@ -452,17 +451,25 @@ func (mds *ModelDaoSqlite) CreateGroup(ctx context.Context,
|
|||||||
|
|
||||||
func (mds *ModelDaoSqlite) DeleteGroup(ctx context.Context, id string) *model.ApiError {
|
func (mds *ModelDaoSqlite) DeleteGroup(ctx context.Context, id string) *model.ApiError {
|
||||||
|
|
||||||
if _, err := mds.db.ExecContext(ctx, `DELETE from groups where id=?;`, id); err != nil {
|
_, err := mds.bundb.NewDelete().
|
||||||
|
Model(&types.Group{}).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetGroup(ctx context.Context,
|
func (mds *ModelDaoSqlite) GetGroup(ctx context.Context,
|
||||||
id string) (*model.Group, *model.ApiError) {
|
id string) (*types.Group, *model.ApiError) {
|
||||||
|
|
||||||
groups := []model.Group{}
|
groups := []types.Group{}
|
||||||
if err := mds.db.Select(&groups, `SELECT id, name FROM groups WHERE id=?`, id); err != nil {
|
if err := mds.bundb.NewSelect().
|
||||||
|
Model(&groups).
|
||||||
|
Where("id = ?", id).
|
||||||
|
Scan(ctx); err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -505,10 +512,13 @@ func (mds *ModelDaoSqlite) GetGroupByName(ctx context.Context,
|
|||||||
return &groups[0], nil
|
return &groups[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetGroups(ctx context.Context) ([]model.Group, *model.ApiError) {
|
// TODO(nitya): should have org id
|
||||||
|
func (mds *ModelDaoSqlite) GetGroups(ctx context.Context) ([]types.Group, *model.ApiError) {
|
||||||
|
|
||||||
groups := []model.Group{}
|
groups := []types.Group{}
|
||||||
if err := mds.db.Select(&groups, "SELECT * FROM groups"); err != nil {
|
if err := mds.bundb.NewSelect().
|
||||||
|
Model(&groups).
|
||||||
|
Scan(ctx); err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -516,10 +526,11 @@ func (mds *ModelDaoSqlite) GetGroups(ctx context.Context) ([]model.Group, *model
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) CreateResetPasswordEntry(ctx context.Context,
|
func (mds *ModelDaoSqlite) CreateResetPasswordEntry(ctx context.Context,
|
||||||
req *model.ResetPasswordEntry) *model.ApiError {
|
req *types.ResetPasswordRequest) *model.ApiError {
|
||||||
|
|
||||||
q := `INSERT INTO reset_password_request (user_id, token) VALUES (?, ?);`
|
if _, err := mds.bundb.NewInsert().
|
||||||
if _, err := mds.db.ExecContext(ctx, q, req.UserId, req.Token); err != nil {
|
Model(req).
|
||||||
|
Exec(ctx); err != nil {
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -527,7 +538,11 @@ func (mds *ModelDaoSqlite) CreateResetPasswordEntry(ctx context.Context,
|
|||||||
|
|
||||||
func (mds *ModelDaoSqlite) DeleteResetPasswordEntry(ctx context.Context,
|
func (mds *ModelDaoSqlite) DeleteResetPasswordEntry(ctx context.Context,
|
||||||
token string) *model.ApiError {
|
token string) *model.ApiError {
|
||||||
_, err := mds.db.ExecContext(ctx, `DELETE from reset_password_request where token=?;`, token)
|
_, err := mds.bundb.NewDelete().
|
||||||
|
Model(&types.ResetPasswordRequest{}).
|
||||||
|
Where("token = ?", token).
|
||||||
|
Exec(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
@ -535,12 +550,14 @@ func (mds *ModelDaoSqlite) DeleteResetPasswordEntry(ctx context.Context,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) GetResetPasswordEntry(ctx context.Context,
|
func (mds *ModelDaoSqlite) GetResetPasswordEntry(ctx context.Context,
|
||||||
token string) (*model.ResetPasswordEntry, *model.ApiError) {
|
token string) (*types.ResetPasswordRequest, *model.ApiError) {
|
||||||
|
|
||||||
entries := []model.ResetPasswordEntry{}
|
entries := []types.ResetPasswordRequest{}
|
||||||
|
|
||||||
q := `SELECT user_id,token FROM reset_password_request WHERE token=?;`
|
if err := mds.bundb.NewSelect().
|
||||||
if err := mds.db.Select(&entries, q, token); err != nil {
|
Model(&entries).
|
||||||
|
Where("token = ?", token).
|
||||||
|
Scan(ctx); err != nil {
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
||||||
}
|
}
|
||||||
if len(entries) > 1 {
|
if len(entries) > 1 {
|
||||||
@ -554,54 +571,6 @@ func (mds *ModelDaoSqlite) GetResetPasswordEntry(ctx context.Context,
|
|||||||
return &entries[0], nil
|
return &entries[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CreateUserFlags inserts user specific flags
|
|
||||||
func (mds *ModelDaoSqlite) UpdateUserFlags(ctx context.Context, userId string, flags map[string]string) (model.UserFlag, *model.ApiError) {
|
|
||||||
|
|
||||||
if len(flags) == 0 {
|
|
||||||
// nothing to do as flags are empty. In this method, we only append the flags
|
|
||||||
// but not set them to empty
|
|
||||||
return flags, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// fetch existing flags
|
|
||||||
userPayload, apiError := mds.GetUser(ctx, userId)
|
|
||||||
if apiError != nil {
|
|
||||||
return nil, apiError
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range userPayload.Flags {
|
|
||||||
if _, ok := flags[k]; !ok {
|
|
||||||
// insert only missing keys as we want to retain the
|
|
||||||
// flags in the db that are not part of this request
|
|
||||||
flags[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// append existing flags with new ones
|
|
||||||
|
|
||||||
// write the updated flags
|
|
||||||
flagsBytes, err := json.Marshal(flags)
|
|
||||||
if err != nil {
|
|
||||||
return nil, model.InternalError(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(userPayload.Flags) == 0 {
|
|
||||||
q := `INSERT INTO user_flags (user_id, flags) VALUES (?, ?);`
|
|
||||||
|
|
||||||
if _, err := mds.db.ExecContext(ctx, q, userId, string(flagsBytes)); err != nil {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
q := `UPDATE user_flags SET flags = ? WHERE user_id = ?;`
|
|
||||||
|
|
||||||
if _, err := mds.db.ExecContext(ctx, q, userId, string(flagsBytes)); err != nil {
|
|
||||||
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return flags, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (mds *ModelDaoSqlite) PrecheckLogin(ctx context.Context, email, sourceUrl string) (*model.PrecheckResponse, model.BaseApiError) {
|
func (mds *ModelDaoSqlite) PrecheckLogin(ctx context.Context, email, sourceUrl string) (*model.PrecheckResponse, model.BaseApiError) {
|
||||||
// assume user is valid unless proven otherwise and assign default values for rest of the fields
|
// assume user is valid unless proven otherwise and assign default values for rest of the fields
|
||||||
resp := &model.PrecheckResponse{IsUser: true, CanSelfRegister: false, SSO: false, SsoUrl: "", SsoError: ""}
|
resp := &model.PrecheckResponse{IsUser: true, CanSelfRegister: false, SSO: false, SsoUrl: "", SsoError: ""}
|
||||||
|
@ -88,11 +88,6 @@ type ChangePasswordRequest struct {
|
|||||||
NewPassword string `json:"newPassword"`
|
NewPassword string `json:"newPassword"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ResetPasswordEntry struct {
|
|
||||||
UserId string `json:"userId" db:"user_id"`
|
|
||||||
Token string `json:"token" db:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserRole struct {
|
type UserRole struct {
|
||||||
UserId string `json:"user_id"`
|
UserId string `json:"user_id"`
|
||||||
GroupName string `json:"group_name"`
|
GroupName string `json:"group_name"`
|
||||||
|
@ -1,46 +1,10 @@
|
|||||||
package model
|
package model
|
||||||
|
|
||||||
import (
|
import "time"
|
||||||
"database/sql/driver"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Organization struct {
|
type ResetPasswordRequest struct {
|
||||||
Id string `json:"id" db:"id"`
|
Password string `json:"password"`
|
||||||
Name string `json:"name" db:"name"`
|
Token string `json:"token"`
|
||||||
CreatedAt int64 `json:"createdAt" db:"created_at"`
|
|
||||||
IsAnonymous bool `json:"isAnonymous" db:"is_anonymous"`
|
|
||||||
HasOptedUpdates bool `json:"hasOptedUpdates" db:"has_opted_updates"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// InvitationObject represents the token object stored in the db
|
|
||||||
type InvitationObject struct {
|
|
||||||
Id string `json:"id" db:"id"`
|
|
||||||
Email string `json:"email" db:"email"`
|
|
||||||
Name string `json:"name" db:"name"`
|
|
||||||
Token string `json:"token" db:"token"`
|
|
||||||
CreatedAt int64 `json:"createdAt" db:"created_at"`
|
|
||||||
Role string `json:"role" db:"role"`
|
|
||||||
OrgId string `json:"orgId" db:"org_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
Id string `json:"id" db:"id"`
|
|
||||||
Name string `json:"name" db:"name"`
|
|
||||||
Email string `json:"email" db:"email"`
|
|
||||||
Password string `json:"password,omitempty" db:"password"`
|
|
||||||
CreatedAt int64 `json:"createdAt" db:"created_at"`
|
|
||||||
ProfilePictureURL string `json:"profilePictureURL" db:"profile_picture_url"`
|
|
||||||
OrgId string `json:"orgId,omitempty" db:"org_id"`
|
|
||||||
GroupId string `json:"groupId,omitempty" db:"group_id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ApdexSettings struct {
|
|
||||||
ServiceName string `json:"serviceName" db:"service_name"`
|
|
||||||
Threshold float64 `json:"threshold" db:"threshold"`
|
|
||||||
ExcludeStatusCodes string `json:"excludeStatusCodes" db:"exclude_status_codes"` // sqlite doesn't support array type
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type IngestionKey struct {
|
type IngestionKey struct {
|
||||||
@ -51,50 +15,3 @@ type IngestionKey struct {
|
|||||||
IngestionURL string `json:"ingestionURL" db:"ingestion_url"`
|
IngestionURL string `json:"ingestionURL" db:"ingestion_url"`
|
||||||
DataRegion string `json:"dataRegion" db:"data_region"`
|
DataRegion string `json:"dataRegion" db:"data_region"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type UserFlag map[string]string
|
|
||||||
|
|
||||||
func (uf UserFlag) Value() (driver.Value, error) {
|
|
||||||
f := make(map[string]string, 0)
|
|
||||||
for k, v := range uf {
|
|
||||||
f[k] = v
|
|
||||||
}
|
|
||||||
return json.Marshal(f)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (uf *UserFlag) Scan(value interface{}) error {
|
|
||||||
if value == "" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
b, ok := value.(string)
|
|
||||||
if !ok {
|
|
||||||
return fmt.Errorf("type assertion to []byte failed while scanning user flag")
|
|
||||||
}
|
|
||||||
f := make(map[string]string, 0)
|
|
||||||
if err := json.Unmarshal([]byte(b), &f); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
*uf = make(UserFlag, len(f))
|
|
||||||
for k, v := range f {
|
|
||||||
(*uf)[k] = v
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserPayload struct {
|
|
||||||
User
|
|
||||||
Role string `json:"role"`
|
|
||||||
Organization string `json:"organization"`
|
|
||||||
Flags UserFlag `json:"flags"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Group struct {
|
|
||||||
Id string `json:"id" db:"id"`
|
|
||||||
Name string `json:"name" db:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResetPasswordRequest struct {
|
|
||||||
Password string `json:"password"`
|
|
||||||
Token string `json:"token"`
|
|
||||||
}
|
|
||||||
|
@ -20,6 +20,7 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||||
"go.signoz.io/signoz/pkg/query-service/version"
|
"go.signoz.io/signoz/pkg/query-service/version"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -206,7 +207,7 @@ type Telemetry struct {
|
|||||||
alertsInfoCallback func(ctx context.Context) (*model.AlertsInfo, error)
|
alertsInfoCallback func(ctx context.Context) (*model.AlertsInfo, error)
|
||||||
userCountCallback func(ctx context.Context) (int, error)
|
userCountCallback func(ctx context.Context) (int, error)
|
||||||
userRoleCallback func(ctx context.Context, groupId string) (string, error)
|
userRoleCallback func(ctx context.Context, groupId string) (string, error)
|
||||||
getUsersCallback func(ctx context.Context) ([]model.UserPayload, *model.ApiError)
|
getUsersCallback func(ctx context.Context) ([]types.GettableUser, *model.ApiError)
|
||||||
dashboardsInfoCallback func(ctx context.Context) (*model.DashboardsInfo, error)
|
dashboardsInfoCallback func(ctx context.Context) (*model.DashboardsInfo, error)
|
||||||
savedViewsInfoCallback func(ctx context.Context) (*model.SavedViewsInfo, error)
|
savedViewsInfoCallback func(ctx context.Context) (*model.SavedViewsInfo, error)
|
||||||
}
|
}
|
||||||
@ -223,7 +224,7 @@ func (a *Telemetry) SetUserRoleCallback(callback func(ctx context.Context, group
|
|||||||
a.userRoleCallback = callback
|
a.userRoleCallback = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Telemetry) SetGetUsersCallback(callback func(ctx context.Context) ([]model.UserPayload, *model.ApiError)) {
|
func (a *Telemetry) SetGetUsersCallback(callback func(ctx context.Context) ([]types.GettableUser, *model.ApiError)) {
|
||||||
a.getUsersCallback = callback
|
a.getUsersCallback = callback
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -524,7 +525,7 @@ func getOutboundIP() string {
|
|||||||
return string(ip)
|
return string(ip)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Telemetry) IdentifyUser(user *model.User) {
|
func (a *Telemetry) IdentifyUser(user *types.User) {
|
||||||
if user.Email == DEFAULT_CLOUD_EMAIL {
|
if user.Email == DEFAULT_CLOUD_EMAIL {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -534,7 +535,7 @@ func (a *Telemetry) IdentifyUser(user *model.User) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
// extract user group from user.groupId
|
// extract user group from user.groupId
|
||||||
role, _ := a.userRoleCallback(context.Background(), user.GroupId)
|
role, _ := a.userRoleCallback(context.Background(), user.GroupID)
|
||||||
|
|
||||||
if a.saasOperator != nil {
|
if a.saasOperator != nil {
|
||||||
if role != "" {
|
if role != "" {
|
||||||
|
@ -17,9 +17,9 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||||
"go.signoz.io/signoz/pkg/query-service/featureManager"
|
"go.signoz.io/signoz/pkg/query-service/featureManager"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
|
||||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -260,7 +260,7 @@ func (tb *FilterSuggestionsTestBed) mockAttribValuesQueryResponse(
|
|||||||
|
|
||||||
type FilterSuggestionsTestBed struct {
|
type FilterSuggestionsTestBed struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
testUser *model.User
|
testUser *types.User
|
||||||
qsHttpHandler http.Handler
|
qsHttpHandler http.Handler
|
||||||
mockClickhouse mockhouse.ClickConnMockCommon
|
mockClickhouse mockhouse.ClickConnMockCommon
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,11 @@ import (
|
|||||||
opampModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model"
|
opampModel "go.signoz.io/signoz/pkg/query-service/app/opamp/model"
|
||||||
"go.signoz.io/signoz/pkg/query-service/constants"
|
"go.signoz.io/signoz/pkg/query-service/constants"
|
||||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
|
||||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||||
"go.signoz.io/signoz/pkg/query-service/queryBuilderToExpr"
|
"go.signoz.io/signoz/pkg/query-service/queryBuilderToExpr"
|
||||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||||
"go.signoz.io/signoz/pkg/sqlstore"
|
"go.signoz.io/signoz/pkg/sqlstore"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
"golang.org/x/exp/slices"
|
"golang.org/x/exp/slices"
|
||||||
)
|
)
|
||||||
@ -441,7 +441,7 @@ func TestCanSavePipelinesWithoutConnectedAgents(t *testing.T) {
|
|||||||
// configuring log pipelines and provides test helpers.
|
// configuring log pipelines and provides test helpers.
|
||||||
type LogPipelinesTestBed struct {
|
type LogPipelinesTestBed struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
testUser *model.User
|
testUser *types.User
|
||||||
apiHandler *app.APIHandler
|
apiHandler *app.APIHandler
|
||||||
agentConfMgr *agentConf.Manager
|
agentConfMgr *agentConf.Manager
|
||||||
opampServer *opamp.Server
|
opampServer *opamp.Server
|
||||||
|
@ -17,9 +17,9 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/query-service/auth"
|
"go.signoz.io/signoz/pkg/query-service/auth"
|
||||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||||
"go.signoz.io/signoz/pkg/query-service/featureManager"
|
"go.signoz.io/signoz/pkg/query-service/featureManager"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
|
||||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||||
"go.signoz.io/signoz/pkg/sqlstore"
|
"go.signoz.io/signoz/pkg/sqlstore"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -338,7 +338,7 @@ func TestConfigReturnedWhenAgentChecksIn(t *testing.T) {
|
|||||||
|
|
||||||
type CloudIntegrationsTestBed struct {
|
type CloudIntegrationsTestBed struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
testUser *model.User
|
testUser *types.User
|
||||||
qsHttpHandler http.Handler
|
qsHttpHandler http.Handler
|
||||||
mockClickhouse mockhouse.ClickConnMockCommon
|
mockClickhouse mockhouse.ClickConnMockCommon
|
||||||
}
|
}
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
v3 "go.signoz.io/signoz/pkg/query-service/model/v3"
|
||||||
"go.signoz.io/signoz/pkg/query-service/utils"
|
"go.signoz.io/signoz/pkg/query-service/utils"
|
||||||
"go.signoz.io/signoz/pkg/sqlstore"
|
"go.signoz.io/signoz/pkg/sqlstore"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -367,7 +368,7 @@ func TestDashboardsForInstalledIntegrationDashboards(t *testing.T) {
|
|||||||
|
|
||||||
type IntegrationsTestBed struct {
|
type IntegrationsTestBed struct {
|
||||||
t *testing.T
|
t *testing.T
|
||||||
testUser *model.User
|
testUser *types.User
|
||||||
qsHttpHandler http.Handler
|
qsHttpHandler http.Handler
|
||||||
mockClickhouse mockhouse.ClickConnMockCommon
|
mockClickhouse mockhouse.ClickConnMockCommon
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@ import (
|
|||||||
"go.signoz.io/signoz/pkg/query-service/dao"
|
"go.signoz.io/signoz/pkg/query-service/dao"
|
||||||
"go.signoz.io/signoz/pkg/query-service/interfaces"
|
"go.signoz.io/signoz/pkg/query-service/interfaces"
|
||||||
"go.signoz.io/signoz/pkg/query-service/model"
|
"go.signoz.io/signoz/pkg/query-service/model"
|
||||||
|
"go.signoz.io/signoz/pkg/types"
|
||||||
"go.signoz.io/signoz/pkg/types/authtypes"
|
"go.signoz.io/signoz/pkg/types/authtypes"
|
||||||
"golang.org/x/exp/maps"
|
"golang.org/x/exp/maps"
|
||||||
)
|
)
|
||||||
@ -150,10 +151,10 @@ func makeTestSignozLog(
|
|||||||
return testLog
|
return testLog
|
||||||
}
|
}
|
||||||
|
|
||||||
func createTestUser() (*model.User, *model.ApiError) {
|
func createTestUser() (*types.User, *model.ApiError) {
|
||||||
// Create a test user for auth
|
// Create a test user for auth
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
org, apiErr := dao.DB().CreateOrg(ctx, &model.Organization{
|
org, apiErr := dao.DB().CreateOrg(ctx, &types.Organization{
|
||||||
Name: "test",
|
Name: "test",
|
||||||
})
|
})
|
||||||
if apiErr != nil {
|
if apiErr != nil {
|
||||||
@ -170,20 +171,20 @@ func createTestUser() (*model.User, *model.ApiError) {
|
|||||||
userId := uuid.NewString()
|
userId := uuid.NewString()
|
||||||
return dao.DB().CreateUser(
|
return dao.DB().CreateUser(
|
||||||
ctx,
|
ctx,
|
||||||
&model.User{
|
&types.User{
|
||||||
Id: userId,
|
ID: userId,
|
||||||
Name: "test",
|
Name: "test",
|
||||||
Email: userId[:8] + "test@test.com",
|
Email: userId[:8] + "test@test.com",
|
||||||
Password: "test",
|
Password: "test",
|
||||||
OrgId: org.Id,
|
OrgID: org.ID,
|
||||||
GroupId: group.ID,
|
GroupID: group.ID,
|
||||||
},
|
},
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AuthenticatedRequestForTest(
|
func AuthenticatedRequestForTest(
|
||||||
user *model.User,
|
user *types.User,
|
||||||
path string,
|
path string,
|
||||||
postData interface{},
|
postData interface{},
|
||||||
) (*http.Request, error) {
|
) (*http.Request, error) {
|
||||||
|
@ -45,6 +45,9 @@ func NewTestSqliteDB(t *testing.T) (sqlStore sqlstore.SQLStore, testDBFilePath s
|
|||||||
sqlmigration.NewAddIntegrationsFactory(),
|
sqlmigration.NewAddIntegrationsFactory(),
|
||||||
sqlmigration.NewAddLicensesFactory(),
|
sqlmigration.NewAddLicensesFactory(),
|
||||||
sqlmigration.NewAddPatsFactory(),
|
sqlmigration.NewAddPatsFactory(),
|
||||||
|
sqlmigration.NewModifyDatetimeFactory(),
|
||||||
|
sqlmigration.NewModifyOrgDomainFactory(),
|
||||||
|
sqlmigration.NewUpdateOrganizationFactory(sqlStore),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -62,7 +65,10 @@ func NewTestSqliteDB(t *testing.T) (sqlStore sqlstore.SQLStore, testDBFilePath s
|
|||||||
func NewQueryServiceDBForTests(t *testing.T) sqlstore.SQLStore {
|
func NewQueryServiceDBForTests(t *testing.T) sqlstore.SQLStore {
|
||||||
sqlStore, _ := NewTestSqliteDB(t)
|
sqlStore, _ := NewTestSqliteDB(t)
|
||||||
|
|
||||||
dao.InitDao(sqlStore)
|
err := dao.InitDao(sqlStore)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("could not initialize dao: %v", err)
|
||||||
|
}
|
||||||
dashboards.InitDB(sqlStore.SQLxDB())
|
dashboards.InitDB(sqlStore.SQLxDB())
|
||||||
|
|
||||||
return sqlStore
|
return sqlStore
|
||||||
|
@ -74,6 +74,9 @@ func New(
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// add the org migration here since we need to pass the sqlstore
|
||||||
|
providerConfig.SQLMigrationProviderFactories.Add(sqlmigration.NewUpdateOrganizationFactory(sqlstore))
|
||||||
|
|
||||||
// Initialize telemetrystore from the available telemetrystore provider factories
|
// Initialize telemetrystore from the available telemetrystore provider factories
|
||||||
telemetrystore, err := factory.NewProviderFromNamedMap(
|
telemetrystore, err := factory.NewProviderFromNamedMap(
|
||||||
ctx,
|
ctx,
|
||||||
|
@ -35,7 +35,7 @@ func (migration *addPats) Up(ctx context.Context, db *bun.DB) error {
|
|||||||
OrgID string `bun:"org_id,type:text,notnull"`
|
OrgID string `bun:"org_id,type:text,notnull"`
|
||||||
Name string `bun:"name,type:varchar(50),notnull,unique"`
|
Name string `bun:"name,type:varchar(50),notnull,unique"`
|
||||||
CreatedAt int `bun:"created_at,notnull"`
|
CreatedAt int `bun:"created_at,notnull"`
|
||||||
UpdatedAt int `bun:"updated_at,type:timestamp"`
|
UpdatedAt int `bun:"updated_at"`
|
||||||
Data string `bun:"data,type:text,notnull"`
|
Data string `bun:"data,type:text,notnull"`
|
||||||
}{}).
|
}{}).
|
||||||
ForeignKey(`("org_id") REFERENCES "organizations" ("id")`).
|
ForeignKey(`("org_id") REFERENCES "organizations" ("id")`).
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/dialect"
|
||||||
"github.com/uptrace/bun/migrate"
|
"github.com/uptrace/bun/migrate"
|
||||||
"go.signoz.io/signoz/pkg/factory"
|
"go.signoz.io/signoz/pkg/factory"
|
||||||
)
|
)
|
||||||
@ -27,6 +28,11 @@ func (migration *modifyOrgDomain) Register(migrations *migrate.Migrations) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (migration *modifyOrgDomain) Up(ctx context.Context, db *bun.DB) error {
|
func (migration *modifyOrgDomain) Up(ctx context.Context, db *bun.DB) error {
|
||||||
|
// only run this for old sqlite db
|
||||||
|
if db.Dialect().Name() != dialect.SQLite {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// begin transaction
|
// begin transaction
|
||||||
tx, err := db.BeginTx(ctx, nil)
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
152
pkg/sqlmigration/013_update_organization.go
Normal file
152
pkg/sqlmigration/013_update_organization.go
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
package sqlmigration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
"github.com/uptrace/bun/migrate"
|
||||||
|
"go.signoz.io/signoz/pkg/factory"
|
||||||
|
"go.signoz.io/signoz/pkg/sqlstore"
|
||||||
|
)
|
||||||
|
|
||||||
|
type updateOrganization struct {
|
||||||
|
store sqlstore.SQLStore
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUpdateOrganizationFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
|
||||||
|
return factory.NewProviderFactory(factory.MustNewName("update_organization"), func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
|
||||||
|
return newUpdateOrganization(ctx, ps, c, sqlstore)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func newUpdateOrganization(_ context.Context, _ factory.ProviderSettings, _ Config, store sqlstore.SQLStore) (SQLMigration, error) {
|
||||||
|
return &updateOrganization{
|
||||||
|
store: store,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updateOrganization) Register(migrations *migrate.Migrations) error {
|
||||||
|
if err := migrations.Register(migration.Up, migration.Down); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updateOrganization) Up(ctx context.Context, db *bun.DB) error {
|
||||||
|
|
||||||
|
// begin transaction
|
||||||
|
tx, err := db.BeginTx(ctx, nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer tx.Rollback()
|
||||||
|
|
||||||
|
// update apdex settings table
|
||||||
|
if err := updateApdexSettings(ctx, tx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop user_flags table
|
||||||
|
if _, err := tx.NewDropTable().IfExists().Table("user_flags").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add org id to groups table
|
||||||
|
if exists, err := migration.store.Dialect().ColumnExists(ctx, tx, "groups", "org_id"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !exists {
|
||||||
|
if _, err := tx.NewAddColumn().Table("groups").ColumnExpr("org_id TEXT").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add created_at to groups table
|
||||||
|
for _, table := range []string{"groups"} {
|
||||||
|
if exists, err := migration.store.Dialect().ColumnExists(ctx, tx, table, "created_at"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !exists {
|
||||||
|
if _, err := tx.NewAddColumn().Table(table).ColumnExpr("created_at TIMESTAMP").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add updated_at to organizations, users, groups table
|
||||||
|
for _, table := range []string{"organizations", "users", "groups"} {
|
||||||
|
if exists, err := migration.store.Dialect().ColumnExists(ctx, tx, table, "updated_at"); err != nil {
|
||||||
|
return err
|
||||||
|
} else if !exists {
|
||||||
|
if _, err := tx.NewAddColumn().Table(table).ColumnExpr("updated_at TIMESTAMP").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// since organizations, users has created_at as integer instead of timestamp
|
||||||
|
for _, table := range []string{"organizations", "users", "invites"} {
|
||||||
|
if err := migration.store.Dialect().MigrateIntToTimestamp(ctx, tx, table, "created_at"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// migrate is_anonymous and has_opted_updates to boolean from int
|
||||||
|
for _, column := range []string{"is_anonymous", "has_opted_updates"} {
|
||||||
|
if err := migration.store.Dialect().MigrateIntToBoolean(ctx, tx, "organizations", column); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (migration *updateOrganization) Down(ctx context.Context, db *bun.DB) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateApdexSettings(ctx context.Context, tx bun.Tx) error {
|
||||||
|
if _, err := tx.NewCreateTable().
|
||||||
|
Model(&struct {
|
||||||
|
bun.BaseModel `bun:"table:apdex_settings_new"`
|
||||||
|
OrgID string `bun:"org_id,pk,type:text"`
|
||||||
|
ServiceName string `bun:"service_name,pk,type:text"`
|
||||||
|
Threshold float64 `bun:"threshold,type:float,notnull"`
|
||||||
|
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull"`
|
||||||
|
}{}).
|
||||||
|
ForeignKey(`("org_id") REFERENCES "organizations" ("id")`).
|
||||||
|
IfNotExists().
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// get org id from organizations table
|
||||||
|
var orgID string
|
||||||
|
if err := tx.QueryRowContext(ctx, `SELECT id FROM organizations LIMIT 1`).Scan(&orgID); err != nil && !errors.Is(err, sql.ErrNoRows) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if orgID != "" {
|
||||||
|
// copy old data
|
||||||
|
if _, err := tx.ExecContext(ctx, `INSERT INTO apdex_settings_new (org_id, service_name, threshold, exclude_status_codes) SELECT ?, service_name, threshold, exclude_status_codes FROM apdex_settings`, orgID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop old table
|
||||||
|
if _, err := tx.NewDropTable().IfExists().Table("apdex_settings").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// rename new table to old table
|
||||||
|
if _, err := tx.ExecContext(ctx, `ALTER TABLE apdex_settings_new RENAME TO apdex_settings`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -86,3 +86,29 @@ func WrapIfNotExists(ctx context.Context, db *bun.DB, table string, column strin
|
|||||||
return q.Err(ErrNoExecute)
|
return q.Err(ErrNoExecute)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetColumnType(ctx context.Context, bun bun.IDB, table string, column string) (string, error) {
|
||||||
|
var columnType string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if bun.Dialect().Name() == dialect.SQLite {
|
||||||
|
err = bun.NewSelect().
|
||||||
|
ColumnExpr("type").
|
||||||
|
TableExpr("pragma_table_info(?)", table).
|
||||||
|
Where("name = ?", column).
|
||||||
|
Scan(ctx, &columnType)
|
||||||
|
} else {
|
||||||
|
err = bun.NewSelect().
|
||||||
|
ColumnExpr("data_type").
|
||||||
|
TableExpr("information_schema.columns").
|
||||||
|
Where("table_name = ?", table).
|
||||||
|
Where("column_name = ?", column).
|
||||||
|
Scan(ctx, &columnType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return columnType, nil
|
||||||
|
}
|
||||||
|
116
pkg/sqlstore/postgressqlstore/dialect.go
Normal file
116
pkg/sqlstore/postgressqlstore/dialect.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package postgressqlstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PGDialect struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *PGDialect) MigrateIntToTimestamp(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// bigint for postgres and INTEGER for sqlite
|
||||||
|
if columnType != "bigint" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the columns is integer then do this
|
||||||
|
if _, err := bun.ExecContext(ctx, `ALTER TABLE `+table+` RENAME COLUMN `+column+` TO `+column+`_old`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add new timestamp column
|
||||||
|
if _, err := bun.NewAddColumn().Table(table).ColumnExpr(column + " TIMESTAMP").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := bun.NewUpdate().
|
||||||
|
Table(table).
|
||||||
|
Set(column + " = to_timestamp(cast(" + column + "_old as INTEGER))").
|
||||||
|
Where("1=1").
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop old column
|
||||||
|
if _, err := bun.NewDropColumn().Table(table).Column(column + "_old").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *PGDialect) MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if columnType != "bigint" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := bun.ExecContext(ctx, `ALTER TABLE `+table+` RENAME COLUMN `+column+` TO `+column+`_old`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add new boolean column
|
||||||
|
if _, err := bun.NewAddColumn().Table(table).ColumnExpr(column + " BOOLEAN").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy data from old column to new column, converting from int to boolean
|
||||||
|
if _, err := bun.NewUpdate().
|
||||||
|
Table(table).
|
||||||
|
Set(column + " = CASE WHEN " + column + "_old = 1 THEN true ELSE false END").
|
||||||
|
Where("1=1").
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop old column
|
||||||
|
if _, err := bun.NewDropColumn().Table(table).Column(column + "_old").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *PGDialect) GetColumnType(ctx context.Context, bun bun.IDB, table string, column string) (string, error) {
|
||||||
|
var columnType string
|
||||||
|
var err error
|
||||||
|
err = bun.NewSelect().
|
||||||
|
ColumnExpr("data_type").
|
||||||
|
TableExpr("information_schema.columns").
|
||||||
|
Where("table_name = ?", table).
|
||||||
|
Where("column_name = ?", column).
|
||||||
|
Scan(ctx, &columnType)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return columnType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *PGDialect) ColumnExists(ctx context.Context, bun bun.IDB, table string, column string) (bool, error) {
|
||||||
|
var count int
|
||||||
|
err := bun.NewSelect().
|
||||||
|
ColumnExpr("COUNT(*)").
|
||||||
|
TableExpr("information_schema.columns").
|
||||||
|
Where("table_name = ?", table).
|
||||||
|
Where("column_name = ?", column).
|
||||||
|
Scan(ctx, &count)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count > 0, nil
|
||||||
|
}
|
@ -18,6 +18,7 @@ type provider struct {
|
|||||||
sqldb *sql.DB
|
sqldb *sql.DB
|
||||||
bundb *sqlstore.BunDB
|
bundb *sqlstore.BunDB
|
||||||
sqlxdb *sqlx.DB
|
sqlxdb *sqlx.DB
|
||||||
|
dialect *PGDialect
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactory(hookFactories ...factory.ProviderFactory[sqlstore.SQLStoreHook, sqlstore.Config]) factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] {
|
func NewFactory(hookFactories ...factory.ProviderFactory[sqlstore.SQLStoreHook, sqlstore.Config]) factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] {
|
||||||
@ -59,6 +60,7 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config
|
|||||||
sqldb: sqldb,
|
sqldb: sqldb,
|
||||||
bundb: sqlstore.NewBunDB(settings, sqldb, pgdialect.New(), hooks),
|
bundb: sqlstore.NewBunDB(settings, sqldb, pgdialect.New(), hooks),
|
||||||
sqlxdb: sqlx.NewDb(sqldb, "postgres"),
|
sqlxdb: sqlx.NewDb(sqldb, "postgres"),
|
||||||
|
dialect: &PGDialect{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,6 +76,10 @@ func (provider *provider) SQLxDB() *sqlx.DB {
|
|||||||
return provider.sqlxdb
|
return provider.sqlxdb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Dialect() sqlstore.SQLDialect {
|
||||||
|
return provider.dialect
|
||||||
|
}
|
||||||
|
|
||||||
func (provider *provider) BunDBCtx(ctx context.Context) bun.IDB {
|
func (provider *provider) BunDBCtx(ctx context.Context) bun.IDB {
|
||||||
return provider.bundb.BunDBCtx(ctx)
|
return provider.bundb.BunDBCtx(ctx)
|
||||||
}
|
}
|
||||||
|
115
pkg/sqlstore/sqlitesqlstore/dialect.go
Normal file
115
pkg/sqlstore/sqlitesqlstore/dialect.go
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
package sqlitesqlstore
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SQLiteDialect struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *SQLiteDialect) MigrateIntToTimestamp(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if columnType != "INTEGER" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// if the columns is integer then do this
|
||||||
|
if _, err := bun.ExecContext(ctx, `ALTER TABLE `+table+` RENAME COLUMN `+column+` TO `+column+`_old`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add new timestamp column
|
||||||
|
if _, err := bun.NewAddColumn().Table(table).ColumnExpr(column + " TIMESTAMP").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy data from old column to new column, converting from int (unix timestamp) to timestamp
|
||||||
|
if _, err := bun.NewUpdate().
|
||||||
|
Table(table).
|
||||||
|
Set(column + " = datetime(" + column + "_old, 'unixepoch')").
|
||||||
|
Where("1=1").
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop old column
|
||||||
|
if _, err := bun.NewDropColumn().Table(table).Column(column + "_old").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *SQLiteDialect) MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
columnType, err := dialect.GetColumnType(ctx, bun, table, column)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if columnType != "INTEGER" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := bun.ExecContext(ctx, `ALTER TABLE `+table+` RENAME COLUMN `+column+` TO `+column+`_old`); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// add new boolean column
|
||||||
|
if _, err := bun.NewAddColumn().Table(table).ColumnExpr(column + " BOOLEAN").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// copy data from old column to new column, converting from int to boolean
|
||||||
|
if _, err := bun.NewUpdate().
|
||||||
|
Table(table).
|
||||||
|
Set(column + " = CASE WHEN " + column + "_old = 1 THEN true ELSE false END").
|
||||||
|
Where("1=1").
|
||||||
|
Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// drop old column
|
||||||
|
if _, err := bun.NewDropColumn().Table(table).Column(column + "_old").Exec(ctx); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *SQLiteDialect) GetColumnType(ctx context.Context, bun bun.IDB, table string, column string) (string, error) {
|
||||||
|
var columnType string
|
||||||
|
var err error
|
||||||
|
|
||||||
|
err = bun.NewSelect().
|
||||||
|
ColumnExpr("type").
|
||||||
|
TableExpr("pragma_table_info(?)", table).
|
||||||
|
Where("name = ?", column).
|
||||||
|
Scan(ctx, &columnType)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return columnType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *SQLiteDialect) ColumnExists(ctx context.Context, bun bun.IDB, table string, column string) (bool, error) {
|
||||||
|
var count int
|
||||||
|
err := bun.NewSelect().
|
||||||
|
ColumnExpr("COUNT(*)").
|
||||||
|
TableExpr("pragma_table_info(?)", table).
|
||||||
|
Where("name = ?", column).
|
||||||
|
Scan(ctx, &count)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count > 0, nil
|
||||||
|
}
|
@ -17,6 +17,7 @@ type provider struct {
|
|||||||
sqldb *sql.DB
|
sqldb *sql.DB
|
||||||
bundb *sqlstore.BunDB
|
bundb *sqlstore.BunDB
|
||||||
sqlxdb *sqlx.DB
|
sqlxdb *sqlx.DB
|
||||||
|
dialect *SQLiteDialect
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFactory(hookFactories ...factory.ProviderFactory[sqlstore.SQLStoreHook, sqlstore.Config]) factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] {
|
func NewFactory(hookFactories ...factory.ProviderFactory[sqlstore.SQLStoreHook, sqlstore.Config]) factory.ProviderFactory[sqlstore.SQLStore, sqlstore.Config] {
|
||||||
@ -49,6 +50,7 @@ func New(ctx context.Context, providerSettings factory.ProviderSettings, config
|
|||||||
sqldb: sqldb,
|
sqldb: sqldb,
|
||||||
bundb: sqlstore.NewBunDB(settings, sqldb, sqlitedialect.New(), hooks),
|
bundb: sqlstore.NewBunDB(settings, sqldb, sqlitedialect.New(), hooks),
|
||||||
sqlxdb: sqlx.NewDb(sqldb, "sqlite3"),
|
sqlxdb: sqlx.NewDb(sqldb, "sqlite3"),
|
||||||
|
dialect: &SQLiteDialect{},
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +66,10 @@ func (provider *provider) SQLxDB() *sqlx.DB {
|
|||||||
return provider.sqlxdb
|
return provider.sqlxdb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (provider *provider) Dialect() sqlstore.SQLDialect {
|
||||||
|
return provider.dialect
|
||||||
|
}
|
||||||
|
|
||||||
func (provider *provider) BunDBCtx(ctx context.Context) bun.IDB {
|
func (provider *provider) BunDBCtx(ctx context.Context) bun.IDB {
|
||||||
return provider.bundb.BunDBCtx(ctx)
|
return provider.bundb.BunDBCtx(ctx)
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,9 @@ type SQLStore interface {
|
|||||||
// SQLxDB returns an instance of sqlx.DB. This is the legacy ORM used.
|
// SQLxDB returns an instance of sqlx.DB. This is the legacy ORM used.
|
||||||
SQLxDB() *sqlx.DB
|
SQLxDB() *sqlx.DB
|
||||||
|
|
||||||
|
// Returns the dialect of the database.
|
||||||
|
Dialect() SQLDialect
|
||||||
|
|
||||||
// RunInTxCtx runs the given callback in a transaction. It creates and injects a new context with the transaction.
|
// RunInTxCtx runs the given callback in a transaction. It creates and injects a new context with the transaction.
|
||||||
// If a transaction is present in the context, it will be used.
|
// If a transaction is present in the context, it will be used.
|
||||||
RunInTxCtx(ctx context.Context, opts *SQLStoreTxOptions, cb func(ctx context.Context) error) error
|
RunInTxCtx(ctx context.Context, opts *SQLStoreTxOptions, cb func(ctx context.Context) error) error
|
||||||
@ -32,3 +35,10 @@ type SQLStore interface {
|
|||||||
type SQLStoreHook interface {
|
type SQLStoreHook interface {
|
||||||
bun.QueryHook
|
bun.QueryHook
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SQLDialect interface {
|
||||||
|
MigrateIntToTimestamp(ctx context.Context, bun bun.IDB, table string, column string) error
|
||||||
|
MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error
|
||||||
|
GetColumnType(ctx context.Context, bun bun.IDB, table string, column string) (string, error)
|
||||||
|
ColumnExists(ctx context.Context, bun bun.IDB, table string, column string) (bool, error)
|
||||||
|
}
|
||||||
|
26
pkg/sqlstore/sqlstoretest/dialect.go
Normal file
26
pkg/sqlstore/sqlstoretest/dialect.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package sqlstoretest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestDialect struct {
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *TestDialect) MigrateIntToTimestamp(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *TestDialect) MigrateIntToBoolean(ctx context.Context, bun bun.IDB, table string, column string) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *TestDialect) GetColumnType(ctx context.Context, bun bun.IDB, table string, column string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dialect *TestDialect) ColumnExists(ctx context.Context, bun bun.IDB, table string, column string) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
@ -15,10 +15,11 @@ import (
|
|||||||
var _ sqlstore.SQLStore = (*Provider)(nil)
|
var _ sqlstore.SQLStore = (*Provider)(nil)
|
||||||
|
|
||||||
type Provider struct {
|
type Provider struct {
|
||||||
db *sql.DB
|
db *sql.DB
|
||||||
mock sqlmock.Sqlmock
|
mock sqlmock.Sqlmock
|
||||||
bunDB *bun.DB
|
bunDB *bun.DB
|
||||||
sqlxDB *sqlx.DB
|
sqlxDB *sqlx.DB
|
||||||
|
dialect *TestDialect
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(config sqlstore.Config, matcher sqlmock.QueryMatcher) *Provider {
|
func New(config sqlstore.Config, matcher sqlmock.QueryMatcher) *Provider {
|
||||||
@ -38,10 +39,11 @@ func New(config sqlstore.Config, matcher sqlmock.QueryMatcher) *Provider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &Provider{
|
return &Provider{
|
||||||
db: db,
|
db: db,
|
||||||
mock: mock,
|
mock: mock,
|
||||||
bunDB: bunDB,
|
bunDB: bunDB,
|
||||||
sqlxDB: sqlxDB,
|
sqlxDB: sqlxDB,
|
||||||
|
dialect: &TestDialect{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +63,10 @@ func (provider *Provider) Mock() sqlmock.Sqlmock {
|
|||||||
return provider.mock
|
return provider.mock
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (provider *Provider) Dialect() sqlstore.SQLDialect {
|
||||||
|
return provider.dialect
|
||||||
|
}
|
||||||
|
|
||||||
func (provider *Provider) BunDBCtx(ctx context.Context) bun.IDB {
|
func (provider *Provider) BunDBCtx(ctx context.Context) bun.IDB {
|
||||||
return provider.bunDB
|
return provider.bunDB
|
||||||
}
|
}
|
||||||
|
13
pkg/types/auditable.go
Normal file
13
pkg/types/auditable.go
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type TimeAuditable struct {
|
||||||
|
CreatedAt time.Time `bun:"created_at" json:"createdAt"`
|
||||||
|
UpdatedAt time.Time `bun:"updated_at" json:"updatedAt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UserAuditable struct {
|
||||||
|
CreatedBy string `bun:"created_by" json:"createdBy"`
|
||||||
|
UpdatedBy string `bun:"updated_by" json:"updatedBy"`
|
||||||
|
}
|
@ -1,8 +1,6 @@
|
|||||||
package types
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/uptrace/bun"
|
"github.com/uptrace/bun"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -10,69 +8,16 @@ import (
|
|||||||
type Organization struct {
|
type Organization struct {
|
||||||
bun.BaseModel `bun:"table:organizations"`
|
bun.BaseModel `bun:"table:organizations"`
|
||||||
|
|
||||||
ID string `bun:"id,pk,type:text"`
|
TimeAuditable
|
||||||
Name string `bun:"name,type:text,notnull"`
|
ID string `bun:"id,pk,type:text" json:"id"`
|
||||||
CreatedAt int `bun:"created_at,notnull"`
|
Name string `bun:"name,type:text,notnull" json:"name"`
|
||||||
IsAnonymous int `bun:"is_anonymous,notnull,default:0,CHECK(is_anonymous IN (0,1))"`
|
IsAnonymous bool `bun:"is_anonymous,notnull,default:0,CHECK(is_anonymous IN (0,1))" json:"isAnonymous"`
|
||||||
HasOptedUpdates int `bun:"has_opted_updates,notnull,default:1,CHECK(has_opted_updates IN (0,1))"`
|
HasOptedUpdates bool `bun:"has_opted_updates,notnull,default:1,CHECK(has_opted_updates IN (0,1))" json:"hasOptedUpdates"`
|
||||||
}
|
|
||||||
|
|
||||||
type Invite struct {
|
|
||||||
bun.BaseModel `bun:"table:invites"`
|
|
||||||
|
|
||||||
ID int `bun:"id,pk,autoincrement"`
|
|
||||||
Name string `bun:"name,type:text,notnull"`
|
|
||||||
Email string `bun:"email,type:text,notnull,unique"`
|
|
||||||
Token string `bun:"token,type:text,notnull"`
|
|
||||||
CreatedAt int `bun:"created_at,notnull"`
|
|
||||||
Role string `bun:"role,type:text,notnull"`
|
|
||||||
OrgID string `bun:"org_id,type:text,notnull"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Group struct {
|
|
||||||
bun.BaseModel `bun:"table:groups"`
|
|
||||||
ID string `bun:"id,pk,type:text" json:"id"`
|
|
||||||
Name string `bun:"name,type:text,notnull,unique" json:"name"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
bun.BaseModel `bun:"table:users"`
|
|
||||||
ID string `bun:"id,pk,type:text"`
|
|
||||||
Name string `bun:"name,type:text,notnull"`
|
|
||||||
Email string `bun:"email,type:text,notnull,unique"`
|
|
||||||
Password string `bun:"password,type:text,notnull"`
|
|
||||||
CreatedAt int `bun:"created_at,notnull"`
|
|
||||||
ProfilePictureURL string `bun:"profile_picture_url,type:text"`
|
|
||||||
GroupID string `bun:"group_id,type:text,notnull"`
|
|
||||||
OrgID string `bun:"org_id,type:text,notnull"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type ResetPasswordRequest struct {
|
|
||||||
bun.BaseModel `bun:"table:reset_password_request"`
|
|
||||||
ID int `bun:"id,pk,autoincrement"`
|
|
||||||
Token string `bun:"token,type:text,notnull"`
|
|
||||||
UserID string `bun:"user_id,type:text,notnull"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserFlags struct {
|
|
||||||
bun.BaseModel `bun:"table:user_flags"`
|
|
||||||
UserID string `bun:"user_id,pk,type:text,notnull"`
|
|
||||||
Flags string `bun:"flags,type:text"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ApdexSettings struct {
|
type ApdexSettings struct {
|
||||||
bun.BaseModel `bun:"table:apdex_settings"`
|
OrgID string `bun:"org_id,pk,type:text" json:"orgId"`
|
||||||
ServiceName string `bun:"service_name,pk,type:text"`
|
ServiceName string `bun:"service_name,pk,type:text" json:"serviceName"`
|
||||||
Threshold float64 `bun:"threshold,type:float,notnull"`
|
Threshold float64 `bun:"threshold,type:float,notnull" json:"threshold"`
|
||||||
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull"`
|
ExcludeStatusCodes string `bun:"exclude_status_codes,type:text,notnull" json:"excludeStatusCodes"`
|
||||||
}
|
|
||||||
|
|
||||||
type IngestionKey struct {
|
|
||||||
bun.BaseModel `bun:"table:ingestion_keys"`
|
|
||||||
KeyId string `bun:"key_id,pk,type:text"`
|
|
||||||
Name string `bun:"name,type:text"`
|
|
||||||
CreatedAt time.Time `bun:"created_at,default:current_timestamp"`
|
|
||||||
IngestionKey string `bun:"ingestion_key,type:text,notnull"`
|
|
||||||
IngestionURL string `bun:"ingestion_url,type:text,notnull"`
|
|
||||||
DataRegion string `bun:"data_region,type:text,notnull"`
|
|
||||||
}
|
}
|
||||||
|
54
pkg/types/user.go
Normal file
54
pkg/types/user.go
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/uptrace/bun"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Invite struct {
|
||||||
|
bun.BaseModel `bun:"table:invites"`
|
||||||
|
|
||||||
|
OrgID string `bun:"org_id,type:text,notnull" json:"orgId"`
|
||||||
|
ID int `bun:"id,pk,autoincrement" json:"id"`
|
||||||
|
Name string `bun:"name,type:text,notnull" json:"name"`
|
||||||
|
Email string `bun:"email,type:text,notnull,unique" json:"email"`
|
||||||
|
Token string `bun:"token,type:text,notnull" json:"token"`
|
||||||
|
CreatedAt time.Time `bun:"created_at,notnull" json:"createdAt"`
|
||||||
|
Role string `bun:"role,type:text,notnull" json:"role"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Group struct {
|
||||||
|
bun.BaseModel `bun:"table:groups"`
|
||||||
|
|
||||||
|
TimeAuditable
|
||||||
|
OrgID string `bun:"org_id,type:text"`
|
||||||
|
ID string `bun:"id,pk,type:text" json:"id"`
|
||||||
|
Name string `bun:"name,type:text,notnull,unique" json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GettableUser struct {
|
||||||
|
User
|
||||||
|
Role string `json:"role"`
|
||||||
|
Organization string `json:"organization"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
bun.BaseModel `bun:"table:users"`
|
||||||
|
|
||||||
|
TimeAuditable
|
||||||
|
ID string `bun:"id,pk,type:text" json:"id"`
|
||||||
|
Name string `bun:"name,type:text,notnull" json:"name"`
|
||||||
|
Email string `bun:"email,type:text,notnull,unique" json:"email"`
|
||||||
|
Password string `bun:"password,type:text,notnull" json:"-"`
|
||||||
|
ProfilePictureURL string `bun:"profile_picture_url,type:text" json:"profilePictureURL"`
|
||||||
|
GroupID string `bun:"group_id,type:text,notnull" json:"groupId"`
|
||||||
|
OrgID string `bun:"org_id,type:text,notnull" json:"orgId"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ResetPasswordRequest struct {
|
||||||
|
bun.BaseModel `bun:"table:reset_password_request"`
|
||||||
|
ID int `bun:"id,pk,autoincrement" json:"id"`
|
||||||
|
Token string `bun:"token,type:text,notnull" json:"token"`
|
||||||
|
UserID string `bun:"user_id,type:text,notnull" json:"userId"`
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user