From d26a2f403d7b425f972e73b85a7b111d7dd576a2 Mon Sep 17 00:00:00 2001 From: Hongbo Wu Date: Mon, 26 Feb 2024 16:47:04 +0800 Subject: [PATCH] replace segment with posthog --- packages/api/.env.test | 2 +- packages/api/package.json | 2 +- packages/api/src/resolvers/api_key/index.ts | 8 +- packages/api/src/resolvers/article/index.ts | 30 +++--- .../resolvers/article_saving_request/index.ts | 7 +- packages/api/src/resolvers/filters/index.ts | 4 +- packages/api/src/resolvers/highlight/index.ts | 8 +- .../importers/uploadImportFileResolver.ts | 4 +- .../api/src/resolvers/integrations/index.ts | 12 +-- packages/api/src/resolvers/labels/index.ts | 24 ++--- packages/api/src/resolvers/links/index.ts | 4 +- .../api/src/resolvers/newsletters/index.ts | 16 +-- .../src/resolvers/recommendations/index.ts | 14 +-- packages/api/src/resolvers/reminders/index.ts | 8 +- packages/api/src/resolvers/report/index.ts | 12 +-- packages/api/src/resolvers/save/index.ts | 12 +-- .../api/src/resolvers/subscriptions/index.ts | 16 +-- .../api/src/resolvers/upload_files/index.ts | 4 +- .../src/resolvers/user_device_tokens/index.ts | 8 +- packages/api/src/resolvers/webhooks/index.ts | 8 +- packages/api/src/routers/auth/apple_auth.ts | 8 +- packages/api/src/routers/auth/auth_router.ts | 18 ++-- .../api/src/routers/svc/email_attachment.ts | 8 +- packages/api/src/routers/svc/emails.ts | 8 +- packages/api/src/services/create_user.ts | 8 +- .../api/src/services/save_newsletter_email.ts | 4 +- .../api/src/services/user_device_tokens.ts | 8 +- packages/api/src/util.ts | 12 +-- packages/api/src/utils/analytics.ts | 4 +- packages/api/src/utils/sendNotification.ts | 8 +- yarn.lock | 97 ++++++------------- 31 files changed, 172 insertions(+), 214 deletions(-) diff --git a/packages/api/.env.test b/packages/api/.env.test index 52cc7075b..9620deb1a 100644 --- a/packages/api/.env.test +++ b/packages/api/.env.test @@ -24,6 +24,6 @@ GCS_UPLOAD_BUCKET= GCS_UPLOAD_SA_KEY_FILE_PATH= GCS_UPLOAD_PRIVATE_BUCKET= TWITTER_BEARER_TOKEN= -SEGMENT_WRITE_KEY='test' +POSTHOG_API_KEY='test' PUBSUB_VERIFICATION_TOKEN='123456' CONTENT_FETCH_URL=http://localhost:9090/ diff --git a/packages/api/package.json b/packages/api/package.json index 3316dca1f..1c45f926e 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -45,7 +45,6 @@ "@sentry/node": "^5.26.0", "@sentry/tracing": "^7.9.0", "addressparser": "^1.0.1", - "analytics-node": "^6.0.0", "apollo-datasource": "^3.3.1", "apollo-server-express": "^3.6.3", "axios": "^0.27.2", @@ -88,6 +87,7 @@ "oauth": "^0.10.0", "pg": "^8.3.3", "postgrator": "^4.2.0", + "posthog-node": "^3.6.3", "private-ip": "^2.3.3", "prom-client": "^15.1.0", "rss-parser": "^3.13.0", diff --git a/packages/api/src/resolvers/api_key/index.ts b/packages/api/src/resolvers/api_key/index.ts index 4ca0d4c3e..a0604f91f 100644 --- a/packages/api/src/resolvers/api_key/index.ts +++ b/packages/api/src/resolvers/api_key/index.ts @@ -52,8 +52,8 @@ export const generateApiKeyResolver = authorized< expiresAt: exp, }) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'api_key_generated', properties: { name, @@ -92,8 +92,8 @@ export const revokeApiKeyResolver = authorized< const deletedApiKey = await apiRepo.remove(apiKey) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'api_key_revoked', properties: { id, diff --git a/packages/api/src/resolvers/article/index.ts b/packages/api/src/resolvers/article/index.ts index 363f97f13..0f43d5a1c 100644 --- a/packages/api/src/resolvers/article/index.ts +++ b/packages/api/src/resolvers/article/index.ts @@ -152,8 +152,8 @@ export const createArticleResolver = authorized< }, { log, uid, pubsub } ) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'link_saved', properties: { url, @@ -491,7 +491,7 @@ export const getArticleResolver = authorized< // source: 'resolver', // resolver: 'setShareArticleResolver', // articleId: article.id, -// userId: uid, +// distinctId: uid, // }, // }) @@ -535,8 +535,8 @@ export const setBookmarkArticleResolver = authorized< // delete the item and its metadata const deletedLibraryItem = await softDeleteLibraryItem(articleID, uid, pubsub) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'link_removed', properties: { id: articleID, @@ -833,8 +833,8 @@ export const bulkActionResolver = authorized< { uid, log } ) => { try { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'BulkAction', properties: { env: env.server.apiEnv, @@ -891,8 +891,8 @@ export const setFavoriteArticleResolver = authorized< MutationSetFavoriteArticleArgs >(async (_, { id }, { uid, log }) => { try { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'setFavoriteArticle', properties: { env: env.server.apiEnv, @@ -928,8 +928,8 @@ export const moveToFolderResolver = authorized< MoveToFolderError, MutationMoveToFolderArgs >(async (_, { id, folder }, { authTrx, log, pubsub, uid }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'move_to_folder', properties: { id, @@ -1002,8 +1002,8 @@ export const fetchContentResolver = authorized< FetchContentError, MutationFetchContentArgs >(async (_, { id }, { authTrx, uid, log, pubsub }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'fetch_content', properties: { id, @@ -1052,8 +1052,8 @@ export const emptyTrashResolver = authorized< EmptyTrashSuccess, EmptyTrashError >(async (_, __, { uid }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'empty_trash', }) diff --git a/packages/api/src/resolvers/article_saving_request/index.ts b/packages/api/src/resolvers/article_saving_request/index.ts index b42171f6f..467c00cc5 100644 --- a/packages/api/src/resolvers/article_saving_request/index.ts +++ b/packages/api/src/resolvers/article_saving_request/index.ts @@ -18,13 +18,12 @@ import { findLibraryItemByUrl, } from '../../services/library_item' import { analytics } from '../../utils/analytics' +import { authorized } from '../../utils/gql-utils' import { cleanUrl, isParsingTimeout, libraryItemToArticleSavingRequest, } from '../../utils/helpers' -import { authorized } from '../../utils/gql-utils' - import { isErrorWithCode } from '../user' export const createArticleSavingRequestResolver = authorized< @@ -32,8 +31,8 @@ export const createArticleSavingRequestResolver = authorized< CreateArticleSavingRequestError, MutationCreateArticleSavingRequestArgs >(async (_, { input: { url } }, { uid, pubsub, log }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'link_saved', properties: { url: url, diff --git a/packages/api/src/resolvers/filters/index.ts b/packages/api/src/resolvers/filters/index.ts index b31760f7e..f0b0db872 100644 --- a/packages/api/src/resolvers/filters/index.ts +++ b/packages/api/src/resolvers/filters/index.ts @@ -244,8 +244,8 @@ export const moveFilterResolver = authorized< } } - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'filter_moved', properties: { filterId, diff --git a/packages/api/src/resolvers/highlight/index.ts b/packages/api/src/resolvers/highlight/index.ts index c384298d4..8e0f22c9e 100644 --- a/packages/api/src/resolvers/highlight/index.ts +++ b/packages/api/src/resolvers/highlight/index.ts @@ -59,8 +59,8 @@ export const createHighlightResolver = authorized< pubsub ) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'highlight_created', properties: { libraryItemId: input.articleId, @@ -143,8 +143,8 @@ export const mergeHighlightResolver = authorized< pubsub ) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'highlight_created', properties: { libraryItemId: input.articleId, diff --git a/packages/api/src/resolvers/importers/uploadImportFileResolver.ts b/packages/api/src/resolvers/importers/uploadImportFileResolver.ts index fdb39e7d9..0a1c0dd53 100644 --- a/packages/api/src/resolvers/importers/uploadImportFileResolver.ts +++ b/packages/api/src/resolvers/importers/uploadImportFileResolver.ts @@ -47,8 +47,8 @@ export const uploadImportFileResolver = authorized< } } - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'upload_import_file', properties: { type, diff --git a/packages/api/src/resolvers/integrations/index.ts b/packages/api/src/resolvers/integrations/index.ts index 145c9ec81..90c118129 100644 --- a/packages/api/src/resolvers/integrations/index.ts +++ b/packages/api/src/resolvers/integrations/index.ts @@ -117,8 +117,8 @@ export const setIntegrationResolver = authorized< integration.taskName = null } - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'integration_set', properties: { id: integrationToSave.id, @@ -182,8 +182,8 @@ export const deleteIntegrationResolver = authorized< const deletedIntegration = await removeIntegration(integration, uid) deletedIntegration.id = id - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'integration_delete', properties: { integrationId: deletedIntegration.id, @@ -238,8 +238,8 @@ export const importFromIntegrationResolver = authorized< // update task name in integration await updateIntegration(integration.id, { taskName }, uid) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'integration_import', properties: { integrationId, diff --git a/packages/api/src/resolvers/labels/index.ts b/packages/api/src/resolvers/labels/index.ts index cf9d6e3b6..d71e224ba 100644 --- a/packages/api/src/resolvers/labels/index.ts +++ b/packages/api/src/resolvers/labels/index.ts @@ -61,8 +61,8 @@ export const labelsResolver = authorized( }) }) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'labels', properties: { env: env.server.apiEnv, @@ -95,8 +95,8 @@ export const createLabelResolver = authorized< return tx.withRepository(labelRepository).createLabel(input, uid) }) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'label_created', properties: { ...input, @@ -128,8 +128,8 @@ export const deleteLabelResolver = authorized< } } - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'label_deleted', properties: { labelId, @@ -207,8 +207,8 @@ export const setLabelsResolver = authorized< // save labels in the library item await saveLabelsInLibraryItem(labelsSet, pageId, uid, labelSource, pubsub) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'labels_set', properties: { pageId, @@ -282,8 +282,8 @@ export const setLabelsForHighlightResolver = authorized< // save labels in the highlight await saveLabelsInHighlight(labelsSet, input.highlightId, uid, pubsub) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'labels_set_for_highlight', properties: { highlightId, @@ -374,8 +374,8 @@ export const moveLabelResolver = authorized< } } - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'label_moved', properties: { labelId, diff --git a/packages/api/src/resolvers/links/index.ts b/packages/api/src/resolvers/links/index.ts index 657866d0c..e3b30fa13 100644 --- a/packages/api/src/resolvers/links/index.ts +++ b/packages/api/src/resolvers/links/index.ts @@ -65,8 +65,8 @@ export const setLinkArchivedResolver = authorized< event = 'link_unarchived' } - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event, properties: { env: env.server.apiEnv, diff --git a/packages/api/src/resolvers/newsletters/index.ts b/packages/api/src/resolvers/newsletters/index.ts index 0415041eb..1e0a0da43 100644 --- a/packages/api/src/resolvers/newsletters/index.ts +++ b/packages/api/src/resolvers/newsletters/index.ts @@ -40,10 +40,10 @@ export const createNewsletterEmailResolver = authorized< CreateNewsletterEmailSuccessPartial, CreateNewsletterEmailError, MutationCreateNewsletterEmailArgs ->(async (_parent, { input }, { claims, log }) => { +>(async (_parent, { input }, { uid, log }) => { log.info('createNewsletterEmailResolver') - analytics.track({ - userId: claims.uid, + analytics.capture({ + distinctId: uid, event: 'newsletter_email_address_created', properties: { env: env.server.apiEnv, @@ -52,7 +52,7 @@ export const createNewsletterEmailResolver = authorized< try { const newsletterEmail = await createNewsletterEmail( - claims.uid, + uid, undefined, input?.folder || DEFAULT_NEWSLETTER_FOLDER, input?.name || undefined, @@ -103,8 +103,8 @@ export const deleteNewsletterEmailResolver = authorized< DeleteNewsletterEmailError, MutationDeleteNewsletterEmailArgs >(async (_parent, args, { uid, log }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'newsletter_email_address_deleted', properties: { env: env.server.apiEnv, @@ -158,8 +158,8 @@ export const updateNewsletterEmailResolver = authorized< UpdateNewsletterEmailError, MutationUpdateNewsletterEmailArgs >(async (_parent, { input }, { uid, log }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'newsletter_email_updated', properties: { env: env.server.apiEnv, diff --git a/packages/api/src/resolvers/recommendations/index.ts b/packages/api/src/resolvers/recommendations/index.ts index a23a573e2..061db3728 100644 --- a/packages/api/src/resolvers/recommendations/index.ts +++ b/packages/api/src/resolvers/recommendations/index.ts @@ -40,8 +40,8 @@ import { import { findLibraryItemById } from '../../services/library_item' import { analytics } from '../../utils/analytics' import { enqueueRecommendation } from '../../utils/createTask' -import { userDataToUser } from '../../utils/helpers' import { authorized } from '../../utils/gql-utils' +import { userDataToUser } from '../../utils/helpers' export const createGroupResolver = authorized< CreateGroupSuccess, @@ -67,8 +67,8 @@ export const createGroupResolver = authorized< onlyAdminCanSeeMembers: input.onlyAdminCanSeeMembers, }) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'group_created', properties: { group_id: group.id, @@ -213,8 +213,8 @@ export const joinGroupResolver = authorized< const group = await joinGroup(user, inviteCode) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'group_joined', properties: { group_id: group.id, @@ -319,8 +319,8 @@ export const leaveGroupResolver = authorized< const success = await leaveGroup(user, groupId) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'group_left', properties: { group_id: groupId, diff --git a/packages/api/src/resolvers/reminders/index.ts b/packages/api/src/resolvers/reminders/index.ts index 4f311e6e8..e48b5e6a8 100644 --- a/packages/api/src/resolvers/reminders/index.ts +++ b/packages/api/src/resolvers/reminders/index.ts @@ -37,7 +37,7 @@ const validScheduleTime = (str: string): Date | undefined => { // } // } -// analytics.track({ +// analytics.capture({ // userId: uid, // event: 'reminder_created', // properties: { @@ -123,7 +123,7 @@ const validScheduleTime = (str: string): Date | undefined => { // >(async (_, { linkId: pageId }, { models, claims: { uid }, log }) => { // log.info('reminderResolver') -// analytics.track({ +// analytics.capture({ // userId: uid, // event: 'reminder', // properties: { @@ -191,7 +191,7 @@ const validScheduleTime = (str: string): Date | undefined => { // } // } -// analytics.track({ +// analytics.capture({ // userId: uid, // event: 'reminder_updated', // properties: { @@ -265,7 +265,7 @@ const validScheduleTime = (str: string): Date | undefined => { // >(async (_, { id }, { models, claims: { uid }, log, authTrx }) => { // log.info('deleteReminderResolver') -// analytics.track({ +// analytics.capture({ // userId: uid, // event: 'reminder_deleted', // properties: { diff --git a/packages/api/src/resolvers/report/index.ts b/packages/api/src/resolvers/report/index.ts index edec570fd..6ccbf76a5 100644 --- a/packages/api/src/resolvers/report/index.ts +++ b/packages/api/src/resolvers/report/index.ts @@ -1,5 +1,5 @@ /* eslint-disable @typescript-eslint/no-floating-promises */ -import { WithDataSourcesContext } from '../types' +import { env } from '../../env' import { MutationReportItemArgs, ReportItemResult, @@ -11,7 +11,7 @@ import { saveContentDisplayReport, } from '../../services/reports' import { analytics } from '../../utils/analytics' -import { env } from '../../env' +import { WithDataSourcesContext } from '../types' const SUCCESS_MESSAGE = `Your report has been submitted. Thank you.` const FAILURE_MESSAGE = @@ -42,8 +42,8 @@ export const reportItemResolver: ResolverFn< const { sharedBy, reportTypes } = args.input if (sharedBy && isAbuseReport(reportTypes)) { - analytics.track({ - userId: sharedBy, + analytics.capture({ + distinctId: sharedBy, event: 'report_created', properties: { type: 'abuse', @@ -70,8 +70,8 @@ export const reportItemResolver: ResolverFn< } } - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'report_created', properties: { type: 'content', diff --git a/packages/api/src/resolvers/save/index.ts b/packages/api/src/resolvers/save/index.ts index 5c22c86d1..36a46b6f1 100644 --- a/packages/api/src/resolvers/save/index.ts +++ b/packages/api/src/resolvers/save/index.ts @@ -19,8 +19,8 @@ export const savePageResolver = authorized< SaveError, MutationSavePageArgs >(async (_, { input }, { uid }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'link_saved', properties: { url: input.url, @@ -43,8 +43,8 @@ export const saveUrlResolver = authorized< SaveError, MutationSaveUrlArgs >(async (_, { input }, { uid }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'link_saved', properties: { url: input.url, @@ -67,8 +67,8 @@ export const saveFileResolver = authorized< SaveError, MutationSaveFileArgs >(async (_, { input }, { uid }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'link_saved', properties: { url: input.url, diff --git a/packages/api/src/resolvers/subscriptions/index.ts b/packages/api/src/resolvers/subscriptions/index.ts index bc10867fb..82eae37cf 100644 --- a/packages/api/src/resolvers/subscriptions/index.ts +++ b/packages/api/src/resolvers/subscriptions/index.ts @@ -153,8 +153,8 @@ export const unsubscribeResolver = authorized< await unsubscribe(subscription) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'unsubscribed', properties: { name, @@ -183,8 +183,8 @@ export const subscribeResolver = authorized< MutationSubscribeArgs >(async (_, { input }, { uid, log }) => { try { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'subscribed', properties: { ...input, @@ -323,8 +323,8 @@ export const updateSubscriptionResolver = authorized< MutationUpdateSubscriptionArgs >(async (_, { input }, { uid, log }) => { try { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'update_subscription', properties: { ...input, @@ -401,8 +401,8 @@ export const scanFeedsResolver = authorized< ScanFeedsError, QueryScanFeedsArgs >(async (_, { input: { opml, url } }, { log, uid }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'scan_feeds', properties: { opml, diff --git a/packages/api/src/resolvers/upload_files/index.ts b/packages/api/src/resolvers/upload_files/index.ts index 74caee344..ba80a0a6d 100644 --- a/packages/api/src/resolvers/upload_files/index.ts +++ b/packages/api/src/resolvers/upload_files/index.ts @@ -12,8 +12,8 @@ export const uploadFileRequestResolver = authorized< UploadFileRequestError, MutationUploadFileRequestArgs >(async (_, { input }, { uid }) => { - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'file_upload_request', properties: { url: input.url, diff --git a/packages/api/src/resolvers/user_device_tokens/index.ts b/packages/api/src/resolvers/user_device_tokens/index.ts index 8fcc4d868..19629e9e9 100644 --- a/packages/api/src/resolvers/user_device_tokens/index.ts +++ b/packages/api/src/resolvers/user_device_tokens/index.ts @@ -60,8 +60,8 @@ export const setDeviceTokenResolver = authorized< } } - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'device_token_deleted', properties: { id: deviceToken.id, @@ -77,8 +77,8 @@ export const setDeviceTokenResolver = authorized< // create token const deviceToken = await createDeviceToken(uid, token) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'device_token_created', properties: { id: deviceToken.id, diff --git a/packages/api/src/resolvers/webhooks/index.ts b/packages/api/src/resolvers/webhooks/index.ts index a164797f3..925ea13e7 100644 --- a/packages/api/src/resolvers/webhooks/index.ts +++ b/packages/api/src/resolvers/webhooks/index.ts @@ -85,8 +85,8 @@ export const deleteWebhookResolver = authorized< try { const webhook = await deleteWebhook(id, uid) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'webhook_delete', properties: { webhookId: id, @@ -144,8 +144,8 @@ export const setWebhookResolver = authorized< }) ) - analytics.track({ - userId: uid, + analytics.capture({ + distinctId: uid, event: 'webhook_set', properties: { webhookId: webhook.id, diff --git a/packages/api/src/routers/auth/apple_auth.ts b/packages/api/src/routers/auth/apple_auth.ts index 99590ff4d..e9dfaf0b3 100644 --- a/packages/api/src/routers/auth/apple_auth.ts +++ b/packages/api/src/routers/auth/apple_auth.ts @@ -3,9 +3,11 @@ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ import * as jwt from 'jsonwebtoken' import jwksClient from 'jwks-rsa' +import { StatusType } from '../../entity/user' import { env, homePageURL } from '../../env' import { LoginErrorCode } from '../../generated/graphql' import { userRepository } from '../../repository/user' +import { analytics } from '../../utils/analytics' import { logger } from '../../utils/logger' import { createSsoToken, ssoRedirectURL } from '../../utils/sso' import { DecodeTokenResult } from './auth_types' @@ -14,8 +16,6 @@ import { createWebAuthToken, suggestedUsername, } from './jwt_helpers' -import { analytics } from '../../utils/analytics' -import { StatusType } from '../../entity/user' const appleBaseURL = 'https://appleid.apple.com' const audienceName = 'app.omnivore.app' @@ -147,8 +147,8 @@ export async function handleAppleWebAuth( ? ssoRedirectURL(ssoToken) : `${baseURL()}/home` - analytics.track({ - userId: user.id, + analytics.capture({ + distinctId: user.id, event: 'login', properties: { method: 'apple', diff --git a/packages/api/src/routers/auth/auth_router.ts b/packages/api/src/routers/auth/auth_router.ts index 7779b9af8..5a48e9634 100644 --- a/packages/api/src/routers/auth/auth_router.ts +++ b/packages/api/src/routers/auth/auth_router.ts @@ -11,6 +11,7 @@ import axios from 'axios' import cors from 'cors' import type { Request, Response } from 'express' import express from 'express' +import rateLimit from 'express-rate-limit' import * as jwt from 'jsonwebtoken' import url from 'url' import { promisify } from 'util' @@ -46,7 +47,6 @@ import { } from './google_auth' import { createWebAuthToken } from './jwt_helpers' import { createMobileAccountCreationResponse } from './mobile/account_creation' -import rateLimit from 'express-rate-limit' export interface SignupRequest { email: string @@ -328,8 +328,8 @@ export function authRouter() { ) } - analytics.track({ - userId: user.id, + analytics.capture({ + distinctId: user.id, event: 'login', properties: { method: 'google', @@ -463,8 +463,8 @@ export function authRouter() { ) } - analytics.track({ - userId: user.id, + analytics.capture({ + distinctId: user.id, event: 'login', properties: { method: 'email', @@ -576,8 +576,8 @@ export function authRouter() { } } - analytics.track({ - userId: user.id, + analytics.capture({ + distinctId: user.id, event: 'login', properties: { method: 'email_verification', @@ -708,8 +708,8 @@ export function authRouter() { ) } - analytics.track({ - userId: user.id, + analytics.capture({ + distinctId: user.id, event: 'login', properties: { method: 'password_reset', diff --git a/packages/api/src/routers/svc/email_attachment.ts b/packages/api/src/routers/svc/email_attachment.ts index 9f6e2ee0a..2cf88f726 100644 --- a/packages/api/src/routers/svc/email_attachment.ts +++ b/packages/api/src/routers/svc/email_attachment.ts @@ -49,8 +49,8 @@ export function emailAttachmentRouter() { const user = newsletterEmail.user - analytics.track({ - userId: user.id, + analytics.capture({ + distinctId: user.id, event: 'email_attachment_upload', properties: { env: env.server.apiEnv, @@ -116,8 +116,8 @@ export function emailAttachmentRouter() { const user = newsletterEmail.user - analytics.track({ - userId: user.id, + analytics.capture({ + distinctId: user.id, event: 'email_attachment_create_article', properties: { env: env.server.apiEnv, diff --git a/packages/api/src/routers/svc/emails.ts b/packages/api/src/routers/svc/emails.ts index 3d66ee5c9..3d5075cd1 100644 --- a/packages/api/src/routers/svc/emails.ts +++ b/packages/api/src/routers/svc/emails.ts @@ -99,8 +99,8 @@ export function emailsServiceRouter() { return } - analytics.track({ - userId: user.id, + analytics.capture({ + distinctId: user.id, event: 'non_newsletter_email_received', properties: { env: env.server.apiEnv, @@ -168,8 +168,8 @@ export function emailsServiceRouter() { user.id ) - analytics.track({ - userId: user.id, + analytics.capture({ + distinctId: user.id, event: 'received_email_saved', properties: { env: env.server.apiEnv, diff --git a/packages/api/src/services/create_user.ts b/packages/api/src/services/create_user.ts index fdeb188c2..062dcccad 100644 --- a/packages/api/src/services/create_user.ts +++ b/packages/api/src/services/create_user.ts @@ -48,8 +48,8 @@ export const createUser = async (input: { user: existingUser, }) - analytics.track({ - userId: existingUser.id, + analytics.capture({ + distinctId: existingUser.id, event: 'create_user', properties: { env: env.server.apiEnv, @@ -131,8 +131,8 @@ export const createUser = async (input: { profile.username ) - analytics.track({ - userId: user.id, + analytics.capture({ + distinctId: user.id, event: 'create_user', properties: { env: env.server.apiEnv, diff --git a/packages/api/src/services/save_newsletter_email.ts b/packages/api/src/services/save_newsletter_email.ts index 8bbde8820..08d6fa67b 100644 --- a/packages/api/src/services/save_newsletter_email.ts +++ b/packages/api/src/services/save_newsletter_email.ts @@ -28,8 +28,8 @@ export const saveNewsletter = async ( newsletterEmail: NewsletterEmail, existingSubscription?: Subscription ): Promise => { - analytics.track({ - userId: newsletterEmail.user.id, + analytics.capture({ + distinctId: newsletterEmail.user.id, event: 'newsletter_email_received', properties: { url: data.url, diff --git a/packages/api/src/services/user_device_tokens.ts b/packages/api/src/services/user_device_tokens.ts index 180fca7cf..a8d8821e1 100644 --- a/packages/api/src/services/user_device_tokens.ts +++ b/packages/api/src/services/user_device_tokens.ts @@ -47,8 +47,8 @@ export const createDeviceToken = async ( userId: string, token: string ): Promise => { - analytics.track({ - userId: userId, + analytics.capture({ + distinctId: userId, event: 'device_token_created', properties: { env: env.server.apiEnv, @@ -70,8 +70,8 @@ export const deleteDeviceToken = async ( id: string, userId: string ): Promise => { - analytics.track({ - userId: userId, + analytics.capture({ + distinctId: userId, event: 'device_token_deleted', properties: { env: env.server.apiEnv, diff --git a/packages/api/src/util.ts b/packages/api/src/util.ts index 3f1570c26..b03a19c15 100755 --- a/packages/api/src/util.ts +++ b/packages/api/src/util.ts @@ -40,8 +40,8 @@ export interface BackendEnv { secret: string } } - segment: { - writeKey: string + posthog: { + apiKey: string } intercom: { token: string @@ -136,7 +136,7 @@ const nullableEnvVars = [ 'GAUTH_ANDROID_CLIENT_ID', 'GAUTH_CLIENT_ID', 'GAUTH_SECRET', - 'SEGMENT_WRITE_KEY', + 'POSTHOG_API_KEY', 'TWITTER_BEARER_TOKEN', 'GCS_UPLOAD_PRIVATE_BUCKET', 'SENDER_MESSAGE', @@ -226,8 +226,8 @@ export function getEnv(): BackendEnv { secret: parse('GAUTH_SECRET'), }, } - const segment = { - writeKey: parse('SEGMENT_WRITE_KEY'), + const posthog = { + apiKey: parse('POSTHOG_API_KEY'), } const intercom = { token: parse('INTERCOM_TOKEN'), @@ -317,7 +317,7 @@ export function getEnv(): BackendEnv { client, server, google, - segment, + posthog, intercom, sentry, jaeger, diff --git a/packages/api/src/utils/analytics.ts b/packages/api/src/utils/analytics.ts index f2992c474..8b3717cb8 100644 --- a/packages/api/src/utils/analytics.ts +++ b/packages/api/src/utils/analytics.ts @@ -1,4 +1,4 @@ -import Analytics = require('analytics-node') +import { PostHog } from 'posthog-node' import { env } from '../env' -export const analytics = new Analytics(env.segment.writeKey || 'test') +export const analytics = new PostHog(env.posthog.apiKey || 'test') diff --git a/packages/api/src/utils/sendNotification.ts b/packages/api/src/utils/sendNotification.ts index 26adf675c..07f19fd37 100644 --- a/packages/api/src/utils/sendNotification.ts +++ b/packages/api/src/utils/sendNotification.ts @@ -22,8 +22,8 @@ export const sendPushNotification = async ( type: PushNotificationType ): Promise => { try { - analytics.track({ - userId, + analytics.capture({ + distinctId: userId, event: 'notification_sent', properties: { type, @@ -49,8 +49,8 @@ export const sendMulticastPushNotifications = async ( type: PushNotificationType ): Promise => { try { - analytics.track({ - userId, + analytics.capture({ + distinctId: userId, event: 'notification_sent', properties: { type, diff --git a/yarn.lock b/yarn.lock index ffa3ed7f5..b442a9303 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5542,14 +5542,6 @@ dependencies: any-observable "^0.3.0" -"@segment/loosely-validate-event@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@segment/loosely-validate-event/-/loosely-validate-event-2.0.0.tgz#87dfc979e5b4e7b82c5f1d8b722dfd5d77644681" - integrity sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw== - dependencies: - component-type "^1.2.1" - join-component "^1.1.0" - "@selderee/plugin-htmlparser2@^0.6.0": version "0.6.0" resolved "https://registry.yarnpkg.com/@selderee/plugin-htmlparser2/-/plugin-htmlparser2-0.6.0.tgz#27e994afd1c2cb647ceb5406a185a5574188069d" @@ -9033,20 +9025,6 @@ ajv@^8.11.0: require-from-string "^2.0.2" uri-js "^4.2.2" -analytics-node@^6.0.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/analytics-node/-/analytics-node-6.2.0.tgz#8ae2ebc73d85e5b2aac8d366b974ad36996f629d" - integrity sha512-NLU4tCHlWt0tzEaFQL7NIoWhq2KmQSmz0JvyS2lYn6fc4fEjTMSabhJUx8H1r5995FX8fE3rZ15uIHU6u+ovlQ== - dependencies: - "@segment/loosely-validate-event" "^2.0.0" - axios "^0.27.2" - axios-retry "3.2.0" - lodash.isstring "^4.0.1" - md5 "^2.2.1" - ms "^2.0.0" - remove-trailing-slash "^0.1.0" - uuid "^8.3.2" - ansi-align@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.0.tgz#b536b371cf687caaef236c18d3e21fe3797467cb" @@ -9961,13 +9939,6 @@ axe-core@^4.6.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.8.2.tgz#2f6f3cde40935825cf4465e3c1c9e77b240ff6ae" integrity sha512-/dlp0fxyM3R8YW7MFzaHWXrf4zzbr0vaYb23VBFCl83R7nWNPg/yaQw2Dc8jzCMmDVLhSdzH8MjrsuIUuvX+6g== -axios-retry@3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/axios-retry/-/axios-retry-3.2.0.tgz#eb48e72f90b177fde62329b2896aa8476cfb90ba" - integrity sha512-RK2cLMgIsAQBDhlIsJR5dOhODPigvel18XUv1dDXW+4k1FzebyfRk+C+orot6WPZOYFKSfhLwHPwVmTVOODQ5w== - dependencies: - is-retry-allowed "^1.1.0" - axios@^0.24.0: version "0.24.0" resolved "https://registry.yarnpkg.com/axios/-/axios-0.24.0.tgz#804e6fa1e4b9c5288501dd9dff56a7a0940d20d6" @@ -10017,6 +9988,15 @@ axios@^1.4.0: form-data "^4.0.0" proxy-from-env "^1.1.0" +axios@^1.6.2: + version "1.6.7" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.6.7.tgz#7b48c2e27c96f9c68a2f8f31e2ab19f59b06b0a7" + integrity sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA== + dependencies: + follow-redirects "^1.15.4" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + axobject-query@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" @@ -11308,11 +11288,6 @@ chardet@^0.7.0: resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== -charenc@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667" - integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc= - check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" @@ -11923,11 +11898,6 @@ component-emitter@^1.2.1, component-emitter@^1.3.0: resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== -component-type@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/component-type/-/component-type-1.2.1.tgz#8a47901700238e4fc32269771230226f24b415a9" - integrity sha1-ikeQFwAjjk/DIml3EjAibyS0Fak= - compressible@^2.0.12, compressible@~2.0.16: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -12511,11 +12481,6 @@ cross-undici-fetch@^0.1.19: undici "^4.9.3" web-streams-polyfill "^3.2.0" -crypt@0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b" - integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs= - crypto-browserify@^3.11.0: version "3.12.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec" @@ -15432,6 +15397,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.4, follow-redirects@^1.14.8, fol resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf" integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw== +follow-redirects@^1.15.4: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + for-each@^0.3.3: version "0.3.3" resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e" @@ -17852,7 +17822,7 @@ is-boolean-object@^1.1.0: call-bind "^1.0.2" has-tostringtag "^1.0.0" -is-buffer@^1.0.2, is-buffer@^1.1.5, is-buffer@~1.1.6: +is-buffer@^1.0.2, is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== @@ -18263,11 +18233,6 @@ is-relative@^1.0.0: dependencies: is-unc-path "^1.0.0" -is-retry-allowed@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4" - integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg== - is-set@^2.0.1, is-set@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.2.tgz#90755fa4c2562dc1c5d4024760d6119b94ca18ec" @@ -19158,11 +19123,6 @@ jest@^27.4.5: import-local "^3.0.2" jest-cli "^27.5.1" -join-component@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/join-component/-/join-component-1.1.0.tgz#b8417b750661a392bee2c2537c68b2a9d4977cd5" - integrity sha1-uEF7dQZho5K+4sJTfGiyqdSXfNU= - jose@^2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/jose/-/jose-2.0.6.tgz#894ba19169af339d3911be933f913dd02fc57c7c" @@ -20877,15 +20837,6 @@ md5.js@^1.3.4: inherits "^2.0.1" safe-buffer "^5.1.2" -md5@^2.2.1: - version "2.3.0" - resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f" - integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g== - dependencies: - charenc "0.0.2" - crypt "0.0.2" - is-buffer "~1.1.6" - mdast-squeeze-paragraphs@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/mdast-squeeze-paragraphs/-/mdast-squeeze-paragraphs-4.0.0.tgz#7c4c114679c3bee27ef10b58e2e015be79f1ef97" @@ -24531,6 +24482,14 @@ posthog-js@^1.78.2: dependencies: fflate "^0.4.1" +posthog-node@^3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/posthog-node/-/posthog-node-3.6.3.tgz#4d3a2a4385e01c4d9e91d01dbde104e60285853d" + integrity sha512-JB+ei0LkwE+rKHyW5z79Nd1jUaGxU6TvkfjFqY9vQaHxU5aU8dRl0UUaEmZdZbHwjp3WmXCBQQRNyimwbNQfCw== + dependencies: + axios "^1.6.2" + rusha "^0.8.14" + prebuild-install@^7.1.1: version "7.1.1" resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" @@ -26533,11 +26492,6 @@ remove-trailing-separator@^1.0.1: resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= -remove-trailing-slash@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz#be2285a59f39c74d1bce4f825950061915e3780d" - integrity sha512-o4S4Qh6L2jpnCy83ysZDau+VORNvnFw07CKSAymkd6ICNVEPisMyzlc00KlvvicsxKck94SEwhDnMNdICzO+tA== - remove-trailing-spaces@^1.0.6: version "1.0.7" resolved "https://registry.yarnpkg.com/remove-trailing-spaces/-/remove-trailing-spaces-1.0.7.tgz#491f04e11d98880714d12429b0d0938cbe030ae6" @@ -26858,6 +26812,11 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" +rusha@^0.8.14: + version "0.8.14" + resolved "https://registry.yarnpkg.com/rusha/-/rusha-0.8.14.tgz#a977d0de9428406138b7bb90d3de5dcd024e2f68" + integrity sha512-cLgakCUf6PedEu15t8kbsjnwIFFR2D4RfL+W3iWFJ4iac7z4B0ZI8fxy4R3J956kAI68HclCFGL8MPoUVC3qVA== + rxjs@^6.3.3, rxjs@^6.4.0, rxjs@^6.6.0, rxjs@^6.6.3: version "6.6.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9"