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:
commit
f346ecb3a0
13 changed files with 0 additions and 247 deletions
|
@ -22,4 +22,3 @@ AWS_SESSION_TOKEN=
|
|||
NX_PLAID_SECRET=
|
||||
NX_FINICITY_APP_KEY=
|
||||
NX_FINICITY_PARTNER_SECRET=
|
||||
NX_CONVERTKIT_SECRET=
|
1
.github/workflows/validate-pull-request.yml
vendored
1
.github/workflows/validate-pull-request.yml
vendored
|
@ -75,7 +75,6 @@ jobs:
|
|||
NX_STRIPE_SECRET_KEY=${{ secrets.NX_STRIPE_SECRET_KEY }}
|
||||
NX_STRIPE_WEBHOOK_SECRET=${{ secrets.NX_STRIPE_WEBHOOK_SECRET }}
|
||||
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_REDIS_URL=redis://localhost:6379
|
||||
EOF
|
||||
|
|
|
@ -42,7 +42,6 @@ import {
|
|||
securitiesRouter,
|
||||
plansRouter,
|
||||
toolsRouter,
|
||||
notificationsRouter,
|
||||
publicRouter,
|
||||
e2eRouter,
|
||||
} from './routes'
|
||||
|
@ -167,7 +166,6 @@ app.use('/v1/transactions', transactionsRouter)
|
|||
app.use('/v1/holdings', holdingsRouter)
|
||||
app.use('/v1/securities', securitiesRouter)
|
||||
app.use('/v1/plans', plansRouter)
|
||||
app.use('/v1/notifications', notificationsRouter)
|
||||
|
||||
// Sentry must be the *first* handler
|
||||
app.use(identifySentryUser)
|
||||
|
|
|
@ -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
|
|
@ -55,7 +55,6 @@ import prisma from './prisma'
|
|||
import plaid, { getPlaidWebhookUrl } from './plaid'
|
||||
import finicity, { getFinicityTxPushUrl, getFinicityWebhookUrl } from './finicity'
|
||||
import stripe from './stripe'
|
||||
import convertKit from './convertKit'
|
||||
import postmark from './postmark'
|
||||
import { managementClient } from './auth0'
|
||||
import s3 from './s3'
|
||||
|
@ -315,7 +314,6 @@ export async function createContext(req: Request) {
|
|||
prisma,
|
||||
plaid,
|
||||
stripe,
|
||||
convertKit,
|
||||
s3,
|
||||
secretsClient,
|
||||
managementClient,
|
||||
|
|
|
@ -13,6 +13,5 @@ export { default as holdingsRouter } from './holdings.router'
|
|||
export { default as securitiesRouter } from './securities.router'
|
||||
export { default as plansRouter } from './plans.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 e2eRouter } from './e2e.router'
|
||||
|
|
|
@ -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
|
|
@ -79,7 +79,6 @@ const envSchema = z.object({
|
|||
// Key to Cloudfront pub key
|
||||
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_REPLY_TO_ADDRESS: z.string().default('support@maybe.co'),
|
||||
NX_POSTMARK_API_TOKEN: z.string().default('REPLACE_THIS'),
|
||||
|
|
|
@ -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(
|
||||
StringParameter.fromSecureStringParameterAttributes(
|
||||
this,
|
||||
|
|
|
@ -9,4 +9,3 @@ export * from './useTransactionApi'
|
|||
export * from './useHoldingApi'
|
||||
export * from './useSecurityApi'
|
||||
export * from './usePlanApi'
|
||||
export * from './useNotificationsApi'
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -295,23 +295,3 @@ export interface LinkConfig {
|
|||
export type PublicTokenExchange = LinkConfig & {
|
||||
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'>
|
||||
}
|
||||
|
|
|
@ -409,9 +409,6 @@ model User {
|
|||
|
||||
isoCurrencyCode String @default("USD") @map("iso_currency_code")
|
||||
|
||||
// Notification preferences
|
||||
convertKitId Int? @map("convert_kit_id")
|
||||
|
||||
// Financial preferences and info
|
||||
monthlyIncomeUser Decimal? @map("monthly_income_user") @db.Decimal(19, 4)
|
||||
monthlyExpensesUser Decimal? @map("monthly_expenses_user") @db.Decimal(19, 4)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue