Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,4 @@ release:
owner: onflow
name: flow-cli
prerelease: auto
draft: false
draft: false
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ require (
github.com/onflow/fcl-dev-wallet v0.8.0
github.com/onflow/flixkit-go/v2 v2.3.0
github.com/onflow/flow-core-contracts/lib/go/templates v1.6.1
github.com/onflow/flow-emulator v1.3.0
github.com/onflow/flow-emulator v1.4.0
github.com/onflow/flow-evm-gateway v1.0.3
github.com/onflow/flow-go v0.38.1-0.20250218174738-2181389f9f7d
github.com/onflow/flow-go-sdk v1.4.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -844,8 +844,8 @@ github.com/onflow/flow-core-contracts/lib/go/contracts v1.5.1-preview h1:W+QkNQc
github.com/onflow/flow-core-contracts/lib/go/contracts v1.5.1-preview/go.mod h1:LyCICUK6sK1jtEyb+3GuRw5tYfHT1uxACLwLTLxw/0I=
github.com/onflow/flow-core-contracts/lib/go/templates v1.6.1 h1:Y0bDvS5fTOCrKr7QFl0by3qTq7MFnauVnHoxwW6nQzo=
github.com/onflow/flow-core-contracts/lib/go/templates v1.6.1/go.mod h1:pN768Al/wLRlf3bwugv9TyxniqJxMu4sxnX9eQJam64=
github.com/onflow/flow-emulator v1.3.0 h1:SqKbXZ79GcfnDDaggQccEkp6fBMFQVG4AxRtpT+jYYk=
github.com/onflow/flow-emulator v1.3.0/go.mod h1:+RV5j5TYEE0VYtUAEn/vduecy8yK8P+rB+a9BCc3gQA=
github.com/onflow/flow-emulator v1.4.0 h1:LSNYV4Dd6Aetd2Q/4qc1mes5BIt4JTKMoQfr5QXdBFQ=
github.com/onflow/flow-emulator v1.4.0/go.mod h1:+RV5j5TYEE0VYtUAEn/vduecy8yK8P+rB+a9BCc3gQA=
github.com/onflow/flow-evm-gateway v1.0.3 h1:Mlz3EBVTz7wpUwrDTVSJtd8CG/DtEe920UDUPiTGN7Y=
github.com/onflow/flow-evm-gateway v1.0.3/go.mod h1:JukrDrisLQvup7FwCkA/z7LEKtLpCETPSu25CAuuEN0=
github.com/onflow/flow-ft/lib/go/contracts v1.0.1 h1:Ts5ob+CoCY2EjEd0W6vdLJ7hLL3SsEftzXG2JlmSe24=
Expand Down
6 changes: 3 additions & 3 deletions internal/command/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -364,14 +364,14 @@ func initCrashReporting() {
}

// The token is injected at build-time using ldflags
var mixpanelToken = ""
var MixpanelToken = ""

func UsageMetrics(command *cobra.Command, wg *sync.WaitGroup) {
if !settings.MetricsEnabled() || mixpanelToken == "" {
if !settings.MetricsEnabled() || MixpanelToken == "" {
return
}
wg.Add(1)
client := mixpanel.New(mixpanelToken, "")
client := mixpanel.New(MixpanelToken, "")

// calculates a user ID that doesn't leak any personal information
usr, _ := user.Current() // ignore err, just use empty string
Expand Down
50 changes: 49 additions & 1 deletion internal/emulator/start.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,17 @@
package emulator

import (
"crypto/sha256"
"encoding/base64"
"errors"
"fmt"
"net/http"
"os"
"os/user"
"runtime"
"sync"

"github.com/dukex/mixpanel"
"github.com/onflow/flow-emulator/cmd/emulator/start"
"github.com/onflow/flow-go-sdk/crypto"
"github.com/spf13/afero"
Expand All @@ -32,12 +38,17 @@ import (
"github.com/onflow/flowkit/v2"
"github.com/onflow/flowkit/v2/config"

"github.com/onflow/flow-cli/build"
"github.com/onflow/flow-cli/internal/command"
"github.com/onflow/flow-cli/internal/settings"
"github.com/onflow/flow-cli/internal/util"
)

var Cmd *cobra.Command

// Mixpanel client to be reused on each http request of the middleware
var mixpanelClient mixpanel.Mixpanel

func configuredServiceKey(
init bool,
_ crypto.SignatureAlgorithm,
Expand Down Expand Up @@ -97,8 +108,45 @@ func configuredServiceKey(
return *privateKey, serviceAccount.Key.SigAlgo(), serviceAccount.Key.HashAlgo()
}

func trackRequestMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Generate a unique user ID
usr, _ := user.Current() // ignore err, just use empty string
hash := sha256.Sum256([]byte(fmt.Sprintf("%s%s", usr.Username, usr.Uid)))
userID := base64.StdEncoding.EncodeToString(hash[:])

// Track the request in Mixpanel
_ = mixpanelClient.Track(userID, "emulator-request", &mixpanel.Event{
IP: "0", // do not track IPs
Properties: map[string]any{
"method": r.Method,
"url": r.URL.String(),
"version": build.Semver(),
"os": runtime.GOOS,
"ci": os.Getenv("CI") != "", // CI is commonly set by CI providers
},
})

// Call the next handler
next.ServeHTTP(w, r)
})
}

func init() {
Cmd = start.Cmd(configuredServiceKey)
// Initialize mixpanel client only if metrics are enabled and token is not empty
if settings.MetricsEnabled() && command.MixpanelToken != "" {
mixpanelClient = mixpanel.New(command.MixpanelToken, "")
Cmd = start.Cmd(start.StartConfig{
GetServiceKey: configuredServiceKey,
RestMiddlewares: []start.HttpMiddleware{trackRequestMiddleware},
})
} else {
Cmd = start.Cmd(start.StartConfig{
GetServiceKey: configuredServiceKey,
RestMiddlewares: []start.HttpMiddleware{},
})
}

Cmd.Use = "emulator"
Cmd.Short = "Run Flow network for development"
Cmd.GroupID = "tools"
Expand Down
Loading