Every change we ship to katura1999.com β features, fixes, security patches, the lot. Pulled straight from our private GitHub repository so you can see exactly what was built and when.
By the numbers
Lines of code
828,292
Web platform β TypeScript, React, Prisma, CSS
iOS
78,204
Swift + SwiftUI lines
Android
3,117
Kotlin + Jetpack Compose lines
All platforms
909,613
Web + iOS + Android combined (44.5Γ the King James Bible)
Characters written
35.04M
35,041,420 total characters
Updates pushed
1,893
exact commit count on main
Current version
v1.18.93
build 1893 Β· 1c76417
Database models
392
across 47 schema files β most SaaS platforms have 20β50
API endpoints
955
individually routed β Stripe's public API has ~400
Translated strings
82,512
every string, in 24 languages
System permutations
10^287
2^955 endpoint combinations β more than atoms in the observable universe (10^80)
Project age
6mo 15d
since Dec 14, 2025
Pre-AI dev hours
30.3K hrs
909,613 lines Γ· 30 LOC/hr β equivalent to 14.6 years (senior engineer, no AI)
With-AI dev hours
7.6K hrs
4Γ AI productivity multiplier (2024β2026 studies) β equivalent to 948 days
Equivalent firm cost
$7,858,741
live ticker Β· Katura rate: $60 USD/hr
Hours estimated from source line count at 30 LOC/hr (industry benchmark for production-quality TypeScript/React without AI assistance), with a 4Γ multiplier for AI-assisted development per published 2024β2026 enterprise studies. Equivalent Firm Cost uses a $250/hr loaded billable rate reflecting a premium engineering firm building enterprise-grade SaaS β and ticks up live, because the project is still being actively built.
Commit history
1,893 updates pushed
Showing page 6 of 40 Β· 251β300 of 2,000 fetched
Monday, May 18, 2026
15 updates pushed
Docs3:57 AM Β· ZRosserMcIntosh
session summary + production incident postmortem 2026-05-18
api/upload: filePath was '${bucket}/${fileName}' β Supabase Storage's .from(bucket).upload(path) prepends the bucket automatically, so this produced 'order-attachments/order-attachments/filename' β 400 HTML response (not JSON) β StorageUnknownError. Fix: filePath = fileName only.
prisma/migrations/add_consultation_meeting_fields.sql: Consultation model had ~30 new columns (meetingId, invite tokens, UTMs, GDPR consent, region routing, CRM/EngagementLead FKs, etc.) defined in schema but never migrated to the live DB. prisma.consultation.findMany() returns all columns by default β P2022 on every /admin dashboard load. Migration uses ADD COLUMN IF NOT EXISTS throughout β safe to re-run.
meeting-cleanup cron: x-vercel-cron header value was 'true' should be '1'; CANCELLED enum doesn't exist in MeetingStatus β changed to ENDED; removed CANCELLED from participant reconcile IN clause
home-cache: bump getNewArrivals timeout 5000β8000ms (matches all other calls); old deployed build was 2000ms, this ensures new deploy never regresses
jewel-vox: revalidate 300β3600s β 5-min ISR was causing cold-start DB stampede (3 concurrent revalidations Γ article.count() exhausting PgBouncer connection_limit=1)
admin/reviews: p."images" column does not exist β replaced with correlated subquery against ProductImage table; productImage now returned directly in row
staff-alerts: StaffAlert.title column in Prisma schema was never migrated to DB; added prisma/migrations/add_staff_alert_title.sql with IF NOT EXISTS guard
Account takeover via register route: passwordless users (Shopify imports, guest checkout) can no longer have their password set by anyone who knows their email. Now sends a signed verification email (same token model as forgot-password) requiring email access.
check-email route: no longer returns 'set-password' action (was leaking account state). Returns identical 'continue' for all cases.
LiveKit rooms: GET/DELETE now require requireKaturaAdmin() auth. Previously fully unauthenticated β anyone could list/delete rooms.
Employee login open redirect: callbackUrl now validated via safeRedirectUrl() β rejects //, /\, protocol-relative, absolute.
K99 platform login: same fix via safeRedirectUrl().
Customer login: reject protocol-relative //evil.com in callbackUrl.
Kill switches: upgraded from auth() to requireKaturaAdmin({roles: ['OWNER']}) + tenant_katura check. Previously any authenticated user could toggle platform kill switches.
ShipStation webhook: added SHIPSTATION_WEBHOOK_SECRET enforcement. Previously unauthenticated β spoofable User-Agent was only check.
Token summary route: now requires requireKaturaAdmin().
Cron auth: created src/lib/cron-auth.ts helper that requires CRON_SECRET (rejects spoofable x-vercel-cron: 1 from externals). Applied to email/scheduled route as template for remaining crons.
WishlistProvider now checks useSession() status before fetching. Unauthenticated users never hit /api/wishlist (zero wasted compute, eliminates the 401 spam in logs).
add missing engagement carousel + DB resilience hardening pass
src/components/engagement/love-stories-carousel.tsx and src/app/api/articles/engagement-stories/route.ts were created in an earlier session but never committed. The consultation page already imports the carousel, so the Vercel Turbopack build failed with 'Module not found'. Now tracked.
src/lib/db-resilience.ts: Added 'database operation timed out' and '[home-cache]' to retryable message detection.
src/lib/cache/home-cache.ts: Wrapped all homepage DB reads in withFallback() + withResilientQuery(). Sections degrade to empty arrays instead of crashing.
src/app/admin/page.tsx: Replaced 7-way Promise.all() dashboard burst with sequential resilient reads. Dashboard widgets degrade to zeros on failure.
src/app/[locale]/jewel-vox/page.tsx: Sequential resilient article/category fetch with typed serialized shapes. Added CategoryWithCount and SerializedArticle types.
src/app/[locale]/jewel-vox/[slug]/page.tsx: View-count update made non-blocking. Related articles wrapped in fallback. Metadata lookup uses retry.
src/app/[locale]/jewel-vox/article-list.tsx: publishedAt typed as string|null (JSON serialization converts Dates to strings).
src/app/api/articles/route.ts: Sequential resilient list + count queries.
src/app/api/admin/articles/route.ts: Same pattern.
src/app/api/articles/engagement-stories/route.ts: Wrapped in resilient queries.
alertQuotaWarning(): fires when tenant hits β₯90% of any feature quota (ALLOW_WITH_WARNING); fetches tenant name/slug lazily, posts to #alerts and #tenant-{slug}; fully fire-and-forget
alertBillingLock(): fires on billingLock=true from overage cron or billing reset; posts critical alert to #alerts + #tenant-{slug} with admin billing link
alertKillSwitchEnabled/Disabled(): fires on every kill switch toggle; critical alert with switch key, reason, and activating admin
alertChargeRequiresAction(): fires when Stripe requires 3DS/SCA on an off-session overage charge
Wire alertQuotaWarning into usage-gateway.ts:
Fires fire-and-forget at ALLOW_WITH_WARNING decision point
Skipped on dryRun requests
Wire alertBillingLock into overage-charge cron:
Fires at both exception and result-failure lock paths
Also fires alertChargeRequiresAction on requires_action outcome
Includes tenant name/slug via query include
Wire alertBillingLock into billing-period-reset cron:
Fires when end-of-period unpaid overage exceeds lock threshold
storage category tabs, server-side channel bootstrap, messenger split foundation
Photos / Videos / CAD & 3D / Documents / All tabs above file grid
Client-side filter via matchesCategory() β no extra API calls
CAD_EXTS set covers obj,stl,step,fbx,glb,gltf,3dm,blend,skp etc.
Tab shows file count; section header updates to reflect active category
Empty-category state with 'Show all files' link
Category resets to 'all' when navigating folders or switching views
useMemo'd displayFiles derived from files + category
New src/lib/messenger/get-channels.ts β shared function returning the same shape as the channels API route
messages/page.tsx converted to async server component β prefetches channels via getChannelsForUser() before first paint
messages-client.tsx updated to accept + thread initialChannels prop
Messenger.tsx: initialChannels prop seeds channels state immediately; isLoading starts false when data is provided; background refresh still runs for freshness β eliminating the initial channel-list spinner entirely
layout lock, translation mode, voice messages, + attachment menu
Globe icon button in channel header opens an inline popover with:
Toggle switch (on/off)
Language picker: English / Portuguese / Spanish / French
When mode is ON, every inbound message auto-translates on render (useEffect in MessageBubble β fires once per message ID per session).
Cache-first via existing /api/admin/translate (chat_message entity type). First user to translate a message pays the AI cost; every subsequent user in any channel that shares that messageId gets the cached result instantly.
'Show original (Portuguese)' / 'β English' toggle per bubble.
Translation mode preference persisted per channel in localStorage so it survives navigation and page refresh.
When mode is off, translate button shows on hover (existing behaviour).
signature HTML preserved, on by default, image attachments wired end-to-end
Add separate signatureHtml state (never touches the textarea, so HTML tags survive).
Add signatureEnabled boolean state (default true β on by default).
New useEffect: rebuilds signatureHtml whenever the compose dialog opens or the fromAccountId changes, so the correct per-account signature is always loaded automatically.
insertSignature() now toggles signatureEnabled instead of appending raw HTML to the body.
Signature rendered as a read-only HTML preview below the textarea with a small β dismiss button; the βοΈ Sig toolbar button shows strikethrough when disabled.
At send time, bodyWithSignature = body + signatureHtml (when enabled) is assembled server-side-safe before translation / dispatch, so the full HTML β including the logo <img> β is sent.
Fix attachment upload: formData field renamed 'file' β 'files' to match the route's formData.getAll('files').
Send payload changed from attachmentIds (just IDs) to attachments (full objects with storageUrl / fileName / mimeType / fileSize) so the send route has everything it needs to fetch and encode files.
Add attachments field to sendEmailSchema (array of storageUrl / fileName / mimeType / fileSize objects).
Before the Brevo call, iterate attachments: download each file from Supabase Storage, base64-encode it, create the EmailAttachment DB record, and push to brevoAttachments[].
Pass brevoAttachments to sendBrevoEmail({ attachment: [...] }) so files actually arrive in the recipient's inbox.