forked from tangled.org/core
this repo has no description

appview: initial posthog events

authored by anirudh.fi and committed by Tangled 9d303653 3953ee26

+6
appview/config.go
··· 36 SharedSecret string `env:"SHARED_SECRET"` 37 } 38 39 type Config struct { 40 Core CoreConfig `env:",prefix=TANGLED_"` 41 Jetstream JetstreamConfig `env:",prefix=TANGLED_JETSTREAM_"` 42 Resend ResendConfig `env:",prefix=TANGLED_RESEND_"` 43 Camo CamoConfig `env:",prefix=TANGLED_CAMO_"` 44 Avatar AvatarConfig `env:",prefix=TANGLED_AVATAR_"` 45 OAuth OAuthConfig `env:",prefix=TANGLED_OAUTH_"`
··· 36 SharedSecret string `env:"SHARED_SECRET"` 37 } 38 39 + type PosthogConfig struct { 40 + ApiKey string `env:"API_KEY"` 41 + Endpoint string `env:"ENDPOINT, default=https://eu.i.posthog.com"` 42 + } 43 + 44 type Config struct { 45 Core CoreConfig `env:",prefix=TANGLED_"` 46 Jetstream JetstreamConfig `env:",prefix=TANGLED_JETSTREAM_"` 47 Resend ResendConfig `env:",prefix=TANGLED_RESEND_"` 48 + Posthog PosthogConfig `env:",prefix=TANGLED_POSTHOG_"` 49 Camo CamoConfig `env:",prefix=TANGLED_CAMO_"` 50 Avatar AvatarConfig `env:",prefix=TANGLED_AVATAR_"` 51 OAuth OAuthConfig `env:",prefix=TANGLED_OAUTH_"`
+12
appview/oauth/handler/handler.go
··· 12 "github.com/gorilla/sessions" 13 "github.com/haileyok/atproto-oauth-golang/helpers" 14 "github.com/lestrrat-go/jwx/v2/jwk" 15 "tangled.sh/tangled.sh/core/appview" 16 "tangled.sh/tangled.sh/core/appview/db" 17 "tangled.sh/tangled.sh/core/appview/knotclient" ··· 34 Store *sessions.CookieStore 35 OAuth *oauth.OAuth 36 Enforcer *rbac.Enforcer 37 } 38 39 func (o *OAuthHandler) Router() http.Handler { ··· 247 248 log.Println("session saved successfully") 249 go o.addToDefaultKnot(oauthRequest.Did) 250 251 http.Redirect(w, r, "/", http.StatusFound) 252 }
··· 12 "github.com/gorilla/sessions" 13 "github.com/haileyok/atproto-oauth-golang/helpers" 14 "github.com/lestrrat-go/jwx/v2/jwk" 15 + "github.com/posthog/posthog-go" 16 "tangled.sh/tangled.sh/core/appview" 17 "tangled.sh/tangled.sh/core/appview/db" 18 "tangled.sh/tangled.sh/core/appview/knotclient" ··· 35 Store *sessions.CookieStore 36 OAuth *oauth.OAuth 37 Enforcer *rbac.Enforcer 38 + Posthog posthog.Client 39 } 40 41 func (o *OAuthHandler) Router() http.Handler { ··· 249 250 log.Println("session saved successfully") 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 + } 262 263 http.Redirect(w, r, "/", http.StatusFound) 264 }
+23
appview/state/follow.go
··· 7 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 lexutil "github.com/bluesky-social/indigo/lex/util" 10 "tangled.sh/tangled.sh/core/api/tangled" 11 "tangled.sh/tangled.sh/core/appview" 12 "tangled.sh/tangled.sh/core/appview/db" ··· 70 FollowStatus: db.IsFollowing, 71 }) 72 73 return 74 case http.MethodDelete: 75 // find the record in the db ··· 100 UserDid: subjectIdent.DID.String(), 101 FollowStatus: db.IsNotFollowing, 102 }) 103 104 return 105 }
··· 7 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 lexutil "github.com/bluesky-social/indigo/lex/util" 10 + "github.com/posthog/posthog-go" 11 "tangled.sh/tangled.sh/core/api/tangled" 12 "tangled.sh/tangled.sh/core/appview" 13 "tangled.sh/tangled.sh/core/appview/db" ··· 71 FollowStatus: db.IsFollowing, 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 + 85 return 86 case http.MethodDelete: 87 // find the record in the db ··· 112 UserDid: subjectIdent.DID.String(), 113 FollowStatus: db.IsNotFollowing, 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 + } 126 127 return 128 }
+11
appview/state/profile.go
··· 15 "github.com/bluesky-social/indigo/atproto/syntax" 16 lexutil "github.com/bluesky-social/indigo/lex/util" 17 "github.com/go-chi/chi/v5" 18 "tangled.sh/tangled.sh/core/api/tangled" 19 "tangled.sh/tangled.sh/core/appview/db" 20 "tangled.sh/tangled.sh/core/appview/pages" ··· 345 log.Println("failed to update profile", err) 346 s.pages.Notice(w, "update-profile", "Failed to update profile, try again later.") 347 return 348 } 349 350 s.pages.HxRedirect(w, "/"+user.Did)
··· 15 "github.com/bluesky-social/indigo/atproto/syntax" 16 lexutil "github.com/bluesky-social/indigo/lex/util" 17 "github.com/go-chi/chi/v5" 18 + "github.com/posthog/posthog-go" 19 "tangled.sh/tangled.sh/core/api/tangled" 20 "tangled.sh/tangled.sh/core/appview/db" 21 "tangled.sh/tangled.sh/core/appview/pages" ··· 346 log.Println("failed to update profile", err) 347 s.pages.Notice(w, "update-profile", "Failed to update profile, try again later.") 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 + } 359 } 360 361 s.pages.HxRedirect(w, "/"+user.Did)
+23
appview/state/pull.go
··· 28 lexutil "github.com/bluesky-social/indigo/lex/util" 29 "github.com/go-chi/chi/v5" 30 "github.com/google/uuid" 31 ) 32 33 // htmx fragment ··· 605 return 606 } 607 608 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", f.OwnerSlashRepo(), pull.PullId, commentId)) 609 return 610 } ··· 980 log.Println("failed to create pull request", err) 981 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 982 return 983 } 984 985 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId))
··· 28 lexutil "github.com/bluesky-social/indigo/lex/util" 29 "github.com/go-chi/chi/v5" 30 "github.com/google/uuid" 31 + "github.com/posthog/posthog-go" 32 ) 33 34 // htmx fragment ··· 606 return 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 + 620 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d#comment-%d", f.OwnerSlashRepo(), pull.PullId, commentId)) 621 return 622 } ··· 992 log.Println("failed to create pull request", err) 993 s.pages.Notice(w, "pull", "Failed to create pull request. Try again later.") 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 + } 1006 } 1007 1008 s.pages.HxLocation(w, fmt.Sprintf("/%s/pulls/%d", f.OwnerSlashRepo(), pullId))
+12
appview/state/repo.go
··· 33 securejoin "github.com/cyphar/filepath-securejoin" 34 "github.com/go-chi/chi/v5" 35 "github.com/go-git/go-git/v5/plumbing" 36 37 comatproto "github.com/bluesky-social/indigo/api/atproto" 38 lexutil "github.com/bluesky-social/indigo/lex/util" ··· 1875 log.Println("failed to set issue at", err) 1876 s.pages.Notice(w, "issues", "Failed to create issue.") 1877 return 1878 } 1879 1880 s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId))
··· 33 securejoin "github.com/cyphar/filepath-securejoin" 34 "github.com/go-chi/chi/v5" 35 "github.com/go-git/go-git/v5/plumbing" 36 + "github.com/posthog/posthog-go" 37 38 comatproto "github.com/bluesky-social/indigo/api/atproto" 39 lexutil "github.com/bluesky-social/indigo/lex/util" ··· 1876 log.Println("failed to set issue at", err) 1877 s.pages.Notice(w, "issues", "Failed to create issue.") 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 + } 1890 } 1891 1892 s.pages.HxLocation(w, fmt.Sprintf("/%s/issues/%d", f.OwnerSlashRepo(), issueId))
+1
appview/state/router.go
··· 269 Store: sessions.NewCookieStore([]byte(s.config.Core.CookieSecret)), 270 OAuth: s.oauth, 271 Enforcer: s.enforcer, 272 } 273 274 return oauth.Router()
··· 269 Store: sessions.NewCookieStore([]byte(s.config.Core.CookieSecret)), 270 OAuth: s.oauth, 271 Enforcer: s.enforcer, 272 + Posthog: s.posthog, 273 } 274 275 return oauth.Router()
+23
appview/state/star.go
··· 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 lexutil "github.com/bluesky-social/indigo/lex/util" 11 "tangled.sh/tangled.sh/core/api/tangled" 12 "tangled.sh/tangled.sh/core/appview" 13 "tangled.sh/tangled.sh/core/appview/db" ··· 75 }, 76 }) 77 78 return 79 case http.MethodDelete: 80 // find the record in the db ··· 114 StarCount: starCount, 115 }, 116 }) 117 118 return 119 }
··· 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 "github.com/bluesky-social/indigo/atproto/syntax" 10 lexutil "github.com/bluesky-social/indigo/lex/util" 11 + "github.com/posthog/posthog-go" 12 "tangled.sh/tangled.sh/core/api/tangled" 13 "tangled.sh/tangled.sh/core/appview" 14 "tangled.sh/tangled.sh/core/appview/db" ··· 76 }, 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 + 90 return 91 case http.MethodDelete: 92 // find the record in the db ··· 126 StarCount: starCount, 127 }, 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 + } 140 141 return 142 }
+19 -76
appview/state/state.go
··· 17 lexutil "github.com/bluesky-social/indigo/lex/util" 18 securejoin "github.com/cyphar/filepath-securejoin" 19 "github.com/go-chi/chi/v5" 20 "tangled.sh/tangled.sh/core/api/tangled" 21 "tangled.sh/tangled.sh/core/appview" 22 "tangled.sh/tangled.sh/core/appview/db" ··· 34 tidClock syntax.TIDClock 35 pages *pages.Pages 36 resolver *appview.Resolver 37 jc *jetstream.JetstreamClient 38 config *appview.Config 39 } ··· 57 58 oauth := oauth.NewOAuth(d, config) 59 60 wrapper := db.DbWrapper{d} 61 jc, err := jetstream.NewJetstreamClient( 62 config.Jetstream.Endpoint, ··· 88 clock, 89 pgs, 90 resolver, 91 jc, 92 config, 93 } ··· 98 func TID(c *syntax.TIDClock) string { 99 return c.Next().String() 100 } 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 178 func (s *State) Logout(w http.ResponseWriter, r *http.Request) { 179 s.oauth.ClearSession(r, w) ··· 773 log.Println("failed to update ACLs", err) 774 http.Error(w, err.Error(), http.StatusInternalServerError) 775 return 776 } 777 778 s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s", user.Handle, repoName))
··· 17 lexutil "github.com/bluesky-social/indigo/lex/util" 18 securejoin "github.com/cyphar/filepath-securejoin" 19 "github.com/go-chi/chi/v5" 20 + "github.com/posthog/posthog-go" 21 "tangled.sh/tangled.sh/core/api/tangled" 22 "tangled.sh/tangled.sh/core/appview" 23 "tangled.sh/tangled.sh/core/appview/db" ··· 35 tidClock syntax.TIDClock 36 pages *pages.Pages 37 resolver *appview.Resolver 38 + posthog posthog.Client 39 jc *jetstream.JetstreamClient 40 config *appview.Config 41 } ··· 59 60 oauth := oauth.NewOAuth(d, config) 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 + 67 wrapper := db.DbWrapper{d} 68 jc, err := jetstream.NewJetstreamClient( 69 config.Jetstream.Endpoint, ··· 95 clock, 96 pgs, 97 resolver, 98 + posthog, 99 jc, 100 config, 101 } ··· 106 func TID(c *syntax.TIDClock) string { 107 return c.Next().String() 108 } 109 110 func (s *State) Logout(w http.ResponseWriter, r *http.Request) { 111 s.oauth.ClearSession(r, w) ··· 705 log.Println("failed to update ACLs", err) 706 http.Error(w, err.Error(), http.StatusInternalServerError) 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 + } 719 } 720 721 s.pages.HxLocation(w, fmt.Sprintf("/@%s/%s", user.Handle, repoName))
+1
go.mod
··· 24 github.com/lestrrat-go/jwx/v2 v2.0.12 25 github.com/mattn/go-sqlite3 v1.14.24 26 github.com/microcosm-cc/bluemonday v1.0.27 27 github.com/resend/resend-go/v2 v2.15.0 28 github.com/sethvargo/go-envconfig v1.1.0 29 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e
··· 24 github.com/lestrrat-go/jwx/v2 v2.0.12 25 github.com/mattn/go-sqlite3 v1.14.24 26 github.com/microcosm-cc/bluemonday v1.0.27 27 + github.com/posthog/posthog-go v1.5.5 28 github.com/resend/resend-go/v2 v2.15.0 29 github.com/sethvargo/go-envconfig v1.1.0 30 github.com/whyrusleeping/cbor-gen v0.2.1-0.20241030202151-b7a6831be65e
+2
go.sum
··· 224 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 225 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0= 226 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= 227 github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= 228 github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= 229 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
··· 224 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 225 github.com/polydawn/refmt v0.89.1-0.20221221234430-40501e09de1f h1:VXTQfuJj9vKR4TCkEuWIckKvdHFeJH/huIFJ9/cXOB0= 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= 229 github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= 230 github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= 231 github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=