diff --git a/daemon/client.go b/daemon/client.go index d03177d..af5460a 100644 --- a/daemon/client.go +++ b/daemon/client.go @@ -2,12 +2,12 @@ package daemon import ( "context" + "encoding/json" "net" "os" "time" "github.com/malamtime/cli/model" - "github.com/vmihailenco/msgpack/v5" ) func IsSocketReady(ctx context.Context, socketPath string) bool { @@ -38,7 +38,7 @@ func SendLocalDataToSocket( }, } - encoded, err := msgpack.Marshal(data) + encoded, err := json.Marshal(data) if err != nil { return err } diff --git a/daemon/handlers.go b/daemon/handlers.go index 1a18599..a8310a7 100644 --- a/daemon/handlers.go +++ b/daemon/handlers.go @@ -2,10 +2,10 @@ package daemon import ( "context" + "encoding/json" "log/slog" "github.com/ThreeDotsLabs/watermill/message" - "github.com/vmihailenco/msgpack/v5" ) func SocketTopicProccessor(messages <-chan *message.Message) { @@ -14,7 +14,7 @@ func SocketTopicProccessor(messages <-chan *message.Message) { slog.InfoContext(ctx, "received message: ", slog.String("msg.uuid", msg.UUID)) var socketMsg SocketMessage - if err := msgpack.Unmarshal(msg.Payload, &socketMsg); err != nil { + if err := json.Unmarshal(msg.Payload, &socketMsg); err != nil { slog.ErrorContext(ctx, "failed to parse socket message", slog.Any("err", err)) msg.Nack() } diff --git a/daemon/handlers.sync.go b/daemon/handlers.sync.go index 5223135..783ab44 100644 --- a/daemon/handlers.sync.go +++ b/daemon/handlers.sync.go @@ -7,18 +7,17 @@ import ( "time" "github.com/malamtime/cli/model" - "github.com/vmihailenco/msgpack/v5" ) func handlePubSubSync(ctx context.Context, socketMsgPayload interface{}) error { - pb, err := msgpack.Marshal(socketMsgPayload) + pb, err := json.Marshal(socketMsgPayload) if err != nil { slog.Error("Failed to marshal the sync payload again for unmarshal", slog.Any("payload", socketMsgPayload)) return err } var syncMsg model.PostTrackArgs - err = msgpack.Unmarshal(pb, &syncMsg) + err = json.Unmarshal(pb, &syncMsg) if err != nil { slog.Error("Failed to parse sync payload", slog.Any("payload", socketMsgPayload)) return err diff --git a/daemon/handlers_test.go b/daemon/handlers_test.go index 6ae2dff..9336a93 100644 --- a/daemon/handlers_test.go +++ b/daemon/handlers_test.go @@ -2,6 +2,7 @@ package daemon import ( + "encoding/json" "net/http" "net/http/httptest" "testing" @@ -12,7 +13,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" - "github.com/vmihailenco/msgpack/v5" ) type handlersTestSuite struct { @@ -49,7 +49,7 @@ func (s *handlersTestSuite) TestSocketTopicProcessorValidSync() { }, }, } - payload, err := msgpack.Marshal(socketMsg) + payload, err := json.Marshal(socketMsg) assert.NoError(s.T(), err) msg := message.NewMessage("test-uuid", payload) @@ -91,7 +91,7 @@ func (s *handlersTestSuite) TestSocketTopicProcessorNonSync() { }, }, } - payload, err := msgpack.Marshal(socketMsg) + payload, err := json.Marshal(socketMsg) assert.NoError(s.T(), err) msg := message.NewMessage("test-uuid", payload) @@ -112,7 +112,7 @@ func (s *handlersTestSuite) TestSocketTopicProcessorInvalidPayload() { Type: "sync", Payload: []byte(`invalid json`), } - payload, err := msgpack.Marshal(socketMsg) + payload, err := json.Marshal(socketMsg) assert.NoError(s.T(), err) msg := message.NewMessage("test-uuid", payload) @@ -140,7 +140,7 @@ func (s *handlersTestSuite) TestSocketTopicProcessorMultipleMessages() { }, }, } - payload1, err := msgpack.Marshal(socketMsg1) + payload1, err := json.Marshal(socketMsg1) assert.NoError(s.T(), err) socketMsg2 := SocketMessage{ @@ -154,7 +154,7 @@ func (s *handlersTestSuite) TestSocketTopicProcessorMultipleMessages() { }, }, } - payload2, err := msgpack.Marshal(socketMsg2) + payload2, err := json.Marshal(socketMsg2) assert.NoError(s.T(), err) msg1 := message.NewMessage("test-uuid-1", payload1) diff --git a/daemon/socket.go b/daemon/socket.go index 9f218bb..704b5fa 100644 --- a/daemon/socket.go +++ b/daemon/socket.go @@ -1,13 +1,13 @@ package daemon import ( + "encoding/json" "log/slog" "net" "os" "github.com/ThreeDotsLabs/watermill" "github.com/ThreeDotsLabs/watermill/message" - "github.com/vmihailenco/msgpack/v5" ) type SocketMessageType string @@ -17,9 +17,9 @@ const ( ) type SocketMessage struct { - Type SocketMessageType `msgpack:"type"` + Type SocketMessageType `json:"type"` // if parse from buffer, it will be the map[any]any - Payload interface{} `msgpack:"payload"` + Payload interface{} `json:"payload"` } type SocketHandler struct { @@ -89,7 +89,7 @@ func (p *SocketHandler) acceptConnections() { func (p *SocketHandler) handleConnection(conn net.Conn) { defer conn.Close() - decoder := msgpack.NewDecoder(conn) + decoder := json.NewDecoder(conn) var msg SocketMessage if err := decoder.Decode(&msg); err != nil { slog.Error("Error decoding message", slog.Any("err", err)) @@ -102,7 +102,7 @@ func (p *SocketHandler) handleConnection(conn net.Conn) { // case "track": // p.handleTrack(conn, msg.Payload) case SocketMessageTypeSync: - buf, err := msgpack.Marshal(msg) + buf, err := json.Marshal(msg) if err != nil { slog.Error("Error encoding message", slog.Any("err", err)) } diff --git a/go.mod b/go.mod index 027f3af..a72ecdf 100644 --- a/go.mod +++ b/go.mod @@ -14,10 +14,8 @@ require ( github.com/pkg/errors v0.9.1 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.10.0 - github.com/ugorji/go/codec v1.3.0 github.com/uptrace/uptrace-go v1.37.0 github.com/urfave/cli/v2 v2.27.7 - github.com/vmihailenco/msgpack/v5 v5.4.1 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 go.opentelemetry.io/otel v1.37.0 go.opentelemetry.io/otel/trace v1.37.0 @@ -66,7 +64,6 @@ require ( github.com/sergi/go-diff v1.4.0 // indirect github.com/skeema/knownhosts v1.3.1 // indirect github.com/stretchr/objx v0.5.2 // indirect - github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect github.com/xanzy/ssh-agent v0.3.3 // indirect github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect github.com/xrash/smetrics v0.0.0-20250705151800-55b8f293f342 // indirect diff --git a/model/alias.go b/model/alias.go index dff05e8..dfccc85 100644 --- a/model/alias.go +++ b/model/alias.go @@ -18,15 +18,15 @@ type Alias struct { } type importShellAliasRequest struct { - Aliases []string `json:"aliases" msgpack:"aliases"` - IsFullRefresh bool `json:"isFullRefresh" msgpack:"isFullRefresh"` - ShellType string `json:"shellType" msgpack:"shellType"` - FileLocation string `json:"fileLocation" msgpack:"fileLocation"` + Aliases []string `json:"aliases"` + IsFullRefresh bool `json:"isFullRefresh"` + ShellType string `json:"shellType"` + FileLocation string `json:"fileLocation"` - Hostname string `json:"hostname" msgpack:"hostname"` - Username string `json:"username" msgpack:"username"` - OS string `json:"os" msgpack:"os"` - OSVersion string `json:"osVersion" msgpack:"osVersion"` + Hostname string `json:"hostname"` + Username string `json:"username"` + OS string `json:"os"` + OSVersion string `json:"osVersion"` } type importShellAliasResponse struct { diff --git a/model/api.base.go b/model/api.base.go index b376d3c..f98e307 100644 --- a/model/api.base.go +++ b/model/api.base.go @@ -8,11 +8,9 @@ import ( "fmt" "io" "net/http" - "strings" "time" "github.com/sirupsen/logrus" - "github.com/vmihailenco/msgpack/v5" "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) @@ -24,102 +22,10 @@ type HTTPRequestOptions[T any, R any] struct { Path string Payload T Response *R - ContentType string // Optional, defaults to "application/msgpack" + ContentType string // Optional, defaults to "application/json" Timeout time.Duration // Optional, defaults to 10 seconds } -// SendHTTPRequest is a legacy HTTP request function that uses msgpack encoding -// Deprecated: Use SendHTTPRequestJSON for new implementations -func SendHTTPRequest[T any, R any](opts HTTPRequestOptions[T, R]) error { - ctx, span := modelTracer.Start(opts.Context, "http.send") - defer span.End() - - jsonData, err := msgpack.Marshal(opts.Payload) - if err != nil { - logrus.Errorln(err) - return err - } - - timeout := time.Second * 10 - if opts.Timeout > 0 { - timeout = opts.Timeout - } - - client := &http.Client{ - Timeout: timeout, - Transport: otelhttp.NewTransport(http.DefaultTransport), - } - - req, err := http.NewRequestWithContext(ctx, opts.Method, opts.Endpoint.APIEndpoint+opts.Path, bytes.NewBuffer(jsonData)) - if err != nil { - logrus.Errorln(err) - return err - } - - contentType := "application/msgpack" - if opts.ContentType != "" { - contentType = opts.ContentType - } - - req.Header.Set("Content-Type", contentType) - req.Header.Set("User-Agent", fmt.Sprintf("shelltimeCLI@%s", commitID)) - req.Header.Set("Authorization", "CLI "+opts.Endpoint.Token) - - logrus.Traceln("http: ", req.URL.String()) - - resp, err := client.Do(req) - if err != nil { - logrus.Errorln(err) - return err - } - defer resp.Body.Close() - - logrus.Traceln("http: ", resp.Status) - - if resp.StatusCode == http.StatusNoContent { - return nil - } - - buf, err := io.ReadAll(resp.Body) - if err != nil { - logrus.Errorln(err) - return err - } - - if resp.StatusCode != http.StatusOK { - var msg errorResponse - err = json.Unmarshal(buf, &msg) - if err != nil { - logrus.Errorln("Failed to parse error response:", err) - return fmt.Errorf("HTTP error: %d", resp.StatusCode) - } - logrus.Errorln("Error response:", msg.ErrorMessage) - return errors.New(msg.ErrorMessage) - } - - // Only try to unmarshal if we have a response struct - if opts.Response != nil { - contentType := resp.Header.Get("Content-Type") - if strings.Contains(contentType, "json") { - err = json.Unmarshal(buf, opts.Response) - if err != nil { - logrus.Errorln("Failed to unmarshal JSON response:", err) - return err - } - return nil - } - if strings.Contains(contentType, "msgpack") { - err = msgpack.Unmarshal(buf, opts.Response) - if err != nil { - logrus.Errorln("Failed to unmarshal response:", err) - return err - } - } - } - - return nil -} - // SendHTTPRequestJSON is a generic HTTP request function that sends JSON data and unmarshals the response func SendHTTPRequestJSON[T any, R any](opts HTTPRequestOptions[T, R]) error { ctx, span := modelTracer.Start(opts.Context, "http.send.json") diff --git a/model/api.go b/model/api.go index 8149f60..4776912 100644 --- a/model/api.go +++ b/model/api.go @@ -14,37 +14,37 @@ type errorResponse struct { } type TrackingData struct { - SessionID int64 `json:"sessionId" msgpack:"sessionId"` - Command string `json:"command" msgpack:"command"` - StartTime int64 `json:"startTime" msgpack:"startTime"` - EndTime int64 `json:"endTime" msgpack:"endTime"` - StartTimeNano int64 `json:"startTimeNano" msgpack:"startTimeNano"` - EndTimeNano int64 `json:"endTimeNano" msgpack:"endTimeNano"` - Result int `json:"result" msgpack:"result"` + SessionID int64 `json:"sessionId"` + Command string `json:"command"` + StartTime int64 `json:"startTime"` + EndTime int64 `json:"endTime"` + StartTimeNano int64 `json:"startTimeNano"` + EndTimeNano int64 `json:"endTimeNano"` + Result int `json:"result"` } type TrackingMetaData struct { - Hostname string `json:"hostname" msgpack:"hostname"` - Username string `json:"username" msgpack:"username"` - OS string `json:"os" msgpack:"os"` - OSVersion string `json:"osVersion" msgpack:"osVersion"` - Shell string `json:"shell" msgpack:"shell"` + Hostname string `json:"hostname"` + Username string `json:"username"` + OS string `json:"os"` + OSVersion string `json:"osVersion"` + Shell string `json:"shell"` // 0: cli, 1: daemon - Source int `json:"source" msgpack:"source"` + Source int `json:"source"` } type PostTrackArgs struct { // nano timestamp - CursorID int64 `json:"cursorId" msgpack:"cursorId"` - Data []TrackingData `json:"data" msgpack:"data"` - Meta TrackingMetaData `json:"meta" msgpack:"meta"` + CursorID int64 `json:"cursorId"` + Data []TrackingData `json:"data"` + Meta TrackingMetaData `json:"meta"` - Encrypted string `json:"encrypted" msgpack:"encrypted"` + Encrypted string `json:"encrypted"` // a base64 encoded AES-GCM key that encrypted by PublicKey from open token - AesKey string `json:"aesKey" msgpack:"aesKey"` + AesKey string `json:"aesKey"` // the AES-GCM nonce. not encrypted - Nonce string `json:"nonce" msgpack:"nonce"` + Nonce string `json:"nonce"` } func doSendData(ctx context.Context, endpoint Endpoint, data PostTrackArgs) error { diff --git a/model/ccusage_service.go b/model/ccusage_service.go index c6e5108..36a9cc1 100644 --- a/model/ccusage_service.go +++ b/model/ccusage_service.go @@ -16,12 +16,12 @@ import ( // CCUsageData represents the usage data collected from ccusage command type CCUsageData struct { - Timestamp string `json:"timestamp" msgpack:"timestamp"` - Hostname string `json:"hostname" msgpack:"hostname"` - Username string `json:"username" msgpack:"username"` - OS string `json:"os" msgpack:"os"` - OSVersion string `json:"osVersion" msgpack:"osVersion"` - Data CCUsageProjectDailyOutput `json:"data" msgpack:"data"` + Timestamp string `json:"timestamp"` + Hostname string `json:"hostname"` + Username string `json:"username"` + OS string `json:"os"` + OSVersion string `json:"osVersion"` + Data CCUsageProjectDailyOutput `json:"data"` } // CCUsageService defines the interface for CC usage collection diff --git a/model/dotfile.go b/model/dotfile.go index 3a19071..1aa50f2 100644 --- a/model/dotfile.go +++ b/model/dotfile.go @@ -12,17 +12,17 @@ import ( // DotfileItem represents a single dotfile to be sent to the server type DotfileItem struct { - App string `json:"app" msgpack:"app"` - Path string `json:"path" msgpack:"path"` - Content string `json:"content" msgpack:"content"` - FileModifiedAt *time.Time `json:"fileModifiedAt" msgpack:"fileModifiedAt"` - FileType string `json:"fileType" msgpack:"fileType"` - Metadata map[string]interface{} `json:"metadata" msgpack:"metadata"` - Hostname string `json:"hostname" msgpack:"hostname"` + App string `json:"app"` + Path string `json:"path"` + Content string `json:"content"` + FileModifiedAt *time.Time `json:"fileModifiedAt"` + FileType string `json:"fileType"` + Metadata map[string]interface{} `json:"metadata"` + Hostname string `json:"hostname"` } type dotfilePushRequest struct { - Dotfiles []DotfileItem `json:"dotfiles" msgpack:"dotfiles"` + Dotfiles []DotfileItem `json:"dotfiles"` } type dotfileResponseItem struct { diff --git a/model/handshake.go b/model/handshake.go index eba1975..7015753 100644 --- a/model/handshake.go +++ b/model/handshake.go @@ -76,9 +76,9 @@ func (hs handshakeService) send(ctx context.Context, path string, jsonData []byt } type handshakeInitRequest struct { - Hostname string `json:"hostname" msgpack:"hostname"` - OS string `json:"os" msgpack:"os"` - OSVersion string `json:"osVersion" msgpack:"osVersion"` + Hostname string `json:"hostname"` + OS string `json:"os"` + OSVersion string `json:"osVersion"` } func (hs handshakeService) Init(ctx context.Context) (string, error) { @@ -121,7 +121,7 @@ func (hs handshakeService) Init(ctx context.Context) (string, error) { } type handshakeCheckRequest struct { - EncodedID string `json:"hid" msgpack:"hid"` + EncodedID string `json:"hid"` } func (hs handshakeService) Check(ctx context.Context, handshakeId string) (token string, err error) { diff --git a/model/msgpack.go b/model/msgpack.go deleted file mode 100644 index 7778f27..0000000 --- a/model/msgpack.go +++ /dev/null @@ -1,36 +0,0 @@ -package model - -import ( - "bytes" - "io" - - "github.com/ugorji/go/codec" -) - -var msgpackHandle = &codec.MsgpackHandle{ - WriteExt: true, -} - -func MsgpackEncode(v interface{}) ([]byte, error) { - var buf bytes.Buffer - enc := codec.NewEncoder(&buf, msgpackHandle) - if err := enc.Encode(v); err != nil { - return nil, err - } - return buf.Bytes(), nil -} - -func MsgpackDecode(data []byte, v interface{}) error { - dec := codec.NewDecoder(bytes.NewReader(data), msgpackHandle) - return dec.Decode(v) -} - -func MsgpackEncodeWriter(w io.Writer, v interface{}) error { - enc := codec.NewEncoder(w, msgpackHandle) - return enc.Encode(v) -} - -func MsgpackDecodeReader(r io.Reader, v interface{}) error { - dec := codec.NewDecoder(r, msgpackHandle) - return dec.Decode(v) -} diff --git a/model/msgpack_test.go b/model/msgpack_test.go deleted file mode 100644 index 2cd4d76..0000000 --- a/model/msgpack_test.go +++ /dev/null @@ -1,186 +0,0 @@ -package model - -import ( - "bytes" - "testing" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestMsgpackEncodeDecode(t *testing.T) { - t.Run("encode and decode struct", func(t *testing.T) { - type TestStruct struct { - Name string - Age int - Active bool - Tags []string - } - - original := TestStruct{ - Name: "John Doe", - Age: 30, - Active: true, - Tags: []string{"developer", "golang"}, - } - - encoded, err := MsgpackEncode(original) - require.NoError(t, err) - assert.NotEmpty(t, encoded) - - var decoded TestStruct - err = MsgpackDecode(encoded, &decoded) - require.NoError(t, err) - assert.Equal(t, original, decoded) - }) - - t.Run("encode and decode map", func(t *testing.T) { - original := map[string]interface{}{ - "name": "test", - "value": 123, - "enabled": true, - "items": []int{1, 2, 3}, - } - - encoded, err := MsgpackEncode(original) - require.NoError(t, err) - assert.NotEmpty(t, encoded) - - var decoded map[string]interface{} - err = MsgpackDecode(encoded, &decoded) - require.NoError(t, err) - assert.Equal(t, original["name"], decoded["name"]) - assert.Equal(t, original["enabled"], decoded["enabled"]) - }) - - t.Run("encode and decode slice", func(t *testing.T) { - original := []string{"apple", "banana", "cherry"} - - encoded, err := MsgpackEncode(original) - require.NoError(t, err) - assert.NotEmpty(t, encoded) - - var decoded []string - err = MsgpackDecode(encoded, &decoded) - require.NoError(t, err) - assert.Equal(t, original, decoded) - }) - - t.Run("encode nil value", func(t *testing.T) { - encoded, err := MsgpackEncode(nil) - require.NoError(t, err) - assert.NotEmpty(t, encoded) - - var decoded interface{} - err = MsgpackDecode(encoded, &decoded) - require.NoError(t, err) - assert.Nil(t, decoded) - }) - - t.Run("decode error with invalid data", func(t *testing.T) { - var decoded string - err := MsgpackDecode([]byte{0xFF, 0xFF, 0xFF}, &decoded) - assert.Error(t, err) - }) -} - -func TestMsgpackEncodeDecodeWriter(t *testing.T) { - t.Run("encode and decode with writer/reader", func(t *testing.T) { - type TestData struct { - ID int - Message string - } - - original := TestData{ID: 42, Message: "hello world"} - - var buf bytes.Buffer - err := MsgpackEncodeWriter(&buf, original) - require.NoError(t, err) - - var decoded TestData - err = MsgpackDecodeReader(&buf, &decoded) - require.NoError(t, err) - assert.Equal(t, original, decoded) - }) - - t.Run("multiple encode decode operations", func(t *testing.T) { - var buf bytes.Buffer - - // Write multiple values - err := MsgpackEncodeWriter(&buf, "first") - require.NoError(t, err) - - err = MsgpackEncodeWriter(&buf, 42) - require.NoError(t, err) - - err = MsgpackEncodeWriter(&buf, true) - require.NoError(t, err) - - // Read them back - var str string - err = MsgpackDecodeReader(&buf, &str) - require.NoError(t, err) - assert.Equal(t, "first", str) - - var num int - err = MsgpackDecodeReader(&buf, &num) - require.NoError(t, err) - assert.Equal(t, 42, num) - - var boolean bool - err = MsgpackDecodeReader(&buf, &boolean) - require.NoError(t, err) - assert.True(t, boolean) - }) -} - -func BenchmarkMsgpackEncode(b *testing.B) { - type BenchData struct { - ID int - Name string - Values []float64 - Metadata map[string]string - } - - data := BenchData{ - ID: 12345, - Name: "benchmark test", - Values: []float64{1.1, 2.2, 3.3, 4.4, 5.5}, - Metadata: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - } - - b.ResetTimer() - for i := 0; i < b.N; i++ { - _, _ = MsgpackEncode(data) - } -} - -func BenchmarkMsgpackDecode(b *testing.B) { - type BenchData struct { - ID int - Name string - Values []float64 - Metadata map[string]string - } - - data := BenchData{ - ID: 12345, - Name: "benchmark test", - Values: []float64{1.1, 2.2, 3.3, 4.4, 5.5}, - Metadata: map[string]string{ - "key1": "value1", - "key2": "value2", - }, - } - - encoded, _ := MsgpackEncode(data) - - b.ResetTimer() - for i := 0; i < b.N; i++ { - var decoded BenchData - _ = MsgpackDecode(encoded, &decoded) - } -} \ No newline at end of file