fix coderabbit comments

This commit is contained in:
Carl-Gerhard Lindesvärd
2026-01-21 15:31:24 +01:00
parent 3fa1a5429e
commit f9b1ec5038
12 changed files with 44 additions and 15 deletions

View File

@@ -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

View File

@@ -1,4 +1,5 @@
import { Button as EmailButton } from '@react-email/components';
import type * as React from 'react';
export function Button({
href,

View File

@@ -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,

View File

@@ -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>

View File

@@ -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>

View File

@@ -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';
}

View File

@@ -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 {