forked from tangled.org/core
Monorepo for Tangled

appview: initial posthog events

anirudh.fi e33874a9 3953ee26

verified
+6
appview/config.go
··· 36 36 SharedSecret string `env:"SHARED_SECRET"` 37 37 } 38 38 39 + type PosthogConfig struct { 40 + ApiKey string `env:"API_KEY"` 41 + Endpoint string `env:"ENDPOINT, default=https://eu.i.posthog.com"` 42 + } 43 + 39 44 type Config struct { 40 45 Core CoreConfig `env:",prefix=TANGLED_"` 41 46 Jetstream JetstreamConfig `env:",prefix=TANGLED_JETSTREAM_"` 42 47 Resend ResendConfig `env:",prefix=TANGLED_RESEND_"` 48 + Posthog PosthogConfig `env:",prefix=TANGLED_POSTHOG_"` 43 49 Camo CamoConfig `env:",prefix=TANGLED_CAMO_"` 44 50 Avatar AvatarConfig `env:",prefix=TANGLED_AVATAR_"` 45 51 OAuth OAuthConfig `env:",prefix=TANGLED_OAUTH_"`
+12
appview/oauth/handler/handler.go
··· 12 12 "github.com/gorilla/sessions" 13 13 "github.com/haileyok/atproto-oauth-golang/helpers" 14 14 "github.com/lestrrat-go/jwx/v2/jwk" 15 + "github.com/posthog/posthog-go" 15 16 "tangled.sh/tangled.sh/core/appview" 16 17 "tangled.sh/tangled.sh/core/appview/db" 17 18 "tangled.sh/tangled.sh/core/appview/knotclient" ··· 34 35 Store *sessions.CookieStore 35 36 OAuth *oauth.OAuth 36 37 Enforcer *rbac.Enforcer 38 + Posthog posthog.Client 37 39 } 38 40 39 41 func (o *OAuthHandler) Router() http.Handler { ··· 247 249 248 250 log.Println("session saved successfully") 249 251 go o.addToDefaultKnot(oauthRequest.Did) 252 + 253 + if !o.Config.Core.Dev { 254 + err = o.Posthog.Enqueue(posthog.Capture{ 255 + DistinctId: oauthRequest.Did, 256 + Event: "signin", 257 + }) 258 + if err != nil { 259 + log.Println("failed to enqueue posthog event:", err) 260 + } 261 + } 250 262 251 263 http.Redirect(w, r, "/", http.StatusFound) 252 264 }
+23
appview/state/follow.go
··· 7 7 8 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 9 lexutil "github.com/bluesky-social/indigo/lex/util" 10 + "github.com/posthog/posthog-go" 10 11 "tangled.sh/tangled.sh/core/api/tangled" 11 12 "tangled.sh/tangled.sh/core/appview" 12 13 "tangled.sh/tangled.sh/core/appview/db" ··· 70 71 FollowStatus: db.IsFollowing, 71 72 }) 72 73 74 + if !s.config.Core.Dev { 75 + err = s.posthog.Enqueue(posthog.Capture{ 76 + DistinctId: currentUser.Did, 77 + Event: "follow", 78 + Properties: posthog.Properties{"subject": subjectIdent.DID.String()}, 79 + }) 80 + if err != nil { 81 + log.Println("failed to enqueue posthog event:", err) 82 + } 83 + } 84 + 73 85 return 74 86 case http.MethodDelete: 75 87 // find the record in the db ··· 100 112 UserDid: subjectIdent.DID.String(), 101 113 FollowStatus: db.IsNotFollowing, 102 114 }) 115 + 116 + if !s.config.Core.Dev { 117 + err = s.posthog.Enqueue(posthog.Capture{ 118 + DistinctId: currentUser.Did, 119 + Event: "unfollow", 120 + Properties: posthog.Properties{"subject": subjectIdent.DID.String()}, 121 + }) 122 + if err != nil { 123 + log.Println("failed to enqueue posthog event:", err) 124 + } 125 + } 103 126 104 127 return 105 128 }
+11
appview/state/profile.go
··· 15 15 "github.com/bluesky-social/indigo/atproto/syntax" 16 16 lexutil "github.com/bluesky-social/indigo/lex/util" 17 17 "github.com/go-chi/chi/v5" 18 + "github.com/posthog/posthog-go" 18 19 "tangled.sh/tangled.sh/core/api/tangled" 19 20 "tangled.sh/tangled.sh/core/appview/db" 20 21 "tangled.sh/tangled.sh/core/appview/pages" ··· 345 346 log.Println("failed to update profile", err) 346 347 s.pages.Notice(w, "update-profile", "Failed to update profile, try again later.") 347 348 return 349 + } 350 + 351 + if !s.config.Core.Dev { 352 + err = s.posthog.Enqueue(posthog.Capture{ 353 + DistinctId: user.Did, 354 + Event: "edit_profile", 355 + }) 356 + if err != nil { 357 + log.Println("failed to enqueue posthog event:", err) 358 + } 348 359 } 349 360 350 361 s.pages.HxRedirect(w, "/"+user.Did)
+23
appview/state/pull.go
··· 28 28 lexutil "github.com/bluesky-social/indigo/lex/util" 29 29 "github.com/go-chi/chi/v5" 30 30 "github.com/google/uuid" 31 + "github.com/posthog/posthog-go" 31 32 ) 32 33 33 34 // htmx fragment ··· 605 606 return 606 607 } 607 608 609 + if !s.config.Core.Dev { 610 + err = s.posthog.Enqueue(posthog.Capture{ 611 + DistinctId: user.Did, 612 + Event: "new_pull_comment", 613 + Properties: posthog.Properties{"repo_at": f.RepoAt.String(), "pull_id": pull.PullId}, 614 + }) 615 + if err != nil { 616 + log.Println("failed to enqueue posthog event:", err) 617 + } 618 + } 619 + 608 620 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", f.OwnerSlashRepo(), pull.PullId, commentId)) 609 621 return 610 622 } ··· 980 992 log.Println("failed to create pull request", err) 981 993 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 982 994 return 995 + } 996 + 997 + if !s.config.Core.Dev { 998 + err = s.posthog.Enqueue(posthog.Capture{ 999 + DistinctId: user.Did, 1000 + Event: "new_pull", 1001 + Properties: posthog.Properties{"repo_at": f.RepoAt.String(), "pull_id": pullId}, 1002 + }) 1003 + if err != nil { 1004 + log.Println("failed to enqueue posthog event:", err) 1005 + } 983 1006 } 984 1007 985 1008 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId))
+12
appview/state/repo.go
··· 33 33 securejoin "github.com/cyphar/filepath-securejoin" 34 34 "github.com/go-chi/chi/v5" 35 35 "github.com/go-git/go-git/v5/plumbing" 36 + "github.com/posthog/posthog-go" 36 37 37 38 comatproto "github.com/bluesky-social/indigo/api/atproto" 38 39 lexutil "github.com/bluesky-social/indigo/lex/util" ··· 1875 1876 log.Println("failed to set issue at", err) 1876 1877 s.pages.Notice(w, "issues", "Failed to create issue.") 1877 1878 return 1879 + } 1880 + 1881 + if !s.config.Core.Dev { 1882 + err = s.posthog.Enqueue(posthog.Capture{ 1883 + DistinctId: user.Did, 1884 + Event: "new_issue", 1885 + Properties: posthog.Properties{"repo_at": f.RepoAt.String(), "issue_id": issueId}, 1886 + }) 1887 + if err != nil { 1888 + log.Println("failed to enqueue posthog event:", err) 1889 + } 1878 1890 } 1879 1891 1880 1892 s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId))
+1
appview/state/router.go
··· 269 269 Store: sessions.NewCookieStore([]byte(s.config.Core.CookieSecret)), 270 270 OAuth: s.oauth, 271 271 Enforcer: s.enforcer, 272 + Posthog: s.posthog, 272 273 } 273 274 274 275 return oauth.Router()
+23
appview/state/star.go
··· 8 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 10 lexutil "github.com/bluesky-social/indigo/lex/util" 11 + "github.com/posthog/posthog-go" 11 12 "tangled.sh/tangled.sh/core/api/tangled" 12 13 "tangled.sh/tangled.sh/core/appview" 13 14 "tangled.sh/tangled.sh/core/appview/db" ··· 75 76 }, 76 77 }) 77 78 79 + if !s.config.Core.Dev { 80 + err = s.posthog.Enqueue(posthog.Capture{ 81 + DistinctId: currentUser.Did, 82 + Event: "star", 83 + Properties: posthog.Properties{"repo_at": subjectUri.String()}, 84 + }) 85 + if err != nil { 86 + log.Println("failed to enqueue posthog event:", err) 87 + } 88 + } 89 + 78 90 return 79 91 case http.MethodDelete: 80 92 // find the record in the db ··· 114 126 StarCount: starCount, 115 127 }, 116 128 }) 129 + 130 + if !s.config.Core.Dev { 131 + err = s.posthog.Enqueue(posthog.Capture{ 132 + DistinctId: currentUser.Did, 133 + Event: "unstar", 134 + Properties: posthog.Properties{"repo_at": subjectUri.String()}, 135 + }) 136 + if err != nil { 137 + log.Println("failed to enqueue posthog event:", err) 138 + } 139 + } 117 140 118 141 return 119 142 }
+19 -76
appview/state/state.go
··· 17 17 lexutil "github.com/bluesky-social/indigo/lex/util" 18 18 securejoin "github.com/cyphar/filepath-securejoin" 19 19 "github.com/go-chi/chi/v5" 20 + "github.com/posthog/posthog-go" 20 21 "tangled.sh/tangled.sh/core/api/tangled" 21 22 "tangled.sh/tangled.sh/core/appview" 22 23 "tangled.sh/tangled.sh/core/appview/db" ··· 34 35 tidClock syntax.TIDClock 35 36 pages *pages.Pages 36 37 resolver *appview.Resolver 38 + posthog posthog.Client 37 39 jc *jetstream.JetstreamClient 38 40 config *appview.Config 39 41 } ··· 57 59 58 60 oauth := oauth.NewOAuth(d, config) 59 61 62 + posthog, err := posthog.NewWithConfig(config.Posthog.ApiKey, posthog.Config{Endpoint: config.Posthog.Endpoint}) 63 + if err != nil { 64 + return nil, fmt.Errorf("failed to create posthog client: %w", err) 65 + } 66 + 60 67 wrapper := db.DbWrapper{d} 61 68 jc, err := jetstream.NewJetstreamClient( 62 69 config.Jetstream.Endpoint, ··· 88 95 clock, 89 96 pgs, 90 97 resolver, 98 + posthog, 91 99 jc, 92 100 config, 93 101 } ··· 98 106 func TID(c *syntax.TIDClock) string { 99 107 return c.Next().String() 100 108 } 101 - 102 - // func (s *State) Login(w http.ResponseWriter, r *http.Request) { 103 - // ctx := r.Context() 104 - 105 - // switch r.Method { 106 - // case http.MethodGet: 107 - // err := s.pages.Login(w, pages.LoginParams{}) 108 - // if err != nil { 109 - // log.Printf("rendering login page: %s", err) 110 - // } 111 - 112 - // return 113 - // case http.MethodPost: 114 - // handle := strings.TrimPrefix(r.FormValue("handle"), "@") 115 - // appPassword := r.FormValue("app_password") 116 - 117 - // resolved, err := s.resolver.ResolveIdent(ctx, handle) 118 - // if err != nil { 119 - // log.Println("failed to resolve handle:", err) 120 - // s.pages.Notice(w, "login-msg", fmt.Sprintf("\"%s\" is an invalid handle.", handle)) 121 - // return 122 - // } 123 - 124 - // atSession, err := s.oauth.CreateInitialSession(ctx, resolved, appPassword) 125 - // if err != nil { 126 - // s.pages.Notice(w, "login-msg", "Invalid handle or password.") 127 - // return 128 - // } 129 - // sessionish := auth.CreateSessionWrapper{ServerCreateSession_Output: atSession} 130 - 131 - // err = s.oauth.StoreSession(r, w, &sessionish, resolved.PDSEndpoint()) 132 - // if err != nil { 133 - // s.pages.Notice(w, "login-msg", "Failed to login, try again later.") 134 - // return 135 - // } 136 - 137 - // log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did) 138 - 139 - // did := resolved.DID.String() 140 - // defaultKnot := "knot1.tangled.sh" 141 - 142 - // go func() { 143 - // log.Printf("adding %s to default knot", did) 144 - // err = s.enforcer.AddMember(defaultKnot, did) 145 - // if err != nil { 146 - // log.Println("failed to add user to knot1.tangled.sh: ", err) 147 - // return 148 - // } 149 - // err = s.enforcer.E.SavePolicy() 150 - // if err != nil { 151 - // log.Println("failed to add user to knot1.tangled.sh: ", err) 152 - // return 153 - // } 154 - 155 - // secret, err := db.GetRegistrationKey(s.db, defaultKnot) 156 - // if err != nil { 157 - // log.Println("failed to get registration key for knot1.tangled.sh") 158 - // return 159 - // } 160 - // signedClient, err := NewSignedClient(defaultKnot, secret, s.config.Core.Dev) 161 - // resp, err := signedClient.AddMember(did) 162 - // if err != nil { 163 - // log.Println("failed to add user to knot1.tangled.sh: ", err) 164 - // return 165 - // } 166 - 167 - // if resp.StatusCode != http.StatusNoContent { 168 - // log.Println("failed to add user to knot1.tangled.sh: ", resp.StatusCode) 169 - // return 170 - // } 171 - // }() 172 - 173 - // s.pages.HxRedirect(w, "/") 174 - // return 175 - // } 176 - // } 177 109 178 110 func (s *State) Logout(w http.ResponseWriter, r *http.Request) { 179 111 s.oauth.ClearSession(r, w) ··· 773 705 log.Println("failed to update ACLs", err) 774 706 http.Error(w, err.Error(), http.StatusInternalServerError) 775 707 return 708 + } 709 + 710 + if !s.config.Core.Dev { 711 + err = s.posthog.Enqueue(posthog.Capture{ 712 + DistinctId: user.Did, 713 + Event: "new_repo", 714 + Properties: posthog.Properties{"repo": repoName, "repo_at": repo.AtUri}, 715 + }) 716 + if err != nil { 717 + log.Println("failed to enqueue posthog event:", err) 718 + } 776 719 } 777 720 778 721 s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s", user.Handle, repoName))
+1
go.mod
··· 24 24 github.com/lestrrat-go/jwx/v2 v2.0.12 25 25 github.com/mattn/go-sqlite3 v1.14.24 26 26 github.com/microcosm-cc/bluemonday v1.0.27 27 + github.com/posthog/posthog-go v1.5.5 27 28 github.com/resend/resend-go/v2 v2.15.0 28 29 github.com/sethvargo/go-envconfig v1.1.0 29 30 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e
+2
go.sum
··· 224 224 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 225 225 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0= 226 226 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= 227 + github.com/posthog/posthog-go v1.5.5 h1:2o3j7IrHbTIfxRtj4MPaXKeimuTYg49onNzNBZbwksM= 228 + github.com/posthog/posthog-go v1.5.5/go.mod h1:3RqUmSnPuwmeVj/GYrS75wNGqcAKdpODiwc83xZWgdE= 227 229 github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= 228 230 github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= 229 231 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=