Skip to content

Commit 400eb9b

Browse files
committed
Fix code lint errors
1 parent 7c65baf commit 400eb9b

3 files changed

Lines changed: 108 additions & 51 deletions

File tree

‎src/pages/api/gdpr/consent.ts‎

Lines changed: 101 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,36 @@ export const prerender = false // Force SSR for this endpoint
1212

1313
const ROUTE = '/api/gdpr/consent'
1414

15+
const CONSENT_PURPOSES = ['contact', 'marketing', 'analytics', 'downloads'] as const
16+
type ConsentPurpose = (typeof CONSENT_PURPOSES)[number]
17+
18+
const CONSENT_SOURCES = ['contact_form', 'newsletter_form', 'download_form', 'cookies_modal', 'preferences_page'] as const
19+
type ConsentSource = (typeof CONSENT_SOURCES)[number]
20+
21+
const DEFAULT_SOURCE: ConsentSource = 'cookies_modal'
22+
const DEFAULT_USER_AGENT = 'unknown'
23+
24+
const isConsentPurpose = (value: unknown): value is ConsentPurpose =>
25+
typeof value === 'string' && CONSENT_PURPOSES.includes(value as ConsentPurpose)
26+
27+
const isConsentSource = (value: unknown): value is ConsentSource =>
28+
typeof value === 'string' && CONSENT_SOURCES.includes(value as ConsentSource)
29+
30+
const sanitizePurposes = (purposes: unknown): ConsentPurpose[] =>
31+
Array.isArray(purposes) ? purposes.filter(isConsentPurpose) : []
32+
33+
const sanitizeSource = (source: unknown): ConsentSource => (isConsentSource(source) ? source : DEFAULT_SOURCE)
34+
35+
const normalizeNullableString = (value?: string | null): string | null => {
36+
if (typeof value !== 'string') {
37+
return null
38+
}
39+
const trimmed = value.trim()
40+
return trimmed.length > 0 ? trimmed : null
41+
}
42+
43+
const normalizeUserAgent = (value?: string | null): string => normalizeNullableString(value) ?? DEFAULT_USER_AGENT
44+
1545
type ConsentRecordRow = {
1646
id: string
1747
data_subject_id: string
@@ -46,33 +76,63 @@ const buildRateLimitError = (reset: number | undefined, message?: string) => {
4676
})
4777
}
4878

49-
const mapConsentRecord = (record: ConsentRecordRow): ConsentResponse['record'] => ({
50-
id: record.id,
51-
DataSubjectId: record.data_subject_id,
52-
email: record.email,
53-
purposes: record.purposes,
54-
timestamp: record.timestamp,
55-
source: record.source,
56-
userAgent: record.user_agent,
57-
ipAddress: record.ip_address,
58-
privacyPolicyVersion: record.privacy_policy_version,
59-
consentText: record.consent_text,
60-
verified: record.verified,
61-
})
62-
63-
const buildMockConsentRecord = (body: ConsentRequest): ConsentResponse['record'] => ({
64-
id: randomUUID(),
65-
DataSubjectId: body.DataSubjectId,
66-
email: body.email?.toLowerCase().trim() ?? null,
67-
purposes: body.purposes,
68-
timestamp: new Date().toISOString(),
69-
source: body.source ?? null,
70-
userAgent: body.userAgent ?? null,
71-
ipAddress: body.ipAddress ?? null,
72-
privacyPolicyVersion: getPrivacyPolicyVersion(),
73-
consentText: body.consentText ?? null,
74-
verified: body.verified ?? false,
75-
})
79+
const mapConsentRecord = (record: ConsentRecordRow): ConsentResponse['record'] => {
80+
const normalizedEmail = normalizeNullableString(record.email)
81+
const normalizedIpAddress = normalizeNullableString(record.ip_address)
82+
const normalizedConsentText = normalizeNullableString(record.consent_text)
83+
84+
const mapped: ConsentResponse['record'] = {
85+
id: record.id,
86+
DataSubjectId: record.data_subject_id,
87+
purposes: sanitizePurposes(record.purposes),
88+
timestamp: record.timestamp,
89+
source: sanitizeSource(record.source),
90+
userAgent: normalizeUserAgent(record.user_agent),
91+
privacyPolicyVersion: record.privacy_policy_version ?? getPrivacyPolicyVersion(),
92+
verified: record.verified,
93+
}
94+
95+
if (normalizedEmail) {
96+
mapped.email = normalizedEmail
97+
}
98+
if (normalizedIpAddress) {
99+
mapped.ipAddress = normalizedIpAddress
100+
}
101+
if (normalizedConsentText) {
102+
mapped.consentText = normalizedConsentText
103+
}
104+
105+
return mapped
106+
}
107+
108+
const buildMockConsentRecord = (body: ConsentRequest): ConsentResponse['record'] => {
109+
const normalizedEmail = normalizeNullableString(body.email ?? null)
110+
const normalizedIpAddress = normalizeNullableString(body.ipAddress ?? null)
111+
const normalizedConsentText = normalizeNullableString(body.consentText ?? null)
112+
113+
const mockRecord: ConsentResponse['record'] = {
114+
id: randomUUID(),
115+
DataSubjectId: body.DataSubjectId,
116+
purposes: sanitizePurposes(body.purposes),
117+
timestamp: new Date().toISOString(),
118+
source: sanitizeSource(body.source),
119+
userAgent: normalizeUserAgent(body.userAgent),
120+
privacyPolicyVersion: getPrivacyPolicyVersion(),
121+
verified: body.verified ?? false,
122+
}
123+
124+
if (normalizedEmail) {
125+
mockRecord.email = normalizedEmail
126+
}
127+
if (normalizedIpAddress) {
128+
mockRecord.ipAddress = normalizedIpAddress
129+
}
130+
if (normalizedConsentText) {
131+
mockRecord.consentText = normalizedConsentText
132+
}
133+
134+
return mockRecord
135+
}
76136

77137
const buildErrorResponse = (
78138
error: unknown,
@@ -130,19 +190,26 @@ export const POST: APIRoute = async ({ request, cookies, clientAddress }) => {
130190
})
131191
}
132192

193+
const normalizedEmail = normalizeNullableString(body.email ?? null)
194+
const normalizedPurposes = sanitizePurposes(body.purposes)
195+
const normalizedSource = sanitizeSource(body.source)
196+
const normalizedUserAgent = normalizeUserAgent(body.userAgent)
197+
const normalizedIpAddress = normalizeNullableString(body.ipAddress ?? null)
198+
const normalizedConsentText = normalizeNullableString(body.consentText ?? null)
199+
133200
let record: ConsentResponse['record']
134201
try {
135202
const { data, error } = await supabaseAdmin
136203
.from('consent_records')
137204
.insert({
138205
data_subject_id: body.DataSubjectId,
139-
email: body.email?.toLowerCase().trim(),
140-
purposes: body.purposes,
141-
source: body.source,
142-
user_agent: body.userAgent,
143-
ip_address: body.ipAddress,
206+
email: normalizedEmail,
207+
purposes: normalizedPurposes,
208+
source: normalizedSource,
209+
user_agent: normalizedUserAgent,
210+
ip_address: normalizedIpAddress,
144211
privacy_policy_version: getPrivacyPolicyVersion(),
145-
consent_text: body.consentText,
212+
consent_text: normalizedConsentText,
146213
verified: body.verified ?? false,
147214
})
148215
.select()
@@ -232,19 +299,7 @@ export const GET: APIRoute = async ({ clientAddress, url, request, cookies }) =>
232299
})
233300
}
234301

235-
const records = data.map(record => ({
236-
id: record.id,
237-
DataSubjectId: record.data_subject_id,
238-
email: record.email,
239-
purposes: record.purposes,
240-
timestamp: record.timestamp,
241-
source: record.source,
242-
userAgent: record.user_agent,
243-
ipAddress: record.ip_address,
244-
privacyPolicyVersion: record.privacy_policy_version,
245-
consentText: record.consent_text,
246-
verified: record.verified
247-
}))
302+
const records = data.map(record => mapConsentRecord(record as ConsentRecordRow))
248303

249304
return jsonResponse(
250305
{

‎src/pages/api/newsletter/_email.ts‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ function getMockAuthorizationHeader(): string {
2323
}
2424
}
2525

26+
function resolveResendMockBaseUrl(force?: boolean) {
27+
return force === undefined ? getResendMockBaseUrl() : getResendMockBaseUrl({ force })
28+
}
29+
2630
/**
2731
* Generate the HTML content for the confirmation email
2832
*/
@@ -208,7 +212,7 @@ export async function sendConfirmationEmail(
208212
firstName?: string,
209213
options?: SendConfirmationEmailOptions
210214
): Promise<void> {
211-
const resendMockBaseUrl = getResendMockBaseUrl({ force: options?.forceMockResend })
215+
const resendMockBaseUrl = resolveResendMockBaseUrl(options?.forceMockResend)
212216
const siteUrl = getSiteUrl()
213217
const confirmUrl = `${siteUrl}/newsletter/confirm/${token}`
214218
const expiresIn = '24 hours'
@@ -306,7 +310,7 @@ export async function sendWelcomeEmail(
306310
firstName?: string,
307311
options?: SendWelcomeEmailOptions
308312
): Promise<void> {
309-
const resendMockBaseUrl = getResendMockBaseUrl({ force: options?.forceMockResend })
313+
const resendMockBaseUrl = resolveResendMockBaseUrl(options?.forceMockResend)
310314

311315
if (!resendMockBaseUrl && (isDev() || isTest())) {
312316
console.log('[DEV/TEST MODE] Newsletter welcome email would be sent:', { email })

‎test/e2e/specs/08-api/supabase.spec.ts‎

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ const TEST_USER_AGENT = 'playwright/supabase'
3939
const createdRecordIds: string[] = []
4040

4141
const skipUnlessChromium = (browserName: string) => {
42-
if (browserName !== 'chromium') {
43-
test.skip('Supabase API tests only run once per suite')
44-
}
42+
test.skip(browserName !== 'chromium', 'Supabase API tests only run once per suite')
4543
}
4644

4745
const buildConsentRecordPayload = () => ({

0 commit comments

Comments
 (0)