321 lines
12 KiB
Markdown
321 lines
12 KiB
Markdown
# 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.
|