Commit History
-
fix(audit): resolve all remaining technical debt issues
CognxSafeTrackC2 - analytics SQL injection: UNION detection, ALLOWED_TABLES allowlist, remove SQL from error responses to prevent schema leakage H1/H2 - override-feedback: move organizationId validation to top of handler before any DB mutation; scope enrollment.findFirst to org H6 - UserListPage: add pagination controls (Précédent/Suivant, 20 per page) L4 - TrackDaysPage: await load() before closing editor to prevent stale data race L5 - admin.ts: replace getOrganizationId() with req.organizationId at 3 call sites, remove now-unused import M1 - EntityResolver: replace any[] types with proper Prisma payload types (ResolvedUser/Contact/Organization/Enrollment with exact include shapes) L8 - AI command fuzzy org match: return pending_confirmation when org found by partial name match, inject orgId for unambiguous confirmation retrya888244 -
fix(security): resolve critical and high technical debt from audit
CognxSafeTrackCRITICAL: - Exclude passwordHash from enrollment list response (admin.ts) - Add sanitizeOrg() to strip API keys/tokens from all org GET responses - Fix CORS wildcard on SSE stream; use request origin instead - Restrict super-admin routes to SUPER_ADMIN role only (was also allowing ADMIN) - Replace all Zod .parse() with .safeParse() → proper 400s instead of 500s (ai.ts, admin.ts) HIGH: - Increase ExerciseHandler Redis dedup lock from 30s to 300s - Add take:500 to unbounded trainingData.findMany (OOM risk) - Add take limits to message/contact findMany queries - Add Redis lock in EnrollHandler to prevent duplicate enrollments (TOCTOU) - Wrap override-feedback in try/catch; return 404 on P2025 - Use atomic increment for wallet credits (was TOCTOU race condition) - Scope messages/send to requesting org (was cross-org accessible) MEDIUM: - decryptSecrets returns new object instead of mutating argument in-place - GeminiProvider.generateStructuredData now validates response with Zod schema -…a8e18d6 -
feat(super-admin): WhatsApp OTP registration and template creation
CognxSafeTrack- WhatsApp service: add registerPhoneNumber() and verifyPhoneNumber() methods - Super-admin routes: POST /whatsapp/numbers/register and /verify for Meta OTP flow - Super-admin routes: POST /whatsapp/templates to submit templates to Meta for approval - WhatsAppNumbers page: 2-step modal (orgId + phoneNumberId + PIN → OTP code) - WhatsAppTemplates page: full creation modal with category cards, char counters, live preview bubble170eb5d -
feat(super-admin): org delete, user password reset, complete CRUD coverage
CognxSafeTrackBackend: - DELETE /v1/super-admin/organizations/:id — soft-delete via subscriptionStatus='DELETED' + isHardStopped=true - POST /v1/super-admin/users/:id/reset-password — generates 1h JWT reset token, emails link via scheduleEmail (same flow as /forgot-password) Frontend: - OrganizationsManager: red trash icon per row calls DELETE; confirmation dialog before destructive action; suspend icon color changed to amber - UsersManager: key icon per row calls reset-password endpoint; guards against users with no email addressb8629ec -
feat: complete super-admin interface + enforce Day 11 photo requirement
CognxSafeTrackSuper-admin (3 missing pages + 3 new backend routes): - AuditLogs page: date-range / action filter, paginated table with inline JSON detail expansion - WhatsAppTemplates page: org directory with lazy per-org template fetch; category/status badges; expandable template panel per row - WhatsAppProfiles page: card grid showing org branding (logo, color, WABA ID, token age); inline edit form with live logo preview and color picker; PATCH /v1/super-admin/whatsapp/profiles/:id - Backend: GET /audit-logs, GET/PATCH /whatsapp/profiles, GET /whatsapp/templates added to super-admin.ts - SuperAdminLayout: 10 nav items (added Templates WA, Profils WA, Audit Logs); SuperAdminRouter wired to all new routes Day 11 photo enforcement (tasks/todo.md item resolved): - FeedbackHandler: if currentDay === 11 and no imageUrl, send multilingual photo prompt and return early — exercise cannot be validated without a team member photo8f2286f -
feat: add XAMLÉ Platform Admin super-admin interface
CognxSafeTrackWhen selectedOrgId === 'xamle-admin-org', the admin switches to a full platform management dashboard instead of the normal org layout. Backend (apps/api): - New /v1/super-admin/* routes (SUPER_ADMIN/ADMIN role required): platform stats, organizations CRUD + suspend + impersonate, cross-org users + role change, WhatsApp numbers, billing transactions + manual credits, monitoring health, AI agentic command - Fixed SQL injection in analytics text-to-SQL (UNION/DROP/comment blocking) - Removed JWT secret fallback — crashes on startup if JWT_SECRET is missing - Added ORG_ADMIN role guard to all /v1/admin/* routes Frontend (apps/admin): - SuperAdminRouter: nested routes under /platform/* - SuperAdminLayout: dark sidebar (slate-950/violet), 7 nav items - PlatformDashboard: 6 KPI cards, system health, active alerts - OrganizationsManager: searchable table, suspend/edit drawer, create modal - UsersManager: cross-org table with inline role-change - WhatsAppNumbers: all registered numbers …6282d86 -
feat(whatsapp-setup): make token optional when org already has one stored
CognxSafeTrackIf an org already has a systemUserToken, the /whatsapp-setup route now reuses it automatically. Admins can reconfigure WABA ID + Phone Number ID without re-entering the token — useful for adding a new phone number to an existing connected account.de46926 -
fix(crm-agent): replace generateText+JSON.parse with generateStructuredData for intent detection
CognxSafeTrackThe CRM command agent was always falling back to GENERAL_CHAT and showing the menu again because LLMs wrap JSON in markdown fences, breaking JSON.parse. Switch to generateStructuredData with a Zod schema which guarantees structured output regardless of LLM quirks. Also exposes generateStructuredData as a public method on AIService.3b3d6cd -
fix(audit): resolve remaining TypeScript any types, string enums, and Json validation
CognxSafeTrack +1: Claude Sonnet 4.6TypeScript types (ai-sdk): - Define AIServicePrismaClient structural interface — replaces prisma: any - Define TenantSecrets interface — replaces getTenantSecrets: () => Promise<any> - Define BusinessProfileContext interface — replaces businessProfile?: any in 3 methods - Define BusinessProfileExtraction interface — extractBusinessProfile now fully typed - Define ContactData interface — replaces contact: any in generateCrmCampaign - exerciseCriteria? typed as Record<string, unknown> instead of any - searchResults now typed as SearchResult[] instead of any[] DB enums: - CampaignHistory.status: String → CampaignStatus enum (SENT | DELIVERED | READ | FAILED) - AnalyticsLog.eventType: String → AnalyticsEventType enum (CLICK | READ | RESPONSE) - Migration 20260514000001 applied to production DB Zod validation: - TrackDaySchema: add exerciseCriteria (z.record) and badges (z.array(z.string)) - ai.ts /crm/generate-campaign: enforce contact.phoneNumber (required) via Zod Runtime safety: - wh…2a3e2a1 -
fix(audit): resolve remaining minor technical debt issues
CognxSafeTrack +1: Claude Sonnet 4.6AI/BYOK: - generateText now respects tenant BYOK keys via getProvidersForTenant() (was using global registry) - generateText now applies exponential backoff retry via AIService.withRetry() Backend: - notifications.ts: return 503 instead of 200+undefined when VAPID_PUBLIC_KEY absent - BillingPage: support WA number now reads from VITE_SUPPORT_WA_NUMBER env var (was hardcoded fake number) - KnowledgeBasePage: show toast.error on delete failure (was silently swallowed) DB: - Add @@index([organizationId, exerciseStatus]) on UserProgress for live-feed query performance - Add @@index([createdAt]) on AuditLog for time-range admin queries - Migration 20260513000005 applied to production DB i18n: - Add knowledge.delete_error translation key to all 4 locales (fr, en, es, pt) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>d74c1b3
-
fix(audit): resolve 15 critical and major technical debt issues
CognxSafeTrack +1: Claude Sonnet 4.6Security: - campaigns.ts: prevent cross-tenant write by using req.organizationId instead of URL param - billing.ts: restrict org mode change to SUPER_ADMIN only (was incorrectly allowing ADMIN) - CommandHandler: guard SEED command with NODE_ENV !== 'production' - search.ts: return [] instead of fabricated mock data when SERPER_API_KEY absent - scheduler: filter out hard-stopped organizations from nudge and lesson delivery - payments.ts: HMAC signature verification for Wave and Orange Money webhooks + fix track.priceAmount Data integrity: - schema: TrackDay.dayNumber Float → Int, Enrollment.currentDay Float → Int - schema: add @@unique([userId, trackId]) to Enrollment to prevent race condition double-enrollment - Migration 20260513000004 applied to production DB Performance: - admin.ts /live-feed: fix N+1 queries with batch findMany + in-memory map - organizations.ts /:id/messages: add pagination (was unbounded, OOM risk) - crm-service.ts listMessages: add skip/limit/total support Co…97f541f -
chore: remove Stripe — replaced by Orange Money / Wave payment gateway
CognxSafeTrack +1: Claude Sonnet 4.6Delete stripe.ts route file, remove import and /v1/stripe registration from app.ts, drop stripe npm dependency, remove deprecated stripeWebhookRoute stub from payments.ts, and remove stripeCustomerId from admin Organization interface. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>641e72b -
feat: complete agentic audit roadmap — retry, KB generate, real costs, traceId, rate-limit
CognxSafeTrack +1: Claude Sonnet 4.6- ai-sdk: exponential backoff retry (3 attempts, 1s→2s) in callWithFailover; skips 4xx and QuotaExceededError - api/whatsapp: traceId (crypto.randomUUID) propagated to worker bridge via x-trace-id header - api/whatsapp: route-level rate limit 500/min on POST /webhook, returns 200 on rejection - api/organizations: POST /:id/kb/generate — GPT-4o-mini JSON mode generates FAQ pairs, embeds via IndexingService - api/indexing-service: new service batching OpenAI text-embedding-3-small + pgvector INSERT - admin/AnalyticsPage: real cost from UsageEvent aggregate + byFeature breakdown table - admin/KnowledgeBasePage: KB auto-generation panel (description textarea, generate button, FAQ preview) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>b438786 -
fix: security hardening, real AI costs, traceId propagation & audit logging
CognxSafeTrack +1: Claude Sonnet 4.6- S1: enforceOrgIsolation — organizationId always from JWT for non-SUPER_ADMIN (header ignored) - S4: auditService.log() on ORG_MODE_CHANGED, ORG_PERSONALITY_UPDATED, ORG_BYOK_KEYS_UPDATED - O1: traceId (UUID) generated at webhook, forwarded via x-trace-id header, injected into BullMQ job data, threaded through whatsapp-logic handler chain - D3: /v1/analytics/usage now aggregates real UsageEvent.costUsd + tokens (tokensIn/Out) with per-feature breakdown instead of token estimates - D4: BrandingDataSchema added to shared-types — brandingData strictly typed (logoUrl, primaryColor, orgName) instead of z.any() - A6: InboundHandler fetches user.language in parallel with org token, passes it to Whisper - A2: sendLessonDay() caches personalized lesson text in Redis 6h by (trackDayId, activity, language) - P1: sendLessonDay() parallelized — user+trackDay batched in one include, userProgress fetched in parallel - D2: ExerciseHandler Redis 30s dedup lock prevents double exercise submission Co-A…87afdf1 -
feat: agentic platform — text-to-sql, pedagogy advisor, security hardening & performance
CognxSafeTrack +1: Claude Sonnet 4.6- Analytics: POST /v1/analytics/query — natural language → GPT-4o-mini → SELECT → results (Text-to-SQL) - Analytics UI: example chips, SQL results table, 'Voir SQL' toggle - Billing: role guard on change_organization_mode (ADMIN+ only) - Nav: /ai-setup accessible to all org members (not just super admin) - Scheduler: startPedagogyAdvisor() — Wednesday 08:30 UTC cron, at-risk nudges (5 languages) + email report - S1 security: organizationId always from JWT for non-super-admin (never from header) - A6: InboundHandler passes user.language to Whisper transcription (Wolof hint: 'fr') - A2: Redis 6h cache for personalized lessons keyed by (trackDayId, activity, language) - P1: sendLessonDay() parallelized from 4 sequential DB queries to 2 (user+trackDay batch, userProgress parallel) - D2: Redis 30s dedup lock on exercise submission to prevent double-submit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>5b8761d -
feat: Claude Sonnet 4.6 provider + Stripe billing automation
CognxSafeTrack +1: Claude Sonnet 4.6AI: - packages/ai-sdk: ClaudeProvider implements LLMProvider via tool_use structured outputs + zod-to-json-schema - Claude registered at priority 120 (TEXT only), supersedes Gemini for feedback/lessons/chat — -40% cost, better French - ANTHROPIC_API_KEY added to API and worker env validation Stripe: - apps/api/src/routes/stripe.ts: POST /v1/stripe/webhook handles checkout.session.completed, subscription.updated/deleted, invoice.payment_failed/succeeded - Schema: stripeCustomerId + stripeSubscriptionId added to Organization - tenant.ts: 402 gate for isHardStopped orgs (x-api-key calls always pass) - ClientsManagementView: SUSPENDU badge on hard-stopped orgs - STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET added to config Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>4a0c3ba -
feat(crm-ux): tag management, unread badge, handoff release UI
CognxSafeTrack +1: Claude Sonnet 4.6- ContactsPage: tag filter chips, per-contact tag editing with inline input, add/remove tags via PATCH API, colSpan fix for new Tags column - MainLayout: SSE-powered unread message badge on conversations nav item, resets on navigation - UserListPage: soft-delete button, handoff status fetch + "Libérer l'IA" button in conversation modal - admin routes: DELETE /users/:id (soft-delete), GET/DELETE /users/:id/handoff endpoints Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>42a2598 -
fix(org-resolve): support legacy non-UUID custom IDs (e.g. default-org-id) + analytics orgId header
CognxSafeTrack +1: Claude Sonnet 4.6- resolveOrgId now uses findFirst with OR [slug, id] so literal custom IDs like "default-org-id" (the seed org XAMLÉ Global) are resolved correctly instead of returning 404 - ContactsPage.tsx: pass selectedOrgId to analytics/usage call — was missing the x-organization-id header, causing systematic 400 on the contacts page Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>2cc44c6 -
feat(agentic-week2-4): 5 major features — AI content generator, handoff, tags, soft-delete, SSE
CognxSafeTrack +1: Claude Sonnet 4.6## Agent Créateur de Contenu (Semaine 3) - AIService.generateCurriculum() — structured GPT-4o prompt generates full Track + TrackDays - POST /v1/organizations/:id/content/ai-generate — persists curriculum directly to DB - TrackListPage: "Générer avec IA" button + modal (description, numDays, language, audience) - Auto-redirects to the generated program days after creation ## Human Handoff Detection (Semaine 4) - AIAgentHandler: detects 15 multilingual handoff keywords (FR/EN/ES) - Sets Redis flag handoff:{userId}:{orgId} (24h) — pauses AI responses - Emails org admin with contact info + link to admin UI - Handles subsequent messages while in handoff mode gracefully ## Contact Segmentation by Tags (Semaine 2) - Contact.tags String[] added to Prisma schema (migration + db:push applied) - GET /contacts/tags — list all unique tags in org - PATCH /contacts/:id/tags — set tags on a contact - GET /contacts?tags=tag1,tag2 — filter contacts by tags (hasSome) ## SSE Real-time Updates (Semaine…99f6bca -
fix(conversations): resolve /conversations blank page + slug→UUID KB routes + WABA banner
CognxSafeTrack +1: Claude Sonnet 4.6- Add missing /conversations route to App.tsx (was nav-linked but unregistered) - Apply resolveOrgId() to all 5 KB routes so slug-based org IDs (e.g. xamle-admin-org) no longer cause 404/400 - TemplatesPage: detect WABA-not-configured 400 and show amber banner instead of silently failing - KnowledgeBasePage: show toast on index-kb 400 (no KB URL) with actionable message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9f72dd7 -
feat(agentic-week1): conversational memory, RAG threshold, wallet alerts, weekly reports, campaign scheduling
CognxSafeTrack +1: Claude Sonnet 4.6- AIAgentHandler: Redis sliding window (20 entries, TTL 24h) for conversation history injected into system prompt — AI_AGENT now remembers context - IndexingService.searchRelevantContext: cosine threshold 0.70 — returns '' if no chunk is relevant so agent responds honestly instead of hallucinating - add-hnsw-index.ts: one-shot HNSW index script (m=16, ef=64) for ~10x faster pgvector cosine search on large knowledge bases - scheduler.ts: hourly wallet alert (email via Brevo if < 3 days runway, 6h Redis suppression) + weekly report every Monday 07:00 UTC with trend vs previous week and color-coded wallet status - queue.ts + campaigns.ts: sendAt ISO-8601 parameter on broadcast/campaign routes — BullMQ delay option schedules jobs natively, no cron needed - docs/agentic/: roadmap updated with Semaine 1 marked complete Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>98240fd -
feat(admin-chat): make AI copilot a full super-admin via platform knowledge + agentic tools
CognxSafeTrack +1: Claude Sonnet 4.6- Inject PLATFORM_KNOWLEDGE constant into all /chat system prompts: detailed descriptions of all 4 modes (EDTECH/CRM_MARKETING/AI_AGENT/CUSTOMER_SERVICE), plans & credit pricing, WhatsApp setup (WABA ID/Business ID/token), template rules (variables, MARKETING vs UTILITY), KB indexing, wallet/hard-stop logic - Remove "contactez le support" fallback — AI now answers all platform questions - Add OpenAI function-calling tools for agentic admin actions: - get_organization_settings: read full org config - change_organization_mode: switch org mode (EDTECH/CRM_MARKETING/AI_AGENT/CUSTOMER_SERVICE) - update_ai_agent_personality: update coreMission, tone, temperature - Implement two-pass completion loop: tool calls executed against Prisma, results fed back for final natural-language answer with actionPerformed metadata Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9061927 -
feat(admin): UX non-tech complète + i18n 4 langues
CognxSafeTrack +1: Claude Sonnet 4.6- Phases 1-5 : masquage JSON avancé, chat IA global (AdminChat), onboarding WhatsApp guidé, templates assistés IA, agent IA setup guidé - i18n exhaustif : ~70 nouvelles clés en FR/EN/ES/PT pour tous les composants ajoutés (AdminChat, AIAgentSetup, SettingsPage, TemplatesPage, OnboardingWizard) - Chat IA multilingue : l'assistant répond désormais dans la langue de l'utilisateur (FR/EN/ES/PT) sur toutes les pages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>66ff7a1 -
feat(billing): refonte UX non-tech + enrichissement contexte IA
CognxSafeTrack +1: Claude Sonnet 4.6Frontend (BillingPage): - Renommage "Wallet" → "Crédits disponibles" (langage non-technique) - Badge projection dynamique "À ce rythme : encore ~X jours" calculé depuis les transactions des 7 derniers jours - Questions suggérées cliquables (quick-reply chips) avant le 1er message - Suppression "Tokens entrants/sortants" (jargon incompréhensible) - Transactions filtrées : BYOK masqué, layout liste simple vs tableau - Section "Où vont vos crédits IA ?" remplace le jargon feature/type - Indicateur de frappe animé (3 points) dans le chat - Packs recharge avec explication "≈ N messages IA" et badge populaire - Texte WhatsApp clarifié : "Gratuit pour vous — Meta facture directement" Backend (billing.ts /chat): - Wallet balance + isHardStopped ajoutés dans le contexte IA → l'assistant peut maintenant répondre "combien de crédits me reste-t-il" - Burn rate (débit moyen 7j) calculé et injecté : "À ce rythme, votre solde durera encore X jours" - Statut wallet (SUSPENDU / bas / actif) expl…f5ea14d -
fix(security): Sprint 1 — five critical debt fixes
CognxSafeTrack +1: Claude Sonnet 4.6C1: Await debitWallet() in usage-tracker and broadcast/campaign handlers so billing failures are caught and logged, not silently swallowed. C2+C3: Rewrite debitWallet() as a single Prisma interactive transaction: balanceAfter now set atomically; isHardStopped flag set in the same transaction as the balance decrement — no window between debit and stop. C4: Add X-Hub-Signature-256 HMAC-SHA256 verification to POST /webhook. Uses preParsing hook for raw body capture; fails open when WHATSAPP_APP_SECRET is not configured. H3: Fix payment webhook race condition — use updateMany(where: PENDING) as atomic idempotency guard; only the request that flips PENDING→COMPLETED proceeds to enrollment creation. H7: Replace $queryRawUnsafe/$executeRawUnsafe with $queryRaw/$executeRaw Prisma.sql template literals in indexing.ts; user-controlled values (organizationId, limit) are properly parametrized. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>fae3e96 -
feat(billing): close 3 remaining wallet gaps from implementation plan
CognxSafeTrack +1: Claude Sonnet 4.6⑫ sendTextMessage() wallet debit coverage: - Add organizationId?: string to sendTextMessage() config — calls trackWhatsAppSent() automatically after each successful send - NudgeHandler, ContentHandler, EnrollHandler, MediaHandler, AdminHandler: pass organizationId in tenantConfig so every outbound message triggers the debit without touching each callsite individually ⑯ GET /billing/summary now includes wallet block: - wallet: { balance, isHardStopped, creditToFcfa, balanceFcfa } appended to every summary response ⑱ BillingPage: wallet transaction table: - Table showing last 20 transactions (date, type, description, amount, balance after) - Type labels with emoji (recharge vs debit), BYOK badge, red/green amount colouring - Inserted between feature breakdown and chat widget Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>f5126bc -
feat(billing): implement full wallet/ledger system with hard-stop enforcement
CognxSafeTrack +1: Claude Sonnet 4.6- Add walletBalance + isHardStopped to Organization schema; WalletTransaction ledger model with full audit trail - wallet.ts (worker): debitWallet (atomic $transaction), creditWallet (with AuditLog), checkWalletBalance (Redis 30s cache), alert queuing (low/critical/exhausted/restored) - wallet.ts (api): creditWallet for admin top-up with cache invalidation - usage-tracker.ts: debitWallet integrated into trackAiUsage, trackAudioUsage, trackWhatsAppSent; BYOK orgs skipped - BroadcastHandler + CampaignHandler: bulk debit after send (BROADCAST_PER_USER per recipient) - requireCredits.ts: migrated from aiCreditsUsed/Limit to walletBalance/isHardStopped; shares Redis cache key with worker - whatsapp-logic.ts: replaced assertCredits/CreditExhaustedError with checkWalletBalance/WalletExhaustedError - ai.ts: added requireCredits preHandler to all 11 AI routes (was 4) - billing.ts: /admin/allocate now calls creditWallet(); new GET /wallet route (balance + 20 last txns) - BillingPage: wallet card…0fd3320 -
feat(billing): enforce AI credit limits across all surfaces
CognxSafeTrack +1: Claude Sonnet 4.6- credit-guard.ts (worker): assertCredits() blocks AI when creditsUsed >= creditsLimit; Redis cache 60s prevents DB hit on every message - whatsapp-logic.ts: credit guard injected before handler chain; RECHARGE keyword bypasses the guard so users can always request a payment link - CommandHandler: new RECHARGE keyword sends payment URL or contact message - i18n: credit_exhausted + recharge_link + recharge_contact in 5 languages - requireCredits middleware (api): 402 on personalize-lesson, tts, generate-feedback, crm/generate-campaign - billing route: POST /v1/billing/admin/allocate (SUPER_ADMIN only) supports set/add actions + optional usage reset + Redis cache invalidation - ClientsManagementView: "Crédits IA" button (SUPER_ADMIN only) per org opens allocation modal with set/add/reset controls - BillingPage: red exhaustion banner when creditsUsed >= creditsLimit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>6484585
-
feat(billing): complete billing system, push notifications, and tech debt fixes
CognxSafeTrack +1: Claude Sonnet 4.6- Usage tracking: instrument pedagogy, audio transcription, AI Agent mode (was silently untracked) - EmailHandler: implement templates 2 (password reset), 3 (quota alert), 4 (monthly report) - Quota alert: auto-queue email + push at 85% threshold via maybeQueueQuotaAlert - Push notifications: PushHandler + send-push BullMQ job; sendPushToUser scoped by organizationId to prevent cross-tenant reads - BillingPage: add credit recharge modal with 3 packs, null guard for missing orgId - i18n: add nav.billing key in fr/en/es/pt locales - Tests: billing.test.ts (10 tests), payments.test.ts (13 tests) - Tech debt: fix empty catch blocks in ai-pedagogy.ts, exclude usage tokens from CRM API response, pass organizationId to generateChat in AIAgentHandler - Docs: add backlog and business model to docs/business/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>8280d7d
-
fix(audit): resolve technical debt + UX issues from 2026-05-11 audit
CognxSafeTrack +1: Claude Sonnet 4.6Backend: - Add missing DB indexes on Response.userId and Response.enrollmentId - Add 15s timeout with fallback on AI personalization in pedagogy.ts - Fix race condition in NavigationHandler with atomic $transaction - Fix OnboardingHandler hard reset: use FK cascades, 5-lang confirmation - Change seedDatabase to static import in CommandHandler - Add proper pagination to /enrollments admin endpoint UX/Admin: - Fix responsive grid on dashboard stats (1col mobile → 5col desktop) - Show filename + status label during file upload in ContactsPage - Add success/error toast with import summary on contact CSV/XLSX upload - Add inline form validation with field-level errors in ClientsManagementView - Wire up Copy button on AI-generated messages in ContactsPage Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9fe2cc7 -
refactor(debt): resolve all 10 technical debt items from audit
CognxSafeTrack +1: Claude Sonnet 4.6- Redis: singleton pattern (lib/redis.ts) in api + worker, removes 16+ new Redis() calls - Meta token: systemUserTokenIssuedAt field + weekly cron monitor (warn @50d, critical @55d) - Rate limiting: @fastify/rate-limit global 200/min, login 10/min, forgot-password 3/min - Dead code: delete WhatsAppConnectButton.tsx, archive one-off scripts - Silent catches: logger.warn(err) on all backend catch blocks, console.warn on frontend - Meta API types: 5 typed interfaces replace 6 as any casts in organization.ts - Frontend logger: lib/logger.ts wraps Sentry (active when VITE_SENTRY_DSN is set) - Migration: add systemUserTokenIssuedAt + metaBusinessId to Organization schema Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>a966957
-
feat(whatsapp-setup): direct config mode for platform-owner accounts
CognxSafeTrack +1: Claude Sonnet 4.6Backend: - accessToken now optional in /whatsapp-setup — falls back to WHATSAPP_ACCESS_TOKEN env var (for platform-owner self-setup) Frontend: - "Reconfigurer WhatsApp" button now opens a choice modal instead of launching Embedded Signup directly - Option A (Direct): enter WABA ID + optional token — for accounts already configured on Meta (token from env var used if field left empty) - Option B (Embedded Signup): Facebook login popup — for new client accounts - Phone Number ID pre-filled from existing DB record - Auto-refreshes Meta status after successful save Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>2f67731 -
feat(meta): live Meta verification status + WhatsApp setup fixes
CognxSafeTrack +1: Claude Sonnet 4.6- GET /v1/organizations/:id/meta-status — fetches WABA account_review_status and business verification_status live from Meta Graph API, cached 1h in Redis - POST /v1/organizations/:id/meta-status/refresh — force-invalidates the cache - ClientsManagementView: replaced static metaVerificationStatus field with live per-org Meta status (WABA badge, Business badge, derived daily limit) - launchEmbeddedSignup: fixed race condition — now waits for both the OAuth code (FB.login) and WABA/phone IDs (WA_EMBEDDED_SIGNUP_EVENT) before resolving - /whatsapp-setup: exchanges OAuth code for long-lived Meta token (60 days) before storing; bypasses exchange if token already starts with EAA - OnboardingWizard: removed broken WhatsApp step (3 steps: welcome, legal, AI) - OrganizationSchema: removed non-existent DB fields (contractSigned, contractSignedAt, contractSignerName, metaVerificationStatus, dailyMessageLimit) that caused prisma.organization.create() to throw Unknown arg at runtime Re…bd9eb5b -
fix(webhook): forward to localhost:8082 worker bridge, not RAILWAY_INTERNAL_URL
CognxSafeTrack +1: Claude Sonnet 4.6RAILWAY_INTERNAL_URL points to Railway's public URL (port 443 → API on 7860), not the Worker bridge on 8082. Since both HF Space and Railway run the Worker bridge locally in the same container, forward to http://localhost:8082 directly. The bridge owns org resolution, WEBHOOK mode handling, and all Redis queue ops. Remove now-unused whatsappQueue and extractWhatsAppPayload imports from API route. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>c12937b -
fix(webhook+worker): restore gateway forwarding and fix token decryption
CognxSafeTrack +1: Claude Sonnet 4.61. Gateway architecture restored (whatsapp.ts) - When RAILWAY_INTERNAL_URL is set (HF Space), forward raw payload to Railway worker bridge instead of adding directly to Redis. - Original architecture: HF → Railway bridge → Redis → Worker. - Root cause of INSCRIPTION silence: direct Redis queue from HF Space was failing because Redis connection doesn't work from HF containers. 2. MessageHandler: decrypt systemUserToken before use - Was reading org.systemUserToken raw (AES-encrypted) from prisma, sending encrypted string to Meta → auth failure. - Now uses getCachedOrganization() which calls decryptOrgSecrets(). 3. AdminHandler: eliminate double DB query + empty enrollmentId - Was calling prisma.enrollment.findFirst() twice for same record. - Validates enrollment exists before proceeding; adds mediaUrl to Response. 4. CommandHandler: route MENU_HISTORIQUE through queue - Direct sendInteractiveListMessage() call bypassed tenant config. - Now use…2b96936 -
fix(webhook): handle all message types via extractWhatsAppPayload + true Async-First
CognxSafeTrack +1: Claude Sonnet 4.6Previous fix only extracted message.text?.body, silently dropping: - interactive/button_reply (LANG_FR, LANG_WO, SUITE, APPROFONDIR, sector choices) - interactive/list_reply (sector selection list) Now uses extractWhatsAppPayload() from @repo/shared-types which handles all types. Responds 200 before setImmediate enqueue (true Async-First, < 1ms to Meta). Zod schema simplified to z.any() for messages content (Meta types are unbounded). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>ad9d288 -
fix(webhook): resolve org by phone_number_id instead of wabaId, enqueue async
CognxSafeTrack +1: Claude Sonnet 4.6Two silent failures broke INSCRIPTION since multi-org was added: 1. Lookup by wabaId failed when org.wabaId was unset → org=null → message dropped 2. whatsappService.handleIncomingMessage does not exist → TypeError swallowed by catch Fix: resolve organizationId from value.metadata.phone_number_id via getOrganizationByPhoneNumberId(), then enqueue handle-inbound to BullMQ. Restores Async-First rule (webhook responds 200 immediately, worker processes). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>15d45f6 -
feat: backlog P0→P3 — toast system, payments, tenant isolation, feedback handler, i18n parity
CognxSafeTrack +1: Claude Sonnet 4.6P0 — Critical fixes - Fix generate-feedback job silently dropped: register FeedbackHandler in worker index.ts - Wire FeedbackHandler: AI feedback, Day 11 TeamMember extraction, atomicity enforced P0 — Toast system (replace all alert()) - Add useToast hook + ToastContext + Toaster component - Replace every alert() across admin pages with toast.success/error/warning/info - Migrate all raw fetch() in pages to api.upload() / api.get() / api.post() P1 — Type safety - CrmAIAssistant: icon prop any → React.ElementType - useAudioRecorder: add onError callback instead of alert() - api.ts: add upload() method for multipart FormData P1 — Payment gateway - Add payment-gateway.ts: Orange Money + Wave initiation and webhook parsing - Rewrite payments.ts routes: initiate, status, webhook handlers with auto-enrollment on SUCCESS P1 — AI regression tests - Add apps/api/test/regression/ai-gold-standards.test.ts (12 tests, 5 suites) P2 — Visual recruitment (Day 11) - FeedbackHandler extracts TeamMem…6dd9bad
-
fix: resolve login 400 — auto-resolve organizationId from email when omitted
CognxSafeTrack +1: Claude Sonnet 4.6Backend now falls back to findUserByEmailOnly() when the frontend doesn't send organizationId (deployed admin still uses old build). Password verification still required — auth security unchanged. Also adds organizationId field to login form for first-time users, and removes last default-org-id references from tenant.tsx. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>4e2a593
-
feat: implement whatsapp templates management with security hardening, audit logs, and crm integration
CognxSafeTrack0f2f80a -
chore: stabilization, audit fixes and project synchronization
CognxSafeTrack7b0c22b
-
docs: finalize audit report and apply immediate fixes (#1, #10, #16)
CognxSafeTrackb0e801b -
feat: implement bulk whatsapp campaigns with BullMQ and modular activation refactor (isCrmActive/isEdTechActive)
CognxSafeTracke286845 -
fix: security hardening, log all silent catch blocks, remove prisma cast
CognxSafeTrack +1: Claude Sonnet 4.6Security: - whatsapp.ts: remove WHATSAPP_VERIFY_TOKEN hardcoded fallback — return 500 if env var is missing instead of silently accepting a public default - rateLimit.ts: key by req.ip (not spoofable x-organization-id header) Code quality — 7 empty catch blocks now log with context: - NudgeHandler, AdminHandler (+ missing logger import), EnrollHandler (x2), MediaHandler (x2), organization.ts invalidateOrgCache - admin.ts: remove (prisma as any).normalizationRule — model is in schema, cast was unnecessary Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>aa4f69f -
fix: worker cleanup await, Stripe upsert completeness, redundant schema index
CognxSafeTrack +1: Claude Sonnet 4.6- cleanup.ts: cron callback now async/await so cleanWorkerTempFiles() errors are caught instead of silently dropped fire-and-forget - payments.ts: upsert update clause now includes status, amount, currency so a Stripe webhook replay fully idempotent instead of leaving stale fields - schema.prisma: remove redundant @@index([userId, trackId]) on UserProgress — the @@unique constraint already creates a B-tree index on those columns Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>ec9b5da -
feat: centralize AI logic in @repo /ai-sdk, harmonize worker HTTP clients
CognxSafeTrack +1: Claude Sonnet 4.6Architecture refactoring: - Extract all AI providers (OpenAI, Gemini, ProviderRegistry, ffmpeg, search) from apps/api/src/services/ai/ into new packages/ai-sdk package - API's services/ai/index.ts is now a thin wrapper that injects prisma, redis and organization context into the shared AIService - Worker gains @repo/ai-sdk dependency; ai-pedagogy.ts delegates to aiService.generatePersonalizedLesson instead of duplicating logic - New apiClient (Axios, 30s timeout) and whatsappClient replace bare fetch calls in InboundHandler and MediaHandler for consistent error handling - Prisma seed fixed: Tracks now include organizationId for multi-tenant compliance Build: all 8 packages pass pnpm turbo run build from clean state. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>2849f33 -
feat(crm): optimize multimedia pipeline and harden contact schema
CognxSafeTrack- Refactored CrmConversationalDashboard into modular components and hooks (FileImporter, useAudioRecorder) - Centralized contact normalization and bulk import in ContactService - Implemented binary streaming for media proxy to reduce memory pressure - Hardened database schema with MediaType Enum for better data integrityc5fde49 -
feat: implement inbound audio/image transcription, secure media proxy, and client-side bulk contact import
CognxSafeTrack30d60ea
-
chore: CRM stabilization sprint completion
CognxSafeTrack- Refactor: Decomposed CrmConversationalDashboard into modular components (Inbox, AIAssistant, StatsHeader) - Fix: Centralized entity resolution with EntityResolver service (API & Worker) - Fix: Strict Prisma typing, removed 'as any' casts in organization and whatsapp routes - Fix: Implemented broadcast message persistence and accurate usage quota tracking - Schema: Added MessageStatus and status field to Message model4339e77 -
fix: implement GET organization by ID route and direct password setting for new tenants
CognxSafeTracke00083b