A consumer-focused, multi-tenant deals & weekly-flyers aggregator covering Saudi Arabia's retail market. Spans 5 deployable applications — two Node.js backends, two Next.js 16 frontends, and a Flutter mobile app — backed by AI-driven flyer ingestion, an SEO infrastructure at scale, and a partner self-service portal.
Table of contents
- Executive summary
- System architecture
- Tech stack at a glance
- Admin backend (Node.js + AI pipeline)
- Client backend (Consumer API)
- Admin dashboard (Next.js 16)
- Public website (qooty.net, Next.js 16)
- Mobile application (Flutter)
- Cross-cutting concerns
- Highlights & engineering decisions
- Key metrics & achievements
Executive summary
Qooty is a shopping-companion platform that aggregates promotional flyers, weekly deals, and discounts from major Saudi retailers into a single, bilingual (Arabic/English) browsing experience. Users discover offers by city, store, brand, sub-category, or price tier; admins ingest flyers in bulk through three creation flows (URL link / PDF upload / web scraping); supermarket partners log into a self-service portal to manage their own listings; and a Gemini-powered AI pipeline extracts product cards, prices, and brand metadata from raw flyer images.
The platform is deployed as five independently scaled services orchestrated by PM2 (backend) and Vercel/Cloudflare (frontend), with MongoDB, Redis, BullMQ, Firebase, OpenAI, and Google Gemini as foundational services.
System architecture
┌────────────────────────┐
│ Google Gemini / │
│ OpenAI / Firebase │
└───────────┬────────────┘
│
┌────────────────────────────────────┼───────────────────────────────┐
│ │ │
▼ ▼ ▼
┌───────────────┐ PM2 multi-proc ┌───────────────────┐ Redis ┌────────────────┐
│ Admin Backend │ ───────────────► │ BullMQ PDF Worker │ ─────────► │ Daily Cron Jobs│
│ (manager.js, │ └───────────────────┘ └────────────────┘
│ partner.js) │
└──────┬────────┘ ┌───────────────────┐
│ │ Client Backend │ ◄──── Redis + node-cache
│ MongoDB ◄──────────────────│ (index.js) │
│ └────────┬──────────┘
│ │
▼ ▼
┌───────────────┐ ┌───────────────────┐
│ Dashboard │ │ qooty.net │ ◄── Cloudflare CDN + Vercel
│ (Next.js 16) │ │ (Next.js 16) │ SEO sitemaps + IndexNow
│ admin + store │ │ ar/en, RTL │
└───────────────┘ └─────────┬─────────┘
│
▼
┌───────────────────┐
│ Flutter App │ ◄── FCM push, deep brand
│ Android/iOS/Web │ content, PDF flyers
└───────────────────┘
Tech stack at a glance
| Layer | Stack |
|---|---|
| Backends | Node.js, Express 4, Mongoose 8 + MongoDB, BullMQ + Redis, PM2 (8 procs), Firebase Admin (FCM), JWT auth |
| AI ingestion | Google Gemini 2.5 Pro/Flash, OpenAI GPT, prompt caching, Sharp image cropping |
| Frontends | Next.js 16 (App Router), React 19, TypeScript 5, Tailwind v4, SWR, Zustand, next-intl, Recharts, Playwright, Vitest |
| Mobile | Flutter (Cubit/BLoC), Firebase Auth + Messaging + Storage, Google & Apple Sign-In, Syncfusion PDF viewer, easy_localization |
| Storage & cache | MongoDB, Redis, node-cache (in-process LRU), MEGA cloud, Cloudflare CDN |
| Comms | Firebase FCM, Nodemailer SMTP, IndexNow + Google/Bing pings |
| DevOps | PM2 ecosystems, Vercel hosting, Vercel cron, Cloudflare in front of Next.js |
1. Admin backend — qooty_admin_backend
The platform's control plane: powers the admin dashboard, partner portal, AI flyer-extraction pipeline, PDF/image workers, and scheduled cron jobs. Runs as 8 PM2 processes (manager, partner, worker, cron — prod + dev variants).
Stack
Express 4 · Mongoose 8 · MongoDB · BullMQ 5 + IORedis · JWT + bcrypt · Sharp · pdf-lib / pdf2pic / pdf-images · @google/generative-ai (Gemini) · openai · firebase-admin · nodemailer · multer · megajs · node-cron · winston · p-limit · PM2.
Architecture
Layered MVC: routers/ (23) → controllers/ (21) → services/ → models/ (22). Dedicated ai/, scripts/ (cron utilities), services/pdf2images/ (BullMQ worker), services/watermark/, utils/ (FCM, mailer, IndexNow, brand auto-fill, SEO copy prompts).
Entities (22 Mongoose models)
adminModel · affiliateModel · appSettingsModel · bannerModel · blogPostModel · brandModel · cartModel · categoryModel · citiesGroupModel · cityModel · featuredDealModel · marketModel · notifyModel · offerModel · pageModel · productModel · ratingAggregateModel · ratingModel · redirectModel · seoSettingsModel · subCategoryModel · userModel
Feature domains
Catalog & Commerce
- Products with bilingual fields and AI-driven brand auto-fill
- Hierarchical Categories + Sub-categories
- Brands (auto-generated metadata + multi-source logo discovery)
- Offers with PDF flyer upload and automatic expiry
- Featured Deals (curated promotions)
- Markets (retail chains) + Branches (geo-located stores)
- Cities + City Groups (regional segmentation)
- Homepage Banners
CMS & Content
- Blog Posts (admin + public read API)
- CMS Pages (slug-based, served to mobile/web)
- Mobile-app Ads Config (remote feature flags)
- App Settings
SEO infrastructure
- Per-entity SEO Settings (override + template merge)
- App SEO endpoints for the mobile/web app
- 301/302 Redirects manager
- IndexNow notifier — internal webhook that triggers Next.js ISR revalidation + Google/Bing/IndexNow pings
- AI SEO copy generator —
seoCopyGenController.jsproduces meta titles/descriptions via Gemini
Users & Auth
- Admin auth (JWT + bcrypt)
- Market-scoped partner auth — supermarket suppliers only see their own data (multi-tenant isolation)
- Affiliate / referral tracking
- Ratings + aggregated rating model
Notifications
- Firebase Cloud Messaging push (per-device + topic broadcasts)
- In-app notification inbox
- Cron-driven daily push at 05:00 Riyadh time, topic
"all" - Transactional email via Nodemailer
Dashboard analytics
- Aggregated KPI endpoint feeding the admin dashboard charts
AI flyer-extraction pipeline (ai/)
| Module | Purpose |
|---|---|
gemini_flyer_parser.js | Gemini 2.5 Pro spatial product-card detection — bounding boxes (0..1000 normalized), bilingual labels, price extraction, brand hints |
extract_products.js / extract_products_gemini.js / extract_products_method3.js | Three strategies: OpenAI Vision, Gemini, and hybrid YOLO+Flash (method 3 = 77% cost reduction) |
multi_product_parser_with_batch.js | Concurrent batch processing using p-limit |
image2boxes.js, crop_index_overlay.js | Sharp-based cropping using AI bounding boxes |
parsers/gemini_prompt_cache.js | Gemini context caching — avoids re-billing system prompts |
parsers/gemini_batch_parser.js | Batched batch-API parser |
parsers/gemini_client.js | Retry/backoff wrapper |
Brand auto-fill (utils/brandAutoFill.js) — Gemini-driven brand entity creation: bilingual name/description, country of origin, parent company, website. Multi-source logo discovery: Clearbit → Brandfetch → Gemini-suggested URLs → Sharp validation → slug-based upsert with in-flight de-duplication.
Background processing
- BullMQ + Redis PDF queue (
pdf2imagesqueue) —pdf2picrasterizes flyer PDFs at 300 DPI to 1200 px PNGs, then watermarks via Sharp pdf-lib,pdf-images,compress-pdffor additional manipulation- Worker runs as its own PM2 process
Cron jobs (scripts/)
daily_notification.js— node-cron 05:00 Riyadh time → FCM topic broadcastdelete_expired_offers.js— purges expired offersrun_delete_expired_offers_now.js— manual trigger variant
Partner portal (partner.js)
Separate Express app on its own port. Same router code, but every controller is scoped through marketAuth middleware so partners only ever see/edit their own market's offers and products. Tied into the dashboard's (store) route group.
Integrations
Google Gemini · OpenAI · Firebase Admin (FCM) · Clearbit + Brandfetch (logos) · IndexNow · MEGA (archival) · SMTP.
2. Client backend — qooty_client_backend
The consumer-facing read-optimized API serving the public website and mobile app under /v1/app.
Stack
Express 4 · Mongoose 8 · MongoDB · Redis client + node-cache (dual-tier cache) · firebase-admin (FCM) · winston · PM2.
Architecture
21 routers → 21 controllers → 22 models. middlewares/auth.js (user JWT) + adminAuth.js (admin-scoped routes). Single entry: index.js.
Feature domains
Catalog browsing — Products · Sub-categories · Brands · Offers · Featured Deals · Banners · Markets + Branches · Cities
Search & discovery — universal search router · recentRouter for "recently viewed" personalization
User-facing — Registration/login/profile · Cart · Ratings · Subscriptions (newsletter/topic) · Affiliate/referrer attribution · In-app notifications inbox
CMS / public — Blog reader endpoints · public SEO meta endpoints (drives Next.js SSR/ISR) · Redirects resolution · Remote app config
Notable capabilities
- Dual-tier cache — Redis (distributed) + node-cache (in-process LRU) for hot data
- FCM push to end users
- IndexNow integration (shares utility with admin backend)
- Arabic text normalization —
normalize_products_arabic.jshandles diacritics + alef variants - SEO slug backfill —
backfill_subcategory_slugs.js
3. Admin dashboard — qooty_dashboard_frontend
Internal control panel + supermarket-partner self-service portal. Next.js 16 + React 19, Arabic-default RTL, runs on port 3001.
Stack
Next.js 16.1 (App Router, Turbopack-ready) · React 19 · TypeScript 5 · Tailwind v4 · SWR 2 · react-hook-form + zod · Recharts 3 · lucide-react · CVA · Vitest (unit) · Playwright (E2E) · Tajawal font (next/font).
Routing structure (src/app/)
Four route groups: (auth) · (admin) · (store-auth) · (store).
Admin auth ((auth))
/login · /forgot-password · /reset-password
Store-owner auth ((store-auth))
/store/login
Store-owner sub-portal ((store))
/store (dashboard) · /store/offers · /store/offers/new · /store/offers/[offerId]/edit
Admin section ((admin)) — each entity has list + new + [id]/edit:
/dashboard · /admins · /affiliates · /blog · /brands · /categories · /subcategories · /cities · /city-groups · /markets · /featured-deals · /redirects · /offers (with three creation flows: /create/link, /create/pdf, /create/scraping) · /offers/[offerId]/products (nested product management per offer) · /pages (CMS) · /banners · /ads · /ratings · /profile · /settings
SEO suite
/seo/global · /seo/cities · /seo/markets · /seo/offers · /seo/templates · /seo/robots · /seo/sitemap · /seo/bulk-generate
Internal API routes
api/auth/admin/{login,logout,session,forgot-password,reset-password}— HTTP-only cookie sessionsapi/auth/store/{login,logout,session}— partner sessionsapi/manager/[...path]— admin backend proxy (forwards session cookie)api/store/[...path]— partner backend proxyapi/app/[...path]— public-app API proxy
src/lib/server/{cookies,proxy,security}.ts centralizes cookie + upstream proxy logic; upstream URLs are never exposed to the browser.
UI building blocks (components/common/)
entity-table · data-toolbar · pagination-bar · confirm-delete-dialog · form-section · form-actions · upload-field · multi-select-field · localized-name-fields (paired ar/en inputs) · seo-editor-card · page-scaffold · state-panel · status-badge · city-group-selector
components/dashboard/ — kpi-card, trends-chart, store-trends-chart (Recharts).
components/offer/ — base/market/static selectors shared across all three offer-creation flows.
Type-safety + forms
- Centralized zod schemas in
lib/forms/schemas.ts lib/types.tsshared API typeslib/client/api.tstyped SWR clienthooks/use-entity-list— generic SWR list pattern reused across every CRUD page
Hardening
next.config.tsrestrictive remote image patterns (api.qooty.net only)- Global security headers:
X-Content-Type-Options: nosniff·X-Frame-Options: DENY·Referrer-Policy: strict-origin-when-cross-origin·Permissions-Policylocking down camera/mic/geolocation/FLoC robots: { index: false, follow: false }at layout levelimages.unoptimized: true(admin app doesn't need optimization)
Test coverage
/tests/unitVitest suites/tests/e2ePlaywright suites: admin login, store login, password-reset flow, responsive UI sanity
4. Public website — qooty_client_frontend — qooty.net
Public bilingual (ar/en, RTL/LTR) SEO-driven storefront. Heavily performance- and SEO-engineered.
Stack
Next.js 16.1 · React 19 · TypeScript 5 · Tailwind v4 · next-intl 4 · SWR + axios · Zustand · swiper 12 · react-pageflip + react-zoom-pan-pinch (brochure viewer) · leaflet 1.9 (branch maps) · @vercel/speed-insights · lucide-react · Cairo + Inter via next/font.
Routing (src/app/[locale]/)
Locales: ar (default) and en, with localePrefix: "always".
Core pages
/ (home) · /about · /app (mobile-app landing) · /blog · /blog/[slug]
Brands
/brands · /brand/[brandSlug] · /brand/[brandSlug]/deals · /brand/[brandSlug]/deals/[subcategorySlug]
Compare
/compare · /compare/[storesSlug] — multi-store comparison landing pages
Featured / curated
/featured-deals
KSA city hub (the SEO long tail)
/ksa/cities · /ksa/[city] · /ksa/[city]/[marketSlug] · /ksa/[city]/brand/[brandSlug] · /ksa/[city]/deals/[subcategorySlug] · /ksa/deals · /ksa/deals/[subcategorySlug] · /ksa/deals/[subcategorySlug]/price · /ksa/deals/[subcategorySlug]/price/[bucket] (price-bucket landings) · /ksa/weekly-flyers · /ksa/weekly-flyers/[week] (ISO-week scoped)
Offers, products, stores
/offer/[offerId]/[slug] (flyer/brochure viewer) · /product/[productId]/[...slug] · /products · /store/[marketSlug] · /stores
Misc
/compare/[storesSlug] · /wishlist · /page/[slug] (CMS) · /[...slug] catch-all · /nf (localized 404 landing)
Unscoped routes
/ads.txt · /llms.txt · /q/seo/notify · /q/seo/daily-ping · /q/web-vitals · /qproxy
Built-in API routes
api/proxy— generic backend passthroughapi/seo/notify— IndexNow + search-engine notification webhookapi/seo/daily-ping— daily Vercel cron (09:00 UTC) hitting/q/seo/daily-pingapi/web-vitals— CWV beacon endpoint/img/[...path]— same-origin image proxy that fetchesapi.qooty.net/uploads/*, transcodes to WebP via Sharp, re-emits 1-year immutable Cache-Control (197 KB → 3.8 KB real-world wins recorded)
SEO infrastructure (the headline feature)
Sitemap fan-out — index + 18 partitioned sitemaps:
sitemap-static · -blog · -brands · -brand-subcategories · -cities · -city-brands · -city-deals · -compare · -featured-deals · -hubs · -images · -offers · -price-ranges · -products · -store-hubs · -stores · -subcategories · -weekly-flyers
Atom/RSS feeds — blog.xml · offers.xml · products.xml · weekly-flyers.xml
Dynamic OG / Twitter images — Edge ImageResponse for app, blog/[slug], brand/[brandSlug], featured-deals, ksa/[city], ksa/[city]/deals/[sub], ksa/deals/[sub], ksa/weekly-flyers, offer/[offerId]/[slug], product/[productId], store/[marketSlug] — shared template in app/_og/.
JSON-LD — Organization, WebSite, Breadcrumbs, plus per-page schemas. FAQ generators for brand, brand-subcategory, compare, store, subcategory, and weekly-flyers pages.
robots.ts + IndexNow key file (qooty-indexnow-{token}.txt).
Dashboard-managed overrides — template + override merge pattern (lib/seo.ts + lib/seo-api.ts).
Middleware (Node.js runtime)
- 301 redirect manager —
loadActiveRedirects()cached viaunstable_cache(tagredirects-cache, admin-invalidated); fire-and-forgetrecordRedirectHit()analytics - Unknown-segment guard — whitelist of known first segments; unknown locale-prefixed paths rewrite to
/[locale]/nf(preserving locale header) instead of a generic 404 - next-intl delegation with
X-NEXT-INTL-LOCALEforwarded to layout - Edge cache injection — overrides next-intl's default
private, no-cacheby injectingCDN-Cache-Control: public, s-maxage=3600, stale-while-revalidate=86400on anonymous SEO routes (~865 ms TTFB savings on deep pages)
Performance engineering
experimental.optimizePackageImports: ["lucide-react", "swiper"]— kills barrel-import bloatstaticPageGenerationTimeout: 180for heavy city aggregates- Custom image loader (
src/lib/image-loader.ts) rewritesapi.qooty.netURLs to the same-origin/img/...proxy with?w=&q=for propersrcset images.formats: ["image/avif", "image/webp"]- Per-route explicit
CDN-Cache-Controlto bypass Cloudflare's default Vary-header rejection - 1-year immutable
Cache-Controlon/_next/static/*and/img/* - Preloaded LCP hero (
hero.webp+hero-layer.avif) - Recorded results: Lighthouse 84 → 87 on mobile; payload 3.1 MB → 1.0 MB; Speed Index 3.6 s → 1.8 s
Notable interactive features
- Flyer/brochure viewer — react-pageflip + zoom-pan-pinch + flip-sound effects (
public/sounds/flip_sound.mp3,turnPage.mp3); responsive desktop and mobile - Clickable products inside flyer — pixel-mapped to bounding boxes from the AI extractor
- Product modal with similar products and navigation
- Multi-store comparison with price-bucket SEO landings
- Leaflet branch maps with Google Places–backed geocoding (30-day cached)
- Wishlist persisted in Zustand
- Anonymous tracking ID for personalization without auth (
lib/anon-id.ts)
Ezoic ad monetization
Two-tier ad system: 3 global slots + targeted route slots across 28 routes. Components: AdsProvider, AdScript, AdSlot, AdGrid, GlobalAds, EzoicRouteHandler (re-init on client-nav). Server-side config fetch via lib/ads-api.ts.
Analytics & telemetry
Google Analytics · ViewTrackers · WebVitalsReporter (beacon → /api/web-vitals) · Vercel Speed Insights.
Deployment
vercel.json — explicit JS chunk Content-Type workaround + daily SEO ping cron. Dev script offers webpack and Turbopack modes.
5. Mobile application — qooty-mobile-application
Production Flutter app (Android + iOS + Web). Internal name Wafarak (offers_app v1.0.7+9).
Stack
Flutter (Dart >=2.18.5 <3.0.0) · flutter_bloc 8 (Cubit pattern) · Dio 4 · Firebase Core/Auth/Messaging/Storage · google_sign_in + sign_in_with_apple · easy_localization · Cairo via google_fonts · cached_network_image · syncfusion_flutter_pdfviewer + pdfx · photo_view · shimmer · lottie · flutter_local_notifications · permission_handler · share_plus · screenshot · flutter_phoenix · json_serializable + build_runner.
Architecture
Feature-modular: Views → Cubits → Repositories → Dio → Models. 108 Dart files in lib/. Custom internal package packages/utilities (path-referenced) centralizes networking, state primitives, styles, and helpers.
lib/
├── core/ # app shell, theme, helpers (auth, FCM, navigation, locale, networking)
├── modules/
│ ├── auth/ # AuthCubit, GetUserDataCubit, repositories, views
│ └── home/ # 9 cubits, models, shimmers, views, widgets
├── views/ # root, splash
├── widgets/ # 13 shared widgets
└── generated/ # assets.gen.dart, locale_keys.g.dart
Cubits
Auth — AuthCubit, GetUserDataCubit
Home — FetchAllDataCubit, FetchCitiesCubit, FetchFavouritesDataCubit, FetchMarketDetailsCubit, FetchMarketsCubit, FetchNotifiesCubit, FetchRecentDataCubit, FetchSimilarProductsCubit, SearchDataCubit
Features
Authentication — Email/password · Google Sign-In · Apple Sign-In (iOS compliance) · Firebase Auth backbone · Persistent session via SharedPreferences · Localized phone validation
Home & discovery — Tabbed home (Favourites · Explore · dynamic category tabs) · City selector (searchable dropdown) gating data · Pull-to-refresh · Shimmer skeletons per section
Offers — Browse offers · Detail screen with embedded PDF (Syncfusion or pdfx) · Screenshot → share-to-social (screenshot + share_plus) · Lottie empty/error states
Products — Listings · Detail with pinch-zoom imagery (photo_view) · Similar products recommendations · Recently viewed history
Markets / branches — All markets · Single market with branches · City + text filtering
Search — Debounced cross-domain search (markets, products, offers) scoped by city
Favourites — Auto-loaded on app start · Toggle from product/offer cards
Notifications — FCM topic "all" subscription · Foreground display via flutter_local_notifications bridge · onMessageOpenedApp tap-through navigation · In-app inbox · Runtime permission
Profile & settings — Profile screen · Account settings · Firebase Storage profile image upload · Logout with full app restart via flutter_phoenix
Localization & theming — Arabic-first RTL with English (ar-EG, en-US) · Live language switching with Phoenix restart · language_change_dialog widget · Cairo font · Direction-aware widgets and icons
App shell — Splash with auth-state routing · Root scaffold with bottom nav + drawer · Privacy & Terms screens
Notable capabilities
| Capability | Implementation |
|---|---|
| Offline detection | observe_internet_connectivity + data_connection_checker_nulls with no_internet widget + Lottie |
| Image caching | cached_network_image + flutter_cache_manager |
| Push notifications | Firebase Messaging + flutter_local_notifications bridge — foreground/background/terminated coverage |
| Multi-provider auth | Email + Google + Apple (platform-gated) |
| PDF viewing | Syncfusion + pdfx fallback |
| Screenshot sharing | Widget → PNG → native share sheet |
| App restart | Phoenix for locale and auth resets |
| Code generation | flutter_gen (assets), easy_localization (locale keys), json_serializable (models) |
| Skeleton UI | Per-feature shimmer screens |
Internal package — packages/utilities
A reusable Flutter package (v0.0.2) providing: base_networking, request_options, exceptions (Dio abstractions) · error_state, pagination_state (Bloc primitives) · style helpers (CColors, breakpoints) · navigation service · date_time, hex_color, locale, number_formatter, package_info, scroll_controller, strings, time_debouncer. Path-referenced from the main pubspec.yaml — clean monorepo layering.
Platform support
Android (Gradle + keystore configured) · iOS (Podfile + Firebase plist + Apple Sign-In capability) · Web (PWA-capable scaffold with manifest + favicon).
Cross-cutting concerns
Authentication model
| Realm | Mechanism | Storage |
|---|---|---|
| Admin dashboard | JWT issued by admin backend, stored in HTTP-only cookie | Cookie |
| Partner / store-owner | Market-scoped JWT via marketAuth middleware | Cookie |
| Mobile app users | Firebase Auth (email + Google + Apple) | SharedPreferences |
| Website | Anonymous (anon-id.ts) + optional Firebase | Local storage |
Multi-tenancy
The admin backend's partner.js boots a separate Express app sharing all routes/controllers, but every request passes through marketAuth middleware that scopes Mongoose queries to the partner's marketId. Partners never see siblings' offers, products, or analytics.
Caching layers
- MongoDB indexes on slug, city, market, expiry
- Client backend Redis (distributed) + node-cache (in-process LRU)
- Next.js
unstable_cachewith tag-based invalidation (redirects, SEO overrides) - Per-route Cloudflare
CDN-Cache-Controlfor anonymous SEO pages (1 h fresh, 24 h SWR) - Same-origin
/img/proxy with 1-year immutable Cache-Control
Real-time invalidation
Admin actions → backend webhook → frontend api/seo/notify → revalidateTag() + IndexNow + Bing/Yandex pings → Cloudflare cache busts on next miss.
Localization
Arabic (default, RTL) + English (LTR) across dashboard, website (next-intl), and mobile (easy_localization). Bilingual entity fields stored as { ar, en } documents in Mongo, with localized-name-fields admin component enforcing paired entry.
Image pipeline
Sharp on backend (watermark, WebP encode, AVIF) · Next.js custom loader on website rewriting to same-origin proxy · cached_network_image on mobile.
Observability
Winston logging (backends) · bloc_observer.dart (mobile) · Vercel Speed Insights · Google Analytics · WebVitalsReporter beacon → /api/web-vitals.
Deployment topology
- Backends — PM2 ecosystem on VPS (8 procs admin + worker + cron, 1 proc client).
deploy.shfor client backend. - Frontends — Vercel for the public site (with daily SEO ping cron), separate Vercel/Node target for dashboard. Cloudflare CDN in front.
- Mobile — Android + iOS app-store distribution; web build available.
Engineering highlights
-
AI-driven content ingestion pipeline — Gemini 2.5 Pro extracts bilingual product cards with bounding boxes from raw flyer images; Gemini context caching cut prompt-billing cost ~5× on repeated invocations; method-3 hybrid (YOLO + Flash) cut total flyer-extraction cost by 77 %. Brand auto-fill chains Clearbit → Brandfetch → Gemini for logo discovery with Sharp validation.
-
SEO infrastructure at scale — 18 partitioned sitemaps + index, 4 Atom feeds, dynamic OG/Twitter
ImageResponsefor every entity, JSON-LD (Organization, WebSite, Breadcrumb, FAQ generators across 6 entity types), template+override merge, dashboard-managed overrides, IndexNow + Google/Bing pings, robots.ts, llms.txt, daily Vercel cron, anonymous-only edge caching. -
Performance-engineered Next.js 16 — Same-origin image proxy with Sharp WebP transcoding (197 KB → 3.8 KB wins recorded);
optimizePackageImportsfor swiper/lucide; per-route CloudflareCDN-Cache-Controlinjection (~865 ms TTFB win on deep pages); preloaded AVIF/WebP LCP hero; Lighthouse 84→87, payload 3.1 MB→1.0 MB, Speed Index 3.6 s→1.8 s. -
Bilingual RTL/LTR throughout — next-intl on the website with
localePrefix: "always"and separate fonts per locale; easy_localization on mobile with live-restart switching;localized-name-fieldson the dashboard enforcing paired ar/en entry; bilingual Mongoose schemas. -
Sophisticated middleware — Cached redirect manager with fire-and-forget hit analytics, unknown-path rewrite to localized 404, conditional edge-cache injection — all running on Next.js Node runtime.
-
Multi-tenant partner portal — Single codebase running as a second PM2 process (
partner.js), enforcing per-market data isolation via JWT-scoped middleware, paired with a dedicated(store)route group in the dashboard. -
Multi-process PM2 deployment — 8 named processes (manager, partner, PDF worker, expiry cron, daily notification cron) × prod/dev variants, all managed by
ecosystem.config.js. -
Interactive brochure viewer — react-pageflip + zoom-pan-pinch + page-flip sound effects, mapping clicks on flyer pixels back to AI-extracted product bounding boxes.
-
Dashboard depth — ~25 entity CRUD modules + 3 distinct offer-creation flows (URL, PDF upload, web scraping), full SEO control surface, redirect manager, store-owner sub-portal with separate auth — all sharing a reusable
entity-table/data-toolbar/pagination-barpattern and zod-typed forms. -
Test coverage on the dashboard — Vitest unit + Playwright E2E covering auth, password reset, and responsive UI.
-
Custom Flutter monorepo package —
packages/utilitiesextracted with shared networking, state primitives, styles, and helpers, path-referenced from the main app. -
Security hardening — HTTP-only cookie sessions, server-side proxies hiding upstream API, strict global headers (X-Frame-Options DENY, no-sniff, restrictive Permissions-Policy), restrictive image remote-patterns, market-scoped multi-tenant auth.
Key metrics & achievements
| Metric | Result |
|---|---|
| Lighthouse mobile (qooty.net) | 84 → 87 |
| Mobile payload | 3.1 MB → 1.0 MB |
| Speed Index | 3.6 s → 1.8 s |
| Image proxy savings (representative) | 197 KB → 3.8 KB |
| Deep-page TTFB savings (CDN cache injection) | ~865 ms |
| AI extraction cost reduction (method 3) | 77 % |
| Gemini prompt caching savings (method 1) | ~5× |
| GSC crawl boost after SEO push | 3.5× |
| SEO URLs indexed | ~4.8k (28-agent SEO initiative) |
| Backend PM2 processes | 8 (admin) + 1 (client) |
| Sitemaps in fan-out | 18 partitioned + index |
| Atom feeds | 4 |
| Routes with explicit CDN cache headers | ~10 SEO route prefixes |
| Localized routes per locale | 40+ under [locale] |
| Mongoose models | 22 (admin) + 22 (client) |
| Dashboard CRUD modules | ~25 |
| Mobile cubits | 11 |
| Mobile platforms | Android + iOS + Web |
Repository layout
qooty-platform/
├── qooty_admin_backend/ # Node.js — admin API, partner API, AI pipeline, worker, crons
├── qooty_client_backend/ # Node.js — consumer-facing read API
├── qooty_dashboard_frontend/ # Next.js 16 — admin + store-owner dashboard
├── qooty_client_frontend/ # Next.js 16 — qooty.net public website
├── qooty-mobile-application/ # Flutter — Android + iOS + Web
└── docs/ # Specs (Ezoic integration, SEO strategy, etc.)
Built and shipped by Mahmood Makhloof.