1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-09 07:25:19 +02:00

Merge pull request #35 from MichaelDeBoey/remove-convertKit-usage

feat: remove ConvertKit usage
This commit is contained in:
Josh Pigford 2024-01-11 18:11:55 -06:00 committed by GitHub
commit f346ecb3a0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 0 additions and 247 deletions

View file

@ -22,4 +22,3 @@ AWS_SESSION_TOKEN=
NX_PLAID_SECRET= NX_PLAID_SECRET=
NX_FINICITY_APP_KEY= NX_FINICITY_APP_KEY=
NX_FINICITY_PARTNER_SECRET= NX_FINICITY_PARTNER_SECRET=
NX_CONVERTKIT_SECRET=

View file

@ -75,7 +75,6 @@ jobs:
NX_STRIPE_SECRET_KEY=${{ secrets.NX_STRIPE_SECRET_KEY }} NX_STRIPE_SECRET_KEY=${{ secrets.NX_STRIPE_SECRET_KEY }}
NX_STRIPE_WEBHOOK_SECRET=${{ secrets.NX_STRIPE_WEBHOOK_SECRET }} NX_STRIPE_WEBHOOK_SECRET=${{ secrets.NX_STRIPE_WEBHOOK_SECRET }}
NX_PLAID_WEBHOOK_URL=none NX_PLAID_WEBHOOK_URL=none
NX_CONVERTKIT_SECRET=none
NX_DATABASE_URL=postgresql://maybe:maybe@localhost:5432/maybe_local?connection_limit=32&pool_timeout=20 NX_DATABASE_URL=postgresql://maybe:maybe@localhost:5432/maybe_local?connection_limit=32&pool_timeout=20
NX_REDIS_URL=redis://localhost:6379 NX_REDIS_URL=redis://localhost:6379
EOF EOF

View file

@ -42,7 +42,6 @@ import {
securitiesRouter, securitiesRouter,
plansRouter, plansRouter,
toolsRouter, toolsRouter,
notificationsRouter,
publicRouter, publicRouter,
e2eRouter, e2eRouter,
} from './routes' } from './routes'
@ -167,7 +166,6 @@ app.use('/v1/transactions', transactionsRouter)
app.use('/v1/holdings', holdingsRouter) app.use('/v1/holdings', holdingsRouter)
app.use('/v1/securities', securitiesRouter) app.use('/v1/securities', securitiesRouter)
app.use('/v1/plans', plansRouter) app.use('/v1/plans', plansRouter)
app.use('/v1/notifications', notificationsRouter)
// Sentry must be the *first* handler // Sentry must be the *first* handler
app.use(identifySentryUser) app.use(identifySentryUser)

View file

@ -1,66 +0,0 @@
import type { SharedType } from '@maybe-finance/shared'
import type { AxiosInstance } from 'axios'
import axios from 'axios'
import env from '../../env'
class ConvertKitApi {
private axios: AxiosInstance
constructor(private readonly apiSecret: string) {
this.axios = axios.create({
baseURL: 'https://api.convertkit.com/v3',
})
}
async getSubscription(subscriberId: number | null) {
// Until we have the id stored in DB, assume no subscription
if (!subscriberId) {
return {
isSubscribed: false,
}
}
const res = await this.axios.get<{ subscriber: SharedType.ConvertKitSubscriber }>(
`/subscribers/${subscriberId}`,
{
params: {
api_secret: this.apiSecret,
},
}
)
return {
isSubscribed: res.data ? res.data.subscriber.state === 'active' : false,
subscriber: res.data.subscriber,
}
}
async subscribe(email: string) {
const res = await this.axios.post<{ subscription: SharedType.ConvertKitSubscription }>(
'/forms/2279973/subscribe', // The main mailing list ID
{
api_secret: this.apiSecret,
email,
}
)
return res.data
}
async unsubscribe(email: string) {
const res = await this.axios.put<{ subscriber: SharedType.ConvertKitSubscriber }>(
'/unsubscribe',
{
api_secret: this.apiSecret,
email,
}
)
return res.data
}
}
// Prevent multiple instances of S3 client
const convertKit = new ConvertKitApi(env.NX_CONVERTKIT_SECRET)
export default convertKit

View file

@ -55,7 +55,6 @@ import prisma from './prisma'
import plaid, { getPlaidWebhookUrl } from './plaid' import plaid, { getPlaidWebhookUrl } from './plaid'
import finicity, { getFinicityTxPushUrl, getFinicityWebhookUrl } from './finicity' import finicity, { getFinicityTxPushUrl, getFinicityWebhookUrl } from './finicity'
import stripe from './stripe' import stripe from './stripe'
import convertKit from './convertKit'
import postmark from './postmark' import postmark from './postmark'
import { managementClient } from './auth0' import { managementClient } from './auth0'
import s3 from './s3' import s3 from './s3'
@ -315,7 +314,6 @@ export async function createContext(req: Request) {
prisma, prisma,
plaid, plaid,
stripe, stripe,
convertKit,
s3, s3,
secretsClient, secretsClient,
managementClient, managementClient,

View file

@ -13,6 +13,5 @@ export { default as holdingsRouter } from './holdings.router'
export { default as securitiesRouter } from './securities.router' export { default as securitiesRouter } from './securities.router'
export { default as plansRouter } from './plans.router' export { default as plansRouter } from './plans.router'
export { default as toolsRouter } from './tools.router' export { default as toolsRouter } from './tools.router'
export { default as notificationsRouter } from './notifications.router'
export { default as publicRouter } from './public.router' export { default as publicRouter } from './public.router'
export { default as e2eRouter } from './e2e.router' export { default as e2eRouter } from './e2e.router'

View file

@ -1,43 +0,0 @@
import { Router } from 'express'
import endpoint from '../lib/endpoint'
const router = Router()
router.get(
'/convertkit/subscription',
endpoint.create({
resolve: async ({ ctx }) => {
return ctx.convertKit.getSubscription(ctx.user!.convertKitId)
},
})
)
router.post(
'/convertkit/subscribe',
endpoint.create({
resolve: async ({ ctx }) => {
const auth0User = await ctx.managementClient.getUser({ id: ctx.user!.auth0Id })
const { subscription } = await ctx.convertKit.subscribe(auth0User.email!)
await ctx.userService.update(ctx.user!.id, {
convertKitId: subscription.subscriber.id,
})
return subscription
},
})
)
router.post(
'/convertkit/unsubscribe',
endpoint.create({
resolve: async ({ ctx }) => {
const auth0User = await ctx.managementClient.getUser({ id: ctx.user!.auth0Id })
const { subscriber } = await ctx.convertKit.unsubscribe(auth0User.email!)
return subscriber
},
})
)
export default router

View file

@ -79,7 +79,6 @@ const envSchema = z.object({
// Key to Cloudfront pub key // Key to Cloudfront pub key
NX_CDN_SIGNER_PUBKEY_ID: z.string().default('REPLACE_THIS'), NX_CDN_SIGNER_PUBKEY_ID: z.string().default('REPLACE_THIS'),
NX_CONVERTKIT_SECRET: z.string(),
NX_POSTMARK_FROM_ADDRESS: z.string().default('account@maybe.co'), NX_POSTMARK_FROM_ADDRESS: z.string().default('account@maybe.co'),
NX_POSTMARK_REPLY_TO_ADDRESS: z.string().default('support@maybe.co'), NX_POSTMARK_REPLY_TO_ADDRESS: z.string().default('support@maybe.co'),
NX_POSTMARK_API_TOKEN: z.string().default('REPLACE_THIS'), NX_POSTMARK_API_TOKEN: z.string().default('REPLACE_THIS'),

View file

@ -197,24 +197,6 @@ export class ServerStack extends Stack {
} }
) )
), ),
NX_CONVERTKIT_KEY: ECSSecret.fromSsmParameter(
StringParameter.fromSecureStringParameterAttributes(
this,
'ConvertKitKeyParam',
{
parameterName: '/providers/NX_CONVERTKIT_KEY',
}
)
),
NX_CONVERTKIT_SECRET: ECSSecret.fromSsmParameter(
StringParameter.fromSecureStringParameterAttributes(
this,
'ConvertKitSecretParam',
{
parameterName: '/providers/NX_CONVERTKIT_SECRET',
}
)
),
NX_POSTMARK_API_TOKEN: ECSSecret.fromSsmParameter( NX_POSTMARK_API_TOKEN: ECSSecret.fromSsmParameter(
StringParameter.fromSecureStringParameterAttributes( StringParameter.fromSecureStringParameterAttributes(
this, this,

View file

@ -9,4 +9,3 @@ export * from './useTransactionApi'
export * from './useHoldingApi' export * from './useHoldingApi'
export * from './useSecurityApi' export * from './useSecurityApi'
export * from './usePlanApi' export * from './usePlanApi'
export * from './useNotificationsApi'

View file

@ -1,88 +0,0 @@
import type { AxiosInstance } from 'axios'
import type { UseQueryOptions } from '@tanstack/react-query'
import type { SharedType } from '@maybe-finance/shared'
import { useMemo } from 'react'
import { useMutation, useQueryClient, useQuery } from '@tanstack/react-query'
import { useAxiosWithAuth } from '..'
import toast from 'react-hot-toast'
const NotificationsApi = (axios: AxiosInstance) => ({
async getConvertKitSubscription() {
const { data } = await axios.get<{
isSubscribed: boolean
subscriber?: SharedType.ConvertKitSubscriber
}>(`/notifications/convertkit/subscription`)
return data
},
async manageSubscription(action: 'subscribe' | 'unsubscribe') {
const { data } = await axios.post<SharedType.ConvertKitSubscription>(
`/notifications/convertkit/${action}`
)
return data
},
})
type SubscriptionState = {
isSubscribed: boolean
subscriber?: SharedType.ConvertKitSubscriber
}
export function useNotificationsApi() {
const queryClient = useQueryClient()
const { axios } = useAxiosWithAuth()
const api = useMemo(() => NotificationsApi(axios), [axios])
const useConvertKitSubscriber = (
options?: Omit<
UseQueryOptions<
{ isSubscribed: boolean; subscriber?: SharedType.ConvertKitSubscriber },
unknown,
{ isSubscribed: boolean; subscriber?: SharedType.ConvertKitSubscriber },
any[]
>,
'queryKey' | 'queryFn' | 'staleTime'
>
) => {
return useQuery(['notifications', 'convertkit'], api.getConvertKitSubscription, {
staleTime: 30_000,
...options,
})
}
const useConvertKit = () =>
useMutation(api.manageSubscription, {
onMutate: async (action) => {
await queryClient.cancelQueries({ queryKey: ['notifications'] })
const previousSubscription = queryClient.getQueryData<SubscriptionState>([
'notifications',
'convertkit',
])
// Optimistic update to new state
queryClient.setQueryData(['notifications', 'convertkit'], () => ({
...previousSubscription,
isSubscribed: action === 'subscribe',
}))
return { previousSubscription }
},
onSuccess: () => {
toast.success('Newsletter preferences updated!')
},
onError: (err, action, ctx) => {
queryClient.setQueryData(['notifications', 'convertkit'], ctx?.previousSubscription)
toast.error('Error updating newsletter preferences')
},
onSettled: () => {
queryClient.invalidateQueries(['notifications', 'convertkit'])
},
})
return {
useConvertKitSubscriber,
useConvertKit,
}
}

View file

@ -295,23 +295,3 @@ export interface LinkConfig {
export type PublicTokenExchange = LinkConfig & { export type PublicTokenExchange = LinkConfig & {
institution: Institution institution: Institution
} }
/**
* ================================================================
* ====== ConvertKitApi ======
* ================================================================
*/
export type ConvertKitSubscriber = {
id: number
first_name: string
email_address: string
state: 'cancelled' | 'active'
created_at: string
}
export type ConvertKitSubscription = {
id: number
state: 'cancelled' | 'active'
subscriber: Pick<ConvertKitSubscriber, 'id'>
}

View file

@ -409,9 +409,6 @@ model User {
isoCurrencyCode String @default("USD") @map("iso_currency_code") isoCurrencyCode String @default("USD") @map("iso_currency_code")
// Notification preferences
convertKitId Int? @map("convert_kit_id")
// Financial preferences and info // Financial preferences and info
monthlyIncomeUser Decimal? @map("monthly_income_user") @db.Decimal(19, 4) monthlyIncomeUser Decimal? @map("monthly_income_user") @db.Decimal(19, 4)
monthlyExpensesUser Decimal? @map("monthly_expenses_user") @db.Decimal(19, 4) monthlyExpensesUser Decimal? @map("monthly_expenses_user") @db.Decimal(19, 4)