diff --git a/api/v1/analytics.go b/api/v1/analytics.go index a31428b..cf60b9f 100644 --- a/api/v1/analytics.go +++ b/api/v1/analytics.go @@ -10,6 +10,7 @@ import ( "github.com/mssola/useragent" "golang.org/x/exp/slices" + "github.com/boojack/slash/server/metric" "github.com/boojack/slash/store" ) @@ -78,6 +79,7 @@ func (s *APIV1Service) registerAnalyticsRoutes(g *echo.Group) { browserMap[browserName]++ } + metric.Enqueue("shortcut analytics") return c.JSON(http.StatusOK, &AnalysisData{ ReferenceData: mapToReferenceInfoSlice(referenceMap), DeviceData: mapToDeviceInfoSlice(deviceMap), diff --git a/api/v1/auth.go b/api/v1/auth.go index 9747d8a..3fc192e 100644 --- a/api/v1/auth.go +++ b/api/v1/auth.go @@ -13,6 +13,7 @@ import ( "github.com/boojack/slash/api/auth" storepb "github.com/boojack/slash/proto/gen/store" + "github.com/boojack/slash/server/metric" "github.com/boojack/slash/server/service/license" "github.com/boojack/slash/store" ) @@ -63,6 +64,7 @@ func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) { cookieExp := time.Now().Add(auth.CookieExpDuration) setTokenCookie(c, auth.AccessTokenCookieName, accessToken, cookieExp) + metric.Enqueue("user sign in") return c.JSON(http.StatusOK, convertUserFromStore(user)) }) @@ -129,6 +131,7 @@ func (s *APIV1Service) registerAuthRoutes(g *echo.Group, secret string) { cookieExp := time.Now().Add(auth.CookieExpDuration) setTokenCookie(c, auth.AccessTokenCookieName, accessToken, cookieExp) + metric.Enqueue("user sign up") return c.JSON(http.StatusOK, convertUserFromStore(user)) }) diff --git a/api/v1/redirector.go b/api/v1/redirector.go index d5d8553..187d715 100644 --- a/api/v1/redirector.go +++ b/api/v1/redirector.go @@ -12,6 +12,7 @@ import ( "github.com/pkg/errors" storepb "github.com/boojack/slash/proto/gen/store" + "github.com/boojack/slash/server/metric" "github.com/boojack/slash/store" ) @@ -46,6 +47,7 @@ func (s *APIV1Service) registerRedirectorRoutes(g *echo.Group) { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to create activity, err: %s", err)).SetInternal(err) } + metric.Enqueue("shortcut redirect") return redirectToShortcut(c, shortcut) }) } diff --git a/api/v1/shortcut.go b/api/v1/shortcut.go index 9ed1bb2..6600c66 100644 --- a/api/v1/shortcut.go +++ b/api/v1/shortcut.go @@ -12,6 +12,7 @@ import ( "github.com/boojack/slash/internal/util" storepb "github.com/boojack/slash/proto/gen/store" + "github.com/boojack/slash/server/metric" "github.com/boojack/slash/store" ) @@ -121,6 +122,7 @@ func (s *APIV1Service) registerShortcutRoutes(g *echo.Group) { if err != nil { return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to compose shortcut, err: %s", err)).SetInternal(err) } + metric.Enqueue("shortcut create") return c.JSON(http.StatusOK, shortcutMessage) }) diff --git a/api/v1/user.go b/api/v1/user.go index 7075f0b..6ff0014 100644 --- a/api/v1/user.go +++ b/api/v1/user.go @@ -11,6 +11,7 @@ import ( "golang.org/x/crypto/bcrypt" "github.com/boojack/slash/internal/util" + "github.com/boojack/slash/server/metric" "github.com/boojack/slash/server/service/license" "github.com/boojack/slash/store" ) @@ -137,6 +138,7 @@ func (s *APIV1Service) registerUserRoutes(g *echo.Group) { } userMessage := convertUserFromStore(user) + metric.Enqueue("user create") return c.JSON(http.StatusOK, userMessage) }) diff --git a/cmd/slash/main.go b/cmd/slash/main.go index 8250349..81a2d78 100644 --- a/cmd/slash/main.go +++ b/cmd/slash/main.go @@ -15,6 +15,7 @@ import ( "github.com/boojack/slash/internal/log" "github.com/boojack/slash/server" + "github.com/boojack/slash/server/metric" "github.com/boojack/slash/server/profile" "github.com/boojack/slash/store" "github.com/boojack/slash/store/db" @@ -50,6 +51,9 @@ var ( return } + // nolint + metric.NewMetricClient(s.Secret, *serverProfile) + c := make(chan os.Signal, 1) // Trigger graceful shutdown on SIGINT or SIGTERM. // The default signal sent by the `kill` command is SIGTERM, diff --git a/go.mod b/go.mod index 235e25f..6925ef1 100644 --- a/go.mod +++ b/go.mod @@ -78,6 +78,7 @@ require ( github.com/mssola/useragent v1.0.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 + github.com/posthog/posthog-go v0.0.0-20230801140217-d607812dee69 go.uber.org/zap v1.21.0 golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df golang.org/x/mod v0.11.0 diff --git a/go.sum b/go.sum index f80b133..c02e32d 100644 --- a/go.sum +++ b/go.sum @@ -390,6 +390,8 @@ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qR github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posthog/posthog-go v0.0.0-20230801140217-d607812dee69 h1:01dHVodha5BzrMtVmcpPeA4VYbZEsTXQ6m4123zQXJk= +github.com/posthog/posthog-go v0.0.0-20230801140217-d607812dee69/go.mod h1:migYMxlAqcnQy+3eN8mcL0b2tpKy6R+8Zc0lxwk4dKM= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= @@ -476,6 +478,7 @@ github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.5/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= diff --git a/server/metric/metric.go b/server/metric/metric.go new file mode 100644 index 0000000..aa6c992 --- /dev/null +++ b/server/metric/metric.go @@ -0,0 +1,51 @@ +package metric + +import ( + "github.com/posthog/posthog-go" + + "github.com/boojack/slash/server/profile" +) + +const ( + PostHogAPIKey = "phc_YFEi1aqUBW9sX2KDzdvMtK43DNu0mkeoKMKc0EQum2t" +) + +var ( + client *MetricClient +) + +type MetricClient struct { + workspaceID string + profile *profile.Profile + phClient *posthog.Client +} + +func NewMetricClient(workspaceID string, profile profile.Profile) (*MetricClient, error) { + phClient, err := posthog.NewWithConfig(PostHogAPIKey, posthog.Config{ + Endpoint: "https://app.posthog.com", + }) + if err != nil { + return nil, err + } + client = &MetricClient{ + workspaceID: workspaceID, + profile: &profile, + phClient: &phClient, + } + return client, nil +} + +func Enqueue(event string) { + if client == nil { + return + } + if client.profile.Mode != "prod" { + return + } + + // nolint + (*client.phClient).Enqueue(posthog.Capture{ + DistinctId: `slash-` + client.workspaceID, + Event: event, + }) +} diff --git a/server/server.go b/server/server.go index 5451002..0f88c4a 100644 --- a/server/server.go +++ b/server/server.go @@ -18,6 +18,7 @@ import ( apiv2 "github.com/boojack/slash/api/v2" "github.com/boojack/slash/internal/log" storepb "github.com/boojack/slash/proto/gen/store" + "github.com/boojack/slash/server/metric" "github.com/boojack/slash/server/profile" "github.com/boojack/slash/server/service/license" "github.com/boojack/slash/server/service/resource" @@ -29,6 +30,7 @@ type Server struct { Profile *profile.Profile Store *store.Store + Secret string licenseService *license.LicenseService @@ -93,11 +95,12 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store secret := "slash" if profile.Mode == "prod" { var err error - secret, err = s.getSystemSecretSessionName(ctx) + secret, err = s.getSecretSessionName(ctx) if err != nil { return nil, err } } + s.Secret = secret rootGroup := e.Group("") // Register API v1 routes. @@ -133,6 +136,7 @@ func (s *Server) Start(ctx context.Context) error { } }() + metric.Enqueue("server start") return s.e.Start(fmt.Sprintf(":%d", s.Profile.Port)) } @@ -161,7 +165,7 @@ func grpcRequestSkipper(c echo.Context) bool { return strings.HasPrefix(c.Request().URL.Path, "/slash.api.v2.") } -func (s *Server) getSystemSecretSessionName(ctx context.Context) (string, error) { +func (s *Server) getSecretSessionName(ctx context.Context) (string, error) { secretSessionSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{ Key: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_SECRET_SESSION, })