From fc8d9e2511e16979bcdbd7afbb96fa0d1e47515c Mon Sep 17 00:00:00 2001 From: Rushi Patel Date: Fri, 10 May 2024 14:13:18 -0400 Subject: [PATCH 01/10] migrated trail expire email to postmark --- pages/api/cron/update-pro-accounts-plan.ts | 60 +++++++++++----------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/pages/api/cron/update-pro-accounts-plan.ts b/pages/api/cron/update-pro-accounts-plan.ts index d612bf56f2..0bffac367d 100644 --- a/pages/api/cron/update-pro-accounts-plan.ts +++ b/pages/api/cron/update-pro-accounts-plan.ts @@ -8,6 +8,7 @@ import { import dayjs from 'dayjs'; import timezone from 'dayjs/plugin/timezone'; import utc from 'dayjs/plugin/utc'; +import { ServerClient } from 'postmark'; dayjs.extend(utc); dayjs.extend(timezone); @@ -41,37 +42,36 @@ const handler = async (req: Request): Promise => { } }; -const sendOutTrialEndEmail = async (userEmail: string) => { - const response = await fetch('https://api.sendgrid.com/v3/mail/send', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${process.env.SENDGRID_API_KEY}`, - }, - body: JSON.stringify({ - from: { - email: 'team@chateverywhere.app', - name: 'Chat Everywhere Team', - }, - reply_to: { - email: 'jack@exploratorlabs.com', - name: 'Jack', - }, - personalizations: [ - { - to: [ - { - email: userEmail, - }, - ], - }, - ], - template_id: 'd-e5fff0aa9b5948b4871c436812392134', - }), - }); +const postmarkClient = new ServerClient(process.env.SENDGRID_WEBHOOK_PUBLIC_KEY || ''); - if (!response.ok) { - throw new Error(`Failed to send email: ${response.statusText}`); +const sendOutTrialEndEmail = async (userEmail: string) => { + try { + const response = await postmarkClient.sendEmailWithTemplate({ + From: "Chat Everywhere Team ", + ReplyTo: 'Jack ', + To: userEmail, + TemplateId: 35875013, // Replace with your actual numeric template ID + TemplateModel: { + // The properties here should match the variables in your Postmark template + "product_url": "product_url_Value", + "product_name": "Chat Everywhere", + "action_url": "action_url_Value", + "trial_extension_url": "trial_extension_url_Value", + "feedback_url": "feedback_url_Value", + "export_url": "export_url_Value", + "close_account_url": "close_account_url_Value", + "sender_name": "sender_name_Value", + "company_name": "company_name_Value", + "company_address": "company_address_Value" + } + }); + console.log('Email sent successfully:', response); + } catch (error) { + if (error instanceof Error) { + console.error('Failed to send email:', error.message); + } else { + console.error('An unexpected error occurred:', error); + } } }; From 08b457d6d0af18c5f0601d1fe653bace61b79de0 Mon Sep 17 00:00:00 2001 From: Rushi Patel Date: Fri, 10 May 2024 14:13:36 -0400 Subject: [PATCH 02/10] installed postmark sdk --- package-lock.json | 49 +++++++++++++++++++++++++++++++++++++++++------ package.json | 1 + 2 files changed, 44 insertions(+), 6 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8c2ec2249e..9ee245a17c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -62,6 +62,7 @@ "notion-client": "^6.16.0", "notion-utils": "^6.16.0", "posthog-js": "^1.96.1", + "postmark": "^4.0.2", "puppeteer": "^19.11.1", "raw-body": "^2.5.2", "react": "18.2.0", @@ -5582,6 +5583,16 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -8561,9 +8572,9 @@ "dev": true }, "node_modules/follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==", "funding": [ { "type": "individual", @@ -14147,6 +14158,14 @@ "fflate": "^0.4.1" } }, + "node_modules/postmark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postmark/-/postmark-4.0.2.tgz", + "integrity": "sha512-2zlCv+KVVQ0KoamXZHE7d+gXzLlr8tPE+PxQmtUaIZhbHzZAq4D6yH2b+ykhA8wYCc5ISodcx8U1aNLenXBs9g==", + "dependencies": { + "axios": "^1.6.2" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -22252,6 +22271,16 @@ "integrity": "sha512-/BQzOX780JhsxDnPpH4ZiyrJAzcd8AfzFPkv+89veFSr1rcMjuq2JDCwypKaPeB6ljHp9KjXhPpjgCvQlWYuqg==", "dev": true }, + "axios": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", + "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "requires": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "axobject-query": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.1.1.tgz", @@ -24450,9 +24479,9 @@ "dev": true }, "follow-redirects": { - "version": "1.15.2", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", - "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + "version": "1.15.6", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz", + "integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==" }, "for-each": { "version": "0.3.3", @@ -27932,6 +27961,14 @@ "fflate": "^0.4.1" } }, + "postmark": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postmark/-/postmark-4.0.2.tgz", + "integrity": "sha512-2zlCv+KVVQ0KoamXZHE7d+gXzLlr8tPE+PxQmtUaIZhbHzZAq4D6yH2b+ykhA8wYCc5ISodcx8U1aNLenXBs9g==", + "requires": { + "axios": "^1.6.2" + } + }, "prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", diff --git a/package.json b/package.json index 5412ce336c..60a182ccf7 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "notion-client": "^6.16.0", "notion-utils": "^6.16.0", "posthog-js": "^1.96.1", + "postmark": "^4.0.2", "puppeteer": "^19.11.1", "raw-body": "^2.5.2", "react": "18.2.0", From dee0d8d5c40f63026dc00ef8e1f179e68b55e68f Mon Sep 17 00:00:00 2001 From: Rushi Patel Date: Mon, 13 May 2024 12:44:25 -0400 Subject: [PATCH 03/10] postmark test webhook --- pages/api/webhooks/postmark.ts | 97 ++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 pages/api/webhooks/postmark.ts diff --git a/pages/api/webhooks/postmark.ts b/pages/api/webhooks/postmark.ts new file mode 100644 index 0000000000..99288cdaec --- /dev/null +++ b/pages/api/webhooks/postmark.ts @@ -0,0 +1,97 @@ +import { NextApiRequest, NextApiResponse } from 'next'; + +import { serverSideTrackEvent } from '@/utils/app/eventTracking'; +import { getAdminSupabaseClient } from '@/utils/server/supabase'; + +//import { EventWebhook } from '@sendgrid/eventwebhook'; + +type PostmarkPayload = { + RecordType: string; + MessageStream: string; + FirstOpen: boolean; + Client: { + Name: string; + Company: string; + Family: string; + }; + OS: { + Name: string; + Company: string; + Family: string; + }; + Platform: string; + UserAgent: string; + Geo: { + CountryISOCode: string; + Country: string; + RegionISOCode: string; + Region: string; + City: string; + Zip: string; + Coords: string; + IP: string; + }; + MessageID: string; + Metadata: { + [key: string]: string; + }; + ReceivedAt: string; + Tag: string; + Recipient: string; +}; + +const handler = async (req: NextApiRequest, res: NextApiResponse) => { + console.log('Webhook triggered:', req); + if (req.method !== 'POST') { + res.status(405).end('Method Not Allowed'); + } + + // const publicKey = process.env.SENDGRID_WEBHOOK_PUBLIC_KEY || ''; + // const signature = req.headers[ + // 'x-twilio-email-event-webhook-signature' + // ] as string; + // const timestamp = req.headers[ + // 'x-twilio-email-event-webhook-timestamp' + // ] as string; + const payload = req.body; + console.log('payload:', payload); + //const eventWebhook = new EventWebhook(); + // const key = eventWebhook.convertPublicKeyToECDSA(publicKey); + // const isValidWebHookEvent = eventWebhook.verifySignature( + // key, + // payload, + // signature, + // timestamp, + // ); + + // if (!isValidWebHookEvent) { + // return res.status(400).send(`Webhook signature verification failed.`); + // } + + const eventPayload = JSON.parse(payload) as PostmarkPayload[]; + console.log('eventPayload:', eventPayload); + const openedEvents = eventPayload.filter( + (event) => + event.RecordType === "Open" + ); + + for (const event of openedEvents) { + const supabase = getAdminSupabaseClient(); + + const { data: user } = await supabase + .from('profiles') + .select('id, email') + .eq('email', event.Recipient) + .single(); + + if (user) { + await serverSideTrackEvent(user.id, `Trial end email opened`); + console.log('Trial end email opened on user with email', user.email); + } + console.log('No user found for:', event.Recipient); + } + + return res.status(200); +}; + +export default handler; From 19b5f68e8813ce467735622ed847c8970dd4dd92 Mon Sep 17 00:00:00 2001 From: Rushi Patel Date: Mon, 13 May 2024 13:34:48 -0400 Subject: [PATCH 04/10] check connection --- pages/api/webhooks/postmark.ts | 89 +++++++++++++++++----------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/pages/api/webhooks/postmark.ts b/pages/api/webhooks/postmark.ts index 99288cdaec..03a7a4635f 100644 --- a/pages/api/webhooks/postmark.ts +++ b/pages/api/webhooks/postmark.ts @@ -41,57 +41,58 @@ type PostmarkPayload = { }; const handler = async (req: NextApiRequest, res: NextApiResponse) => { - console.log('Webhook triggered:', req); - if (req.method !== 'POST') { - res.status(405).end('Method Not Allowed'); - } + console.log('Webhook triggered:', res); + // if (req.method !== 'POST') { + // res.status(405).end('Method Not Allowed'); + // } - // const publicKey = process.env.SENDGRID_WEBHOOK_PUBLIC_KEY || ''; - // const signature = req.headers[ - // 'x-twilio-email-event-webhook-signature' - // ] as string; - // const timestamp = req.headers[ - // 'x-twilio-email-event-webhook-timestamp' - // ] as string; - const payload = req.body; - console.log('payload:', payload); - //const eventWebhook = new EventWebhook(); - // const key = eventWebhook.convertPublicKeyToECDSA(publicKey); - // const isValidWebHookEvent = eventWebhook.verifySignature( - // key, - // payload, - // signature, - // timestamp, - // ); + // // const publicKey = process.env.SENDGRID_WEBHOOK_PUBLIC_KEY || ''; + // // const signature = req.headers[ + // // 'x-twilio-email-event-webhook-signature' + // // ] as string; + // // const timestamp = req.headers[ + // // 'x-twilio-email-event-webhook-timestamp' + // // ] as string; + // const payload = req.body; + // console.log('payload:', payload); + // //const eventWebhook = new EventWebhook(); + // // const key = eventWebhook.convertPublicKeyToECDSA(publicKey); + // // const isValidWebHookEvent = eventWebhook.verifySignature( + // // key, + // // payload, + // // signature, + // // timestamp, + // // ); - // if (!isValidWebHookEvent) { - // return res.status(400).send(`Webhook signature verification failed.`); - // } + // // if (!isValidWebHookEvent) { + // // return res.status(400).send(`Webhook signature verification failed.`); + // // } - const eventPayload = JSON.parse(payload) as PostmarkPayload[]; - console.log('eventPayload:', eventPayload); - const openedEvents = eventPayload.filter( - (event) => - event.RecordType === "Open" - ); + // const eventPayload = payload + // console.log('eventPayload:', eventPayload); + // // const openedEvents = eventPayload.filter( + // // (event) => + // // event.RecordType === "Open" + // // ); - for (const event of openedEvents) { - const supabase = getAdminSupabaseClient(); + // const openedEvents = payload + // for (const event of openedEvents) { + // const supabase = getAdminSupabaseClient(); - const { data: user } = await supabase - .from('profiles') - .select('id, email') - .eq('email', event.Recipient) - .single(); + // const { data: user } = await supabase + // .from('profiles') + // .select('id, email') + // .eq('email', event.Recipient) + // .single(); - if (user) { - await serverSideTrackEvent(user.id, `Trial end email opened`); - console.log('Trial end email opened on user with email', user.email); - } - console.log('No user found for:', event.Recipient); - } + // if (user) { + // await serverSideTrackEvent(user.id, `Trial end email opened`); + // console.log('Trial end email opened on user with email', user.email); + // } + // console.log('No user found for:', event.Recipient); + // } - return res.status(200); + //return res.status(200); }; export default handler; From a1844a42e73f35d7ee7bc4e1e4f4eda5e2d14f1f Mon Sep 17 00:00:00 2001 From: Rushi Patel Date: Mon, 13 May 2024 13:48:12 -0400 Subject: [PATCH 05/10] check connection --- pages/api/webhooks/postmark.ts | 104 ++++++++++++++++++--------------- 1 file changed, 56 insertions(+), 48 deletions(-) diff --git a/pages/api/webhooks/postmark.ts b/pages/api/webhooks/postmark.ts index 03a7a4635f..4ea4307bd0 100644 --- a/pages/api/webhooks/postmark.ts +++ b/pages/api/webhooks/postmark.ts @@ -40,59 +40,67 @@ type PostmarkPayload = { Recipient: string; }; -const handler = async (req: NextApiRequest, res: NextApiResponse) => { - console.log('Webhook triggered:', res); - // if (req.method !== 'POST') { - // res.status(405).end('Method Not Allowed'); - // } +// const handler = async (req: NextApiRequest, res: NextApiResponse) => { +// console.log('Webhook triggered:', res); +// // if (req.method !== 'POST') { +// // res.status(405).end('Method Not Allowed'); +// // } - // // const publicKey = process.env.SENDGRID_WEBHOOK_PUBLIC_KEY || ''; - // // const signature = req.headers[ - // // 'x-twilio-email-event-webhook-signature' - // // ] as string; - // // const timestamp = req.headers[ - // // 'x-twilio-email-event-webhook-timestamp' - // // ] as string; - // const payload = req.body; - // console.log('payload:', payload); - // //const eventWebhook = new EventWebhook(); - // // const key = eventWebhook.convertPublicKeyToECDSA(publicKey); - // // const isValidWebHookEvent = eventWebhook.verifySignature( - // // key, - // // payload, - // // signature, - // // timestamp, - // // ); +// // // const publicKey = process.env.SENDGRID_WEBHOOK_PUBLIC_KEY || ''; +// // // const signature = req.headers[ +// // // 'x-twilio-email-event-webhook-signature' +// // // ] as string; +// // // const timestamp = req.headers[ +// // // 'x-twilio-email-event-webhook-timestamp' +// // // ] as string; +// // const payload = req.body; +// // console.log('payload:', payload); +// // //const eventWebhook = new EventWebhook(); +// // // const key = eventWebhook.convertPublicKeyToECDSA(publicKey); +// // // const isValidWebHookEvent = eventWebhook.verifySignature( +// // // key, +// // // payload, +// // // signature, +// // // timestamp, +// // // ); - // // if (!isValidWebHookEvent) { - // // return res.status(400).send(`Webhook signature verification failed.`); - // // } +// // // if (!isValidWebHookEvent) { +// // // return res.status(400).send(`Webhook signature verification failed.`); +// // // } - // const eventPayload = payload - // console.log('eventPayload:', eventPayload); - // // const openedEvents = eventPayload.filter( - // // (event) => - // // event.RecordType === "Open" - // // ); +// // const eventPayload = payload +// // console.log('eventPayload:', eventPayload); +// // // const openedEvents = eventPayload.filter( +// // // (event) => +// // // event.RecordType === "Open" +// // // ); - // const openedEvents = payload - // for (const event of openedEvents) { - // const supabase = getAdminSupabaseClient(); +// // const openedEvents = payload +// // for (const event of openedEvents) { +// // const supabase = getAdminSupabaseClient(); - // const { data: user } = await supabase - // .from('profiles') - // .select('id, email') - // .eq('email', event.Recipient) - // .single(); +// // const { data: user } = await supabase +// // .from('profiles') +// // .select('id, email') +// // .eq('email', event.Recipient) +// // .single(); - // if (user) { - // await serverSideTrackEvent(user.id, `Trial end email opened`); - // console.log('Trial end email opened on user with email', user.email); - // } - // console.log('No user found for:', event.Recipient); - // } +// // if (user) { +// // await serverSideTrackEvent(user.id, `Trial end email opened`); +// // console.log('Trial end email opened on user with email', user.email); +// // } +// // console.log('No user found for:', event.Recipient); +// // } - //return res.status(200); -}; +// //return res.status(200); +// }; + +// export default handler; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + console.log('Webhook triggered with body:', req.body); + + // Your logic here... -export default handler; + res.status(200).json({ message: 'Webhook received' }); +} \ No newline at end of file From 57ca200228ffb166c8763247993a83f605e0f5ae Mon Sep 17 00:00:00 2001 From: Rushi Patel Date: Mon, 13 May 2024 13:59:04 -0400 Subject: [PATCH 06/10] trigger serverSideTrackEvent --- pages/api/webhooks/postmark.ts | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/pages/api/webhooks/postmark.ts b/pages/api/webhooks/postmark.ts index 4ea4307bd0..e2fec49da3 100644 --- a/pages/api/webhooks/postmark.ts +++ b/pages/api/webhooks/postmark.ts @@ -97,10 +97,40 @@ type PostmarkPayload = { // export default handler; +// export default async function handler(req: NextApiRequest, res: NextApiResponse) { +// console.log('Webhook triggered with body:', req.body); + +// res.status(200).json({ message: 'Webhook received' }); +// } + export default async function handler(req: NextApiRequest, res: NextApiResponse) { console.log('Webhook triggered with body:', req.body); - // Your logic here... + // Assuming req.body is already parsed as JSON + const payload: PostmarkPayload = req.body; + + // Check if the RecordType is 'Open' and it's the first time the email is opened + if (payload.RecordType === 'Open') { + const supabase = getAdminSupabaseClient(); + + // Fetch the user from your database using the recipient's email + const { data: user, error } = await supabase + .from('profiles') + .select('id, email') + .eq('email', payload.Recipient) + .single(); + // If a user is found, track the event and log it + if (user) { + // Replace `serverSideTrackEvent` with your actual event tracking function + await serverSideTrackEvent(user.id, `Trial end email opened`); + console.log('Trial end email opened on user with email', user.email); + } else if (error) { + console.error('Error fetching user:', error); + } else { + console.log('No user found for:', payload.Recipient); + } + } + // Respond to Postmark to acknowledge receipt of the webhook res.status(200).json({ message: 'Webhook received' }); } \ No newline at end of file From b8b4c764f84d8d4b54ab9d201db44a464e5453e4 Mon Sep 17 00:00:00 2001 From: Rushi Patel Date: Mon, 13 May 2024 14:21:59 -0400 Subject: [PATCH 07/10] auth --- pages/api/webhooks/postmark.ts | 85 +++++++++------------------------- 1 file changed, 21 insertions(+), 64 deletions(-) diff --git a/pages/api/webhooks/postmark.ts b/pages/api/webhooks/postmark.ts index e2fec49da3..af61fa87f2 100644 --- a/pages/api/webhooks/postmark.ts +++ b/pages/api/webhooks/postmark.ts @@ -40,75 +40,32 @@ type PostmarkPayload = { Recipient: string; }; -// const handler = async (req: NextApiRequest, res: NextApiResponse) => { -// console.log('Webhook triggered:', res); -// // if (req.method !== 'POST') { -// // res.status(405).end('Method Not Allowed'); -// // } - -// // // const publicKey = process.env.SENDGRID_WEBHOOK_PUBLIC_KEY || ''; -// // // const signature = req.headers[ -// // // 'x-twilio-email-event-webhook-signature' -// // // ] as string; -// // // const timestamp = req.headers[ -// // // 'x-twilio-email-event-webhook-timestamp' -// // // ] as string; -// // const payload = req.body; -// // console.log('payload:', payload); -// // //const eventWebhook = new EventWebhook(); -// // // const key = eventWebhook.convertPublicKeyToECDSA(publicKey); -// // // const isValidWebHookEvent = eventWebhook.verifySignature( -// // // key, -// // // payload, -// // // signature, -// // // timestamp, -// // // ); - -// // // if (!isValidWebHookEvent) { -// // // return res.status(400).send(`Webhook signature verification failed.`); -// // // } - -// // const eventPayload = payload -// // console.log('eventPayload:', eventPayload); -// // // const openedEvents = eventPayload.filter( -// // // (event) => -// // // event.RecordType === "Open" -// // // ); - -// // const openedEvents = payload -// // for (const event of openedEvents) { -// // const supabase = getAdminSupabaseClient(); - -// // const { data: user } = await supabase -// // .from('profiles') -// // .select('id, email') -// // .eq('email', event.Recipient) -// // .single(); - -// // if (user) { -// // await serverSideTrackEvent(user.id, `Trial end email opened`); -// // console.log('Trial end email opened on user with email', user.email); -// // } -// // console.log('No user found for:', event.Recipient); -// // } - -// //return res.status(200); -// }; - -// export default handler; - -// export default async function handler(req: NextApiRequest, res: NextApiResponse) { -// console.log('Webhook triggered with body:', req.body); - -// res.status(200).json({ message: 'Webhook received' }); -// } - export default async function handler(req: NextApiRequest, res: NextApiResponse) { console.log('Webhook triggered with body:', req.body); - // Assuming req.body is already parsed as JSON const payload: PostmarkPayload = req.body; + // Verifying the authenticity of request + if (req.method !== 'POST') { + res.status(405).end('Method Not Allowed'); + } + // The name of the header or body field where the token will be expected + const tokenHeaderName = 'x-custom-auth-token'; + + // The expected token value + // const expectedToken = process.env.WEBHOOK_SECRET_TOKEN; + const expectedToken = "12345678"; + + // Extract the token from the request headers + const providedToken = req.headers[tokenHeaderName]; + + // Check if the token matches the expected value + if (!providedToken || providedToken !== expectedToken) { + res.status(401).json({ message: `Unauthorized: ${providedToken}`}); + return; + } +//------- + // Check if the RecordType is 'Open' and it's the first time the email is opened if (payload.RecordType === 'Open') { const supabase = getAdminSupabaseClient(); From 091c9b29319ad7b1f4d98ace65ec754d363cae08 Mon Sep 17 00:00:00 2001 From: Rushi Patel Date: Mon, 13 May 2024 14:29:38 -0400 Subject: [PATCH 08/10] webhook for event tracking done --- pages/api/webhooks/postmark.ts | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/pages/api/webhooks/postmark.ts b/pages/api/webhooks/postmark.ts index af61fa87f2..49f46826cb 100644 --- a/pages/api/webhooks/postmark.ts +++ b/pages/api/webhooks/postmark.ts @@ -1,10 +1,7 @@ import { NextApiRequest, NextApiResponse } from 'next'; - import { serverSideTrackEvent } from '@/utils/app/eventTracking'; import { getAdminSupabaseClient } from '@/utils/server/supabase'; -//import { EventWebhook } from '@sendgrid/eventwebhook'; - type PostmarkPayload = { RecordType: string; MessageStream: string; @@ -41,42 +38,30 @@ type PostmarkPayload = { }; export default async function handler(req: NextApiRequest, res: NextApiResponse) { - console.log('Webhook triggered with body:', req.body); - const payload: PostmarkPayload = req.body; // Verifying the authenticity of request if (req.method !== 'POST') { res.status(405).end('Method Not Allowed'); } - // The name of the header or body field where the token will be expected const tokenHeaderName = 'x-custom-auth-token'; - - // The expected token value - // const expectedToken = process.env.WEBHOOK_SECRET_TOKEN; - const expectedToken = "12345678"; - - // Extract the token from the request headers + const expectedToken = process.env.POSTMARK_WEBHOOK_KEY; const providedToken = req.headers[tokenHeaderName]; - // Check if the token matches the expected value if (!providedToken || providedToken !== expectedToken) { - res.status(401).json({ message: `Unauthorized: ${providedToken}`}); + res.status(401).end('Unauthorized'); return; } -//------- - // Check if the RecordType is 'Open' and it's the first time the email is opened + // Check if the RecordType is 'Open' if (payload.RecordType === 'Open') { const supabase = getAdminSupabaseClient(); - // Fetch the user from your database using the recipient's email const { data: user, error } = await supabase .from('profiles') .select('id, email') .eq('email', payload.Recipient) .single(); - // If a user is found, track the event and log it if (user) { // Replace `serverSideTrackEvent` with your actual event tracking function @@ -88,6 +73,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) console.log('No user found for:', payload.Recipient); } } + // Respond to Postmark to acknowledge receipt of the webhook res.status(200).json({ message: 'Webhook received' }); } \ No newline at end of file From 40ee938b6fa97b51c34104497a7182903b8a6c6b Mon Sep 17 00:00:00 2001 From: Rushi Patel Date: Mon, 13 May 2024 14:32:45 -0400 Subject: [PATCH 09/10] update key --- pages/api/cron/update-pro-accounts-plan.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/api/cron/update-pro-accounts-plan.ts b/pages/api/cron/update-pro-accounts-plan.ts index 0bffac367d..5898688701 100644 --- a/pages/api/cron/update-pro-accounts-plan.ts +++ b/pages/api/cron/update-pro-accounts-plan.ts @@ -42,7 +42,7 @@ const handler = async (req: Request): Promise => { } }; -const postmarkClient = new ServerClient(process.env.SENDGRID_WEBHOOK_PUBLIC_KEY || ''); +const postmarkClient = new ServerClient(process.env.POSTMARK_SERVER_TOKEN || ''); const sendOutTrialEndEmail = async (userEmail: string) => { try { From 96e3d1fc3fdeead8728fe6f759475312bacdef6a Mon Sep 17 00:00:00 2001 From: Rushi Patel Date: Mon, 13 May 2024 14:37:00 -0400 Subject: [PATCH 10/10] removed sendgrid event tracking --- pages/api/webhooks/sendgrid.ts | 71 ---------------------------------- 1 file changed, 71 deletions(-) delete mode 100644 pages/api/webhooks/sendgrid.ts diff --git a/pages/api/webhooks/sendgrid.ts b/pages/api/webhooks/sendgrid.ts deleted file mode 100644 index 7621e84435..0000000000 --- a/pages/api/webhooks/sendgrid.ts +++ /dev/null @@ -1,71 +0,0 @@ -import { NextApiRequest, NextApiResponse } from 'next'; - -import { serverSideTrackEvent } from '@/utils/app/eventTracking'; -import { getAdminSupabaseClient } from '@/utils/server/supabase'; - -import { EventWebhook } from '@sendgrid/eventwebhook'; - -type SendGridPayload = { - email: string; - timestamp: number; - 'smtp-id': string; - event: string; - category: string[]; - sg_event_id: string; - sg_message_id: string; - sg_template_id: string; -}; - -const handler = async (req: NextApiRequest, res: NextApiResponse) => { - if (req.method !== 'POST') { - res.status(405).end('Method Not Allowed'); - } - - const publicKey = process.env.SENDGRID_WEBHOOK_PUBLIC_KEY || ''; - const signature = req.headers[ - 'x-twilio-email-event-webhook-signature' - ] as string; - const timestamp = req.headers[ - 'x-twilio-email-event-webhook-timestamp' - ] as string; - const payload = req.body; - - const eventWebhook = new EventWebhook(); - const key = eventWebhook.convertPublicKeyToECDSA(publicKey); - const isValidWebHookEvent = eventWebhook.verifySignature( - key, - payload, - signature, - timestamp, - ); - - if (!isValidWebHookEvent) { - return res.status(400).send(`Webhook signature verification failed.`); - } - - const eventPayload = JSON.parse(payload) as SendGridPayload[]; - const openedEvents = eventPayload.filter( - (event) => - event.event === 'open' && - event.sg_template_id === 'd-e5fff0aa9b5948b4871c436812392134', - ); - - for (const event of openedEvents) { - const supabase = getAdminSupabaseClient(); - - const { data: user } = await supabase - .from('profiles') - .select('id, email') - .eq('email', event.email) - .single(); - - if (user) { - await serverSideTrackEvent(user.id, `Trial end email opened`); - console.log('Trial end email opened on user with email', user.email); - } - } - - return res.status(200); -}; - -export default handler;