fix coderabbit comments
This commit is contained in:
@@ -99,6 +99,6 @@ If OpenPanel has been useful, upgrading just keeps it going. Plans start at $2.5
|
||||
|
||||
If something's holding you back, I'd like to hear about it. Just reply.
|
||||
|
||||
Your project will recieve events for the next 30 days, if you haven't upgraded by then we'll remove your workspace and projects.
|
||||
Your project will receive events for the next 30 days, if you haven't upgraded by then we'll remove your workspace and projects.
|
||||
|
||||
Carl
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Button as EmailButton } from '@react-email/components';
|
||||
import type * as React from 'react';
|
||||
|
||||
export function Button({
|
||||
href,
|
||||
|
||||
@@ -58,7 +58,7 @@ export const templates = {
|
||||
schema: zOnboardingDashboards,
|
||||
category: 'onboarding' as const,
|
||||
},
|
||||
'onboarding-featue-request': {
|
||||
'onboarding-feature-request': {
|
||||
subject: () => 'One provider to rule them all',
|
||||
Component: OnboardingFeatureRequest,
|
||||
schema: zOnboardingFeatureRequest,
|
||||
|
||||
@@ -24,7 +24,7 @@ export function OnboardingTrialEnded({
|
||||
newUrl.searchParams.set('utm_campaign', 'onboarding-trial-ended');
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Layout unsubscribeUrl={unsubscribeUrl}>
|
||||
<Text>Hi{firstName ? ` ${firstName}` : ''},</Text>
|
||||
<Text>Your OpenPanel trial has ended.</Text>
|
||||
<Text>
|
||||
|
||||
@@ -26,7 +26,7 @@ export function OnboardingTrialEnding({
|
||||
newUrl.searchParams.set('utm_campaign', 'onboarding-trial-ending');
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Layout unsubscribeUrl={unsubscribeUrl}>
|
||||
<Text>Hi{firstName ? ` ${firstName}` : ''},</Text>
|
||||
<Text>Quick heads up: your OpenPanel trial ends soon.</Text>
|
||||
<Text>
|
||||
@@ -45,7 +45,7 @@ export function OnboardingTrialEnding({
|
||||
If something's holding you back, I'd like to hear about it. Just reply.
|
||||
</Text>
|
||||
<Text>
|
||||
Your project will recieve events for the next 30 days, if you haven't
|
||||
Your project will receive events for the next 30 days, if you haven't
|
||||
upgraded by then we'll remove your workspace and projects.
|
||||
</Text>
|
||||
<Text>
|
||||
|
||||
@@ -61,7 +61,7 @@ export async function sendEmail<T extends TemplateKey>(
|
||||
const headers: Record<string, string> = {};
|
||||
if ('category' in template && template.category) {
|
||||
const unsubscribeUrl = getUnsubscribeUrl(to, template.category);
|
||||
(data as any).unsubscribeUrl = unsubscribeUrl;
|
||||
(props.data as any).unsubscribeUrl = unsubscribeUrl;
|
||||
headers['List-Unsubscribe'] = `<${unsubscribeUrl}>`;
|
||||
headers['List-Unsubscribe-Post'] = 'List-Unsubscribe=One-Click';
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { createHmac } from 'crypto';
|
||||
import { createHmac, timingSafeEqual } from 'crypto';
|
||||
|
||||
const SECRET =
|
||||
process.env.UNSUBSCRIBE_SECRET ||
|
||||
@@ -17,7 +17,18 @@ export function verifyUnsubscribeToken(
|
||||
token: string,
|
||||
): boolean {
|
||||
const expectedToken = generateUnsubscribeToken(email, category);
|
||||
return token === expectedToken;
|
||||
const tokenBuffer = Buffer.from(token, 'hex');
|
||||
const expectedBuffer = Buffer.from(expectedToken, 'hex');
|
||||
|
||||
// Handle length mismatch safely to avoid timing leaks
|
||||
if (tokenBuffer.length !== expectedBuffer.length) {
|
||||
// Compare against zero-filled buffer of same length as token to maintain constant time
|
||||
const zeroBuffer = Buffer.alloc(tokenBuffer.length);
|
||||
timingSafeEqual(tokenBuffer, zeroBuffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
return timingSafeEqual(tokenBuffer, expectedBuffer);
|
||||
}
|
||||
|
||||
export function getUnsubscribeUrl(email: string, category: string): string {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { emailCategories } from '@openpanel/constants';
|
||||
import { db } from '@openpanel/db';
|
||||
import { verifyUnsubscribeToken } from '@openpanel/email';
|
||||
import { z } from 'zod';
|
||||
import { TRPCBadRequestError } from '../errors';
|
||||
import { createTRPCRouter, protectedProcedure, publicProcedure } from '../trpc';
|
||||
|
||||
export const emailRouter = createTRPCRouter({
|
||||
@@ -18,7 +19,7 @@ export const emailRouter = createTRPCRouter({
|
||||
|
||||
// Verify token
|
||||
if (!verifyUnsubscribeToken(email, category, token)) {
|
||||
throw new Error('Invalid unsubscribe link');
|
||||
throw TRPCBadRequestError('Invalid unsubscribe link');
|
||||
}
|
||||
|
||||
// Upsert the unsubscribe record
|
||||
|
||||
Reference in New Issue
Block a user