# ADVdoors.ru Modern Site Rebuild > Rebuild advdoors.ru as a modern, self-hosted product catalog with cart/order functionality and an intuitive admin panel, using a Turborepo + pnpm monorepo with Next.js + Payload CMS + PostgreSQL + MinIO, deployed with Docker Compose. Includes a scraper app for migrating data from the old site. ## Current State The existing advdoors.ru is a dated product catalog for Finnish doors (KASKI, SWEDOOR/JELD-WEN, ALAVUS, Abloy). It has ~200-500 products across categories (exterior doors, interior doors, accessories), with product pages containing images, prices, discounts, technical specs, and availability. The repo is a fresh start. ## Stack ### Core: Turborepo + pnpm monorepo The project uses a **Turborepo** monorepo managed by **pnpm** workspaces. This keeps the main web app, the migration scraper, and shared packages in one repo with fast, cached builds. - **Turborepo** -- orchestrates builds, dev, lint, typecheck across apps/packages with caching - **pnpm** -- fast, disk-efficient package manager with strict dependency isolation ### Apps and Packages - **`apps/web`** -- Next.js 15 + Payload CMS 3 (storefront + admin in one app) - **`apps/scraper`** -- Node.js CLI tool to crawl advdoors.ru and import data into the new site - **`packages/shared`** -- Shared TypeScript types, constants (brands, availability statuses, etc.) - **`packages/tsconfig`** -- Shared TypeScript configurations - **`packages/eslint-config`** -- Shared ESLint configurations ### Web App: Next.js 15 + Payload CMS 3 Payload CMS v3 embeds directly inside a Next.js application -- one codebase, one deployment: - **Payload CMS 3** provides the admin panel (`/admin`), database ORM, authentication, media management, and API layer -- all as a Next.js plugin, not a separate service - **Next.js 15 (App Router)** provides SSR/SSG for SEO, React Server Components for performance, and the storefront UI - **PostgreSQL 16** as the database (Payload's Drizzle adapter) - **MinIO** as S3-compatible object storage for product images and media from day one - **Tailwind CSS v4** for styling the storefront - **TypeScript** throughout ### Scraper App A standalone Node.js CLI tool in `apps/scraper` for one-time data migration: - Crawls advdoors.ru catalog pages, following pagination - Extracts product data: name, article number, price, discount, images, specs, category, availability - Downloads product images and uploads them to MinIO - Creates products/categories in Payload CMS via its REST or Local API - Imports shared types from `packages/shared` to ensure data consistency - Uses Cheerio for HTML parsing, got/undici for HTTP ### Why this stack over alternatives - **Strapi + Next.js** -- Two separate apps to deploy/maintain; Payload v3 integrates into Next.js directly - **Medusa.js** -- Full e-commerce engine -- overkill when no payment processing is needed - **WordPress + WooCommerce** -- Not modern, poor DX, PHP stack - **Directus + Nuxt** -- Good option, but two apps; Payload's admin UI is more polished for this use case ### Deployment: Docker Compose (self-hosted) ```mermaid graph LR subgraph docker [Docker Compose] Nginx["Nginx (reverse proxy + SSL)"] App["Next.js + Payload CMS"] DB["PostgreSQL 16"] MinIO["MinIO (S3 storage)"] end Browser --> Nginx Nginx --> App Nginx -->|"static media"| MinIO App --> DB App -->|"upload/fetch media"| MinIO ``` - **Nginx** -- reverse proxy, SSL termination (Let's Encrypt), static asset caching, proxies `/media` to MinIO - **App** -- single Next.js container serving both storefront and `/admin` panel - **PostgreSQL 16** -- product data, orders, users, media metadata - **MinIO** -- S3-compatible object storage for all product images and media uploads; Payload's S3 storage adapter connects directly to it; Nginx serves media publicly with caching headers --- ## Data Model (Payload Collections) ### Products - `name` (text, localized if needed later) - `slug` (auto-generated from name) - `sku` / `articleNumber` (text, e.g., "73146") - `category` (relationship to Categories) - `brand` (select: KASKI, ALAVUS, SWEDOOR/JELD-WEN, etc.) - `images` (array of uploads, with gallery support) - `price` (number, rubles) - `discountPrice` (number, optional) - `availability` (select: in-stock / made-to-order / coming-soon) - `shortDescription` (text) - `technicalSpecs` (rich text -- for detailed specs like on the current site) - `options` (array of {name, priceModifier, description} -- for paid customizations) - `relatedProducts` (relationship, self-referencing) - `seoMeta` (meta title, description, OG image) ### Categories - `name` (text) - `slug` (auto) - `parent` (self-relationship for hierarchy: "Exterior Doors" > "With Glass") - `description` (rich text) - `image` (upload) ### Orders - `orderNumber` (auto-incrementing) - `items` (array of {product, quantity, priceAtOrder}) - `customer` (group: name, phone, email, comment) - `status` (select: new / in-progress / completed / cancelled) - `createdAt` (auto) - Triggers email notification to admin on new order ### Pages (for static content) - `title`, `slug`, `content` (rich text), `seoMeta` - Used for: About, Delivery, Installation, Warranty, Contacts ### SiteSettings (global) - `phone`, `whatsapp`, `telegram`, `email` - `address` - `workingHours` - `footerText` - `socialLinks` ### Media (Payload uploads with S3/MinIO) - Payload's `@payloadcms/storage-s3` adapter stores all uploads in MinIO - Auto image resizing/optimization via Sharp - WebP generation for thumbnails and display sizes - MinIO bucket: `advdoors-media`, public-read policy for product images --- ## Storefront Pages ### Public pages (Next.js App Router) - **`/`** -- Hero section, featured products, brand highlights, "why choose Finnish doors" section - **`/catalog`** -- All products with sidebar filters (category, brand, price range, availability), search, pagination - **`/catalog/[categorySlug]`** -- Category-filtered view - **`/product/[slug]`** -- Product detail: image gallery (lightbox), specs, price, options, "add to cart", related products - **`/cart`** -- Cart contents, quantity adjustment, order form (name, phone, email, comment), submit order - **`/[pageSlug]`** -- Dynamic content pages (about, delivery, installation, warranty, contacts) ### Admin panel (Payload CMS, `/admin`) Out-of-the-box from Payload: - Dashboard with order count, recent orders - Product CRUD with image upload, drag-and-drop reordering - Category management with tree view - Order management with status updates - Page content editor (rich text with embedded images) - Site settings editor - User management (admin accounts) The admin UI is modern, responsive, and intuitive enough for a non-technical user -- it resembles familiar CMS interfaces. --- ## Key Features ### For customers - Fast, responsive, mobile-first design - Product search (full-text via PostgreSQL) - Category filtering + price range + brand filters - Image galleries with zoom - Shopping cart (persisted in localStorage, synced on order submit) - Order form with phone/email (no payment -- order goes to admin) - WhatsApp / Telegram click-to-chat buttons - SEO: server-rendered pages, meta tags, structured data (JSON-LD Product schema), sitemap.xml ### For the admin (father) - Simple login at `/admin` - Add/edit products with drag-and-drop image upload - Set prices and discounts - Manage categories - View and manage incoming orders (with email notifications) - Edit content pages (about, delivery, etc.) - Update contact info and site settings ### Technical - Turborepo cached builds (`pnpm turbo build` -- only rebuilds what changed) - Docker Compose one-command deployment (`docker compose up -d`) - Automatic SSL via Let's Encrypt (Nginx + certbot) - MinIO for all media storage (S3-compatible, self-hosted, with web console at `:9001`) - Database backups (pg_dump cron job in Docker) - Image optimization (Sharp, WebP auto-conversion) - Rate limiting on order submission - CSRF protection --- ## Project Structure (Turborepo + pnpm) ``` advdoors/ apps/ web/ # Next.js 15 + Payload CMS 3 src/ app/ # Next.js App Router (frontend)/ # Route group for public pages page.tsx # Home catalog/ product/ cart/ [slug]/ # Dynamic content pages (payload)/ # Payload admin routes (auto-generated) collections/ # Payload collection definitions Products.ts Categories.ts Orders.ts Pages.ts Media.ts globals/ # Payload globals SiteSettings.ts components/ # React components (storefront UI) lib/ # Utilities, cart logic, API helpers payload.config.ts # Payload CMS configuration public/ # Static assets (favicon, robots.txt) Dockerfile next.config.ts tailwind.config.ts package.json scraper/ # Migration scraper CLI src/ index.ts # Entry point crawl.ts # Page crawler (pagination, link following) extract.ts # HTML parser (Cheerio) for product data import.ts # Payload API client for creating records download-media.ts # Image downloader + MinIO uploader package.json packages/ shared/ # Shared types, constants, brand lists src/ types.ts # Product, Category, Order types constants.ts # Brands, availability statuses package.json tsconfig/ # Shared tsconfig bases base.json nextjs.json node.json package.json eslint-config/ # Shared ESLint configs base.js next.js package.json docker/ docker-compose.yml # PostgreSQL + MinIO + App + Nginx docker-compose.dev.yml # Dev overrides (hot reload, exposed ports) nginx.conf .env.example turbo.json # Turborepo pipeline config pnpm-workspace.yaml # pnpm workspace definition package.json # Root package.json (scripts: dev, build, lint) .gitignore ``` --- ## Implementation Phases ### Phase 1: Foundation (monorepo + infrastructure) - Initialize Turborepo + pnpm workspace with `apps/web`, `apps/scraper`, `packages/*` - Scaffold Next.js 15 + Payload CMS 3 in `apps/web` - Define data model: collections (Products, Categories, Orders, Pages, Media) and globals (SiteSettings) - Docker Compose: PostgreSQL 16 + MinIO + App + Nginx - Configure Payload S3 storage adapter pointing to MinIO - Shared types in `packages/shared` - Basic admin panel working, can create products and upload images to MinIO ### Phase 2: Storefront - Home page design and implementation - Catalog page with sidebar filters (category, brand, price range) and pagination - Product detail page with image gallery, specs, pricing, options - Content pages (about, delivery, installation, warranty, contacts) - Responsive mobile-first design with Tailwind CSS v4 ### Phase 3: Cart and Orders - Shopping cart (client-side state + UI) - Order form and submission - Order management in admin panel - Email notifications on new orders ### Phase 4: Scraper / Data Migration - Build scraper in `apps/scraper` using Cheerio + undici - Crawl advdoors.ru: all catalog pages, follow pagination, extract category structure - Extract per-product: name, SKU, prices, images, specs, availability, options - Download images and upload to MinIO - Import all data into Payload CMS via Local API - Validate imported data against the live site ### Phase 5: Polish and Deploy - SEO (meta tags, structured data, sitemap) - Image optimization pipeline - Performance optimization (caching, ISR) - Nginx config with SSL - Database backup strategy - Production deployment --- ## Open Questions / Future Considerations - **Multi-language**: Currently Russian only. Payload supports localization if needed later. - **Analytics**: Yandex.Metrika integration (simple script tag). - **CDN**: Not needed initially for a Russian-focused site, but can add later. - **MinIO replication**: Single-node MinIO is fine to start; can add erasure coding or replication later. - **CI/CD**: Turborepo's remote caching could be added with a self-hosted cache server if build times grow.