mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-07-26 05:44:28 +08:00

From the Go specification [1]: "1. For a nil slice, the number of iterations is 0." "3. If the map is nil, the number of iterations is 0." Therefore, an additional nil check for before the loop is unnecessary. [1]: https://go.dev/ref/spec#For_range Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
615 lines
16 KiB
Go
615 lines
16 KiB
Go
package sqlite
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/pkg/errors"
|
|
"go.signoz.io/signoz/pkg/query-service/model"
|
|
"go.signoz.io/signoz/pkg/query-service/telemetry"
|
|
)
|
|
|
|
func (mds *ModelDaoSqlite) CreateInviteEntry(ctx context.Context,
|
|
req *model.InvitationObject) *model.ApiError {
|
|
|
|
_, 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 {
|
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) DeleteInvitation(ctx context.Context, email string) *model.ApiError {
|
|
_, err := mds.db.ExecContext(ctx, `DELETE from invites where email=?;`, email)
|
|
if err != nil {
|
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetInviteFromEmail(ctx context.Context, email string,
|
|
) (*model.InvitationObject, *model.ApiError) {
|
|
|
|
invites := []model.InvitationObject{}
|
|
err := mds.db.Select(&invites,
|
|
`SELECT * FROM invites WHERE email=?;`, email)
|
|
|
|
if err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
if len(invites) > 1 {
|
|
return nil, &model.ApiError{
|
|
Typ: model.ErrorInternal,
|
|
Err: errors.Errorf("Found multiple invites for the email: %s", email)}
|
|
}
|
|
|
|
if len(invites) == 0 {
|
|
return nil, nil
|
|
}
|
|
return &invites[0], nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetInviteFromToken(ctx context.Context, token string,
|
|
) (*model.InvitationObject, *model.ApiError) {
|
|
|
|
invites := []model.InvitationObject{}
|
|
err := mds.db.Select(&invites,
|
|
`SELECT * FROM invites WHERE token=?;`, token)
|
|
|
|
if err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
if len(invites) > 1 {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
|
|
if len(invites) == 0 {
|
|
return nil, nil
|
|
}
|
|
return &invites[0], nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetInvites(ctx context.Context,
|
|
) ([]model.InvitationObject, *model.ApiError) {
|
|
|
|
invites := []model.InvitationObject{}
|
|
err := mds.db.Select(&invites, "SELECT * FROM invites")
|
|
if err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return invites, nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) CreateOrg(ctx context.Context,
|
|
org *model.Organization) (*model.Organization, *model.ApiError) {
|
|
|
|
org.Id = uuid.NewString()
|
|
org.CreatedAt = time.Now().Unix()
|
|
_, err := mds.db.ExecContext(ctx,
|
|
`INSERT INTO organizations (id, name, created_at) VALUES (?, ?, ?);`,
|
|
org.Id, org.Name, org.CreatedAt)
|
|
|
|
if err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return org, nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetOrg(ctx context.Context,
|
|
id string) (*model.Organization, *model.ApiError) {
|
|
|
|
orgs := []model.Organization{}
|
|
err := mds.db.Select(&orgs, `SELECT * FROM organizations WHERE id=?;`, id)
|
|
|
|
if err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
if len(orgs) > 1 {
|
|
return nil, &model.ApiError{
|
|
Typ: model.ErrorInternal,
|
|
Err: errors.New("Found multiple org with same ID"),
|
|
}
|
|
}
|
|
|
|
if len(orgs) == 0 {
|
|
return nil, nil
|
|
}
|
|
return &orgs[0], nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetOrgByName(ctx context.Context,
|
|
name string) (*model.Organization, *model.ApiError) {
|
|
|
|
orgs := []model.Organization{}
|
|
|
|
if err := mds.db.Select(&orgs, `SELECT * FROM organizations WHERE name=?;`, name); err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
|
|
if len(orgs) > 1 {
|
|
return nil, &model.ApiError{
|
|
Typ: model.ErrorInternal,
|
|
Err: errors.New("Multiple orgs with same ID found"),
|
|
}
|
|
}
|
|
if len(orgs) == 0 {
|
|
return nil, nil
|
|
}
|
|
return &orgs[0], nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetOrgs(ctx context.Context) ([]model.Organization, *model.ApiError) {
|
|
orgs := []model.Organization{}
|
|
err := mds.db.Select(&orgs, `SELECT * FROM organizations`)
|
|
|
|
if err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return orgs, nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) EditOrg(ctx context.Context, org *model.Organization) *model.ApiError {
|
|
|
|
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 {
|
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
|
|
telemetry.GetInstance().SetTelemetryAnonymous(org.IsAnonymous)
|
|
telemetry.GetInstance().SetDistinctId(org.Id)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) DeleteOrg(ctx context.Context, id string) *model.ApiError {
|
|
|
|
_, err := mds.db.ExecContext(ctx, `DELETE from organizations where id=?;`, id)
|
|
if err != nil {
|
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) CreateUser(ctx context.Context,
|
|
user *model.User, isFirstUser bool) (*model.User, *model.ApiError) {
|
|
|
|
_, err := mds.db.ExecContext(ctx,
|
|
`INSERT INTO users (id, name, email, password, created_at, profile_picture_url, group_id, org_id)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?,?);`,
|
|
user.Id, user.Name, user.Email, user.Password, user.CreatedAt,
|
|
user.ProfilePictureURL, user.GroupId, user.OrgId,
|
|
)
|
|
|
|
if err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
|
|
data := map[string]interface{}{
|
|
"name": user.Name,
|
|
"email": user.Email,
|
|
"firstRegistration": false,
|
|
}
|
|
|
|
if isFirstUser {
|
|
data["firstRegistration"] = true
|
|
}
|
|
|
|
telemetry.GetInstance().IdentifyUser(user)
|
|
telemetry.GetInstance().SendEvent(telemetry.TELEMETRY_EVENT_USER, data)
|
|
|
|
return user, nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) EditUser(ctx context.Context,
|
|
update *model.User) (*model.User, *model.ApiError) {
|
|
|
|
_, 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 {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return update, nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) UpdateUserPassword(ctx context.Context, passwordHash,
|
|
userId string) *model.ApiError {
|
|
|
|
q := `UPDATE users SET password=? WHERE id=?;`
|
|
if _, err := mds.db.ExecContext(ctx, q, passwordHash, userId); err != nil {
|
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) UpdateUserGroup(ctx context.Context, userId, groupId string) *model.ApiError {
|
|
|
|
q := `UPDATE users SET group_id=? WHERE id=?;`
|
|
if _, err := mds.db.ExecContext(ctx, q, groupId, userId); err != nil {
|
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) DeleteUser(ctx context.Context, id string) *model.ApiError {
|
|
|
|
result, err := mds.db.ExecContext(ctx, `DELETE from users where id=?;`, id)
|
|
if err != nil {
|
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
|
|
affectedRows, err := result.RowsAffected()
|
|
if err != nil {
|
|
return &model.ApiError{Typ: model.ErrorExec, Err: err}
|
|
}
|
|
if affectedRows == 0 {
|
|
return &model.ApiError{
|
|
Typ: model.ErrorNotFound,
|
|
Err: fmt.Errorf("no user found with id: %s", id),
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetUser(ctx context.Context,
|
|
id string) (*model.UserPayload, *model.ApiError) {
|
|
|
|
users := []model.UserPayload{}
|
|
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,
|
|
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 {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
if len(users) > 1 {
|
|
return nil, &model.ApiError{
|
|
Typ: model.ErrorInternal,
|
|
Err: errors.New("Found multiple users with same ID"),
|
|
}
|
|
}
|
|
|
|
if len(users) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
return &users[0], nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetUserByEmail(ctx context.Context,
|
|
email string) (*model.UserPayload, *model.ApiError) {
|
|
|
|
if email == "" {
|
|
return nil, &model.ApiError{
|
|
Typ: model.ErrorBadData,
|
|
Err: fmt.Errorf("empty email address"),
|
|
}
|
|
}
|
|
|
|
users := []model.UserPayload{}
|
|
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
|
|
g.id=u.group_id and
|
|
o.id = u.org_id and
|
|
u.email=?;`
|
|
|
|
if err := mds.db.Select(&users, query, email); err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
if len(users) > 1 {
|
|
return nil, &model.ApiError{
|
|
Typ: model.ErrorInternal,
|
|
Err: errors.New("Found multiple users with same ID."),
|
|
}
|
|
}
|
|
|
|
if len(users) == 0 {
|
|
return nil, nil
|
|
}
|
|
return &users[0], nil
|
|
}
|
|
|
|
// GetUsers fetches total user count
|
|
func (mds *ModelDaoSqlite) GetUsers(ctx context.Context) ([]model.UserPayload, *model.ApiError) {
|
|
return mds.GetUsersWithOpts(ctx, 0)
|
|
}
|
|
|
|
// GetUsersWithOpts fetches users and supports additional search options
|
|
func (mds *ModelDaoSqlite) GetUsersWithOpts(ctx context.Context, limit int) ([]model.UserPayload, *model.ApiError) {
|
|
users := []model.UserPayload{}
|
|
|
|
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
|
|
g.id = u.group_id and
|
|
o.id = u.org_id`
|
|
|
|
if limit > 0 {
|
|
query = fmt.Sprintf("%s LIMIT %d", query, limit)
|
|
}
|
|
err := mds.db.Select(&users, query)
|
|
|
|
if err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return users, nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetUsersByOrg(ctx context.Context,
|
|
orgId string) ([]model.UserPayload, *model.ApiError) {
|
|
|
|
users := []model.UserPayload{}
|
|
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 {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return users, nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetUsersByGroup(ctx context.Context,
|
|
groupId string) ([]model.UserPayload, *model.ApiError) {
|
|
|
|
users := []model.UserPayload{}
|
|
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 {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return users, nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) CreateGroup(ctx context.Context,
|
|
group *model.Group) (*model.Group, *model.ApiError) {
|
|
|
|
group.Id = uuid.NewString()
|
|
|
|
q := `INSERT INTO groups (id, name) VALUES (?, ?);`
|
|
if _, err := mds.db.ExecContext(ctx, q, group.Id, group.Name); err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
|
|
return group, nil
|
|
}
|
|
|
|
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 {
|
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetGroup(ctx context.Context,
|
|
id string) (*model.Group, *model.ApiError) {
|
|
|
|
groups := []model.Group{}
|
|
if err := mds.db.Select(&groups, `SELECT id, name FROM groups WHERE id=?`, id); err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
|
|
if len(groups) > 1 {
|
|
return nil, &model.ApiError{
|
|
Typ: model.ErrorInternal,
|
|
Err: errors.New("Found multiple groups with same ID."),
|
|
}
|
|
}
|
|
|
|
if len(groups) == 0 {
|
|
return nil, nil
|
|
}
|
|
return &groups[0], nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetGroupByName(ctx context.Context,
|
|
name string) (*model.Group, *model.ApiError) {
|
|
|
|
groups := []model.Group{}
|
|
if err := mds.db.Select(&groups, `SELECT id, name FROM groups WHERE name=?`, name); err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
|
|
if len(groups) > 1 {
|
|
return nil, &model.ApiError{
|
|
Typ: model.ErrorInternal,
|
|
Err: errors.New("Found multiple groups with same name"),
|
|
}
|
|
}
|
|
|
|
if len(groups) == 0 {
|
|
return nil, nil
|
|
}
|
|
|
|
return &groups[0], nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetGroups(ctx context.Context) ([]model.Group, *model.ApiError) {
|
|
|
|
groups := []model.Group{}
|
|
if err := mds.db.Select(&groups, "SELECT * FROM groups"); err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
|
|
return groups, nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) CreateResetPasswordEntry(ctx context.Context,
|
|
req *model.ResetPasswordEntry) *model.ApiError {
|
|
|
|
q := `INSERT INTO reset_password_request (user_id, token) VALUES (?, ?);`
|
|
if _, err := mds.db.ExecContext(ctx, q, req.UserId, req.Token); err != nil {
|
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) DeleteResetPasswordEntry(ctx context.Context,
|
|
token string) *model.ApiError {
|
|
_, err := mds.db.ExecContext(ctx, `DELETE from reset_password_request where token=?;`, token)
|
|
if err != nil {
|
|
return &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (mds *ModelDaoSqlite) GetResetPasswordEntry(ctx context.Context,
|
|
token string) (*model.ResetPasswordEntry, *model.ApiError) {
|
|
|
|
entries := []model.ResetPasswordEntry{}
|
|
|
|
q := `SELECT user_id,token FROM reset_password_request WHERE token=?;`
|
|
if err := mds.db.Select(&entries, q, token); err != nil {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal, Err: err}
|
|
}
|
|
if len(entries) > 1 {
|
|
return nil, &model.ApiError{Typ: model.ErrorInternal,
|
|
Err: errors.New("Multiple entries for reset token is found")}
|
|
}
|
|
|
|
if len(entries) == 0 {
|
|
return nil, 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) {
|
|
// 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: ""}
|
|
|
|
// check if email is a valid user
|
|
userPayload, baseApiErr := mds.GetUserByEmail(ctx, email)
|
|
if baseApiErr != nil {
|
|
return resp, baseApiErr
|
|
}
|
|
|
|
if userPayload == nil {
|
|
resp.IsUser = false
|
|
}
|
|
|
|
return resp, nil
|
|
}
|