diff --git a/backend/build.sh b/backend/build.sh index 70a29c5af..e67178c8c 100755 --- a/backend/build.sh +++ b/backend/build.sh @@ -23,15 +23,15 @@ function build_service() { image="$1" echo "BUILDING $image" case "$image" in - http | db | ender | heuristics) + http | db | sink | ender | heuristics) echo build http - docker build -t ${DOCKER_REPO:-'local'}/$image:${git_sha1} --build-arg SERVICE_NAME=$image -f ./cmd/Dockerfile . + docker build -t ${DOCKER_REPO:-'local'}/$image:${git_sha1} --platform linux/amd64 --build-arg SERVICE_NAME=$image -f ./cmd/Dockerfile . [[ $PUSH_IMAGE -eq 1 ]] && { docker push ${DOCKER_REPO:-'local'}/$image:${git_sha1} } ;; *) - docker build -t ${DOCKER_REPO:-'local'}/$image:${git_sha1} --build-arg SERVICE_NAME=$image . + docker build -t ${DOCKER_REPO:-'local'}/$image:${git_sha1} --platform linux/amd64 --build-arg SERVICE_NAME=$image . [[ $PUSH_IMAGE -eq 1 ]] && { docker push ${DOCKER_REPO:-'local'}/$image:${git_sha1} } diff --git a/backend/cmd/Dockerfile b/backend/cmd/Dockerfile index e2f611afa..f36bfc99e 100644 --- a/backend/cmd/Dockerfile +++ b/backend/cmd/Dockerfile @@ -18,7 +18,7 @@ COPY cmd cmd ARG SERVICE_NAME RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 go build -o service -tags musl openreplay/backend/cmd/$SERVICE_NAME -FROM alpine +FROM alpine AS entrypoint RUN apk add --no-cache ca-certificates ENV TZ=UTC \ diff --git a/backend/cmd/http/main.go b/backend/cmd/http/main.go index 541baab62..3fdd8d34a 100644 --- a/backend/cmd/http/main.go +++ b/backend/cmd/http/main.go @@ -2,6 +2,10 @@ package main import ( "log" + "os" + "os/signal" + "syscall" + "openreplay/backend/internal/config" "openreplay/backend/internal/router" "openreplay/backend/internal/server" @@ -10,9 +14,6 @@ import ( "openreplay/backend/pkg/db/postgres" "openreplay/backend/pkg/pprof" "openreplay/backend/pkg/queue" - "os" - "os/signal" - "syscall" ) func main() { diff --git a/backend/services/sink/main.go b/backend/cmd/sink/main.go similarity index 67% rename from backend/services/sink/main.go rename to backend/cmd/sink/main.go index a649bb6ef..269b8fab0 100644 --- a/backend/services/sink/main.go +++ b/backend/cmd/sink/main.go @@ -9,42 +9,48 @@ import ( "os/signal" "syscall" - "openreplay/backend/pkg/env" + "openreplay/backend/internal/assetscache" + "openreplay/backend/internal/config/sink" + "openreplay/backend/internal/oswriter" . "openreplay/backend/pkg/messages" "openreplay/backend/pkg/queue" "openreplay/backend/pkg/queue/types" + "openreplay/backend/pkg/url/assets" ) func main() { log.SetFlags(log.LstdFlags | log.LUTC | log.Llongfile) - FS_DIR := env.String("FS_DIR") - if _, err := os.Stat(FS_DIR); os.IsNotExist(err) { - log.Fatalf("%v doesn't exist. %v", FS_DIR, err) + cfg := sink.New() + + if _, err := os.Stat(cfg.FsDir); os.IsNotExist(err) { + log.Fatalf("%v doesn't exist. %v", cfg.FsDir, err) } - writer := NewWriter(env.Uint16("FS_ULIMIT"), FS_DIR) + writer := oswriter.NewWriter(cfg.FsUlimit, cfg.FsDir) + + producer := queue.NewProducer() + defer producer.Close(cfg.ProducerCloseTimeout) + rewriter := assets.NewRewriter(cfg.AssetsOrigin) + assetMessageHandler := assetscache.New(cfg, rewriter, producer) count := 0 consumer := queue.NewMessageConsumer( - env.String("GROUP_SINK"), + cfg.GroupSink, []string{ - env.String("TOPIC_RAW_WEB"), - env.String("TOPIC_RAW_IOS"), + cfg.TopicRawIOS, + cfg.TopicRawWeb, }, func(sessionID uint64, message Message, _ *types.Meta) { - //typeID, err := GetMessageTypeID(value) - // if err != nil { - // log.Printf("Message type decoding error: %v", err) - // return - // } - typeID := message.Meta().TypeID + count++ + + typeID := message.TypeID() if !IsReplayerType(typeID) { return } - count++ + message = assetMessageHandler.ParseAssets(sessionID, message) value := message.Encode() var data []byte diff --git a/backend/internal/assetscache/assets.go b/backend/internal/assetscache/assets.go index 1ef70b56c..cf76bed8c 100644 --- a/backend/internal/assetscache/assets.go +++ b/backend/internal/assetscache/assets.go @@ -1,19 +1,19 @@ package assetscache import ( - "openreplay/backend/internal/config" + "openreplay/backend/internal/config/sink" "openreplay/backend/pkg/messages" "openreplay/backend/pkg/queue/types" "openreplay/backend/pkg/url/assets" ) type AssetsCache struct { - cfg *config.Config + cfg *sink.Config rewriter *assets.Rewriter producer types.Producer } -func New(cfg *config.Config, rewriter *assets.Rewriter, producer types.Producer) *AssetsCache { +func New(cfg *sink.Config, rewriter *assets.Rewriter, producer types.Producer) *AssetsCache { return &AssetsCache{ cfg: cfg, rewriter: rewriter, diff --git a/backend/internal/config/config.go b/backend/internal/config/config.go index 5b55ba346..b98f910c2 100644 --- a/backend/internal/config/config.go +++ b/backend/internal/config/config.go @@ -11,12 +11,9 @@ type Config struct { HTTPTimeout time.Duration TopicRawWeb string TopicRawIOS string - TopicCache string - CacheAssets bool BeaconSizeLimit int64 JsonSizeLimit int64 FileSizeLimit int64 - AssetsOrigin string AWSRegion string S3BucketIOSImages string Postgres string @@ -33,12 +30,9 @@ func New() *Config { HTTPTimeout: time.Second * 60, TopicRawWeb: env.String("TOPIC_RAW_WEB"), TopicRawIOS: env.String("TOPIC_RAW_IOS"), - TopicCache: env.String("TOPIC_CACHE"), - CacheAssets: env.Bool("CACHE_ASSETS"), BeaconSizeLimit: int64(env.Uint64("BEACON_SIZE_LIMIT")), JsonSizeLimit: 1e3, // 1Kb FileSizeLimit: 1e7, // 10Mb - AssetsOrigin: env.String("ASSETS_ORIGIN"), AWSRegion: env.String("AWS_REGION"), S3BucketIOSImages: env.String("S3_BUCKET_IOS_IMAGES"), Postgres: env.String("POSTGRES_STRING"), diff --git a/backend/internal/config/sink/config.go b/backend/internal/config/sink/config.go new file mode 100644 index 000000000..a78bfba63 --- /dev/null +++ b/backend/internal/config/sink/config.go @@ -0,0 +1,31 @@ +package sink + +import ( + "openreplay/backend/pkg/env" +) + +type Config struct { + FsDir string + FsUlimit uint16 + GroupSink string + TopicRawWeb string + TopicRawIOS string + TopicCache string + CacheAssets bool + AssetsOrigin string + ProducerCloseTimeout int +} + +func New() *Config { + return &Config{ + FsDir: env.String("FS_DIR"), + FsUlimit: env.Uint16("FS_ULIMIT"), + GroupSink: env.String("GROUP_SINK"), + TopicRawWeb: env.String("TOPIC_RAW_WEB"), + TopicRawIOS: env.String("TOPIC_RAW_IOS"), + TopicCache: env.String("TOPIC_CACHE"), + CacheAssets: env.Bool("CACHE_ASSETS"), + AssetsOrigin: env.String("ASSETS_ORIGIN"), + ProducerCloseTimeout: 15000, + } +} diff --git a/backend/services/sink/writer.go b/backend/internal/oswriter/oswriter.go similarity index 99% rename from backend/services/sink/writer.go rename to backend/internal/oswriter/oswriter.go index 6fcfdddbc..839e61eba 100644 --- a/backend/services/sink/writer.go +++ b/backend/internal/oswriter/oswriter.go @@ -1,4 +1,4 @@ -package main +package oswriter import ( "math" diff --git a/backend/internal/router/handlers-web.go b/backend/internal/router/handlers-web.go index fc7c6421d..13301d97e 100644 --- a/backend/internal/router/handlers-web.go +++ b/backend/internal/router/handlers-web.go @@ -1,9 +1,9 @@ package router import ( - "bytes" "encoding/json" "errors" + "io/ioutil" "log" "math/rand" "net/http" @@ -64,14 +64,14 @@ func (e *Router) startSessionHandlerWeb(w http.ResponseWriter, r *http.Request) ResponseWithError(w, http.StatusForbidden, errors.New("browser not recognized")) return } - sessionID, err := e.services.Flaker.Compose(uint64(startTime.UnixNano() / 1e6)) + sessionID, err := e.services.Flaker.Compose(uint64(startTime.UnixMilli())) if err != nil { ResponseWithError(w, http.StatusInternalServerError, err) return } // TODO: if EXPIRED => send message for two sessions association expTime := startTime.Add(time.Duration(p.MaxSessionDuration) * time.Millisecond) - tokenData = &token.TokenData{ID: sessionID, ExpTime: expTime.UnixNano() / 1e6} + tokenData = &token.TokenData{ID: sessionID, ExpTime: expTime.UnixMilli()} e.services.Producer.Produce(e.cfg.TopicRawWeb, tokenData.ID, Encode(&SessionStart{ Timestamp: req.Timestamp, @@ -117,20 +117,15 @@ func (e *Router) pushMessagesHandlerWeb(w http.ResponseWriter, r *http.Request) body := http.MaxBytesReader(w, r.Body, e.cfg.BeaconSizeLimit) defer body.Close() - var handledMessages bytes.Buffer - - // Process each message in request data - err = ReadBatchReader(body, func(msg Message) { - msg = e.services.Assets.ParseAssets(sessionData.ID, msg) - handledMessages.Write(msg.Encode()) - }) + bytes, err := ioutil.ReadAll(body) if err != nil { - ResponseWithError(w, http.StatusForbidden, err) + ResponseWithError(w, http.StatusInternalServerError, err) // TODO: Split environments; send error here only on staging return } // Send processed messages to queue as array of bytes - err = e.services.Producer.Produce(e.cfg.TopicRawWeb, sessionData.ID, handledMessages.Bytes()) + // TODO: check bytes for nonsense crap + err = e.services.Producer.Produce(e.cfg.TopicRawWeb, sessionData.ID, bytes) if err != nil { log.Printf("can't send processed messages to queue: %s", err) } diff --git a/backend/internal/services/services.go b/backend/internal/services/services.go index 5b84e1dfb..c9915c78e 100644 --- a/backend/internal/services/services.go +++ b/backend/internal/services/services.go @@ -1,7 +1,6 @@ package services import ( - "openreplay/backend/internal/assetscache" "openreplay/backend/internal/config" "openreplay/backend/internal/geoip" "openreplay/backend/internal/uaparser" @@ -10,13 +9,11 @@ import ( "openreplay/backend/pkg/queue/types" "openreplay/backend/pkg/storage" "openreplay/backend/pkg/token" - "openreplay/backend/pkg/url/assets" ) type ServicesBuilder struct { Database *cache.PGCache Producer types.Producer - Assets *assetscache.AssetsCache Flaker *flakeid.Flaker UaParser *uaparser.UAParser GeoIP *geoip.GeoIP @@ -25,11 +22,9 @@ type ServicesBuilder struct { } func New(cfg *config.Config, producer types.Producer, pgconn *cache.PGCache) *ServicesBuilder { - rewriter := assets.NewRewriter(cfg.AssetsOrigin) return &ServicesBuilder{ Database: pgconn, Producer: producer, - Assets: assetscache.New(cfg, rewriter, producer), Storage: storage.NewS3(cfg.AWSRegion, cfg.S3BucketIOSImages), Tokenizer: token.NewTokenizer(cfg.TokenSecret), UaParser: uaparser.NewUAParser(cfg.UAParserFile), diff --git a/backend/pkg/messages/filters.go b/backend/pkg/messages/filters.go index 44b2c7959..a74d49eec 100644 --- a/backend/pkg/messages/filters.go +++ b/backend/pkg/messages/filters.go @@ -1,10 +1,10 @@ // Auto-generated, do not edit package messages -func IsReplayerType(id uint64) bool { +func IsReplayerType(id int) bool { return 0 == id || 2 == id || 4 == id || 5 == id || 6 == id || 7 == id || 8 == id || 9 == id || 10 == id || 11 == id || 12 == id || 13 == id || 14 == id || 15 == id || 16 == id || 18 == id || 19 == id || 20 == id || 22 == id || 37 == id || 38 == id || 39 == id || 40 == id || 41 == id || 44 == id || 45 == id || 46 == id || 47 == id || 48 == id || 49 == id || 54 == id || 55 == id || 59 == id || 69 == id || 70 == id || 90 == id || 93 == id || 96 == id || 100 == id || 102 == id || 103 == id || 105 == id } -func IsIOSType(id uint64) bool { +func IsIOSType(id int) bool { return 107 == id || 90 == id || 91 == id || 92 == id || 93 == id || 94 == id || 95 == id || 96 == id || 97 == id || 98 == id || 99 == id || 100 == id || 101 == id || 102 == id || 103 == id || 104 == id || 105 == id || 110 == id || 111 == id } diff --git a/backend/pkg/messages/legacy-message-transform.go b/backend/pkg/messages/legacy-message-transform.go index 6774d95b1..3a42cdab0 100644 --- a/backend/pkg/messages/legacy-message-transform.go +++ b/backend/pkg/messages/legacy-message-transform.go @@ -3,10 +3,7 @@ package messages func transformDeprecated(msg Message) Message { switch m := msg.(type) { case *MouseClickDepricated: - meta := m.Meta() - meta.TypeID = 33 return &MouseClick{ - meta: meta, ID: m.ID, HesitationTime: m.HesitationTime, Label: m.Label, diff --git a/backend/pkg/messages/message.go b/backend/pkg/messages/message.go new file mode 100644 index 000000000..d701d474f --- /dev/null +++ b/backend/pkg/messages/message.go @@ -0,0 +1,16 @@ +package messages + +type message struct { + Timestamp int64 + Index uint64 +} + +func (m *message) Meta() *message { + return m +} + +type Message interface { + Encode() []byte + TypeID() int + Meta() *message +} diff --git a/backend/pkg/messages/messages.go b/backend/pkg/messages/messages.go index 418c47342..ba10eb026 100644 --- a/backend/pkg/messages/messages.go +++ b/backend/pkg/messages/messages.go @@ -1,24 +1,8 @@ // Auto-generated, do not edit package messages -type Message interface { - Encode() []byte - Meta() *meta -} - -type meta struct { - Timestamp int64 - Index uint64 - TypeID uint64 -} - -// Might also implement Encode() here (?) -func (m *meta) Meta() *meta { - return m -} - type BatchMeta struct { - *meta + message PageNo uint64 FirstIndex uint64 Timestamp int64 @@ -34,8 +18,12 @@ func (msg *BatchMeta) Encode() []byte { return buf[:p] } +func (msg *BatchMeta) TypeID() int { + return 80 +} + type Timestamp struct { - *meta + message Timestamp uint64 } @@ -47,8 +35,12 @@ func (msg *Timestamp) Encode() []byte { return buf[:p] } +func (msg *Timestamp) TypeID() int { + return 0 +} + type SessionStart struct { - *meta + message Timestamp uint64 ProjectID uint64 TrackerVersion string @@ -90,8 +82,12 @@ func (msg *SessionStart) Encode() []byte { return buf[:p] } +func (msg *SessionStart) TypeID() int { + return 1 +} + type SessionDisconnect struct { - *meta + message Timestamp uint64 } @@ -103,8 +99,12 @@ func (msg *SessionDisconnect) Encode() []byte { return buf[:p] } +func (msg *SessionDisconnect) TypeID() int { + return 2 +} + type SessionEnd struct { - *meta + message Timestamp uint64 } @@ -116,8 +116,12 @@ func (msg *SessionEnd) Encode() []byte { return buf[:p] } +func (msg *SessionEnd) TypeID() int { + return 3 +} + type SetPageLocation struct { - *meta + message URL string Referrer string NavigationStart uint64 @@ -133,8 +137,12 @@ func (msg *SetPageLocation) Encode() []byte { return buf[:p] } +func (msg *SetPageLocation) TypeID() int { + return 4 +} + type SetViewportSize struct { - *meta + message Width uint64 Height uint64 } @@ -148,8 +156,12 @@ func (msg *SetViewportSize) Encode() []byte { return buf[:p] } +func (msg *SetViewportSize) TypeID() int { + return 5 +} + type SetViewportScroll struct { - *meta + message X int64 Y int64 } @@ -163,8 +175,12 @@ func (msg *SetViewportScroll) Encode() []byte { return buf[:p] } +func (msg *SetViewportScroll) TypeID() int { + return 6 +} + type CreateDocument struct { - *meta + message } func (msg *CreateDocument) Encode() []byte { @@ -175,8 +191,12 @@ func (msg *CreateDocument) Encode() []byte { return buf[:p] } +func (msg *CreateDocument) TypeID() int { + return 7 +} + type CreateElementNode struct { - *meta + message ID uint64 ParentID uint64 index uint64 @@ -196,8 +216,12 @@ func (msg *CreateElementNode) Encode() []byte { return buf[:p] } +func (msg *CreateElementNode) TypeID() int { + return 8 +} + type CreateTextNode struct { - *meta + message ID uint64 ParentID uint64 Index uint64 @@ -213,8 +237,12 @@ func (msg *CreateTextNode) Encode() []byte { return buf[:p] } +func (msg *CreateTextNode) TypeID() int { + return 9 +} + type MoveNode struct { - *meta + message ID uint64 ParentID uint64 Index uint64 @@ -230,8 +258,12 @@ func (msg *MoveNode) Encode() []byte { return buf[:p] } +func (msg *MoveNode) TypeID() int { + return 10 +} + type RemoveNode struct { - *meta + message ID uint64 } @@ -243,8 +275,12 @@ func (msg *RemoveNode) Encode() []byte { return buf[:p] } +func (msg *RemoveNode) TypeID() int { + return 11 +} + type SetNodeAttribute struct { - *meta + message ID uint64 Name string Value string @@ -260,8 +296,12 @@ func (msg *SetNodeAttribute) Encode() []byte { return buf[:p] } +func (msg *SetNodeAttribute) TypeID() int { + return 12 +} + type RemoveNodeAttribute struct { - *meta + message ID uint64 Name string } @@ -275,8 +315,12 @@ func (msg *RemoveNodeAttribute) Encode() []byte { return buf[:p] } +func (msg *RemoveNodeAttribute) TypeID() int { + return 13 +} + type SetNodeData struct { - *meta + message ID uint64 Data string } @@ -290,8 +334,12 @@ func (msg *SetNodeData) Encode() []byte { return buf[:p] } +func (msg *SetNodeData) TypeID() int { + return 14 +} + type SetCSSData struct { - *meta + message ID uint64 Data string } @@ -305,8 +353,12 @@ func (msg *SetCSSData) Encode() []byte { return buf[:p] } +func (msg *SetCSSData) TypeID() int { + return 15 +} + type SetNodeScroll struct { - *meta + message ID uint64 X int64 Y int64 @@ -322,8 +374,12 @@ func (msg *SetNodeScroll) Encode() []byte { return buf[:p] } +func (msg *SetNodeScroll) TypeID() int { + return 16 +} + type SetInputTarget struct { - *meta + message ID uint64 Label string } @@ -337,8 +393,12 @@ func (msg *SetInputTarget) Encode() []byte { return buf[:p] } +func (msg *SetInputTarget) TypeID() int { + return 17 +} + type SetInputValue struct { - *meta + message ID uint64 Value string Mask int64 @@ -354,8 +414,12 @@ func (msg *SetInputValue) Encode() []byte { return buf[:p] } +func (msg *SetInputValue) TypeID() int { + return 18 +} + type SetInputChecked struct { - *meta + message ID uint64 Checked bool } @@ -369,8 +433,12 @@ func (msg *SetInputChecked) Encode() []byte { return buf[:p] } +func (msg *SetInputChecked) TypeID() int { + return 19 +} + type MouseMove struct { - *meta + message X uint64 Y uint64 } @@ -384,8 +452,12 @@ func (msg *MouseMove) Encode() []byte { return buf[:p] } +func (msg *MouseMove) TypeID() int { + return 20 +} + type MouseClickDepricated struct { - *meta + message ID uint64 HesitationTime uint64 Label string @@ -401,8 +473,12 @@ func (msg *MouseClickDepricated) Encode() []byte { return buf[:p] } +func (msg *MouseClickDepricated) TypeID() int { + return 21 +} + type ConsoleLog struct { - *meta + message Level string Value string } @@ -416,8 +492,12 @@ func (msg *ConsoleLog) Encode() []byte { return buf[:p] } +func (msg *ConsoleLog) TypeID() int { + return 22 +} + type PageLoadTiming struct { - *meta + message RequestStart uint64 ResponseStart uint64 ResponseEnd uint64 @@ -445,8 +525,12 @@ func (msg *PageLoadTiming) Encode() []byte { return buf[:p] } +func (msg *PageLoadTiming) TypeID() int { + return 23 +} + type PageRenderTiming struct { - *meta + message SpeedIndex uint64 VisuallyComplete uint64 TimeToInteractive uint64 @@ -462,8 +546,12 @@ func (msg *PageRenderTiming) Encode() []byte { return buf[:p] } +func (msg *PageRenderTiming) TypeID() int { + return 24 +} + type JSException struct { - *meta + message Name string Message string Payload string @@ -479,8 +567,12 @@ func (msg *JSException) Encode() []byte { return buf[:p] } +func (msg *JSException) TypeID() int { + return 25 +} + type RawErrorEvent struct { - *meta + message Timestamp uint64 Source string Name string @@ -500,8 +592,12 @@ func (msg *RawErrorEvent) Encode() []byte { return buf[:p] } +func (msg *RawErrorEvent) TypeID() int { + return 26 +} + type RawCustomEvent struct { - *meta + message Name string Payload string } @@ -515,8 +611,12 @@ func (msg *RawCustomEvent) Encode() []byte { return buf[:p] } +func (msg *RawCustomEvent) TypeID() int { + return 27 +} + type UserID struct { - *meta + message ID string } @@ -528,8 +628,12 @@ func (msg *UserID) Encode() []byte { return buf[:p] } +func (msg *UserID) TypeID() int { + return 28 +} + type UserAnonymousID struct { - *meta + message ID string } @@ -541,8 +645,12 @@ func (msg *UserAnonymousID) Encode() []byte { return buf[:p] } +func (msg *UserAnonymousID) TypeID() int { + return 29 +} + type Metadata struct { - *meta + message Key string Value string } @@ -556,8 +664,12 @@ func (msg *Metadata) Encode() []byte { return buf[:p] } +func (msg *Metadata) TypeID() int { + return 30 +} + type PageEvent struct { - *meta + message MessageID uint64 Timestamp uint64 URL string @@ -601,8 +713,12 @@ func (msg *PageEvent) Encode() []byte { return buf[:p] } +func (msg *PageEvent) TypeID() int { + return 31 +} + type InputEvent struct { - *meta + message MessageID uint64 Timestamp uint64 Value string @@ -622,8 +738,12 @@ func (msg *InputEvent) Encode() []byte { return buf[:p] } +func (msg *InputEvent) TypeID() int { + return 32 +} + type ClickEvent struct { - *meta + message MessageID uint64 Timestamp uint64 HesitationTime uint64 @@ -643,8 +763,12 @@ func (msg *ClickEvent) Encode() []byte { return buf[:p] } +func (msg *ClickEvent) TypeID() int { + return 33 +} + type ErrorEvent struct { - *meta + message MessageID uint64 Timestamp uint64 Source string @@ -666,8 +790,12 @@ func (msg *ErrorEvent) Encode() []byte { return buf[:p] } +func (msg *ErrorEvent) TypeID() int { + return 34 +} + type ResourceEvent struct { - *meta + message MessageID uint64 Timestamp uint64 Duration uint64 @@ -701,8 +829,12 @@ func (msg *ResourceEvent) Encode() []byte { return buf[:p] } +func (msg *ResourceEvent) TypeID() int { + return 35 +} + type CustomEvent struct { - *meta + message MessageID uint64 Timestamp uint64 Name string @@ -720,8 +852,12 @@ func (msg *CustomEvent) Encode() []byte { return buf[:p] } +func (msg *CustomEvent) TypeID() int { + return 36 +} + type CSSInsertRule struct { - *meta + message ID uint64 Rule string Index uint64 @@ -737,8 +873,12 @@ func (msg *CSSInsertRule) Encode() []byte { return buf[:p] } +func (msg *CSSInsertRule) TypeID() int { + return 37 +} + type CSSDeleteRule struct { - *meta + message ID uint64 Index uint64 } @@ -752,8 +892,12 @@ func (msg *CSSDeleteRule) Encode() []byte { return buf[:p] } +func (msg *CSSDeleteRule) TypeID() int { + return 38 +} + type Fetch struct { - *meta + message Method string URL string Request string @@ -777,8 +921,12 @@ func (msg *Fetch) Encode() []byte { return buf[:p] } +func (msg *Fetch) TypeID() int { + return 39 +} + type Profiler struct { - *meta + message Name string Duration uint64 Args string @@ -796,8 +944,12 @@ func (msg *Profiler) Encode() []byte { return buf[:p] } +func (msg *Profiler) TypeID() int { + return 40 +} + type OTable struct { - *meta + message Key string Value string } @@ -811,8 +963,12 @@ func (msg *OTable) Encode() []byte { return buf[:p] } +func (msg *OTable) TypeID() int { + return 41 +} + type StateAction struct { - *meta + message Type string } @@ -824,8 +980,12 @@ func (msg *StateAction) Encode() []byte { return buf[:p] } +func (msg *StateAction) TypeID() int { + return 42 +} + type StateActionEvent struct { - *meta + message MessageID uint64 Timestamp uint64 Type string @@ -841,8 +1001,12 @@ func (msg *StateActionEvent) Encode() []byte { return buf[:p] } +func (msg *StateActionEvent) TypeID() int { + return 43 +} + type Redux struct { - *meta + message Action string State string Duration uint64 @@ -858,8 +1022,12 @@ func (msg *Redux) Encode() []byte { return buf[:p] } +func (msg *Redux) TypeID() int { + return 44 +} + type Vuex struct { - *meta + message Mutation string State string } @@ -873,8 +1041,12 @@ func (msg *Vuex) Encode() []byte { return buf[:p] } +func (msg *Vuex) TypeID() int { + return 45 +} + type MobX struct { - *meta + message Type string Payload string } @@ -888,8 +1060,12 @@ func (msg *MobX) Encode() []byte { return buf[:p] } +func (msg *MobX) TypeID() int { + return 46 +} + type NgRx struct { - *meta + message Action string State string Duration uint64 @@ -905,8 +1081,12 @@ func (msg *NgRx) Encode() []byte { return buf[:p] } +func (msg *NgRx) TypeID() int { + return 47 +} + type GraphQL struct { - *meta + message OperationKind string OperationName string Variables string @@ -924,8 +1104,12 @@ func (msg *GraphQL) Encode() []byte { return buf[:p] } +func (msg *GraphQL) TypeID() int { + return 48 +} + type PerformanceTrack struct { - *meta + message Frames int64 Ticks int64 TotalJSHeapSize uint64 @@ -943,8 +1127,12 @@ func (msg *PerformanceTrack) Encode() []byte { return buf[:p] } +func (msg *PerformanceTrack) TypeID() int { + return 49 +} + type GraphQLEvent struct { - *meta + message MessageID uint64 Timestamp uint64 OperationKind string @@ -966,8 +1154,12 @@ func (msg *GraphQLEvent) Encode() []byte { return buf[:p] } +func (msg *GraphQLEvent) TypeID() int { + return 50 +} + type FetchEvent struct { - *meta + message MessageID uint64 Timestamp uint64 Method string @@ -993,8 +1185,12 @@ func (msg *FetchEvent) Encode() []byte { return buf[:p] } +func (msg *FetchEvent) TypeID() int { + return 51 +} + type DOMDrop struct { - *meta + message Timestamp uint64 } @@ -1006,8 +1202,12 @@ func (msg *DOMDrop) Encode() []byte { return buf[:p] } +func (msg *DOMDrop) TypeID() int { + return 52 +} + type ResourceTiming struct { - *meta + message Timestamp uint64 Duration uint64 TTFB uint64 @@ -1033,8 +1233,12 @@ func (msg *ResourceTiming) Encode() []byte { return buf[:p] } +func (msg *ResourceTiming) TypeID() int { + return 53 +} + type ConnectionInformation struct { - *meta + message Downlink uint64 Type string } @@ -1048,8 +1252,12 @@ func (msg *ConnectionInformation) Encode() []byte { return buf[:p] } +func (msg *ConnectionInformation) TypeID() int { + return 54 +} + type SetPageVisibility struct { - *meta + message hidden bool } @@ -1061,8 +1269,12 @@ func (msg *SetPageVisibility) Encode() []byte { return buf[:p] } +func (msg *SetPageVisibility) TypeID() int { + return 55 +} + type PerformanceTrackAggr struct { - *meta + message TimestampStart uint64 TimestampEnd uint64 MinFPS uint64 @@ -1100,8 +1312,12 @@ func (msg *PerformanceTrackAggr) Encode() []byte { return buf[:p] } +func (msg *PerformanceTrackAggr) TypeID() int { + return 56 +} + type LongTask struct { - *meta + message Timestamp uint64 Duration uint64 Context uint64 @@ -1125,8 +1341,12 @@ func (msg *LongTask) Encode() []byte { return buf[:p] } +func (msg *LongTask) TypeID() int { + return 59 +} + type SetNodeAttributeURLBased struct { - *meta + message ID uint64 Name string Value string @@ -1144,8 +1364,12 @@ func (msg *SetNodeAttributeURLBased) Encode() []byte { return buf[:p] } +func (msg *SetNodeAttributeURLBased) TypeID() int { + return 60 +} + type SetCSSDataURLBased struct { - *meta + message ID uint64 Data string BaseURL string @@ -1161,8 +1385,12 @@ func (msg *SetCSSDataURLBased) Encode() []byte { return buf[:p] } +func (msg *SetCSSDataURLBased) TypeID() int { + return 61 +} + type IssueEvent struct { - *meta + message MessageID uint64 Timestamp uint64 Type string @@ -1184,8 +1412,12 @@ func (msg *IssueEvent) Encode() []byte { return buf[:p] } +func (msg *IssueEvent) TypeID() int { + return 62 +} + type TechnicalInfo struct { - *meta + message Type string Value string } @@ -1199,8 +1431,12 @@ func (msg *TechnicalInfo) Encode() []byte { return buf[:p] } +func (msg *TechnicalInfo) TypeID() int { + return 63 +} + type CustomIssue struct { - *meta + message Name string Payload string } @@ -1214,8 +1450,12 @@ func (msg *CustomIssue) Encode() []byte { return buf[:p] } +func (msg *CustomIssue) TypeID() int { + return 64 +} + type PageClose struct { - *meta + message } func (msg *PageClose) Encode() []byte { @@ -1226,8 +1466,12 @@ func (msg *PageClose) Encode() []byte { return buf[:p] } +func (msg *PageClose) TypeID() int { + return 65 +} + type AssetCache struct { - *meta + message URL string } @@ -1239,8 +1483,12 @@ func (msg *AssetCache) Encode() []byte { return buf[:p] } +func (msg *AssetCache) TypeID() int { + return 66 +} + type CSSInsertRuleURLBased struct { - *meta + message ID uint64 Rule string Index uint64 @@ -1258,8 +1506,12 @@ func (msg *CSSInsertRuleURLBased) Encode() []byte { return buf[:p] } +func (msg *CSSInsertRuleURLBased) TypeID() int { + return 67 +} + type MouseClick struct { - *meta + message ID uint64 HesitationTime uint64 Label string @@ -1277,8 +1529,12 @@ func (msg *MouseClick) Encode() []byte { return buf[:p] } +func (msg *MouseClick) TypeID() int { + return 69 +} + type CreateIFrameDocument struct { - *meta + message FrameID uint64 ID uint64 } @@ -1292,8 +1548,12 @@ func (msg *CreateIFrameDocument) Encode() []byte { return buf[:p] } +func (msg *CreateIFrameDocument) TypeID() int { + return 70 +} + type IOSBatchMeta struct { - *meta + message Timestamp uint64 Length uint64 FirstIndex uint64 @@ -1309,8 +1569,12 @@ func (msg *IOSBatchMeta) Encode() []byte { return buf[:p] } +func (msg *IOSBatchMeta) TypeID() int { + return 107 +} + type IOSSessionStart struct { - *meta + message Timestamp uint64 ProjectID uint64 TrackerVersion string @@ -1340,8 +1604,12 @@ func (msg *IOSSessionStart) Encode() []byte { return buf[:p] } +func (msg *IOSSessionStart) TypeID() int { + return 90 +} + type IOSSessionEnd struct { - *meta + message Timestamp uint64 } @@ -1353,8 +1621,12 @@ func (msg *IOSSessionEnd) Encode() []byte { return buf[:p] } +func (msg *IOSSessionEnd) TypeID() int { + return 91 +} + type IOSMetadata struct { - *meta + message Timestamp uint64 Length uint64 Key string @@ -1372,8 +1644,12 @@ func (msg *IOSMetadata) Encode() []byte { return buf[:p] } +func (msg *IOSMetadata) TypeID() int { + return 92 +} + type IOSCustomEvent struct { - *meta + message Timestamp uint64 Length uint64 Name string @@ -1391,8 +1667,12 @@ func (msg *IOSCustomEvent) Encode() []byte { return buf[:p] } +func (msg *IOSCustomEvent) TypeID() int { + return 93 +} + type IOSUserID struct { - *meta + message Timestamp uint64 Length uint64 Value string @@ -1408,8 +1688,12 @@ func (msg *IOSUserID) Encode() []byte { return buf[:p] } +func (msg *IOSUserID) TypeID() int { + return 94 +} + type IOSUserAnonymousID struct { - *meta + message Timestamp uint64 Length uint64 Value string @@ -1425,8 +1709,12 @@ func (msg *IOSUserAnonymousID) Encode() []byte { return buf[:p] } +func (msg *IOSUserAnonymousID) TypeID() int { + return 95 +} + type IOSScreenChanges struct { - *meta + message Timestamp uint64 Length uint64 X uint64 @@ -1448,8 +1736,12 @@ func (msg *IOSScreenChanges) Encode() []byte { return buf[:p] } +func (msg *IOSScreenChanges) TypeID() int { + return 96 +} + type IOSCrash struct { - *meta + message Timestamp uint64 Length uint64 Name string @@ -1469,8 +1761,12 @@ func (msg *IOSCrash) Encode() []byte { return buf[:p] } +func (msg *IOSCrash) TypeID() int { + return 97 +} + type IOSScreenEnter struct { - *meta + message Timestamp uint64 Length uint64 Title string @@ -1488,8 +1784,12 @@ func (msg *IOSScreenEnter) Encode() []byte { return buf[:p] } +func (msg *IOSScreenEnter) TypeID() int { + return 98 +} + type IOSScreenLeave struct { - *meta + message Timestamp uint64 Length uint64 Title string @@ -1507,8 +1807,12 @@ func (msg *IOSScreenLeave) Encode() []byte { return buf[:p] } +func (msg *IOSScreenLeave) TypeID() int { + return 99 +} + type IOSClickEvent struct { - *meta + message Timestamp uint64 Length uint64 Label string @@ -1528,8 +1832,12 @@ func (msg *IOSClickEvent) Encode() []byte { return buf[:p] } +func (msg *IOSClickEvent) TypeID() int { + return 100 +} + type IOSInputEvent struct { - *meta + message Timestamp uint64 Length uint64 Value string @@ -1549,8 +1857,12 @@ func (msg *IOSInputEvent) Encode() []byte { return buf[:p] } +func (msg *IOSInputEvent) TypeID() int { + return 101 +} + type IOSPerformanceEvent struct { - *meta + message Timestamp uint64 Length uint64 Name string @@ -1568,8 +1880,12 @@ func (msg *IOSPerformanceEvent) Encode() []byte { return buf[:p] } +func (msg *IOSPerformanceEvent) TypeID() int { + return 102 +} + type IOSLog struct { - *meta + message Timestamp uint64 Length uint64 Severity string @@ -1587,8 +1903,12 @@ func (msg *IOSLog) Encode() []byte { return buf[:p] } +func (msg *IOSLog) TypeID() int { + return 103 +} + type IOSInternalError struct { - *meta + message Timestamp uint64 Length uint64 Content string @@ -1604,8 +1924,12 @@ func (msg *IOSInternalError) Encode() []byte { return buf[:p] } +func (msg *IOSInternalError) TypeID() int { + return 104 +} + type IOSNetworkCall struct { - *meta + message Timestamp uint64 Length uint64 Duration uint64 @@ -1633,8 +1957,12 @@ func (msg *IOSNetworkCall) Encode() []byte { return buf[:p] } +func (msg *IOSNetworkCall) TypeID() int { + return 105 +} + type IOSPerformanceAggregated struct { - *meta + message TimestampStart uint64 TimestampEnd uint64 MinFPS uint64 @@ -1672,8 +2000,12 @@ func (msg *IOSPerformanceAggregated) Encode() []byte { return buf[:p] } +func (msg *IOSPerformanceAggregated) TypeID() int { + return 110 +} + type IOSIssueEvent struct { - *meta + message Timestamp uint64 Type string ContextString string @@ -1692,3 +2024,7 @@ func (msg *IOSIssueEvent) Encode() []byte { p = WriteString(msg.Payload, buf, p) return buf[:p] } + +func (msg *IOSIssueEvent) TypeID() int { + return 111 +} diff --git a/backend/pkg/messages/read-message.go b/backend/pkg/messages/read-message.go index 60f9c17a8..59c8e739c 100644 --- a/backend/pkg/messages/read-message.go +++ b/backend/pkg/messages/read-message.go @@ -14,7 +14,7 @@ func ReadMessage(reader io.Reader) (Message, error) { switch t { case 80: - msg := &BatchMeta{meta: &meta{TypeID: 80}} + msg := &BatchMeta{} if msg.PageNo, err = ReadUint(reader); err != nil { return nil, err } @@ -27,14 +27,14 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 0: - msg := &Timestamp{meta: &meta{TypeID: 0}} + msg := &Timestamp{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } return msg, nil case 1: - msg := &SessionStart{meta: &meta{TypeID: 1}} + msg := &SessionStart{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -86,21 +86,21 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 2: - msg := &SessionDisconnect{meta: &meta{TypeID: 2}} + msg := &SessionDisconnect{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } return msg, nil case 3: - msg := &SessionEnd{meta: &meta{TypeID: 3}} + msg := &SessionEnd{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } return msg, nil case 4: - msg := &SetPageLocation{meta: &meta{TypeID: 4}} + msg := &SetPageLocation{} if msg.URL, err = ReadString(reader); err != nil { return nil, err } @@ -113,7 +113,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 5: - msg := &SetViewportSize{meta: &meta{TypeID: 5}} + msg := &SetViewportSize{} if msg.Width, err = ReadUint(reader); err != nil { return nil, err } @@ -123,7 +123,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 6: - msg := &SetViewportScroll{meta: &meta{TypeID: 6}} + msg := &SetViewportScroll{} if msg.X, err = ReadInt(reader); err != nil { return nil, err } @@ -133,12 +133,12 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 7: - msg := &CreateDocument{meta: &meta{TypeID: 7}} + msg := &CreateDocument{} return msg, nil case 8: - msg := &CreateElementNode{meta: &meta{TypeID: 8}} + msg := &CreateElementNode{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -157,7 +157,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 9: - msg := &CreateTextNode{meta: &meta{TypeID: 9}} + msg := &CreateTextNode{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -170,7 +170,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 10: - msg := &MoveNode{meta: &meta{TypeID: 10}} + msg := &MoveNode{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -183,14 +183,14 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 11: - msg := &RemoveNode{meta: &meta{TypeID: 11}} + msg := &RemoveNode{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } return msg, nil case 12: - msg := &SetNodeAttribute{meta: &meta{TypeID: 12}} + msg := &SetNodeAttribute{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -203,7 +203,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 13: - msg := &RemoveNodeAttribute{meta: &meta{TypeID: 13}} + msg := &RemoveNodeAttribute{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -213,7 +213,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 14: - msg := &SetNodeData{meta: &meta{TypeID: 14}} + msg := &SetNodeData{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -223,7 +223,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 15: - msg := &SetCSSData{meta: &meta{TypeID: 15}} + msg := &SetCSSData{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -233,7 +233,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 16: - msg := &SetNodeScroll{meta: &meta{TypeID: 16}} + msg := &SetNodeScroll{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -246,7 +246,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 17: - msg := &SetInputTarget{meta: &meta{TypeID: 17}} + msg := &SetInputTarget{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -256,7 +256,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 18: - msg := &SetInputValue{meta: &meta{TypeID: 18}} + msg := &SetInputValue{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -269,7 +269,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 19: - msg := &SetInputChecked{meta: &meta{TypeID: 19}} + msg := &SetInputChecked{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -279,7 +279,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 20: - msg := &MouseMove{meta: &meta{TypeID: 20}} + msg := &MouseMove{} if msg.X, err = ReadUint(reader); err != nil { return nil, err } @@ -289,7 +289,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 21: - msg := &MouseClickDepricated{meta: &meta{TypeID: 21}} + msg := &MouseClickDepricated{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -302,7 +302,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 22: - msg := &ConsoleLog{meta: &meta{TypeID: 22}} + msg := &ConsoleLog{} if msg.Level, err = ReadString(reader); err != nil { return nil, err } @@ -312,7 +312,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 23: - msg := &PageLoadTiming{meta: &meta{TypeID: 23}} + msg := &PageLoadTiming{} if msg.RequestStart, err = ReadUint(reader); err != nil { return nil, err } @@ -343,7 +343,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 24: - msg := &PageRenderTiming{meta: &meta{TypeID: 24}} + msg := &PageRenderTiming{} if msg.SpeedIndex, err = ReadUint(reader); err != nil { return nil, err } @@ -356,7 +356,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 25: - msg := &JSException{meta: &meta{TypeID: 25}} + msg := &JSException{} if msg.Name, err = ReadString(reader); err != nil { return nil, err } @@ -369,7 +369,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 26: - msg := &RawErrorEvent{meta: &meta{TypeID: 26}} + msg := &RawErrorEvent{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -388,7 +388,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 27: - msg := &RawCustomEvent{meta: &meta{TypeID: 27}} + msg := &RawCustomEvent{} if msg.Name, err = ReadString(reader); err != nil { return nil, err } @@ -398,21 +398,21 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 28: - msg := &UserID{meta: &meta{TypeID: 28}} + msg := &UserID{} if msg.ID, err = ReadString(reader); err != nil { return nil, err } return msg, nil case 29: - msg := &UserAnonymousID{meta: &meta{TypeID: 29}} + msg := &UserAnonymousID{} if msg.ID, err = ReadString(reader); err != nil { return nil, err } return msg, nil case 30: - msg := &Metadata{meta: &meta{TypeID: 30}} + msg := &Metadata{} if msg.Key, err = ReadString(reader); err != nil { return nil, err } @@ -422,7 +422,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 31: - msg := &PageEvent{meta: &meta{TypeID: 31}} + msg := &PageEvent{} if msg.MessageID, err = ReadUint(reader); err != nil { return nil, err } @@ -477,7 +477,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 32: - msg := &InputEvent{meta: &meta{TypeID: 32}} + msg := &InputEvent{} if msg.MessageID, err = ReadUint(reader); err != nil { return nil, err } @@ -496,7 +496,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 33: - msg := &ClickEvent{meta: &meta{TypeID: 33}} + msg := &ClickEvent{} if msg.MessageID, err = ReadUint(reader); err != nil { return nil, err } @@ -515,7 +515,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 34: - msg := &ErrorEvent{meta: &meta{TypeID: 34}} + msg := &ErrorEvent{} if msg.MessageID, err = ReadUint(reader); err != nil { return nil, err } @@ -537,7 +537,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 35: - msg := &ResourceEvent{meta: &meta{TypeID: 35}} + msg := &ResourceEvent{} if msg.MessageID, err = ReadUint(reader); err != nil { return nil, err } @@ -577,7 +577,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 36: - msg := &CustomEvent{meta: &meta{TypeID: 36}} + msg := &CustomEvent{} if msg.MessageID, err = ReadUint(reader); err != nil { return nil, err } @@ -593,7 +593,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 37: - msg := &CSSInsertRule{meta: &meta{TypeID: 37}} + msg := &CSSInsertRule{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -606,7 +606,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 38: - msg := &CSSDeleteRule{meta: &meta{TypeID: 38}} + msg := &CSSDeleteRule{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -616,7 +616,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 39: - msg := &Fetch{meta: &meta{TypeID: 39}} + msg := &Fetch{} if msg.Method, err = ReadString(reader); err != nil { return nil, err } @@ -641,7 +641,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 40: - msg := &Profiler{meta: &meta{TypeID: 40}} + msg := &Profiler{} if msg.Name, err = ReadString(reader); err != nil { return nil, err } @@ -657,7 +657,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 41: - msg := &OTable{meta: &meta{TypeID: 41}} + msg := &OTable{} if msg.Key, err = ReadString(reader); err != nil { return nil, err } @@ -667,14 +667,14 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 42: - msg := &StateAction{meta: &meta{TypeID: 42}} + msg := &StateAction{} if msg.Type, err = ReadString(reader); err != nil { return nil, err } return msg, nil case 43: - msg := &StateActionEvent{meta: &meta{TypeID: 43}} + msg := &StateActionEvent{} if msg.MessageID, err = ReadUint(reader); err != nil { return nil, err } @@ -687,7 +687,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 44: - msg := &Redux{meta: &meta{TypeID: 44}} + msg := &Redux{} if msg.Action, err = ReadString(reader); err != nil { return nil, err } @@ -700,7 +700,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 45: - msg := &Vuex{meta: &meta{TypeID: 45}} + msg := &Vuex{} if msg.Mutation, err = ReadString(reader); err != nil { return nil, err } @@ -710,7 +710,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 46: - msg := &MobX{meta: &meta{TypeID: 46}} + msg := &MobX{} if msg.Type, err = ReadString(reader); err != nil { return nil, err } @@ -720,7 +720,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 47: - msg := &NgRx{meta: &meta{TypeID: 47}} + msg := &NgRx{} if msg.Action, err = ReadString(reader); err != nil { return nil, err } @@ -733,7 +733,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 48: - msg := &GraphQL{meta: &meta{TypeID: 48}} + msg := &GraphQL{} if msg.OperationKind, err = ReadString(reader); err != nil { return nil, err } @@ -749,7 +749,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 49: - msg := &PerformanceTrack{meta: &meta{TypeID: 49}} + msg := &PerformanceTrack{} if msg.Frames, err = ReadInt(reader); err != nil { return nil, err } @@ -765,7 +765,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 50: - msg := &GraphQLEvent{meta: &meta{TypeID: 50}} + msg := &GraphQLEvent{} if msg.MessageID, err = ReadUint(reader); err != nil { return nil, err } @@ -787,7 +787,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 51: - msg := &FetchEvent{meta: &meta{TypeID: 51}} + msg := &FetchEvent{} if msg.MessageID, err = ReadUint(reader); err != nil { return nil, err } @@ -815,14 +815,14 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 52: - msg := &DOMDrop{meta: &meta{TypeID: 52}} + msg := &DOMDrop{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } return msg, nil case 53: - msg := &ResourceTiming{meta: &meta{TypeID: 53}} + msg := &ResourceTiming{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -850,7 +850,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 54: - msg := &ConnectionInformation{meta: &meta{TypeID: 54}} + msg := &ConnectionInformation{} if msg.Downlink, err = ReadUint(reader); err != nil { return nil, err } @@ -860,14 +860,14 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 55: - msg := &SetPageVisibility{meta: &meta{TypeID: 55}} + msg := &SetPageVisibility{} if msg.hidden, err = ReadBoolean(reader); err != nil { return nil, err } return msg, nil case 56: - msg := &PerformanceTrackAggr{meta: &meta{TypeID: 56}} + msg := &PerformanceTrackAggr{} if msg.TimestampStart, err = ReadUint(reader); err != nil { return nil, err } @@ -913,7 +913,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 59: - msg := &LongTask{meta: &meta{TypeID: 59}} + msg := &LongTask{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -938,7 +938,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 60: - msg := &SetNodeAttributeURLBased{meta: &meta{TypeID: 60}} + msg := &SetNodeAttributeURLBased{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -954,7 +954,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 61: - msg := &SetCSSDataURLBased{meta: &meta{TypeID: 61}} + msg := &SetCSSDataURLBased{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -967,7 +967,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 62: - msg := &IssueEvent{meta: &meta{TypeID: 62}} + msg := &IssueEvent{} if msg.MessageID, err = ReadUint(reader); err != nil { return nil, err } @@ -989,7 +989,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 63: - msg := &TechnicalInfo{meta: &meta{TypeID: 63}} + msg := &TechnicalInfo{} if msg.Type, err = ReadString(reader); err != nil { return nil, err } @@ -999,7 +999,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 64: - msg := &CustomIssue{meta: &meta{TypeID: 64}} + msg := &CustomIssue{} if msg.Name, err = ReadString(reader); err != nil { return nil, err } @@ -1009,19 +1009,19 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 65: - msg := &PageClose{meta: &meta{TypeID: 65}} + msg := &PageClose{} return msg, nil case 66: - msg := &AssetCache{meta: &meta{TypeID: 66}} + msg := &AssetCache{} if msg.URL, err = ReadString(reader); err != nil { return nil, err } return msg, nil case 67: - msg := &CSSInsertRuleURLBased{meta: &meta{TypeID: 67}} + msg := &CSSInsertRuleURLBased{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -1037,7 +1037,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 69: - msg := &MouseClick{meta: &meta{TypeID: 69}} + msg := &MouseClick{} if msg.ID, err = ReadUint(reader); err != nil { return nil, err } @@ -1053,7 +1053,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 70: - msg := &CreateIFrameDocument{meta: &meta{TypeID: 70}} + msg := &CreateIFrameDocument{} if msg.FrameID, err = ReadUint(reader); err != nil { return nil, err } @@ -1063,7 +1063,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 107: - msg := &IOSBatchMeta{meta: &meta{TypeID: 107}} + msg := &IOSBatchMeta{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1076,7 +1076,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 90: - msg := &IOSSessionStart{meta: &meta{TypeID: 90}} + msg := &IOSSessionStart{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1110,14 +1110,14 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 91: - msg := &IOSSessionEnd{meta: &meta{TypeID: 91}} + msg := &IOSSessionEnd{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } return msg, nil case 92: - msg := &IOSMetadata{meta: &meta{TypeID: 92}} + msg := &IOSMetadata{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1133,7 +1133,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 93: - msg := &IOSCustomEvent{meta: &meta{TypeID: 93}} + msg := &IOSCustomEvent{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1149,7 +1149,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 94: - msg := &IOSUserID{meta: &meta{TypeID: 94}} + msg := &IOSUserID{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1162,7 +1162,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 95: - msg := &IOSUserAnonymousID{meta: &meta{TypeID: 95}} + msg := &IOSUserAnonymousID{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1175,7 +1175,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 96: - msg := &IOSScreenChanges{meta: &meta{TypeID: 96}} + msg := &IOSScreenChanges{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1197,7 +1197,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 97: - msg := &IOSCrash{meta: &meta{TypeID: 97}} + msg := &IOSCrash{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1216,7 +1216,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 98: - msg := &IOSScreenEnter{meta: &meta{TypeID: 98}} + msg := &IOSScreenEnter{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1232,7 +1232,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 99: - msg := &IOSScreenLeave{meta: &meta{TypeID: 99}} + msg := &IOSScreenLeave{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1248,7 +1248,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 100: - msg := &IOSClickEvent{meta: &meta{TypeID: 100}} + msg := &IOSClickEvent{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1267,7 +1267,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 101: - msg := &IOSInputEvent{meta: &meta{TypeID: 101}} + msg := &IOSInputEvent{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1286,7 +1286,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 102: - msg := &IOSPerformanceEvent{meta: &meta{TypeID: 102}} + msg := &IOSPerformanceEvent{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1302,7 +1302,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 103: - msg := &IOSLog{meta: &meta{TypeID: 103}} + msg := &IOSLog{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1318,7 +1318,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 104: - msg := &IOSInternalError{meta: &meta{TypeID: 104}} + msg := &IOSInternalError{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1331,7 +1331,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 105: - msg := &IOSNetworkCall{meta: &meta{TypeID: 105}} + msg := &IOSNetworkCall{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } @@ -1362,7 +1362,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 110: - msg := &IOSPerformanceAggregated{meta: &meta{TypeID: 110}} + msg := &IOSPerformanceAggregated{} if msg.TimestampStart, err = ReadUint(reader); err != nil { return nil, err } @@ -1408,7 +1408,7 @@ func ReadMessage(reader io.Reader) (Message, error) { return msg, nil case 111: - msg := &IOSIssueEvent{meta: &meta{TypeID: 111}} + msg := &IOSIssueEvent{} if msg.Timestamp, err = ReadUint(reader); err != nil { return nil, err } diff --git a/backend/services/sink/build_hack b/backend/services/sink/build_hack new file mode 100644 index 000000000..e69de29bb diff --git a/frontend/app/api_client.js b/frontend/app/api_client.js index 98a1f4dfd..a5fae45e7 100644 --- a/frontend/app/api_client.js +++ b/frontend/app/api_client.js @@ -24,7 +24,8 @@ const siteIdRequiredPaths = [ '/heatmaps', '/custom_metrics', '/dashboards', - '/metrics' + '/metrics', + '/trails', // '/custom_metrics/sessions', ]; diff --git a/frontend/app/components/Client/Audit/AuditDetailModal/AuditDetailModal.tsx b/frontend/app/components/Client/Audit/AuditDetailModal/AuditDetailModal.tsx new file mode 100644 index 000000000..934604dbb --- /dev/null +++ b/frontend/app/components/Client/Audit/AuditDetailModal/AuditDetailModal.tsx @@ -0,0 +1,53 @@ +import React from 'react'; +import { JSONTree } from 'UI'; +import { checkForRecent } from 'App/date'; + +interface Props { + audit: any; +} +function AuditDetailModal(props: Props) { + const { audit } = props; + // const jsonResponse = typeof audit.payload === 'string' ? JSON.parse(audit.payload) : audit.payload; + // console.log('jsonResponse', jsonResponse) + + return ( +
+

Audit Details

+
+
{ 'URL'}
+
{ audit.endPoint }
+ +
+
+
Username
+
{audit.username}
+
+
+
Created At
+
{audit.createdAt && checkForRecent(audit.createdAt, 'LLL dd, yyyy, hh:mm a')}
+
+
+ +
+
+
Action
+
{audit.action}
+
+
+
Method
+
{audit.method}
+
+
+ + { audit.payload && ( +
+
Payload
+ +
+ )} +
+
+ ); +} + +export default AuditDetailModal; \ No newline at end of file diff --git a/frontend/app/components/Client/Audit/AuditDetailModal/index.ts b/frontend/app/components/Client/Audit/AuditDetailModal/index.ts new file mode 100644 index 000000000..fcf6bb2b4 --- /dev/null +++ b/frontend/app/components/Client/Audit/AuditDetailModal/index.ts @@ -0,0 +1 @@ +export { default } from './AuditDetailModal'; \ No newline at end of file diff --git a/frontend/app/components/Client/Audit/AuditList/AuditList.tsx b/frontend/app/components/Client/Audit/AuditList/AuditList.tsx new file mode 100644 index 000000000..5ab4c0d3a --- /dev/null +++ b/frontend/app/components/Client/Audit/AuditList/AuditList.tsx @@ -0,0 +1,67 @@ +import { useModal } from 'App/components/Modal'; +import { useStore } from 'App/mstore'; +import { useObserver } from 'mobx-react-lite'; +import React, { useEffect } from 'react'; +import { Loader, Pagination, NoContent } from 'UI'; +import AuditDetailModal from '../AuditDetailModal'; +import AuditListItem from '../AuditListItem'; + +interface Props { + +} +function AuditList(props: Props) { + const { auditStore } = useStore(); + const loading = useObserver(() => auditStore.isLoading); + const list = useObserver(() => auditStore.list); + const searchQuery = useObserver(() => auditStore.searchQuery); + const page = useObserver(() => auditStore.page); + const order = useObserver(() => auditStore.order); + const period = useObserver(() => auditStore.period); + const { showModal } = useModal(); + console.log('AuditList', period.toTimestamps()); + + useEffect(() => { + const { startTimestamp, endTimestamp } = period.toTimestamps(); + auditStore.fetchAudits({ + page: auditStore.page, + limit: auditStore.pageSize, + query: auditStore.searchQuery, + order: auditStore.order, + startDate: startTimestamp, + endDate: endTimestamp, + }); + }, [page, searchQuery, order, period]); + + return useObserver(() => ( + + +
+
Name
+
Status
+
Time
+
+ + {list.map((item, index) => ( +
+ showModal(, { right: true })} + /> +
+ ))} + +
+ auditStore.updateKey('page', page)} + limit={auditStore.pageSize} + debounceRequest={200} + /> +
+
+
+ )); +} + +export default AuditList; \ No newline at end of file diff --git a/frontend/app/components/Client/Audit/AuditList/index.ts b/frontend/app/components/Client/Audit/AuditList/index.ts new file mode 100644 index 000000000..2e6bc3739 --- /dev/null +++ b/frontend/app/components/Client/Audit/AuditList/index.ts @@ -0,0 +1 @@ +export { default } from './AuditList' \ No newline at end of file diff --git a/frontend/app/components/Client/Audit/AuditListItem/AuditListItem.tsx b/frontend/app/components/Client/Audit/AuditListItem/AuditListItem.tsx new file mode 100644 index 000000000..7d584bb06 --- /dev/null +++ b/frontend/app/components/Client/Audit/AuditListItem/AuditListItem.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { checkForRecent } from 'App/date'; + +interface Props { + audit: any; + onShowDetails: () => void; +} +function AuditListItem(props: Props) { + const { audit, onShowDetails } = props; + return ( +
+
{audit.username}
+
{audit.action}
+
{audit.createdAt && checkForRecent(audit.createdAt, 'LLL dd, yyyy, hh:mm a')}
+
+ ); +} + +export default AuditListItem; \ No newline at end of file diff --git a/frontend/app/components/Client/Audit/AuditListItem/index.ts b/frontend/app/components/Client/Audit/AuditListItem/index.ts new file mode 100644 index 000000000..821ee9639 --- /dev/null +++ b/frontend/app/components/Client/Audit/AuditListItem/index.ts @@ -0,0 +1 @@ +export { default } from './AuditListItem'; \ No newline at end of file diff --git a/frontend/app/components/Client/Audit/AuditSearchField/AuditSearchField.tsx b/frontend/app/components/Client/Audit/AuditSearchField/AuditSearchField.tsx new file mode 100644 index 000000000..5574da847 --- /dev/null +++ b/frontend/app/components/Client/Audit/AuditSearchField/AuditSearchField.tsx @@ -0,0 +1,33 @@ +import React, { useEffect } from 'react'; +import { Icon } from 'UI'; +import { debounce } from 'App/utils'; + +let debounceUpdate: any = () => {} +interface Props { + onChange: (value: string) => void; +} +function AuditSearchField(props: Props) { + const { onChange } = props; + + useEffect(() => { + debounceUpdate = debounce((value) => onChange(value), 500); + }, []) + + const write = ({ target: { name, value } }) => { + debounceUpdate(value); + } + + return ( +
+ + +
+ ); +} + +export default AuditSearchField; \ No newline at end of file diff --git a/frontend/app/components/Client/Audit/AuditSearchField/index.ts b/frontend/app/components/Client/Audit/AuditSearchField/index.ts new file mode 100644 index 000000000..646947095 --- /dev/null +++ b/frontend/app/components/Client/Audit/AuditSearchField/index.ts @@ -0,0 +1 @@ +export { default } from './AuditSearchField'; \ No newline at end of file diff --git a/frontend/app/components/Client/Audit/AuditView/AuditView.tsx b/frontend/app/components/Client/Audit/AuditView/AuditView.tsx new file mode 100644 index 000000000..41d506b11 --- /dev/null +++ b/frontend/app/components/Client/Audit/AuditView/AuditView.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import { PageTitle, Icon } from 'UI'; +import AuditList from '../AuditList'; +import AuditSearchField from '../AuditSearchField'; +import { useStore } from 'App/mstore'; +import { useObserver } from 'mobx-react-lite'; +import Select from 'Shared/Select'; +import SelectDateRange from 'Shared/SelectDateRange'; + +function AuditView(props) { + const { auditStore } = useStore(); + const order = useObserver(() => auditStore.order); + const total = useObserver(() => auditStore.total); + + const exportToCsv = () => { + auditStore.exportToCsv(); + } + + const onChange = (data) => { + auditStore.setDateRange(data); + } + + return useObserver(() => ( +
+
+ + Audit Trail + {total} +
+ } /> +
+
+ +
+
+ +
+ ); +} + +export default SiteSearch; \ No newline at end of file diff --git a/frontend/app/components/Client/Sites/SiteSearch/index.ts b/frontend/app/components/Client/Sites/SiteSearch/index.ts new file mode 100644 index 000000000..54376cf15 --- /dev/null +++ b/frontend/app/components/Client/Sites/SiteSearch/index.ts @@ -0,0 +1 @@ +export { default } from './SiteSearch'; \ No newline at end of file diff --git a/frontend/app/components/Client/Sites/Sites.js b/frontend/app/components/Client/Sites/Sites.js index 4769815e5..ba3c72e6e 100644 --- a/frontend/app/components/Client/Sites/Sites.js +++ b/frontend/app/components/Client/Sites/Sites.js @@ -10,6 +10,7 @@ import GDPRForm from './GDPRForm'; import TrackingCodeModal from 'Shared/TrackingCodeModal'; import BlockedIps from './BlockedIps'; import { confirm } from 'UI/Confirmation'; +import SiteSearch from './SiteSearch'; const STATUS_MESSAGE_MAP = { [ RED ]: ' There seems to be an issue (please verify your installation)', @@ -43,6 +44,7 @@ class Sites extends React.PureComponent { showTrackingCode: false, modalContent: NONE, detailContent: NONE, + searchQuery: '', }; toggleBlockedIp = () => { @@ -85,7 +87,7 @@ class Sites extends React.PureComponent { getModalTitle() { switch (this.state.modalContent) { case NEW_SITE_FORM: - return 'New Project'; + return this.props.site.exists() ? 'Update Project' : 'New Project'; case GDPR_FORM: return 'Project Settings'; default: @@ -119,6 +121,7 @@ class Sites extends React.PureComponent { const isAdmin = user.admin || user.superAdmin; const canAddSites = isAdmin && account.limits.projects && account.limits.projects.remaining !== 0; const canDeleteSites = sites.size > 1 && isAdmin; + const filteredSites = sites.filter(site => site.name.toLowerCase().includes(this.state.searchQuery.toLowerCase())); return ( @@ -159,54 +162,71 @@ class Sites extends React.PureComponent { position="top left" /> - +
+ + this.setState({ searchQuery: value })} /> +
+
+
Name
+
Key
+
+ +
{ - sites.map(_site => ( -
-
- - + filteredSites.map(_site => ( + //
+
+
+
+ + +
+ } + content={ STATUS_MESSAGE_MAP[ _site.status ] } + inverted + position="top center" + /> + { _site.host }
- } - content={ STATUS_MESSAGE_MAP[ _site.status ] } - inverted - position="top center" - /> -
+
+
+ {_site.projectKey} +
+ {/*
{ _site.host }
{_site.projectKey}
+
*/} +
+
+ {/* */} + + {/* */}
-
- - -
- {/* */} -
-
+ //
)) }
diff --git a/frontend/app/components/Client/Users/UsersView.tsx b/frontend/app/components/Client/Users/UsersView.tsx new file mode 100644 index 000000000..37e6fdbcd --- /dev/null +++ b/frontend/app/components/Client/Users/UsersView.tsx @@ -0,0 +1,80 @@ +import React, { useEffect } from 'react'; +import UserList from './components/UserList'; +import { PageTitle, Popup, IconButton } from 'UI'; +import { useStore } from 'App/mstore'; +import { useObserver } from 'mobx-react-lite'; +import UserSearch from './components/UserSearch'; +import { useModal } from 'App/components/Modal'; +import UserForm from './components/UserForm'; +import { connect } from 'react-redux'; + +const PERMISSION_WARNING = 'You don’t have the permissions to perform this action.'; +const LIMIT_WARNING = 'You have reached users limit.'; +interface Props { + account: any; + isEnterprise: boolean; + limits: any; +} +function UsersView(props: Props) { + const { account, limits, isEnterprise } = props; + const { userStore, roleStore } = useStore(); + const userCount = useObserver(() => userStore.list.length); + const roles = useObserver(() => roleStore.list); + const { showModal } = useModal(); + + const reachedLimit = (limits.remaining + userStore.modifiedCount) <= 0; + const isAdmin = account.admin || account.superAdmin; + + const editHandler = (user = null) => { + userStore.initUser(user).then(() => { + showModal(, {}); + }); + } + + useEffect(() => { + if (roles.length === 0 && isEnterprise) { + roleStore.fetchRoles(); + } + }, []); + + return ( +
+
+ Team {userCount}
} + actionButton={( + + editHandler(null) } + /> +
+ } + content={ `${ !isAdmin ? PERMISSION_WARNING : (reachedLimit ? LIMIT_WARNING : 'Add team member') }` } + size="tiny" + inverted + position="top left" + /> + )} + /> +
+ +
+
+ +
+ ); +} + +export default connect(state => ({ + account: state.getIn([ 'user', 'account' ]), + isEnterprise: state.getIn([ 'user', 'client', 'edition' ]) === 'ee', + limits: state.getIn([ 'user', 'account', 'limits', 'teamMember' ]), +}))(UsersView); \ No newline at end of file diff --git a/frontend/app/components/Client/Users/components/UserForm/UserForm.tsx b/frontend/app/components/Client/Users/components/UserForm/UserForm.tsx new file mode 100644 index 000000000..25f35097c --- /dev/null +++ b/frontend/app/components/Client/Users/components/UserForm/UserForm.tsx @@ -0,0 +1,154 @@ +import React from 'react'; +import { Input, CopyButton, Button, Icon } from 'UI' +import cn from 'classnames'; +import { useStore } from 'App/mstore'; +import { useObserver } from 'mobx-react-lite'; +import { useModal } from 'App/components/Modal'; +import Select from 'Shared/Select'; +import { confirm } from 'UI/Confirmation'; +interface Props { + isSmtp?: boolean; + isEnterprise?: boolean; +} +function UserForm(props: Props) { + const { isSmtp = false, isEnterprise = false } = props; + const { hideModal } = useModal(); + const { userStore, roleStore } = useStore(); + const isSaving = useObserver(() => userStore.saving); + const user: any = useObserver(() => userStore.instance); + const roles = useObserver(() => roleStore.list.filter(r => r.isProtected ? user.isSuperAdmin : true).map(r => ({ label: r.name, value: r.roleId }))); + + const onChangeCheckbox = (e: any) => { + user.updateKey('isAdmin', !user.isAdmin); + } + + const onSave = () => { + userStore.saveUser(user).then(() => { + hideModal(); + }); + } + + const write = ({ target: { name, value } }) => { + user.updateKey(name, value); + } + + const deleteHandler = async () => { + if (await confirm({ + header: 'Confirm', + confirmButton: 'Yes, delete', + confirmation: `Are you sure you want to permanently delete this user?` + })) { + userStore.deleteUser(user.userId).then(() => { + hideModal(); + }); + } + } + + return useObserver(() => ( +
+
+

{`${user.exists() ? 'Update' : 'Invite'} User`}

+
+
+
+ + +
+ +
+ + +
+ { !isSmtp && +
+ SMTP is not configured (see here how to set it up). You can still add new users, but you’d have to manually copy then send them the invitation link. +
+ } +
+ +
+ + { !isEnterprise && ( +
+ + +
+ )); +} + +export default UserSearch; \ No newline at end of file diff --git a/frontend/app/components/Client/Users/components/UserSearch/index.ts b/frontend/app/components/Client/Users/components/UserSearch/index.ts new file mode 100644 index 000000000..3810a4030 --- /dev/null +++ b/frontend/app/components/Client/Users/components/UserSearch/index.ts @@ -0,0 +1 @@ +export { default } from './UserSearch'; \ No newline at end of file diff --git a/frontend/app/components/shared/Select/Select.tsx b/frontend/app/components/shared/Select/Select.tsx index 85418f5fc..8d963c16b 100644 --- a/frontend/app/components/shared/Select/Select.tsx +++ b/frontend/app/components/shared/Select/Select.tsx @@ -1,14 +1,17 @@ import React from 'react'; -import Select from 'react-select'; +import Select, { components, DropdownIndicatorProps } from 'react-select'; +import { Icon } from 'UI'; +import colors from 'App/theme/colors'; interface Props { options: any[]; isSearchable?: boolean; defaultValue?: string; plain?: boolean; + components?: any; [x:string]: any; } -export default function({ plain = false, options, isSearchable = false, defaultValue = '', ...rest }: Props) { +export default function({ plain = false, options, isSearchable = false, components = {}, defaultValue = '', ...rest }: Props) { const customStyles = { option: (provided, state) => ({ ...provided, @@ -17,14 +20,26 @@ export default function({ plain = false, options, isSearchable = false, defaultV menu: (provided, state) => ({ ...provided, top: 31, + minWidth: 'fit-content', }), control: (provided) => { const obj = { ...provided, - border: 'solid thin #ddd' + border: 'solid thin #ddd', + cursor: 'pointer', } if (plain) { obj['border'] = '1px solid transparent' + obj['&:hover'] = { + borderColor: 'transparent', + backgroundColor: colors['gray-light'] + } + obj['&:focus'] = { + borderColor: 'transparent' + } + obj['&:active'] = { + borderColor: 'transparent' + } } return obj; }, @@ -39,14 +54,16 @@ export default function({ plain = false, options, isSearchable = false, defaultV return { ...provided, opacity, transition }; } } - const defaultSelected = defaultValue ? options.find(x => x.value === defaultValue) : options[0]; + const defaultSelected = defaultValue ? options.find(x => x.value === defaultValue) : null; return ( onChange(value)} + components={{ SingleValue: ({ children, ...props} : any) => { + return ( + + {period.rangeName === CUSTOM_RANGE ? period.rangeFormatted() : children} + + ) + } }} + period={period} + /> + { + isCustom && + setIsCustom(false)} + > +
+ setIsCustom(false) } + selectedDateRange={ period.range } + /> +
+
+ } +
+ ); +} + +export default SelectDateRange; + + \ No newline at end of file diff --git a/frontend/app/components/shared/SelectDateRange/index.ts b/frontend/app/components/shared/SelectDateRange/index.ts new file mode 100644 index 000000000..9b59aa99b --- /dev/null +++ b/frontend/app/components/shared/SelectDateRange/index.ts @@ -0,0 +1 @@ +export { default } from './SelectDateRange'; \ No newline at end of file diff --git a/frontend/app/components/shared/SessionSettings/components/DefaultTimezone.tsx b/frontend/app/components/shared/SessionSettings/components/DefaultTimezone.tsx index e7af914d4..efb91fed9 100644 --- a/frontend/app/components/shared/SessionSettings/components/DefaultTimezone.tsx +++ b/frontend/app/components/shared/SessionSettings/components/DefaultTimezone.tsx @@ -16,7 +16,6 @@ function DefaultTimezone(props) { const { settingsStore } = useStore(); const [timezone, setTimezone] = React.useState(settingsStore.sessionSettings.timezone); const sessionSettings = useObserver(() => settingsStore.sessionSettings) - console.log('timezone', timezone) return ( <> diff --git a/frontend/app/dateRange.js b/frontend/app/dateRange.js index b50ec2fb9..4a2ea923d 100644 --- a/frontend/app/dateRange.js +++ b/frontend/app/dateRange.js @@ -23,6 +23,13 @@ Object.keys(DATE_RANGE_LABELS).forEach((key) => { DATE_RANGE_VALUES[ key ] = key export { DATE_RANGE_VALUES }; export const dateRangeValues = Object.keys(DATE_RANGE_VALUES); +export const DATE_RANGE_OPTIONS = Object.keys(DATE_RANGE_LABELS).map((key) => { + return { + label: DATE_RANGE_LABELS[ key ], + value: key, + }; +}); + export function getDateRangeFromTs(start, end) { return moment.range( moment(start), diff --git a/frontend/app/duck/errors.js b/frontend/app/duck/errors.js index 2d54b3a0a..65bb70bc1 100644 --- a/frontend/app/duck/errors.js +++ b/frontend/app/duck/errors.js @@ -59,7 +59,12 @@ function reducer(state = initialState, action = {}) { case EDIT_OPTIONS: return state.mergeIn(["options"], action.instance); case success(FETCH): - return state.set("instance", ErrorInfo(action.data)); + if (state.get("list").find(e => e.get("errorId") === action.id)) { + return updateItemInList(state, { errorId: action.data.errorId, viewed: true }) + .set("instance", ErrorInfo(action.data)); + } else { + return state.set("instance", ErrorInfo(action.data)); + } case success(FETCH_TRACE): return state.set("instanceTrace", List(action.data.trace)).set('sourcemapUploaded', action.data.sourcemapUploaded); case success(FETCH_LIST): diff --git a/frontend/app/mstore/auditStore.ts b/frontend/app/mstore/auditStore.ts new file mode 100644 index 000000000..efc34d65d --- /dev/null +++ b/frontend/app/mstore/auditStore.ts @@ -0,0 +1,86 @@ +import { makeAutoObservable, runInAction, observable, action, reaction } from "mobx" +import { auditService } from "App/services" +import Audit from './types/audit' +import Period, { LAST_7_DAYS } from 'Types/app/period'; +import { toast } from 'react-toastify'; +import { exportCSVFile } from 'App/utils'; +import { formatDateTimeDefault } from 'App/date'; +import { DateTime, Duration } from 'luxon'; // TODO + +export default class AuditStore { + list: any[] = []; + total: number = 0; + page: number = 1; + pageSize: number = 20; + searchQuery: string = ''; + isLoading: boolean = false; + order: string = 'desc'; + period: Period|null = Period({ rangeName: LAST_7_DAYS }) + + constructor() { + makeAutoObservable(this, { + searchQuery: observable, + period: observable, + updateKey: action, + fetchAudits: action, + setDateRange: action, + }) + } + + setDateRange(data: any) { + this['period'] = data; + } + + updateKey(key: string, value: any) { + this[key] = value; + } + + fetchAudits = (data: any): Promise => { + this.isLoading = true; + return new Promise((resolve, reject) => { + auditService.all(data).then(response => { + runInAction(() => { + this.list = response.sessions.map(item => Audit.fromJson(item)) + this.total = response.count + }) + resolve() + }).catch(error => { + reject(error) + }).finally(() => { + this.isLoading = false; + }) + }) + } + + fetchAllAudits = async (data: any): Promise => { + return new Promise((resolve, reject) => { + auditService.all(data).then((data) => { + const headers = [ + { label: 'User', key: 'username' }, + { label: 'Email', key: 'email' }, + { label: 'UserID', key: 'userId' }, + { label: 'Method', key: 'method' }, + { label: 'Action', key: 'action' }, + { label: 'Endpoint', key: 'endpoint' }, + { label: 'Created At', key: 'createdAt' }, + ] + data = data.sessions.map(item => ({ + ...item, + createdAt: DateTime.fromMillis(item.createdAt).toFormat('LLL dd yyyy hh:mm a') + })) + exportCSVFile(headers, data, `audit-${new Date().toLocaleDateString()}`); + resolve(data) + }).catch(error => { + reject(error) + }) + }) + } + + exportToCsv = async (): Promise => { + const promise = this.fetchAllAudits({ limit: this.total }) + toast.promise(promise, { + pending: 'Exporting...', + success: 'Export successful', + }) + } +} \ No newline at end of file diff --git a/frontend/app/mstore/index.tsx b/frontend/app/mstore/index.tsx index f72972121..2a1897b42 100644 --- a/frontend/app/mstore/index.tsx +++ b/frontend/app/mstore/index.tsx @@ -1,19 +1,28 @@ import React from 'react'; import DashboardStore, { IDashboardSotre } from './dashboardStore'; import MetricStore, { IMetricStore } from './metricStore'; +import UserStore from './userStore'; +import RoleStore from './roleStore'; import APIClient from 'App/api_client'; -import { dashboardService, metricService, sessionService } from 'App/services'; +import { dashboardService, metricService, sessionService, userService, auditService } from 'App/services'; import SettingsStore from './settingsStore'; +import AuditStore from './auditStore'; export class RootStore { dashboardStore: IDashboardSotre; metricStore: IMetricStore; settingsStore: SettingsStore; + userStore: UserStore; + roleStore: RoleStore; + auditStore: AuditStore; constructor() { this.dashboardStore = new DashboardStore(); this.metricStore = new MetricStore(); this.settingsStore = new SettingsStore(); + this.userStore = new UserStore(); + this.roleStore = new RoleStore(); + this.auditStore = new AuditStore(); } initClient() { @@ -21,6 +30,7 @@ export class RootStore { dashboardService.initClient(client) metricService.initClient(client) sessionService.initClient(client) + userService.initClient(client) } } diff --git a/frontend/app/mstore/roleStore.ts b/frontend/app/mstore/roleStore.ts new file mode 100644 index 000000000..6f87b4bcb --- /dev/null +++ b/frontend/app/mstore/roleStore.ts @@ -0,0 +1,31 @@ +import { makeAutoObservable, observable, action } from "mobx" +import { userService } from "App/services"; +import Role, { IRole } from "./types/role"; + +export default class UserStore { + list: IRole[] = []; + loading: boolean = false; + + constructor() { + makeAutoObservable(this, { + list: observable, + loading: observable, + }) + } + + fetchRoles(): Promise { + this.loading = true; + return new Promise((resolve, reject) => { + userService.getRoles() + .then(response => { + this.list = response.map((role: any) => new Role().fromJson(role)); + resolve(response); + }).catch(error => { + this.loading = false; + reject(error); + }).finally(() => { + this.loading = false; + }); + }); + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/audit.ts b/frontend/app/mstore/types/audit.ts new file mode 100644 index 000000000..dd8f7c095 --- /dev/null +++ b/frontend/app/mstore/types/audit.ts @@ -0,0 +1,40 @@ +import { DateTime } from 'luxon'; +import { unserscoreToSpaceAndCapitalize } from 'App/utils'; + +export default class Audit { + id: string = ''; + username: string = ''; + email: string = ''; + action: string = ''; + createdAt: any = null; + endPoint: string = ''; + parameters: any = {}; + method: string = ''; + status: string = ''; + payload: any = {} + + constructor() { + } + + static fromJson(json: any): Audit { + const audit = new Audit(); + audit.id = json.rn; + audit.username = json.username; + audit.action = unserscoreToSpaceAndCapitalize(json.action); + audit.createdAt = json.createdAt && DateTime.fromMillis(json.createdAt || 0); + audit.endPoint = json.endpoint; + audit.parameters = json.parameters; + audit.method = json.method; + audit.status = json.status + audit.email = json.email + audit.payload = typeof json.payload === 'string' ? JSON.parse(json.payload) : json.payload + return audit; + } + + toJson(): any { + return { + id: this.id, + username: this.username + }; + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/role.ts b/frontend/app/mstore/types/role.ts new file mode 100644 index 000000000..5d8da871a --- /dev/null +++ b/frontend/app/mstore/types/role.ts @@ -0,0 +1,45 @@ +import { makeAutoObservable, observable, runInAction } from "mobx"; + +export interface IRole { + roleId: string; + name: string; + description: string; + isProtected: boolean; + + fromJson(json: any); + toJson(): any; +} + +export default class Role implements IRole { + roleId: string = ''; + name: string = ''; + description: string = ''; + isProtected: boolean = false; + + + constructor() { + makeAutoObservable(this, { + roleId: observable, + name: observable, + description: observable, + }) + } + + fromJson(json: any) { + runInAction(() => { + this.roleId = json.roleId; + this.name = json.name; + this.description = json.description; + this.isProtected = json.protected; + }) + return this; + } + + toJson() { + return { + id: this.roleId, + name: this.name, + description: this.description, + } + } +} \ No newline at end of file diff --git a/frontend/app/mstore/types/user.ts b/frontend/app/mstore/types/user.ts new file mode 100644 index 000000000..53ae5dbf0 --- /dev/null +++ b/frontend/app/mstore/types/user.ts @@ -0,0 +1,105 @@ +import { runInAction, makeAutoObservable, observable } from 'mobx' +import { DateTime } from 'luxon'; +import { validateEmail, validateName } from 'App/validate'; + +export interface IUser { + userId: string + email: string + createdAt: string + isAdmin: boolean + isSuperAdmin: boolean + isJoined: boolean + isExpiredInvite: boolean + roleId: string + roleName: string + invitationLink: string + + + updateKey(key: string, value: any): void + fromJson(json: any): IUser + toJson(): any + toSave(): any +} + +export default class User implements IUser { + userId: string = ''; + name: string = ''; + email: string = ''; + createdAt: string = ''; + isAdmin: boolean = false; + isSuperAdmin: boolean = false; + isJoined: boolean = false; + isExpiredInvite: boolean = false; + roleId: string = ''; + roleName: string = ''; + invitationLink: string = ''; + + constructor() { + makeAutoObservable(this, { + userId: observable, + email: observable, + createdAt: observable, + isAdmin: observable, + isSuperAdmin: observable, + isJoined: observable, + isExpiredInvite: observable, + roleId: observable, + roleName: observable, + invitationLink: observable, + }) + } + + updateKey(key: string, value: any) { + runInAction(() => { + this[key] = value + }) + } + + fromJson(json: any) { + runInAction(() => { + this.userId = json.userId || json.id; // TODO api returning id + this.name = json.name; + this.email = json.email; + this.createdAt = json.createdAt && DateTime.fromMillis(json.createdAt || 0) + this.isAdmin = json.admin + this.isSuperAdmin = json.superAdmin + this.isJoined = json.joined + this.isExpiredInvite = json.expiredInvitation + this.roleId = json.roleId + this.roleName = json.roleName + this.invitationLink = json.invitationLink + }) + return this; + } + + toJson() { + return { + userId: this.userId, + name: this.name, + email: this.email, + admin: this.isAdmin, + superAdmin: this.isSuperAdmin, + roleId: this.roleId, + joined: this.isJoined, + invitationLink: this.invitationLink, + expiredInvitation: this.isExpiredInvite, + } + } + + toSave() { + return { + name: this.name, + email: this.email, + admin: this.isAdmin, + roleId: this.roleId, + } + } + + valid() { + return validateName(this.name, { empty: false }) && validateEmail(this.email) && !!this.roleId; + } + + exists() { + return !!this.userId; + } +} \ No newline at end of file diff --git a/frontend/app/mstore/userStore.ts b/frontend/app/mstore/userStore.ts new file mode 100644 index 000000000..d0633a037 --- /dev/null +++ b/frontend/app/mstore/userStore.ts @@ -0,0 +1,162 @@ +import { makeAutoObservable, observable, action } from "mobx" +import User, { IUser } from "./types/user"; +import { userService } from "App/services"; +import { toast } from 'react-toastify'; +import copy from 'copy-to-clipboard'; + +export default class UserStore { + list: IUser[] = []; + instance: IUser|null = null; + page: number = 1; + pageSize: number = 10; + searchQuery: string = ""; + modifiedCount: number = 0; + + loading: boolean = false; + saving: boolean = false; + + constructor() { + makeAutoObservable(this, { + instance: observable, + updateUser: action, + updateKey: action, + initUser: action, + }) + } + + initUser(user?: any ): Promise { + return new Promise((resolve, reject) => { + if (user) { + this.instance = new User().fromJson(user.toJson()); + } else { + this.instance = new User(); + } + resolve(); + }) + } + + updateKey(key: string, value: any) { + this[key] = value + + if (key === 'searchQuery') { + this.page = 1 + } + } + + updateUser(user: IUser) { + const index = this.list.findIndex(u => u.userId === user.userId); + if (index > -1) { + this.list[index] = user; + } + } + + fetchUser(userId: string): Promise { + this.loading = true; + return new Promise((resolve, reject) => { + userService.one(userId) + .then(response => { + this.instance = new User().fromJson(response.data); + resolve(response); + }).catch(error => { + this.loading = false; + reject(error); + }).finally(() => { + this.loading = false; + }); + }); + } + + fetchUsers(): Promise { + this.loading = true; + return new Promise((resolve, reject) => { + userService.all() + .then(response => { + this.list = response.map(user => new User().fromJson(user)); + resolve(response); + }).catch(error => { + this.loading = false; + reject(error); + }).finally(() => { + this.loading = false; + }); + }); + } + + saveUser(user: IUser): Promise { + this.saving = true; + const wasCreating = !user.userId; + return new Promise((resolve, reject) => { + userService.save(user).then(response => { + const newUser = new User().fromJson(response); + if (wasCreating) { + this.modifiedCount -= 1; + this.list.push(new User().fromJson(newUser)); + toast.success('User created successfully'); + } else { + this.updateUser(newUser); + toast.success('User updated successfully'); + } + resolve(response); + }).catch(error => { + this.saving = false; + reject(error); + }).finally(() => { + this.saving = false; + }); + }); + } + + deleteUser(userId: string): Promise { + this.saving = true; + return new Promise((resolve, reject) => { + userService.delete(userId) + .then(response => { + this.modifiedCount += 1; + this.list = this.list.filter(user => user.userId !== userId); + resolve(response); + }).catch(error => { + this.saving = false; + reject(error); + }).finally(() => { + this.saving = false; + }); + }); + } + + copyInviteCode(userId: string): void { + const content = this.list.find(u => u.userId === userId)?.invitationLink; + if (content) { + copy(content); + toast.success('Invite code copied successfully'); + } else { + toast.error('Invite code not found'); + } + } + + generateInviteCode(userId: string): Promise { + this.saving = true; + const promise = new Promise((resolve, reject) => { + userService.generateInviteCode(userId) + .then(response => { + const index = this.list.findIndex(u => u.userId === userId); + if (index > -1) { + this.list[index].updateKey('isExpiredInvite', false); + this.list[index].updateKey('invitationLink', response.invitationLink); + } + resolve(response); + }).catch(error => { + this.saving = false; + reject(error); + }).finally(() => { + this.saving = false; + }); + }); + + toast.promise(promise, { + pending: 'Generating an invite code...', + success: 'Invite code generated successfully', + }) + + return promise; + } +} \ No newline at end of file diff --git a/frontend/app/player/MessageDistributor/MessageDistributor.ts b/frontend/app/player/MessageDistributor/MessageDistributor.ts index 68ae14c72..653b05f54 100644 --- a/frontend/app/player/MessageDistributor/MessageDistributor.ts +++ b/frontend/app/player/MessageDistributor/MessageDistributor.ts @@ -8,11 +8,11 @@ import Profile from 'Types/session/profile'; import ReduxAction from 'Types/session/reduxAction'; import { update } from '../store'; -import { +import { init as initListsDepr, append as listAppend, - setStartTime as setListsStartTime - } from '../lists'; + setStartTime as setListsStartTime +} from '../lists'; import StatedScreen from './StatedScreen/StatedScreen'; @@ -26,6 +26,7 @@ import ActivityManager from './managers/ActivityManager'; import AssistManager from './managers/AssistManager'; import MFileReader from './messages/MFileReader'; +import loadFiles from './network/loadFiles'; import { INITIAL_STATE as SUPER_INITIAL_STATE, State as SuperState } from './StatedScreen/StatedScreen'; import { INITIAL_STATE as ASSIST_INITIAL_STATE, State as AssistState } from './managers/AssistManager'; @@ -33,7 +34,7 @@ import { INITIAL_STATE as ASSIST_INITIAL_STATE, State as AssistState } from './m import type { PerformanceChartPoint } from './managers/PerformanceTrackManager'; import type { SkipInterval } from './managers/ActivityManager'; -const LIST_NAMES = [ "redux", "mobx", "vuex", "ngrx", "graphql", "exceptions", "profiles", "longtasks" ] as const; +const LIST_NAMES = ["redux", "mobx", "vuex", "ngrx", "graphql", "exceptions", "profiles", "longtasks"] as const; const LISTS_INITIAL_STATE = {}; LIST_NAMES.forEach(name => { LISTS_INITIAL_STATE[`${name}ListNow`] = []; @@ -65,15 +66,15 @@ type ListsObject = { } function initLists(): ListsObject { - const lists: Partial = {} ; + const lists: Partial = {}; for (var i = 0; i < LIST_NAMES.length; i++) { - lists[ LIST_NAMES[i] ] = new ListWalker(); + lists[LIST_NAMES[i]] = new ListWalker(); } return lists as ListsObject; } -import type { +import type { Message, SetPageLocation, ConnectionInformation, @@ -110,7 +111,7 @@ export default class MessageDistributor extends StatedScreen { private navigationStartOffset: number = 0; private lastMessageTime: number = 0; - constructor(private readonly session: any /*Session*/, jwt: string, config, live: boolean) { + constructor(private readonly session: any /*Session*/, jwt: string, config, live: boolean) { super(); this.pagesManager = new PagesManager(this, this.session.isMobile) this.mouseManager = new MouseManager(this); @@ -128,7 +129,7 @@ export default class MessageDistributor extends StatedScreen { /* == REFACTOR_ME == */ const eventList = this.session.events.toJSON(); initListsDepr({ - event: eventList, + event: eventList, stack: this.session.stackEvents.toJSON(), resource: this.session.resources.toJSON(), }); @@ -146,96 +147,83 @@ export default class MessageDistributor extends StatedScreen { } } - - // subscribeOnMessages(sockUrl) { - // this.setMessagesLoading(true); - // const socket = new WebSocket(sockUrl); - // socket.binaryType = 'arraybuffer'; - // socket.onerror = (e) => { - // // TODO: reconnect - // update({ error: true }); - // } - // socket.onmessage = (socketMessage) => { - // const data = new Uint8Array(socketMessage.data); - // const msgs = []; - // messageGenerator // parseBuffer(msgs, data); - // // TODO: count indexes. Now will not work due to wrong indexes - // //msgs.forEach(this.distributeMessage); - // this.setMessagesLoading(false); - // this.setDisconnected(false); - // } - // this._socket = socket; - // } - + private waitingForFiles: boolean = false private loadMessages(): void { - const fileUrl: string = this.session.mobsUrl; - this.setMessagesLoading(true); - window.fetch(fileUrl) - .then(r => r.arrayBuffer()) - .then(b => { - const r = new MFileReader(new Uint8Array(b), this.sessionStart); - const msgs: Array = []; + this.setMessagesLoading(true) + this.waitingForFiles = true - while (r.hasNext()) { - const next = r.next(); - if (next != null) { - this.distributeMessage(next[0], next[1]); - msgs.push(next[0]); + const r = new MFileReader(new Uint8Array(), this.sessionStart) + const msgs: Array = [] + loadFiles(this.session.mobsUrl, + b => { + r.append(b) + let next: ReturnType + while (next = r.next()) { + const [msg, index] = next + this.distributeMessage(msg, index) + this.lastMessageTime = Math.max(msg.time, this.lastMessageTime) + + msgs.push(msg) } - } - // @ts-ignore Hack for upet (TODO: fix ordering in one mutation (removes first)) - const headChildrenIds = msgs.filter(m => m.parentID === 1).map(m => m.id); - //const createNodeTypes = ["create_text_node", "create_element_node"]; - this.pagesManager.sort((m1, m2) =>{ - if (m1.time === m2.time) { - if (m1.tp === "remove_node" && m2.tp !== "remove_node") { - if (headChildrenIds.includes(m1.id)) { - return -1; - } - } else if (m2.tp === "remove_node" && m1.tp !== "remove_node") { - if (headChildrenIds.includes(m2.id)) { - return 1; - } - } else if (m2.tp === "remove_node" && m1.tp === "remove_node") { - const m1FromHead = headChildrenIds.includes(m1.id); - const m2FromHead = headChildrenIds.includes(m2.id); - if (m1FromHead && !m2FromHead) { - return -1; - } else if (m2FromHead && !m1FromHead) { - return 1; + logger.info("Messages count: ", msgs.length, msgs) + + // @ts-ignore Hack for upet (TODO: fix ordering in one mutation in tracker(removes first)) + const headChildrenIds = msgs.filter(m => m.parentID === 1).map(m => m.id); + this.pagesManager.sort((m1, m2) => { + if (m1.time === m2.time) { + if (m1.tp === "remove_node" && m2.tp !== "remove_node") { + if (headChildrenIds.includes(m1.id)) { + return -1; + } + } else if (m2.tp === "remove_node" && m1.tp !== "remove_node") { + if (headChildrenIds.includes(m2.id)) { + return 1; + } + } else if (m2.tp === "remove_node" && m1.tp === "remove_node") { + const m1FromHead = headChildrenIds.includes(m1.id); + const m2FromHead = headChildrenIds.includes(m2.id); + if (m1FromHead && !m2FromHead) { + return -1; + } else if (m2FromHead && !m1FromHead) { + return 1; + } } } - } - return 0; - }) - + return 0; + }) - logger.info("Messages count: ", msgs.length, msgs); - - const stateToUpdate: {[key:string]: any} = { - performanceChartData: this.performanceTrackManager.chartData, - performanceAvaliability: this.performanceTrackManager.avaliability, - }; - this.activirtManager?.end(); - stateToUpdate.skipIntervals = this.activirtManager?.list || []; - LIST_NAMES.forEach(key => { - stateToUpdate[ `${ key }List` ] = this.lists[ key ].list; - }); - update(stateToUpdate); - - this.windowNodeCounter.reset(); - - this.setMessagesLoading(false); + const stateToUpdate: {[key:string]: any} = { + performanceChartData: this.performanceTrackManager.chartData, + performanceAvaliability: this.performanceTrackManager.avaliability, + } + LIST_NAMES.forEach(key => { + stateToUpdate[ `${ key }List` ] = this.lists[ key ].list + }) + update(stateToUpdate) + this.setMessagesLoading(false) + } + ) + .then(() => { + this.windowNodeCounter.reset() + if (this.activirtManager) { + this.activirtManager.end() + update({ + skipIntervals: this.activirtManager.list + }) + } + this.waitingForFiles = false + this.setMessagesLoading(false) + }) + .catch(e => { + logger.error(e) + this.waitingForFiles = false + this.setMessagesLoading(false) + update({ error: true }) }) - .catch((e) => { - logger.error(e); - this.setMessagesLoading(false); - update({ error: true }); - }); } - move(t: number, index?: number):void { + move(t: number, index?: number): void { const stateToUpdate: Partial = {}; /* == REFACTOR_ME == */ const lastLoadedLocationMsg = this.loadedLocationManager.moveToLast(t, index); @@ -248,7 +236,7 @@ export default class MessageDistributor extends StatedScreen { if (llEvent.domContentLoadedTime != null) { stateToUpdate.domContentLoadedTime = { time: llEvent.domContentLoadedTime + this.navigationStartOffset, //TODO: predefined list of load event for the network tab (merge events & SetPageLocation: add navigationStart to db) - value: llEvent.domContentLoadedTime, + value: llEvent.domContentLoadedTime, } } if (llEvent.loadTime != null) { @@ -277,9 +265,9 @@ export default class MessageDistributor extends StatedScreen { } LIST_NAMES.forEach(key => { - const lastMsg = this.lists[ key ].moveToLast(t, key === 'exceptions' ? undefined : index); + const lastMsg = this.lists[key].moveToLast(t, key === 'exceptions' ? undefined : index); if (lastMsg != null) { - stateToUpdate[`${key}ListNow`] = this.lists[ key ].listNow; + stateToUpdate[`${key}ListNow`] = this.lists[key].listNow; } }); @@ -298,21 +286,25 @@ export default class MessageDistributor extends StatedScreen { this.window.scrollTo(lastScroll.x, lastScroll.y); } // Moving mouse and setting :hover classes on ready view - this.mouseManager.move(t); + this.mouseManager.move(t); const lastClick = this.clickManager.moveToLast(t); if (!!lastClick && t - lastClick.time < 600) { // happend during last 600ms this.cursor.click(); } // After all changes - redraw the marker //this.marker.redraw(); - }) + }) + + if (this.waitingForFiles && this.lastMessageTime <= t) { + this.setMessagesLoading(true) + } } _decodeMessage(msg, keys: Array) { const decoded = {}; try { keys.forEach(key => { - decoded[ key ] = this.decoder.decode(msg[ key ]); + decoded[key] = this.decoder.decode(msg[key]); }); } catch (e) { logger.error("Error on message decoding: ", e, msg); @@ -323,15 +315,13 @@ export default class MessageDistributor extends StatedScreen { /* Binded */ distributeMessage = (msg: Message, index: number): void => { - this.lastMessageTime = msg.time; - - if ([ + if ([ "mouse_move", "mouse_click", "create_element_node", // not a user activity, though visual change "set_input_value", "set_input_checked", - "set_viewport_size", + "set_viewport_size", "set_viewport_scroll", ].includes(msg.tp)) { this.activirtManager?.updateAcctivity(msg.time); @@ -343,13 +333,13 @@ export default class MessageDistributor extends StatedScreen { /* Lists: */ case "console_log": if (msg.level === 'debug') break; - listAppend("log", Log({ + listAppend("log", Log({ level: msg.level, value: msg.value, - time, + time, index, })); - break; + break; case "fetch": listAppend("fetch", Resource({ method: msg.method, @@ -362,118 +352,117 @@ export default class MessageDistributor extends StatedScreen { time: msg.timestamp - this.sessionStart, //~ index, })); - break; + break; /* */ case "set_page_location": this.locationManager.add(msg); if (msg.navigationStart > 0) { this.loadedLocationManager.add(msg); } - break; + break; case "set_viewport_size": this.resizeManager.add(msg); - break; + break; case "mouse_move": this.mouseManager.add(msg); - break; + break; case "mouse_click": this.clickManager.add(msg); - break; + break; case "set_viewport_scroll": this.scrollManager.add(msg); - break; + break; case "performance_track": this.performanceTrackManager.add(msg); - break; + break; case "set_page_visibility": this.performanceTrackManager.handleVisibility(msg) - break; + break; case "connection_information": this.connectionInfoManger.add(msg); - break; + break; case "o_table": this.decoder.set(msg.key, msg.value); - break; + break; case "redux": decoded = this._decodeMessage(msg, ["state", "action"]); logger.log(decoded) if (decoded != null) { this.lists.redux.add(decoded); } - break; + break; case "ng_rx": decoded = this._decodeMessage(msg, ["state", "action"]); logger.log(decoded) if (decoded != null) { this.lists.ngrx.add(decoded); - } - break; + } + break; case "vuex": decoded = this._decodeMessage(msg, ["state", "mutation"]); logger.log(decoded) if (decoded != null) { this.lists.vuex.add(decoded); - } - break; + } + break; case "mob_x": decoded = this._decodeMessage(msg, ["payload"]); logger.log(decoded) if (decoded != null) { this.lists.mobx.add(decoded); - } - break; + } + break; case "graph_ql": // @ts-ignore some hack? TODO: remove msg.duration = 0; this.lists.graphql.add(msg); - break; + break; case "profiler": this.lists.profiles.add(msg); - break; + break; case "long_task": this.lists.longtasks.add({ ...msg, time: msg.timestamp - this.sessionStart, }); - break; + break; default: - switch (msg.tp){ + switch (msg.tp) { case "create_document": this.windowNodeCounter.reset(); this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count); - break; + break; case "create_text_node": case "create_element_node": this.windowNodeCounter.addNode(msg.id, msg.parentID); this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count); - break; + break; case "move_node": this.windowNodeCounter.moveNode(msg.id, msg.parentID); this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count); - break; + break; case "remove_node": this.windowNodeCounter.removeNode(msg.id); this.performanceTrackManager.setCurrentNodesCount(this.windowNodeCounter.count); - break; + break; } this.pagesManager.add(msg); - break; + break; } } - getLastMessageTime():number { + getLastMessageTime(): number { return this.lastMessageTime; } - getFirstMessageTime():number { + getFirstMessageTime(): number { return 0; //this.pagesManager.minTime; } // TODO: clean managers? clean() { super.clean(); - //if (this._socket) this._socket.close(); update(INITIAL_STATE); this.assistManager.clear(); } diff --git a/frontend/app/player/MessageDistributor/messages/MFileReader.ts b/frontend/app/player/MessageDistributor/messages/MFileReader.ts index 0204259e5..a4df3e2f8 100644 --- a/frontend/app/player/MessageDistributor/messages/MFileReader.ts +++ b/frontend/app/player/MessageDistributor/messages/MFileReader.ts @@ -7,62 +7,65 @@ import RawMessageReader from './RawMessageReader'; // needSkipMessage() and next() methods here use buf and p protected properties, // which should be probably somehow incapsulated export default class MFileReader extends RawMessageReader { - private pLastMessageID: number = 0; - private currentTime: number = 0; - public error: boolean = false; + private pLastMessageID: number = 0 + private currentTime: number = 0 + public error: boolean = false constructor(data: Uint8Array, private readonly startTime: number) { - super(data); + super(data) } private needSkipMessage(): boolean { - if (this.p === 0) return false; + if (this.p === 0) return false for (let i = 7; i >= 0; i--) { if (this.buf[ this.p + i ] !== this.buf[ this.pLastMessageID + i ]) { - return this.buf[ this.p + i ] - this.buf[ this.pLastMessageID + i ] < 0; + return this.buf[ this.p + i ] - this.buf[ this.pLastMessageID + i ] < 0 } } - return true; + return true } private readRawMessage(): RawMessage | null { - this.skip(8); + this.skip(8) try { - return super.readMessage(); + const msg = super.readMessage() + if (!msg) { + this.skip(-8) + } + return msg } catch (e) { - this.error = true; - logger.error("Read message error:", e); - return null; + this.error = true + logger.error("Read message error:", e) + return null } } - hasNext():boolean { - return !this.error && this.hasNextByte(); - } - next(): [ Message, number] | null { - if (!this.hasNext()) { - return null; + if (this.error || !this.hasNextByte()) { + return null } while (this.needSkipMessage()) { - this.readRawMessage(); + if (!this.readRawMessage()) { + return null + } } - this.pLastMessageID = this.p; - const rMsg = this.readRawMessage(); + this.pLastMessageID = this.p + + const rMsg = this.readRawMessage() if (!rMsg) { - return null; + return null } if (rMsg.tp === "timestamp") { - this.currentTime = rMsg.timestamp - this.startTime; - } else { - const msg = Object.assign(rMsg, { - time: this.currentTime, - _index: this.pLastMessageID, - }) - return [msg, this.pLastMessageID]; - } - return null; + this.currentTime = rMsg.timestamp - this.startTime + return this.next() + } + + const msg = Object.assign(rMsg, { + time: this.currentTime, + _index: this.pLastMessageID, + }) + return [msg, this.pLastMessageID] } } \ No newline at end of file diff --git a/frontend/app/player/MessageDistributor/messages/urlResolve.ts b/frontend/app/player/MessageDistributor/messages/urlResolve.ts index b80ff4f9a..44298ec08 100644 --- a/frontend/app/player/MessageDistributor/messages/urlResolve.ts +++ b/frontend/app/player/MessageDistributor/messages/urlResolve.ts @@ -6,7 +6,6 @@ export function resolveURL(baseURL: string, relURL: string): string { } -var match = /bar/.exec("foobar"); const re1 = /url\(("[^"]*"|'[^']*'|[^)]*)\)/g const re2 = /@import "(.*?)"/g function cssUrlsIndex(css: string): Array<[number, number]> { diff --git a/frontend/app/player/MessageDistributor/network/loadFiles.ts b/frontend/app/player/MessageDistributor/network/loadFiles.ts new file mode 100644 index 000000000..5deecf472 --- /dev/null +++ b/frontend/app/player/MessageDistributor/network/loadFiles.ts @@ -0,0 +1,49 @@ +const NO_NTH_FILE = "nnf" + +export default function load( + urls: string[], + onData: (Uint8Array) => void, +): Promise { + const firstFileURL = urls.shift() + if (!firstFileURL) { + return Promise.reject("No urls provided") + } + return window.fetch(firstFileURL) + .then(r => { + if (r.status >= 400) { + throw new Error(`no start file. status code ${ r.status }`) + } + return r.arrayBuffer() + }) + .then(b => new Uint8Array(b)) + .then(onData) + .then(() => + urls.reduce((p, url) => + p.then(() => + window.fetch(url) + .then(r => { + return new Promise((res, rej) => { + if (r.status == 404) { + rej(NO_NTH_FILE) + return + } + if (r.status >= 400) { + rej(`Bad endfile status code ${r.status}`) + return + } + res(r.arrayBuffer()) + }) + }) + .then(b => new Uint8Array(b)) + .then(onData) + ), + Promise.resolve(), + ) + ) + .catch(e => { + if (e === NO_NTH_FILE) { + return + } + throw e + }) +} diff --git a/frontend/app/routes.js b/frontend/app/routes.js index 7a6c5379e..4492a4b63 100644 --- a/frontend/app/routes.js +++ b/frontend/app/routes.js @@ -61,10 +61,11 @@ export const CLIENT_TABS = { PROFILE: 'account', MANAGE_USERS: 'team', MANAGE_ROLES: 'roles', - SITES: 'projects', + SITES: 'projects', CUSTOM_FIELDS: 'metadata', WEBHOOKS: 'webhooks', NOTIFICATIONS: 'notifications', + AUDIT: 'audit', }; export const CLIENT_DEFAULT_TAB = CLIENT_TABS.PROFILE; const routerClientTabString = `:activeTab(${ Object.values(CLIENT_TABS).join('|') })`; diff --git a/frontend/app/services/AuditService.ts b/frontend/app/services/AuditService.ts new file mode 100644 index 000000000..39b7521ed --- /dev/null +++ b/frontend/app/services/AuditService.ts @@ -0,0 +1,25 @@ +import APIClient from 'App/api_client'; + +export default class AuditService { + private client: APIClient; + + constructor(client?: APIClient) { + this.client = client ? client : new APIClient(); + } + + initClient(client?: APIClient) { + this.client = client || new APIClient(); + } + + all(data: any): Promise { + return this.client.post('/trails', data) + .then(response => response.json()) + .then(response => response.data || []); + } + + one(id: string): Promise { + return this.client.get('/trails/' + id) + .then(response => response.json()) + .then(response => response.data || {}); + } +} \ No newline at end of file diff --git a/frontend/app/services/UserService.ts b/frontend/app/services/UserService.ts new file mode 100644 index 000000000..615368db6 --- /dev/null +++ b/frontend/app/services/UserService.ts @@ -0,0 +1,57 @@ +import APIClient from 'App/api_client'; +import { IUser } from 'App/mstore/types/user' + +export default class UserService { + private client: APIClient; + + constructor(client?: APIClient) { + this.client = client ? client : new APIClient(); + } + + initClient(client?: APIClient) { + this.client = client || new APIClient(); + } + + all() { + return this.client.get('/client/members') + .then(response => response.json()) + .then(response => response.data || []); + } + + one(userId: string) { + return this.client.get('/users/' + userId) + .then(response => response.json()) + .then(response => response.data || {}); + } + + save(user: IUser): Promise { + const data = user.toSave(); + if (user.userId) { + return this.client.put('/client/members/' + user.userId, data) + .then(response => response.json()) + .then(response => response.data || {}) + } else { + return this.client.post('/client/members', data) + .then(response => response.json()) + .then(response => response.data || {}); + } + } + + generateInviteCode(userId: any): Promise { + return this.client.get(`/client/members/${userId}/reset`) + .then(response => response.json()) + .then(response => response.data || {}); + } + + delete(userId: string) { + return this.client.delete('/client/members/' + userId) + .then(response => response.json()) + .then(response => response.data || {}); + } + + getRoles() { + return this.client.get('/client/roles') + .then(response => response.json()) + .then(response => response.data || []); + } +} \ No newline at end of file diff --git a/frontend/app/services/index.ts b/frontend/app/services/index.ts index e721e0f10..5b4d59651 100644 --- a/frontend/app/services/index.ts +++ b/frontend/app/services/index.ts @@ -1,7 +1,11 @@ import DashboardService, { IDashboardService } from "./DashboardService"; import MetricService, { IMetricService } from "./MetricService"; import SessionSerivce from "./SessionService"; +import UserService from "./UserService"; +import AuditService from './AuditService'; export const dashboardService: IDashboardService = new DashboardService(); export const metricService: IMetricService = new MetricService(); -export const sessionService: SessionSerivce = new SessionSerivce(); \ No newline at end of file +export const sessionService: SessionSerivce = new SessionSerivce(); +export const userService: UserService = new UserService(); +export const auditService: AuditService = new AuditService(); \ No newline at end of file diff --git a/frontend/app/styles/main.css b/frontend/app/styles/main.css index 4258e43d7..3b7f5fe4b 100644 --- a/frontend/app/styles/main.css +++ b/frontend/app/styles/main.css @@ -147,13 +147,4 @@ height: 100vh; overflow-y: hidden; padding-right: 15px; -} - -/* .svg-map__location { - fill: #EEE !important; - cursor: pointer; - - &:hover { - fill: #fff !important; - } -} */ \ No newline at end of file +} \ No newline at end of file diff --git a/frontend/app/svg/icons/grid-3x3.svg b/frontend/app/svg/icons/grid-3x3.svg new file mode 100644 index 000000000..c40d98c96 --- /dev/null +++ b/frontend/app/svg/icons/grid-3x3.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/app/svg/icons/list-ul.svg b/frontend/app/svg/icons/list-ul.svg new file mode 100644 index 000000000..a07021b20 --- /dev/null +++ b/frontend/app/svg/icons/list-ul.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/frontend/app/svg/icons/text-paragraph.svg b/frontend/app/svg/icons/text-paragraph.svg index 9779beabf..f06f83174 100644 --- a/frontend/app/svg/icons/text-paragraph.svg +++ b/frontend/app/svg/icons/text-paragraph.svg @@ -1,3 +1,3 @@ - + \ No newline at end of file diff --git a/frontend/app/types/app/period.js b/frontend/app/types/app/period.js index deebf9cad..b4f8798c5 100644 --- a/frontend/app/types/app/period.js +++ b/frontend/app/types/app/period.js @@ -103,6 +103,10 @@ export default Record({ endTimestamp: this.end, }; }, + rangeFormatted(format = 'MMM Do YY, hh:mm A') { + console.log('period', this) + return this.range.start.format(format) + ' - ' + this.range.end.format(format); + }, toTimestampstwo() { return { startTimestamp: this.start / 1000, diff --git a/frontend/app/utils.js b/frontend/app/utils.js index bdece4058..029e24599 100644 --- a/frontend/app/utils.js +++ b/frontend/app/utils.js @@ -264,4 +264,55 @@ export const convertElementToImage = async (el) => { }, }); return image; +} + +export const unserscoreToSpaceAndCapitalize = (str) => { + return str.replace(/_/g, ' ').replace(/\w\S*/g, (txt) => { + return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); + }); +} + +export const convertToCSV = (headers, objArray) => { + var array = typeof objArray != 'object' ? JSON.parse(objArray) : objArray; + var str = ''; + const headersMap = headers.reduce((acc, curr) => { + acc[curr.key] = curr; + return acc; + }, {}); + + str += headers.map(h => h.label).join(',') + '\r\n'; + + for (var i = 0; i < array.length; i++) { + var line = ''; + for (var index in headersMap) { + if (line !== '') line += ','; + line += array[i][index]; + } + str += line + '\r\n'; + } + + + return str; +} + +export const exportCSVFile = (headers, items, fileTitle) => { + var jsonObject = JSON.stringify(items); + var csv = convertToCSV(headers, jsonObject); + var exportedFilenmae = fileTitle + '.csv' || 'export.csv'; + + var blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' }); + if (navigator.msSaveBlob) { // IE 10+ + navigator.msSaveBlob(blob, exportedFilenmae); + } else { + var link = document.createElement("a"); + if (link.download !== undefined) { + var url = URL.createObjectURL(blob); + link.setAttribute("href", url); + link.setAttribute("download", exportedFilenmae); + link.style.visibility = 'hidden'; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } + } } \ No newline at end of file diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js index 04c57948e..a9aa4b8e8 100644 --- a/frontend/tailwind.config.js +++ b/frontend/tailwind.config.js @@ -55,6 +55,7 @@ module.exports = { 'height', 'inset', 'justifyContent', + 'justifySelf', 'letterSpacing', 'lineHeight', // 'listStylePosition', diff --git a/scripts/helmcharts/init.sh b/scripts/helmcharts/init.sh index 7c846356f..bf7c18f32 100644 --- a/scripts/helmcharts/init.sh +++ b/scripts/helmcharts/init.sh @@ -20,9 +20,9 @@ usr=`whoami` # Installing k3s curl -sL https://get.k3s.io | sudo K3S_KUBECONFIG_MODE="644" INSTALL_K3S_VERSION='v1.22.8+k3s1' INSTALL_K3S_EXEC="--no-deploy=traefik" sh - -mkdir ~/.kube +[[ -d ~/.kube ]] || mkdir ~/.kube sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config -chmod 0644 ~/.kube/config +sudo chmod 0644 ~/.kube/config sudo chown -R $usr ~/.kube/config diff --git a/tracker/README.md b/tracker/README.md new file mode 100644 index 000000000..f283ee3a8 --- /dev/null +++ b/tracker/README.md @@ -0,0 +1,31 @@ +## Local build + + +In order to build locally any of the javascript packages located under this directory, go to the corresponding folder first: + +```sh +cd tracker # or any tracker-* plugin + +``` +Then run +```sh +yarn +yarn build +``` +OR + +```sh +npm i +npm run build +``` + +You can then use it as a local javascript package by executing the folowing line under your local project location: + +```sh +yarn add file:../path/to/openreplay/monorepo/tracker/tracker +```` +OR +```sh +npm install --save ../path/to/openreplay/monorepo/tracker/tracker +``` + diff --git a/tracker/tracker-assist/package-lock.json b/tracker/tracker-assist/package-lock.json index f0fd5eb9f..3c159da6d 100644 --- a/tracker/tracker-assist/package-lock.json +++ b/tracker/tracker-assist/package-lock.json @@ -1,16 +1,16 @@ { "name": "@openreplay/tracker-assist", - "version": "3.5.7", + "version": "3.5.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@openreplay/tracker-assist", - "version": "3.5.7", + "version": "3.5.8", "license": "MIT", "dependencies": { "csstype": "^3.0.10", - "peerjs": "^1.3.2", + "peerjs": "1.3.2", "socket.io-client": "^4.4.1" }, "devDependencies": { @@ -25,7 +25,7 @@ }, "../tracker": { "name": "@openreplay/tracker", - "version": "3.5.4", + "version": "3.5.11", "dev": true, "license": "MIT", "dependencies": { @@ -644,15 +644,19 @@ } }, "../tracker/node_modules/ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, "../tracker/node_modules/ansi-escapes": { @@ -1791,12 +1795,6 @@ "node": ">=6" } }, - "../tracker/node_modules/json5/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, "../tracker/node_modules/levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -1853,9 +1851,9 @@ } }, "../tracker/node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "../tracker/node_modules/mkdirp": { @@ -2376,9 +2374,9 @@ } }, "../tracker/node_modules/strip-ansi/node_modules/ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true, "engines": { "node": ">=6" @@ -4444,9 +4442,9 @@ "requires": {} }, "ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -5337,14 +5335,6 @@ "dev": true, "requires": { "minimist": "^1.2.5" - }, - "dependencies": { - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - } } }, "levn": { @@ -5391,9 +5381,9 @@ } }, "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", "dev": true }, "mkdirp": { @@ -5793,9 +5783,9 @@ }, "dependencies": { "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", "dev": true } } diff --git a/tracker/tracker-assist/package.json b/tracker/tracker-assist/package.json index 19149b62a..437a86499 100644 --- a/tracker/tracker-assist/package.json +++ b/tracker/tracker-assist/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker-assist", "description": "Tracker plugin for screen assistance through the WebRTC", - "version": "3.5.8", + "version": "3.5.9", "keywords": [ "WebRTC", "assistance", @@ -25,7 +25,7 @@ }, "dependencies": { "csstype": "^3.0.10", - "peerjs": "^1.3.2", + "peerjs": "1.3.2", "socket.io-client": "^4.4.1" }, "peerDependencies": { diff --git a/tracker/tracker-assist/src/Assist.ts b/tracker/tracker-assist/src/Assist.ts index 24f04e7e0..ab64ab9b2 100644 --- a/tracker/tracker-assist/src/Assist.ts +++ b/tracker/tracker-assist/src/Assist.ts @@ -11,6 +11,7 @@ import AnnotationCanvas from './AnnotationCanvas.js'; import ConfirmWindow, { callConfirmDefault, controlConfirmDefault } from './ConfirmWindow.js'; import type { Options as ConfirmOptions } from './ConfirmWindow.js'; +// TODO: fully specified strict check (everywhere) //@ts-ignore peerjs hack for webpack5 (?!) TODO: ES/node modules; Peer = Peer.default || Peer; diff --git a/tracker/tracker-assist/src/RemoteControl.ts b/tracker/tracker-assist/src/RemoteControl.ts index a32f81035..02137d99f 100644 --- a/tracker/tracker-assist/src/RemoteControl.ts +++ b/tracker/tracker-assist/src/RemoteControl.ts @@ -20,7 +20,7 @@ export default class RemoteControl { reconnect(ids: string[]) { const storedID = sessionStorage.getItem(this.options.session_control_peer_key) - if (storedID !== null && ids.includes(storedID)) { + if (storedID !== null && ids.indexOf(storedID) !== -1) { this.grantControl(storedID) } else { sessionStorage.removeItem(this.options.session_control_peer_key) diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json index 555efe07d..22d39de12 100644 --- a/tracker/tracker/package.json +++ b/tracker/tracker/package.json @@ -1,7 +1,7 @@ { "name": "@openreplay/tracker", "description": "The OpenReplay tracker main package", - "version": "3.5.10", + "version": "3.5.11", "keywords": [ "logging", "replay" diff --git a/tracker/tracker/scripts/compile.js b/tracker/tracker/scripts/compile.js index ab0091dd5..563254016 100644 --- a/tracker/tracker/scripts/compile.js +++ b/tracker/tracker/scripts/compile.js @@ -15,31 +15,31 @@ async function main() { to: webworker.replace(/'/g, "\\'"), }); await fs.rename('build/main', 'lib'); - await fs.rename('build/messages', 'lib/messages'); + await fs.rename('build/common', 'lib/common'); await replaceInFiles({ files: 'lib/*', - from: /\.\.\/messages/g, - to: './messages', + from: /\.\.\/common/g, + to: './common', }); await replaceInFiles({ files: 'lib/**/*', - from: /\.\.\/\.\.\/messages/g, - to: '../messages', + from: /\.\.\/\.\.\/common/g, + to: '../common', }); await fs.rename('build/cjs/main', 'cjs'); - await fs.rename('build/cjs/messages', 'cjs/messages'); + await fs.rename('build/cjs/common', 'cjs/common'); await fs.writeFile('cjs/package.json', `{ "type": "commonjs" }`); await replaceInFiles({ files: 'cjs/*', - from: /\.\.\/messages/g, - to: './messages', + from: /\.\.\/common/g, + to: './common', }); await replaceInFiles({ files: 'cjs/**/*', - from: /\.\.\/\.\.\/messages/g, - to: '../messages', + from: /\.\.\/\.\.\/common/g, + to: '../common', }); } main() diff --git a/tracker/tracker/src/messages/index.ts b/tracker/tracker/src/common/messages.ts similarity index 99% rename from tracker/tracker/src/messages/index.ts rename to tracker/tracker/src/common/messages.ts index f3267bee0..140bcaac6 100644 --- a/tracker/tracker/src/messages/index.ts +++ b/tracker/tracker/src/common/messages.ts @@ -1,6 +1,6 @@ // Auto-generated, do not edit -import Message from "./message.js"; -import Writer from "./writer.js"; +import type { Writer, Message }from "./types.js"; +export default Message function bindNew( Class: C & { new(...args: A): T } diff --git a/tracker/tracker/src/messages/tsconfig.json b/tracker/tracker/src/common/tsconfig.json similarity index 100% rename from tracker/tracker/src/messages/tsconfig.json rename to tracker/tracker/src/common/tsconfig.json diff --git a/tracker/tracker/src/common/types.ts b/tracker/tracker/src/common/types.ts new file mode 100644 index 000000000..6717eb8bc --- /dev/null +++ b/tracker/tracker/src/common/types.ts @@ -0,0 +1,10 @@ +export interface Writer { + uint(n: number): boolean + int(n: number): boolean + string(s: string): boolean + boolean(b: boolean): boolean +} + +export interface Message { + encode(w: Writer): boolean; +} diff --git a/tracker/tracker/src/webworker/types.ts b/tracker/tracker/src/common/webworker.ts similarity index 100% rename from tracker/tracker/src/webworker/types.ts rename to tracker/tracker/src/common/webworker.ts diff --git a/tracker/tracker/src/main/app/index.ts b/tracker/tracker/src/main/app/index.ts index ace8ecf6e..2874a8867 100644 --- a/tracker/tracker/src/main/app/index.ts +++ b/tracker/tracker/src/main/app/index.ts @@ -1,6 +1,6 @@ +import type Message from "../../common/messages.js"; +import { Timestamp, Metadata } from "../../common/messages.js"; import { timestamp, deprecationWarn } from "../utils.js"; -import { Timestamp, Metadata } from "../../messages/index.js"; -import Message from "../../messages/message.js"; import Nodes from "./nodes.js"; import Observer from "./observer/top_observer.js"; import Sanitizer from "./sanitizer.js"; @@ -13,7 +13,7 @@ import { deviceMemory, jsHeapSizeLimit } from "../modules/performance.js"; import type { Options as ObserverOptions } from "./observer/top_observer.js"; import type { Options as SanitizerOptions } from "./sanitizer.js"; import type { Options as LoggerOptions } from "./logger.js" -import type { Options as WebworkerOptions, WorkerMessageData } from "../../webworker/types.js"; +import type { Options as WebworkerOptions, WorkerMessageData } from "../../common/webworker.js"; // TODO: Unify and clearly describe options logic diff --git a/tracker/tracker/src/main/app/observer/iframe_observer.ts b/tracker/tracker/src/main/app/observer/iframe_observer.ts index be0a7182c..1f50e588a 100644 --- a/tracker/tracker/src/main/app/observer/iframe_observer.ts +++ b/tracker/tracker/src/main/app/observer/iframe_observer.ts @@ -1,5 +1,5 @@ import Observer from "./observer.js"; -import { CreateIFrameDocument } from "../../../messages/index.js"; +import { CreateIFrameDocument } from "../../../common/messages.js"; export default class IFrameObserver extends Observer { observe(iframe: HTMLIFrameElement) { diff --git a/tracker/tracker/src/main/app/observer/observer.ts b/tracker/tracker/src/main/app/observer/observer.ts index 06823e07c..dcbba6365 100644 --- a/tracker/tracker/src/main/app/observer/observer.ts +++ b/tracker/tracker/src/main/app/observer/observer.ts @@ -8,7 +8,7 @@ import { CreateElementNode, MoveNode, RemoveNode, -} from "../../../messages/index.js"; +} from "../../../common/messages.js"; import App from "../index.js"; import { isInstance, inDocument } from "../context.js"; diff --git a/tracker/tracker/src/main/app/observer/shadow_root_observer.ts b/tracker/tracker/src/main/app/observer/shadow_root_observer.ts index 244348ea1..ea37bb30f 100644 --- a/tracker/tracker/src/main/app/observer/shadow_root_observer.ts +++ b/tracker/tracker/src/main/app/observer/shadow_root_observer.ts @@ -1,5 +1,5 @@ import Observer from "./observer.js"; -import { CreateIFrameDocument } from "../../../messages/index.js"; +import { CreateIFrameDocument } from "../../../common/messages.js"; export default class ShadowRootObserver extends Observer { observe(el: Element) { diff --git a/tracker/tracker/src/main/app/observer/top_observer.ts b/tracker/tracker/src/main/app/observer/top_observer.ts index 14bed9768..cab84b162 100644 --- a/tracker/tracker/src/main/app/observer/top_observer.ts +++ b/tracker/tracker/src/main/app/observer/top_observer.ts @@ -4,7 +4,7 @@ import type { Window } from "../context.js"; import IFrameObserver from "./iframe_observer.js"; import ShadowRootObserver from "./shadow_root_observer.js"; -import { CreateDocument } from "../../../messages/index.js"; +import { CreateDocument } from "../../../common/messages.js"; import App from "../index.js"; import { IN_BROWSER, hasOpenreplayAttribute } from '../../utils.js' diff --git a/tracker/tracker/src/main/app/session.ts b/tracker/tracker/src/main/app/session.ts index 169241aa7..602ef8280 100644 --- a/tracker/tracker/src/main/app/session.ts +++ b/tracker/tracker/src/main/app/session.ts @@ -1,5 +1,5 @@ import App, { StartOptions } from "./index.js"; -import { UserID, UserAnonymousID, Metadata } from "../../messages/index.js"; +import { UserID, UserAnonymousID, Metadata } from "../../common/messages.js"; enum ActivityState { diff --git a/tracker/tracker/src/main/index.ts b/tracker/tracker/src/main/index.ts index a55df1d1c..e1b25b43b 100644 --- a/tracker/tracker/src/main/index.ts +++ b/tracker/tracker/src/main/index.ts @@ -1,8 +1,8 @@ import App, { DEFAULT_INGEST_POINT } from "./app/index.js"; export { default as App } from './app/index.js'; -import { UserID, UserAnonymousID, Metadata, RawCustomEvent, CustomIssue } from "../messages/index.js"; -import * as _Messages from "../messages/index.js"; +import { UserID, UserAnonymousID, Metadata, RawCustomEvent, CustomIssue } from "../common/messages.js"; +import * as _Messages from "../common/messages.js"; export const Messages = _Messages; import Connection from "./modules/connection.js"; diff --git a/tracker/tracker/src/main/modules/connection.ts b/tracker/tracker/src/main/modules/connection.ts index a2767790c..72ef972f7 100644 --- a/tracker/tracker/src/main/modules/connection.ts +++ b/tracker/tracker/src/main/modules/connection.ts @@ -1,5 +1,5 @@ import App from "../app/index.js"; -import { ConnectionInformation } from "../../messages/index.js"; +import { ConnectionInformation } from "../../common/messages.js"; export default function(app: App): void { const connection: diff --git a/tracker/tracker/src/main/modules/console.ts b/tracker/tracker/src/main/modules/console.ts index 98db6c144..b6e95d14f 100644 --- a/tracker/tracker/src/main/modules/console.ts +++ b/tracker/tracker/src/main/modules/console.ts @@ -1,6 +1,6 @@ import App from "../app/index.js"; import { IN_BROWSER } from "../utils.js"; -import { ConsoleLog } from "../../messages/index.js"; +import { ConsoleLog } from "../../common/messages.js"; const printError: (e: Error) => string = IN_BROWSER && 'InstallTrigger' in window // detect Firefox diff --git a/tracker/tracker/src/main/modules/cssrules.ts b/tracker/tracker/src/main/modules/cssrules.ts index 18aa3f154..8c75a5366 100644 --- a/tracker/tracker/src/main/modules/cssrules.ts +++ b/tracker/tracker/src/main/modules/cssrules.ts @@ -1,5 +1,5 @@ import App from "../app/index.js"; -import { CSSInsertRuleURLBased, CSSDeleteRule, TechnicalInfo } from "../../messages/index.js"; +import { CSSInsertRuleURLBased, CSSDeleteRule, TechnicalInfo } from "../../common/messages.js"; export default function(app: App | null) { if (app === null) { diff --git a/tracker/tracker/src/main/modules/exception.ts b/tracker/tracker/src/main/modules/exception.ts index 848df03be..be02ca291 100644 --- a/tracker/tracker/src/main/modules/exception.ts +++ b/tracker/tracker/src/main/modules/exception.ts @@ -1,6 +1,6 @@ +import type Message from "../../common/messages.js"; import App from "../app/index.js"; -import { JSException } from "../../messages/index.js"; -import Message from "../../messages/message.js"; +import { JSException } from "../../common/messages.js"; import ErrorStackParser from 'error-stack-parser'; export interface Options { diff --git a/tracker/tracker/src/main/modules/img.ts b/tracker/tracker/src/main/modules/img.ts index 8c0f911a8..da260c414 100644 --- a/tracker/tracker/src/main/modules/img.ts +++ b/tracker/tracker/src/main/modules/img.ts @@ -1,6 +1,6 @@ import { timestamp, isURL } from "../utils.js"; import App from "../app/index.js"; -import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from "../../messages/index.js"; +import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from "../../common/messages.js"; const PLACEHOLDER_SRC = "https://static.openreplay.com/tracker/placeholder.jpeg"; diff --git a/tracker/tracker/src/main/modules/input.ts b/tracker/tracker/src/main/modules/input.ts index 0cad3c58b..546204730 100644 --- a/tracker/tracker/src/main/modules/input.ts +++ b/tracker/tracker/src/main/modules/input.ts @@ -5,7 +5,7 @@ import { hasOpenreplayAttribute, } from "../utils.js"; import App from "../app/index.js"; -import { SetInputTarget, SetInputValue, SetInputChecked } from "../../messages/index.js"; +import { SetInputTarget, SetInputValue, SetInputChecked } from "../../common/messages.js"; // TODO: take into consideration "contenteditable" attribute type TextEditableElement = HTMLInputElement | HTMLTextAreaElement diff --git a/tracker/tracker/src/main/modules/longtasks.ts b/tracker/tracker/src/main/modules/longtasks.ts index 0f3a7e82a..589c73de2 100644 --- a/tracker/tracker/src/main/modules/longtasks.ts +++ b/tracker/tracker/src/main/modules/longtasks.ts @@ -1,5 +1,5 @@ import App from "../app/index.js"; -import { LongTask } from "../../messages/index.js"; +import { LongTask } from "../../common/messages.js"; // https://w3c.github.io/performance-timeline/#the-performanceentry-interface interface TaskAttributionTiming extends PerformanceEntry { diff --git a/tracker/tracker/src/main/modules/mouse.ts b/tracker/tracker/src/main/modules/mouse.ts index b72e5dbb9..956108963 100644 --- a/tracker/tracker/src/main/modules/mouse.ts +++ b/tracker/tracker/src/main/modules/mouse.ts @@ -4,7 +4,7 @@ import { getLabelAttribute, } from "../utils.js"; import App from "../app/index.js"; -import { MouseMove, MouseClick } from "../../messages/index.js"; +import { MouseMove, MouseClick } from "../../common/messages.js"; import { getInputLabel } from "./input.js"; function _getSelector(target: Element): string { diff --git a/tracker/tracker/src/main/modules/performance.ts b/tracker/tracker/src/main/modules/performance.ts index 8eb7701eb..c7d911c9f 100644 --- a/tracker/tracker/src/main/modules/performance.ts +++ b/tracker/tracker/src/main/modules/performance.ts @@ -1,6 +1,6 @@ import App from "../app/index.js"; import { IN_BROWSER } from "../utils.js"; -import { PerformanceTrack } from "../../messages/index.js"; +import { PerformanceTrack } from "../../common/messages.js"; type Perf = { diff --git a/tracker/tracker/src/main/modules/scroll.ts b/tracker/tracker/src/main/modules/scroll.ts index f9c80e6d9..9d19ccc24 100644 --- a/tracker/tracker/src/main/modules/scroll.ts +++ b/tracker/tracker/src/main/modules/scroll.ts @@ -1,5 +1,5 @@ import App from "../app/index.js"; -import { SetViewportScroll, SetNodeScroll } from "../../messages/index.js"; +import { SetViewportScroll, SetNodeScroll } from "../../common/messages.js"; export default function (app: App): void { let documentScroll = false; diff --git a/tracker/tracker/src/main/modules/timing.ts b/tracker/tracker/src/main/modules/timing.ts index 033741838..01fbd1d0a 100644 --- a/tracker/tracker/src/main/modules/timing.ts +++ b/tracker/tracker/src/main/modules/timing.ts @@ -1,7 +1,7 @@ +import type Message from "../../common/messages.js"; import { isURL } from "../utils.js"; import App from "../app/index.js"; -import { ResourceTiming, PageLoadTiming, PageRenderTiming } from "../../messages/index.js"; -import type Message from "../../messages/message.js"; +import { ResourceTiming, PageLoadTiming, PageRenderTiming } from "../../common/messages.js"; // Inspired by https://github.com/WPO-Foundation/RUM-SpeedIndex/blob/master/src/rum-speedindex.js diff --git a/tracker/tracker/src/main/modules/viewport.ts b/tracker/tracker/src/main/modules/viewport.ts index 626eadd12..1d70a3ebf 100644 --- a/tracker/tracker/src/main/modules/viewport.ts +++ b/tracker/tracker/src/main/modules/viewport.ts @@ -3,7 +3,7 @@ import { SetPageLocation, SetViewportSize, SetPageVisibility, -} from "../../messages/index.js"; +} from "../../common/messages.js"; export default function (app: App): void { let url: string, width: number, height: number; diff --git a/tracker/tracker/src/main/tsconfig.json b/tracker/tracker/src/main/tsconfig.json index f6ac938a6..14a932c39 100644 --- a/tracker/tracker/src/main/tsconfig.json +++ b/tracker/tracker/src/main/tsconfig.json @@ -5,7 +5,7 @@ "lib": ["es6", "dom"], }, "references": [ - { "path": "../messages" } + { "path": "../common" } ], "exclude": ["app/observer"] } diff --git a/tracker/tracker/src/messages/message.ts b/tracker/tracker/src/messages/message.ts deleted file mode 100644 index aeb8619de..000000000 --- a/tracker/tracker/src/messages/message.ts +++ /dev/null @@ -1,5 +0,0 @@ -import Writer from "./writer.js"; - -export default interface Message { - encode(w: Writer): boolean; -} diff --git a/tracker/tracker/src/webworker/BatchWriter.ts b/tracker/tracker/src/webworker/BatchWriter.ts index 5f220bbec..7ddf97dbe 100644 --- a/tracker/tracker/src/webworker/BatchWriter.ts +++ b/tracker/tracker/src/webworker/BatchWriter.ts @@ -1,14 +1,14 @@ -import Writer from "../messages/writer.js"; -import Message from "../messages/message.js"; +import type Message from "../common/messages.js"; +import PrimitiveWriter from "./PrimitiveWriter.js"; import { BatchMeta, Timestamp, -} from "../messages/index.js"; +} from "../common/messages.js"; export default class BatchWriter { private nextIndex = 0 private beaconSize = 2 * 1e5 // Default 200kB - private writer = new Writer(this.beaconSize) + private writer = new PrimitiveWriter(this.beaconSize) private isEmpty = true constructor( @@ -50,7 +50,7 @@ export default class BatchWriter { } // MBTODO: tempWriter for one message? this.beaconSize = Math.min(this.beaconSize*2, this.beaconSizeLimit) - this.writer = new Writer(this.beaconSize) + this.writer = new PrimitiveWriter(this.beaconSize) this.prepareBatchMeta() } } diff --git a/tracker/tracker/src/messages/writer.ts b/tracker/tracker/src/webworker/PrimitiveWriter.ts similarity index 99% rename from tracker/tracker/src/messages/writer.ts rename to tracker/tracker/src/webworker/PrimitiveWriter.ts index 6947420bc..587291bea 100644 --- a/tracker/tracker/src/messages/writer.ts +++ b/tracker/tracker/src/webworker/PrimitiveWriter.ts @@ -59,7 +59,7 @@ const textEncoder: { encode(str: string): Uint8Array } = }, }; -export default class Writer { +export default class PrimitiveWriter { private offset: number = 0; private checkpointOffset: number = 0; private readonly data: Uint8Array; diff --git a/tracker/tracker/src/webworker/index.ts b/tracker/tracker/src/webworker/index.ts index b9640e9e2..3e9341a2d 100644 --- a/tracker/tracker/src/webworker/index.ts +++ b/tracker/tracker/src/webworker/index.ts @@ -1,13 +1,14 @@ -import Message from "../messages/message.js"; +import type Message from "../common/messages.js"; +import type { WorkerMessageData } from "../common/webworker.js"; + import { classes, SetPageVisibility, MouseMove, -} from "../messages/index.js"; +} from "../common/messages.js"; import QueueSender from "./QueueSender.js"; import BatchWriter from "./BatchWriter.js"; -import type { WorkerMessageData } from "./types.js"; const AUTO_SEND_INTERVAL = 10 * 1000 diff --git a/tracker/tracker/src/webworker/tsconfig.json b/tracker/tracker/src/webworker/tsconfig.json index 9e72f437e..6794ea55c 100644 --- a/tracker/tracker/src/webworker/tsconfig.json +++ b/tracker/tracker/src/webworker/tsconfig.json @@ -4,6 +4,6 @@ "lib": ["es6", "webworker"] }, "references": [ - { "path": "../messages" } + { "path": "../common" } ] } diff --git a/tracker/tracker/src/webworker/url-rewriter.ts.dd b/tracker/tracker/src/webworker/url-rewriter.ts.dd deleted file mode 100644 index 956448df2..000000000 --- a/tracker/tracker/src/webworker/url-rewriter.ts.dd +++ /dev/null @@ -1,9 +0,0 @@ - - - - -function getFullURL(baseURL: string, relativeURL: string) { - if (isRelative(relativeURL)) { - - } -} \ No newline at end of file