Submission Processor
Type de ressource : Cloudflare Worker (Microservice FaaS / Queue Consumer)
Fichier source :services/submission-processor/src/index.ts
Statut : Actif
1. Vue dâensemble
Section titled â1. Vue dâensembleâ1.1. Description
Section titled â1.1. DescriptionâWorker Cloudflare qui consomme les messages de la Queue submissions-queue et crĂ©e des documents dans Sanity. Traite les soumissions de formulaire de maniĂšre asynchrone via un Queue consumer. Inclut un endpoint de test direct (/test-direct) pour le dĂ©veloppement local.
1.2. Objectif
Section titled â1.2. Objectifâ- Consommer les soumissions de formulaire depuis la Queue Cloudflare
- Créer des documents Sanity pour chaque soumission
- Fournir un endpoint de test direct pour le développement local
- Gérer les erreurs avec retry exponentiel
1.3. Caractéristiques
Section titled â1.3. CaractĂ©ristiquesâ| PropriĂ©tĂ© | Valeur |
|---|---|
| Type | Microservice métier / Queue Consumer |
| Trigger | Queue consumer, HTTP fetch |
| Route publique | /health, /test-direct |
| Environnements | Development, Production |
| Consommateurs | Form-handler (producer), Sanity CMS |
2. Architecture et flux
Section titled â2. Architecture et fluxâ[EntrĂ©e : Queue Cloudflare] â âą Message de soumission âŒ[Submission-processor Worker] â âą Queue consumer â âą Initialisation du client Sanity â âą CrĂ©ation du document Sanity âŒ[Sanity CMS] â âą Document submission créé âŒ[Confirmation (ack/retry)]2.2. Positionnement dans la chaĂźne Edge
Section titled â2.2. Positionnement dans la chaĂźne Edgeâ[Client] â âŒ[Astro Frontend] â âŒ[Form Handler Worker] â âą Producer Queue âŒ[Queue Cloudflare] â âŒ[Submission-processor Worker] â âą Consumer Queue âŒ[Sanity CMS]2.3. Composants impliquĂ©s
Section titled â2.3. Composants impliquĂ©sâ| Composant | RĂŽle | Type |
|---|---|---|
| SUBMISSIONS_QUEUE | Queue asynchrone | Cloudflare Binding |
| Sanity Client | Client API Sanity | BibliothĂšque (@sanity/client) |
| Sanity API | Stockage des soumissions | Service tiers |
| Form-handler | Producer de la Queue | Cloudflare Worker |
3. Configuration
Section titled â3. Configurationâ3.1. Variables dâenvironnement
Section titled â3.1. Variables dâenvironnementâ| Variable | Description | Obligatoire | Valeur par dĂ©faut |
|---|---|---|---|
ENVIRONMENT |
Environnement (dev/production) | â | dev |
SANITY_PROJECT_ID |
ID du projet Sanity | â | InjectĂ© via secret |
SANITY_DATASET |
Nom du dataset Sanity | â | InjectĂ© via secret |
SANITY_API_WRITE_TOKEN |
Token dâĂ©criture Sanity | â | InjectĂ© via secret |
3.2. Bindings Cloudflare
Section titled â3.2. Bindings Cloudflareâ| Binding | Type | Nom | Description |
|---|---|---|---|
| SUBMISSIONS_QUEUE | Queue Consumer | SUBMISSIONS_QUEUE | Queue pour les soumissions |
3.3. Configuration Wrangler (wrangler.toml)
Section titled â3.3. Configuration Wrangler (wrangler.toml)âname = "amn-submission-processor"main = "src/index.ts"compatibility_date = "2025-07-18"compatibility_flags = ["nodejs_compat"]
[vars]ENVIRONMENT = "dev"
[env.dev]name = "amn-submission-processor-dev"vars = { ENVIRONMENT = "dev" }
[[env.dev.queues.consumers]]queue = "submissions-queue-dev"max_batch_size = 10max_batch_timeout = 30
[env.production]name = "amn-submission-processor-prod"vars = { ENVIRONMENT = "production" }
[[env.production.queues.consumers]]queue = "submissions-queue-prod"max_batch_size = 10max_batch_timeout = 303.4. Injection des secrets
Section titled â3.4. Injection des secretsâ# Injection des secrets via Wrangler CLIwrangler secret put SANITY_PROJECT_ID --env productionwrangler secret put SANITY_DATASET --env productionwrangler secret put SANITY_API_WRITE_TOKEN --env productionou via le script @inject_secret_to_cloudflare.sh du projet.
4. Implémentation
Section titled â4. ImplĂ©mentationâ4.1. Code de production (src/index.ts)
Section titled â4.1. Code de production (src/index.ts)âimport { createClient } from '@sanity/client'
export interface Env { ENVIRONMENT: string; SANITY_PROJECT_ID: string; SANITY_DATASET: string; SANITY_API_WRITE_TOKEN?: string;}
export interface SubmissionMessage { id: string; name: string; email: string; message: string; submittedAt: string; environment: string;}
export default { async fetch(request: Request, env: Env): Promise<Response> { const url = new URL(request.url);
if (url.pathname === '/health' && request.method === 'GET') { return new Response(JSON.stringify({ status: 'healthy', service: 'submission-processor', environment: env.ENVIRONMENT ?? 'dev', sanityProjectIdPrefix: env.SANITY_PROJECT_ID ? env.SANITY_PROJECT_ID.slice(0, 4) + 'âŠ' : 'NOT SET', sanityDataset: env.SANITY_DATASET ?? 'NOT SET', timestamp: new Date().toISOString(), }), { status: 200, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', }, }); }
// Test endpoint for direct Sanity integration (bypasses Queue for local testing) if (url.pathname === '/test-direct' && request.method === 'POST') { try { const data = await request.json() as { name: string; email: string; message: string };
const sanityClient = createClient({ projectId: env.SANITY_PROJECT_ID, dataset: env.SANITY_DATASET, apiVersion: '2024-01-01', token: env.SANITY_API_WRITE_TOKEN, useCdn: false, });
const result = await sanityClient.create({ _type: 'submission', name: data.name, email: data.email, message: data.message, status: 'pending', submittedAt: new Date().toISOString(), processedAt: new Date().toISOString(), });
return new Response(JSON.stringify({ success: true, sanityId: result._id, message: 'Direct test: submission stored in Sanity' }), { headers: { 'Content-Type': 'application/json' }, }); } catch (error) { console.error('Direct test error:', error); const errorMessage = error instanceof Error ? error.message : 'Unknown error'; return new Response(JSON.stringify({ success: false, error: errorMessage, }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); } }
if (request.method === 'OPTIONS') { return new Response(null, { status: 204, headers: { 'Access-Control-Allow-Origin': '*', 'Access-Control-Allow-Methods': 'GET, POST, OPTIONS', 'Access-Control-Allow-Headers': 'Content-Type', }, }); }
return new Response(JSON.stringify({ error: 'Not Found', hint: 'Try GET /health or POST /test-direct for direct Sanity testing' }), { status: 404, headers: { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*', }, }); },
async queue(batch: MessageBatch<SubmissionMessage>, env: Env): Promise<void> { console.log(`Processing batch of ${batch.messages.length} submissions`);
// Initialize Sanity client const sanityClient = createClient({ projectId: env.SANITY_PROJECT_ID, dataset: env.SANITY_DATASET, apiVersion: '2024-01-01', token: env.SANITY_API_WRITE_TOKEN, useCdn: false, });
for (const message of batch.messages) { try { const submission = message.body as SubmissionMessage; console.log(`Processing submission ${submission.id} from ${submission.email}`);
// Create document in Sanity const result = await sanityClient.create({ _type: 'submission', name: submission.name, email: submission.email, message: submission.message, status: 'pending', submittedAt: submission.submittedAt, processedAt: new Date().toISOString(), });
console.log(`Successfully stored submission ${submission.id} in Sanity with ID: ${result._id}`);
// Acknowledge message message.ack();
} catch (error) { console.error(`Failed to process submission:`, error);
// Retry with exponential backoff message.retry({ delaySeconds: Math.min(60, Math.pow(2, batch.messages.indexOf(message))), }); } } },};4.2. Interfaces et types
Section titled â4.2. Interfaces et typesâ// Interface des bindingsexport interface Env { ENVIRONMENT: string; SANITY_PROJECT_ID: string; SANITY_DATASET: string; SANITY_API_WRITE_TOKEN?: string;}
// Type de message de soumissionexport interface SubmissionMessage { id: string; name: string; email: string; message: string; submittedAt: string; environment: string;}5. Sécurité
Section titled â5. SĂ©curitĂ©â5.1. MĂ©canismes de sĂ©curitĂ©
Section titled â5.1. MĂ©canismes de sĂ©curitĂ©â| MĂ©canisme | ImplĂ©mentation | Rationale |
|---|---|---|
| Secrets injectés | Secrets Sanity via wrangler secret put | Protection des credentials |
| CORS | Headers CORS permissifs | Permettre les requĂȘtes cross-origin |
| Masquage de secrets | Prefixe du project ID masquĂ© dans /health | Protection contre lâexposition |
5.2. Validation des entrées
Section titled â5.2. Validation des entrĂ©esâNon applicable - les donnĂ©es sont dĂ©jĂ validĂ©es par form-handler.
5.3. Gestion des secrets
Section titled â5.3. Gestion des secretsâ- â Les secrets ne sont jamais dans le code source
- â
Injection via
wrangler secret putou@inject_secret_to_cloudflare.sh - â Rotation rĂ©guliĂšre des secrets (tous les 90 jours recommandĂ©)
6. Résilience et gestion des erreurs
Section titled â6. RĂ©silience et gestion des erreursâ6.1. StratĂ©gie de retry
Section titled â6.1. StratĂ©gie de retryâ| ScĂ©nario | StratĂ©gie | Rationale |
|---|---|---|
| Sanity API error | Retry exponentiel (max 60s) | Tolérance aux pannes temporaires |
| Sanity API success | Ack message | Confirmation du traitement |
6.2. Safety Locks
Section titled â6.2. Safety LocksâNon implĂ©mentĂ© - pas de lock configurĂ©.
6.3. Gestion des timeouts
Section titled â6.3. Gestion des timeoutsâ- â Timeout global : DĂ©pend de Cloudflare (30s Free / 50s Paid)
- â Timeout par service tiers : DĂ©pend de Cloudflare
- â Retry exponentiel pour les erreurs Sanity
7. Performance et limites
Section titled â7. Performance et limitesâ7.1. Limites Cloudflare Workers
Section titled â7.1. Limites Cloudflare Workersâ| Limite | Valeur | StratĂ©gie dâoptimisation |
|---|---|---|
| Mémoire RAM | 128 MB | Pas de traitement lourd en mémoire |
| CPU time | 30s (Free) / 50s (Paid) | Batch size limité à 10 |
| Nombre de sous-requĂȘtes | 50 | LimitĂ© par batch size |
| Taille requĂȘte/rĂ©ponse | 100 MB | Payload de soumission limitĂ© |
7.2. Optimisations appliquées
Section titled â7.2. Optimisations appliquĂ©esâ- â Traitement par batch (max 10 messages)
- â Retry exponentiel pour les erreurs
- â Endpoint de test direct pour le dĂ©veloppement local
8. Monitoring et observabilité
Section titled â8. Monitoring et observabilitĂ©â8.1. Logs structurĂ©s
Section titled â8.1. Logs structurĂ©sâ// Logs console.log basiquesconsole.log(`Processing batch of ${batch.messages.length} submissions`);console.log(`Processing submission ${submission.id} from ${submission.email}`);console.log(`Successfully stored submission ${submission.id} in Sanity with ID: ${result._id}`);console.error(`Failed to process submission:`, error);8.2. MĂ©triques clĂ©s
Section titled â8.2. MĂ©triques clĂ©sâNon implĂ©mentĂ© - aucune mĂ©trique ou alerte nâest configurĂ©e.
8.3. Commandes de debug
Section titled â8.3. Commandes de debugâ# Logs en temps rĂ©elwrangler tail submission-processor
# Vérification des bindingswrangler deployments list
# Test de l'endpoint healthcurl http://localhost:8789/health
# Test de l'endpoint test-directcurl -X POST http://localhost:8789/test-direct \ -H "Content-Type: application/json" \ -d '{"name":"Test","email":"test@example.com","message":"Test message"}'9. Déploiement
Section titled â9. DĂ©ploiementâ9.1. Pipeline de dĂ©ploiement
Section titled â9.1. Pipeline de dĂ©ploiementâ# Buildnpm run build --workspace=services/submission-processor
# Déploiement stagingwrangler deploy --env staging
# Déploiement productionwrangler deploy --env production9.2. Rollback
Section titled â9.2. Rollbackâ# Liste des dĂ©ploiementswrangler deployments list
# Rollback vers une version précédentewrangler deployments rollback --deployment-id=[id]9.3. Tests manuels
Section titled â9.3. Tests manuelsâ# Test HTTP (health check)curl http://localhost:8789/health
# Test HTTP (test-direct)curl -X POST http://localhost:8789/test-direct \ -H "Content-Type: application/json" \ -d '{"name":"Test","email":"test@example.com","message":"Test message"}'
# Test Queue (via wrangler)wrangler queues tail submissions-queue-dev10. Débogage et troubleshooting
Section titled â10. DĂ©bogage et troubleshootingâ10.1. ProblĂšmes courants
Section titled â10.1. ProblĂšmes courantsâ| SymptĂŽme | Cause probable | Solution |
|---|---|---|
| Sanity API error | Token invalide ou manquant | Vérifier les secrets injectés |
| Queue non consommée | Binding mal configuré | Vérifier wrangler.toml |
| Test-direct échoue | Credentials Sanity manquants | Vérifier .env local |
10.2. Erreurs fréquentes
Section titled â10.2. Erreurs frĂ©quentesâ| Code HTTP / Erreur | Signification | Action |
|---|---|---|
| 500 | Erreur Sanity API | Vérifier les logs et les credentials |
| 404 | Endpoint inexistant | Vérifier le pathname |
11. Bonnes pratiques
Section titled â11. Bonnes pratiquesâ11.1. Architecture
Section titled â11.1. Architectureâ- â DĂ©coupler le traitement de la validation
- â Utiliser les bindings Cloudflare plutĂŽt que des appels HTTP internes
- â Traitement par batch pour optimiser les performances
11.2. Code
Section titled â11.2. Codeâ- â Typer strictement les bindings et les rĂ©ponses
- â GĂ©rer explicitement les erreurs (try/catch)
- â Logger les erreurs et les succĂšs
- â Endpoint de test direct pour le dĂ©veloppement local
11.3. Sécurité
Section titled â11.3. SĂ©curitĂ©â- â Ne jamais exposer les secrets cĂŽtĂ© client
- â Masquer les secrets dans les logs
- â
Utiliser
@inject_secret_to_cloudflare.shpour lâinjection
11.4. Performance
Section titled â11.4. Performanceâ- â Traitement par batch (max 10 messages)
- â Retry exponentiel pour les erreurs
- â Timeout de batch (30s)
12. Ăvolutions et roadmap
Section titled â12. Ăvolutions et roadmapâ12.1. AmĂ©liorations planifiĂ©es
Section titled â12.1. AmĂ©liorations planifiĂ©esâ- Configuration de Logpush pour les logs
- Ajout de métriques et alertes
- Ajout de structured logging (JSON)
- Amélioration de la gestion des erreurs (DLQ)
12.2. Limitations connues
Section titled â12.2. Limitations connuesâ- â CORS permissif (
*) - Ă restreindre pour la production - â Pas de Dead Letter Queue (DLQ)
- â Pas de logs structurĂ©s
- â Pas de monitoring configurĂ©
- â ïž Endpoint
/test-directà désactiver en production
13. Ressources liées
Section titled â13. Ressources liĂ©esâ- Documentation Cloudflare Workers : Getting Started
- Documentation Cloudflare Queues : Queues
- Documentation Sanity : Client API
- Workers associés :
- form-handler
- Fichiers concernés :
services/submission-processor/src/index.tsservices/submission-processor/wrangler.tomlservices/submission-processor/package.json@inject_secret_to_cloudflare.sh
14. Historique des modifications
Section titled â14. Historique des modificationsâ| Date | Version | Description | Auteur |
|---|---|---|---|
| 2026-09-15 | 1.0 | Création initiale | Devin |