|
1 | 1 | import { serve } from 'https://deno.land/std@0.168.0/http/server.ts' |
2 | 2 | import { createClient } from 'https://esm.sh/@supabase/supabase-js@2' |
| 3 | +import { |
| 4 | + type IdentityMode, |
| 5 | + validateIdentityRequirements, |
| 6 | + determineSubmissionStatus |
| 7 | +} from '../_shared/submissionPolicy.ts' |
3 | 8 |
|
4 | 9 | const corsHeaders = { |
5 | 10 | 'Access-Control-Allow-Origin': '*', |
6 | 11 | 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', |
7 | 12 | 'Access-Control-Allow-Methods': 'POST,OPTIONS' |
8 | 13 | } |
9 | 14 |
|
10 | | -type IdentityMode = 'optional' | 'name_required' | 'name_email_required' | 'authenticated' |
11 | | - |
12 | 15 | interface SubmissionRequest { |
13 | 16 | docket_slug?: string |
14 | 17 | docket_id?: string |
@@ -61,10 +64,6 @@ function normalizeText(value?: string): string | null { |
61 | 64 | return text |
62 | 65 | } |
63 | 66 |
|
64 | | -function isValidEmail(email: string): boolean { |
65 | | - return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email) |
66 | | -} |
67 | | - |
68 | 67 | function toHex(buffer: ArrayBuffer): string { |
69 | 68 | return [...new Uint8Array(buffer)].map((b) => b.toString(16).padStart(2, '0')).join('') |
70 | 69 | } |
@@ -232,24 +231,23 @@ serve(async (req) => { |
232 | 231 | } |
233 | 232 |
|
234 | 233 | const identityMode: IdentityMode = (docket.identity_mode as IdentityMode) || 'optional' |
235 | | - if (identityMode === 'authenticated' && !user) { |
236 | | - await logAbuseEvent(serviceClient, { |
237 | | - agencyId: docket.agency_id, |
238 | | - docketId: docket.id, |
239 | | - ipAddress, |
240 | | - eventType: 'identity_auth_required', |
241 | | - details: { identity_mode: identityMode } |
242 | | - }) |
243 | | - return jsonResponse({ error: 'Authentication is required for this docket' }, 401) |
244 | | - } |
245 | | - if ((identityMode === 'name_required' || identityMode === 'name_email_required') && !commenterName) { |
246 | | - return jsonResponse({ error: 'Name is required for this docket' }, 400) |
247 | | - } |
248 | | - if (identityMode === 'name_email_required' && !commenterEmail) { |
249 | | - return jsonResponse({ error: 'Email is required for this docket' }, 400) |
250 | | - } |
251 | | - if (commenterEmail && !isValidEmail(commenterEmail)) { |
252 | | - return jsonResponse({ error: 'A valid email address is required' }, 400) |
| 234 | + const identityValidation = validateIdentityRequirements({ |
| 235 | + identityMode, |
| 236 | + isAuthenticated: Boolean(user), |
| 237 | + commenterName, |
| 238 | + commenterEmail |
| 239 | + }) |
| 240 | + if (!identityValidation.ok) { |
| 241 | + if (identityValidation.abuseEventType) { |
| 242 | + await logAbuseEvent(serviceClient, { |
| 243 | + agencyId: docket.agency_id, |
| 244 | + docketId: docket.id, |
| 245 | + ipAddress, |
| 246 | + eventType: identityValidation.abuseEventType, |
| 247 | + details: { identity_mode: identityMode } |
| 248 | + }) |
| 249 | + } |
| 250 | + return jsonResponse({ error: identityValidation.error }, identityValidation.status) |
253 | 251 | } |
254 | 252 |
|
255 | 253 | const maxLength = docket.max_comment_length || 4000 |
@@ -348,7 +346,7 @@ serve(async (req) => { |
348 | 346 | } |
349 | 347 |
|
350 | 348 | const trackingId = `CMT-${Date.now().toString(36).toUpperCase()}-${crypto.randomUUID().slice(0, 6).toUpperCase()}` |
351 | | - const status = docket.auto_publish ? 'published' : 'submitted' |
| 349 | + const status = determineSubmissionStatus(Boolean(docket.auto_publish)) |
352 | 350 | const contentHash = await hashContent(content) |
353 | 351 |
|
354 | 352 | const { data: insertedComment, error: insertError } = await serviceClient |
|
0 commit comments