Skip to content

taans14/nodejs-ecommerce

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

77 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Node.js E-Commerce

A fullstack e-commerce system with a Node.js/Express backend providing authentication, catalog, cart, orders, reviews, admin dashboard metrics, and VNPay payment integration. The frontend(s) are React apps that consume these APIs (briefly covered at the end).


Project Overview

This repository implements an e-commerce backend API for:

  • User authentication (email/password) with email verification
  • Google OAuth login
  • Password reset via email
  • Guest checkout initiation via emailed “magic link”
  • Product catalog with brands, categories, variants, and images
  • Cart + cart items management
  • Orders + order items, order status transitions, and loyalty points bookkeeping
  • VNPay (sandbox) payment flow (pay URL generation + return + IPN handling)
  • Reviews (guests can comment; only logged-in users can rate)
  • Product search backed by Elasticsearch (synced from MongoDB)

Backend entrypoint: backend/src/server.js


Tech Stack

Backend

  • Language/runtime: Node.js (ESM modules; see "type": "module")
  • Framework: Express (v5)
  • Database: MongoDB (Mongoose ODM)
  • Auth:
    • JWT (jsonwebtoken)
    • Cookie-based auth for most user flows (cookie-parser)
    • Passport Google OAuth (passport, passport-google-oauth20)
  • Email: Nodemailer (nodemailer)
  • Search: Elasticsearch (@elastic/elasticsearch) + bulk indexing/sync
  • Payments: VNPay SDK (vnpay)
  • Logging: Morgan (morgan)
  • Dev tooling: Nodemon (nodemon)

Frontend

  • React 19 + Vite + Tailwind CSS
  • Axios for API calls

Backend Architecture

High-level flow

  • Express app + middleware are configured in backend/src/server.js.
  • Route modules in backend/src/routes/ define endpoints and mount controller handlers.
  • Controllers in backend/src/controllers/ implement business logic using Mongoose models.
  • Models in backend/src/models/ define MongoDB schemas and relationships.
  • Auth middleware in backend/src/middlewares/authMiddleware.js enforces/derives user identity from JWTs.
  • Config helpers live in:
    • DB connection: backend/src/config/db.js
    • Mailer: backend/src/config/mailer.js
    • Google OAuth: backend/src/config/passport.js
    • Slug generation: backend/src/config/slugify.js
  • Elasticsearch initialization + sync:
    • Client: backend/src/elastic/esClient.js
    • Index creation: backend/src/elastic/esIndex.js
    • MongoDB→ES synchronization: backend/src/elastic/mongoToEsSync.js
  • Seeding script: backend/src/seed/seed.js

Folder responsibilities (as implemented)

  • backend/src/routes/: Express routers + auth middleware wiring (mount points are defined in server.js)
  • backend/src/controllers/: Request handling, DB reads/writes, aggregation pipelines, payment flows
  • backend/src/models/: Mongoose schemas (refs + validation)
  • backend/src/middlewares/: JWT cookie auth (required/optional) + helper middlewares
  • backend/src/config/: DB, mailer, OAuth strategy, slugify helper
  • backend/src/elastic/: Elasticsearch client + indexing + MongoDB change-stream synchronization

Note: There is no separate “services” layer in this repo; controllers call models directly.


API Documentation

Base URL & mounts

All endpoints below are mounted by backend/src/server.js under:

  • /api/auth
  • /api/users
  • /api/addresses
  • /api/brands
  • /api/categories
  • /api/products
  • /api/discount-codes
  • /api/product-images
  • /api/product-variants
  • /api/carts
  • /api/cart-items
  • /api/orders
  • /api/order-items
  • /api/dashboard
  • /api/reviews
  • /api/payments/vnpay

Authentication conventions (as implemented)

  • Most authenticated user endpoints use a JWT stored in an HTTP-only cookie named accessToken (set during login / Google callback).
  • There is also helper middleware for Authorization: Bearer <token> auth, but it is not wired to any route in this repo.
  • “Auth required” below refers to what the route currently enforces in code.

Auth (/api/auth)

Method Path Auth required? Query params Request body Response example
POST /api/auth/signup No json\n{"fullName":"Jane Doe","email":"jane@example.com","password":"secret"}\n json\n{"message":"Tài khoản được đăng ký thành công! Vui lòng kiểm tra email để xác thực tài khoản."}\n
POST /api/auth/login No json\n{"email":"jane@example.com","password":"secret"}\n json\n{"message":"Đăng nhập thành công","user":{"id":"...","full_name":"Jane Doe","email":"jane@example.com","role":"customer"}}\n
POST /api/auth/logout No json\n{"message":"Đăng xuất thành công"}\n
GET /api/auth/me Yes (cookie accessToken) json\n{"message":"User authenticated","user":{"_id":"...","fullName":"...","email":"...","role":"...","cartId":"..."}}\n
GET /api/auth/google No Redirects to Google OAuth (Passport)
GET /api/auth/oauth2/redirect No Sets cookie then redirects to CLIENT_URL
GET /api/auth/verify-email No token json\n{"message":"Email đã được xác thực thành công!"}\n
POST /api/auth/resend-verification No json\n{"email":"jane@example.com"}\n json\n{"message":"Email xác thực đã được gửi lại"}\n
POST /api/auth/guest-checkout-init No json\n{"email":"guest@example.com","localCartItems":[{"variantId":"...","quantity":2,"price":1200000}]}\n json\n{"message":"Link đăng nhập nhanh đã được gửi. Vui lòng kiểm tra email để tiếp tục."}\n
GET /api/auth/verify-guest-token No token Sets cookie then redirects to frontend (/checkout or /profile?...)
POST /api/auth/forgot-password No json\n{"email":"jane@example.com"}\n json\n{"message":"Email đặt lại mật khẩu đã được gửi."}\n
POST /api/auth/reset-password/:token No json\n{"password":"newSecret"}\n json\n{"message":"Mật khẩu đã được đặt lại thành công."}\n
POST /api/auth/admin/login No json\n{"email":"admin@example.com","password":"secret"}\n json\n{"message":"Đăng nhập quản trị thành công","token":"<jwt>","user":{"id":"...","full_name":"...","email":"...","role":"admin"}}\n

Notes:

  • login sets accessToken cookie with secure: true and sameSite: "None".
  • Email verification tokens are signed with EMAIL_KEY; auth tokens use JWT_KEY.

Users (/api/users)

Method Path Auth required? Query params Request body Response example
PUT /api/users/profile Yes (cookie accessToken) json\n{"fullName":"New Name"}\n json\n{"id":"...","fullName":"New Name","email":"...","role":"..."}\n
PUT /api/users/change-password Yes (cookie accessToken) json\n{"currentPassword":"old","newPassword":"new"}\n json\n{"message":"Đổi mật khẩu thành công"}\n
GET /api/users/ No json\n[{"_id":"...","fullName":"...","email":"...","role":"...","banned":false}]\n
PUT /api/users/:id No json\n{"fullName":"...","email":"...","role":"admin"}\n json\n{"_id":"...","fullName":"...","email":"...","role":"admin","isVerified":true,"banned":false}\n
PATCH /api/users/:id/ban-status No json\n{"banned":true}\n json\n{"_id":"...","fullName":"...","email":"...","banned":true,"message":"Đã cấm người dùng"}\n

Addresses (/api/addresses)

All routes in this router call authenticateUser via router.use(authenticateUser).

Method Path Auth required? Query params Request body Response example
POST /api/addresses/ Yes (cookie accessToken) json\n{"addressLine":"...","province":"...","ward":"...","phoneNumber":"...","isDefault":true}\n json\n{"_id":"...","userId":"...","addressLine":"...","province":"...","ward":"...","phoneNumber":"...","isDefault":true}\n
GET /api/addresses/ Yes (cookie accessToken) json\n[{"_id":"...","addressLine":"...","isDefault":true}]\n
GET /api/addresses/ (duplicate registration) Yes (cookie accessToken) Intended to fetch a single address, but the route is registered as GET / again while the controller expects :id
PUT /api/addresses/:id Yes (cookie accessToken) json\n{"addressLine":"...","province":"...","ward":"...","phoneNumber":"...","isDefault":false}\n json\n{"_id":"...","addressLine":"...","isDefault":false}\n
DELETE /api/addresses/:id Yes (cookie accessToken) json\n{"message":"Xóa địa chỉ thành công"}\n
PUT /api/addresses/:id/set-default Yes (cookie accessToken) json\n{"message":"Cập nhật địa chỉ mặc định thành công","address":{"_id":"...","isDefault":true}}\n

Brands (/api/brands)

Method Path Auth required? Query params Request body Response example
GET /api/brands/ No json\n[{"_id":"...","name":"Nike","slug":"nike","description":"...","imageSrc":"..."}]\n
GET /api/brands/:slug No json\n{"brand":{"_id":"...","name":"Nike","slug":"nike"}}\n
POST /api/brands/ No json\n{"name":"Nike","description":"...","imageSrc":"..."}\n json\n{"_id":"...","name":"Nike","slug":"nike","description":"...","imageSrc":"..."}\n
PUT /api/brands/:id No json\n{"name":"New Name"}\n json\n{"_id":"...","name":"New Name","slug":"new-name"}\n
DELETE /api/brands/:id No json\n{"message":"Nhãn hàng được xóa thành công"}\n

Categories (/api/categories)

Method Path Auth required? Query params Request body Response example
GET /api/categories/ No json\n[{"_id":"...","name":"Giày đá bóng","slug":"giay-da-bong"}]\n
GET /api/categories/:slug No json\n{"category":{"_id":"...","name":"...","slug":"..."}}\n
POST /api/categories/ No json\n{"name":"...","description":"...","imageSrc":"..."}\n json\n{"_id":"...","name":"...","slug":"..."}\n
PUT /api/categories/:id No json\n{"name":"Updated"}\n json\n{"_id":"...","name":"Updated","slug":"updated"}\n
DELETE /api/categories/:id No json\n{"message":"Loại sản phẩm được xóa thành công"}\n

Products (/api/products)

Method Path Auth required? Query params Request body Response example
GET /api/products/ No search, categoryId, brandId, minPrice, maxPrice, color, size, sort json\n[{"_id":"...","name":"...","basePrice":1000000,"imageUrl":"...","variants":[...]}]\n
GET /api/products/filters No json\n{"colors":["Black","White"],"sizes":["M","L"],"maxPrice":3500000}\n
GET /api/products/feature No limit json\n[{"id":"...","name":"...","price":900000,"oldPrice":1000000,"salePercent":10,"imageUrl":"..."}]\n
GET /api/products/discount No limit json\n[{"id":"...","name":"...","price":800000,"oldPrice":1000000,"salePercent":20,"imageUrl":"..."}]\n
GET /api/products/search No q (required), sort json\n[{"_id":"...","name":"...","imageUrl":"..."}]\n
GET /api/products/:id No json\n{"_id":"...","name":"...","description":"...","basePrice":900000,"oldPrice":1000000,"discountPercent":10,"brand":"Nike","images":["..."],"variants":[{"_id":"...","sku":"...","price":...,"stock":...,"size":"M","color":"Black"}]}\n
GET /api/products/brand/:slug No json\n[{"_id":"...","name":"...","variants":[...],"brand":{...},"category":{...},"imageUrl":"..."}]\n
GET /api/products/category/:slug No json\n[{"_id":"...","name":"...","variants":[...],"brand":{...},"category":{...},"imageUrl":"..."}]\n
GET /api/products/:productId/variants No json\n[{"sku":"...","price":1200000,"stock":20,"attributes":{"color":"Black","size":"M"}}]\n
GET /api/products/:productId/images No json\n[{"_id":"...","productId":"...","url":"...","alt":"..."}]\n
POST /api/products/ No json\n{"categoryId":"...","brandId":"...","name":"...","description":"...","basePrice":1000000,"sales":0,"discountPercent":0}\n json\n{"_id":"...","categoryId":"...","brandId":"...","name":"...","basePrice":1000000,"discountPercent":0}\n
PUT /api/products/:id No json\n{"name":"Updated","discountPercent":15}\n json\n{"_id":"...","name":"Updated","discountPercent":15}\n
DELETE /api/products/:id No json\n{"message":"Sản phẩm được xóa thành công"}\n

Discount Codes (/api/discount-codes)

Method Path Auth required? Query params Request body Response example
GET /api/discount-codes/ No json\n[{"_id":"...","code":"SALE10","discount_value":10,"max_usage":100,"times_used":0}]\n
POST /api/discount-codes/ No json\n{"code":"SALE10","discount_value":10,"max_usage":100}\n json\n{"_id":"...","code":"SALE10","discount_value":10,"max_usage":100,"times_used":0}\n

Carts (/api/carts)

Method Path Auth required? Query params Request body Response example
GET /api/carts/:id No json\n{"_id":"...","userId":"...","createdAt":"..."}\n
GET /api/carts/:cartId/items No json\n[{"_id":"...","quantity":2,"variant":{"_id":"...","price":...,"attributes":{...}},"product":{"_id":"...","name":"..."}}]\n
POST /api/carts/ No json\n{"userId":"..."}\n json\n{"_id":"...","userId":"..."}\n
PUT /api/carts/:id No json\n{"userId":"..."}\n json\n{"_id":"...","userId":"..."}\n
DELETE /api/carts/:id No json\n{"message":"Giỏ hàng được xóa thành công"}\n

Cart Items (/api/cart-items)

Method Path Auth required? Query params Request body Response example
GET /api/cart-items/:cartId No json\n[{"_id":"...","quantity":1,"variant":{"_id":"...","price":...,"attributes":{...}},"product":{"_id":"...","name":"..."}}]\n
POST /api/cart-items/ Yes (cookie accessToken) json\n{"product_variant_id":"...","quantity":2}\n json\n{"_id":"...","cart_id":"...","product_variant_id":"...","quantity":2}\n
PUT /api/cart-items/:id No json\n{"quantity":3}\n json\n{"_id":"...","quantity":3}\n
PUT /api/cart-items/:id/increase No json\n{"_id":"...","quantity":2}\n
PUT /api/cart-items/:id/decrease No json\n{"_id":"...","quantity":1}\n
DELETE /api/cart-items/:id No json\n{"message":"CartItem deleted successfully"}\n

Orders (/api/orders)

Method Path Auth required? Query params Request body Response example
GET /api/orders/my-orders Yes (cookie accessToken) page (default 1), limit (default 5) json\n{"orders":[...],"currentPage":1,"totalPages":2,"totalOrders":7}\n
GET /api/orders/ No json\n[{"id":"...","customer":{"fullName":"...","email":"...","phone":"..."},"items":[...],"subtotal":...,"total":...,"status":"pending"}]\n
GET /api/orders/:id No json\n{"_id":"...","user_id":"...","address_id":"...","total_price":...,"status":"pending","payment_status":"unpaid"}\n
GET /api/orders/:orderId/items No json\n[{"_id":"...","order_id":"...","product_variant_id":{...},"quantity":2,"price":1000000,"name":"Product name","image":"..."}]\n
POST /api/orders/ No json\n{"user_id":"...","address_id":"...","discount_code_id":null,"total_price":1200000,"loyalty_points_used":0,"loyalty_points_earned":0,"payment_method":"cash"}\n json\n{"_id":"...","user_id":"...","address_id":"...","total_price":1200000,"status":"pending","payment_status":"unpaid"}\n
PUT /api/orders/:id/status No json\n{"status":"confirmed","paymentStatus":"paid"}\n json\n{"message":"Cập nhật thành công","order":{"_id":"...","status":"confirmed","payment_status":"paid"}}\n
PUT /api/orders/:id No json\n{"status":"shipping"}\n json\n{"_id":"...","status":"shipping"}\n
DELETE /api/orders/:id No json\n{"message":"Đơn hàng được xóa thành công"}\n

Order status transitions (as implemented in the Order model):

  • Confirming a pending order deducts stock for each order item variant and adjusts loyalty points.
  • Cancelling may restock (depending on previous status) and refunds used points.
  • VNPay-confirmed orders mark payment_status as paid.

Order Items (/api/order-items)

Method Path Auth required? Query params Request body Response example
POST /api/order-items/ No json\n{"order_id":"...","product_variant_id":"...","quantity":2,"price":1000000}\n json\n{"_id":"...","order_id":"...","product_variant_id":"...","quantity":2,"price":1000000}\n
PUT /api/order-items/:id No json\n{"quantity":3}\n json\n{"_id":"...","quantity":3}\n
DELETE /api/order-items/:id No json\n{"message":"OrderItem deleted successfully"}\n

Reviews (/api/reviews)

Method Path Auth required? Query params Request body Response example
GET /api/reviews/:productId No json\n[{"_id":"...","rating":5,"comment":"Great","author":"Jane Doe","createdAt":"..."}]\n
POST /api/reviews/ Optional (cookie accessToken) json\n{"productId":"...","rating":5,"comment":"Nice!","guest_name":"Guest"}\n json\n{"_id":"...","product_id":"...","user_id":"...","rating":5,"comment":"Nice!"}\n

Review rules (enforced in the model):

  • Logged-in users must provide rating.
  • Guests cannot provide rating (it must be null).

Product Images (/api/product-images)

Method Path Auth required? Query params Request body Response example
GET /api/product-images/:id No json\n{"_id":"...","productId":"...","url":"...","alt":"..."}\n
POST /api/product-images/ No json\n{"productId":"...","url":"...","alt":"..."}\n json\n{"_id":"...","productId":"...","url":"...","alt":"..."}\n
PUT /api/product-images/:id No json\n{"alt":"Updated alt"}\n json\n{"_id":"...","alt":"Updated alt"}\n
DELETE /api/product-images/:id No json\n{"message":"Ảnh sản phẩm đã được xóa thành công"}\n

Also available via Products router:

  • GET /api/products/:productId/images (returns images by productId)

Product Variants (/api/product-variants)

Method Path Auth required? Query params Request body Response example
GET /api/product-variants/:id No json\n{"_id":"...","productId":"...","sku":"SKU123","price":1200000,"stock":10,"attributes":{"color":"Black","size":"M"}}\n
POST /api/product-variants/ No json\n{"productId":"...","sku":"SKU123","price":1200000,"stock":10,"attributes":{"color":"Black","size":"M"}}\n json\n{"_id":"...","sku":"SKU123","price":1200000,"stock":10}\n
PUT /api/product-variants/:id No json\n{"stock":25}\n json\n{"_id":"...","stock":25}\n
DELETE /api/product-variants/:id No json\n{"message":"Biến thể sản phẩm đã được xóa thành công"}\n

Also available via Products router:

  • GET /api/products/:productId/variants (returns variants by productId)

Dashboard (/api/dashboard)

Method Path Auth required? Query params Request body Response example
GET /api/dashboard/metrics No json\n{"totalUsers":100,"newUsers":5,"totalOrders":20,"totalRevenue":123456789}\n
GET /api/dashboard/topselling No json\n[{"rank":1,"name":"Product A","sales":50,"revenue":50000000}]\n
GET /api/dashboard/revenue-chart No month (1-12), year json\n[{"date":"1/4","revenue":0,"profit":0,"orders":0,"products":0}]\n

Payments (VNPay) (/api/payments/vnpay)

Method Path Auth required? Query params Request body Response example
POST /api/payments/vnpay/pay/:orderId Yes (cookie accessToken) json\n{"paymentUrl":"https://sandbox.vnpayment.vn/..."}\n
GET /api/payments/vnpay/vnpay-return No VNPay return query params Redirects to CLIENT_URL/payment-failed or CLIENT_URL/order-success/:orderId
GET /api/payments/vnpay/vnpay-ipn No VNPay IPN query params Returns VNPay IPN JSON (e.g., success / invalid amount / checksum fail)

IPN handling (as implemented):

  • Verifies VNPay signature and success status.
  • Updates order status to confirmed on success (which can also mark payment_status as paid for VNPay).
  • Clears the user’s cart items after successful payment.
  • Sends an “order success” email.

Authentication & Authorization

JWT in cookie (primary)

  • Middleware: authenticateUser in backend/src/middlewares/authMiddleware.js
  • Reads req.cookies.accessToken, verifies with JWT_KEY, and sets req.user.
  • Also loads the user’s Cart and adds cartId to req.user when available.

Optional auth (reviews)

  • Middleware: authenticateUserOptional sets req.user when a valid cookie token exists; otherwise keeps requests as guest.

Admin authorization (present but not wired)

  • authorizeAdmin exists but is not used on any route in this repo.
  • verifyToken (Bearer-token auth) exists but is also not used on any route in this repo.

Database Design

Mongoose models live in backend/src/models/.

Main entities

  • User: full name, email, hashed password, role (customer/admin), verification, loyalty points, banned flag, reset tokens
  • Brand: name, slug (auto-generated), description, image
  • Category: name, slug (auto-generated), description, image
  • Product: belongs to Brand and Category; base price, discount percent, sales counter
  • ProductVariant: belongs to Product; sku, price, stock, attributes (color/size)
  • ProductImage: belongs to either Product or ProductVariant; url/alt
  • Cart: one per User
  • CartItem: belongs to Cart; references ProductVariant; quantity
  • Order: belongs to User + Address; optional DiscountCode; totals; loyalty points; status and payment status
  • OrderItem: belongs to Order; references ProductVariant; quantity and price snapshot
  • Address: belongs to User; address fields + default flag
  • Review: belongs to Product; optionally belongs to User (guest allowed)

Key relationships (refs)

  • Product → Brand (brandId), Category (categoryId)
  • ProductVariant → Product (productId)
  • ProductImage → Product (productId) OR ProductVariant (productVariantId)
  • Cart → User (userId, unique)
  • CartItem → Cart (cart_id), ProductVariant (product_variant_id)
  • Order → User (user_id), Address (address_id), DiscountCode (discount_code_id)
  • OrderItem → Order (order_id), ProductVariant (product_variant_id)
  • Address → User (userId)
  • Review → Product (product_id), optionally User (user_id)

Error Handling

There is no centralized Express error-handling middleware registered in backend/src/server.js. Each controller generally:

  • Uses try/catch
  • Returns JSON errors like { "message": "..." } or { "error": "..." }
  • Uses standard HTTP status codes (commonly 400/401/403/404/500)

Environment Variables

These are read via process.env in the backend code:

Variable Used for
PORT Backend listen port (defaults to 5000)
ATLAS_URI MongoDB connection string (Mongoose)
JWT_KEY Signing/verifying auth JWTs
EMAIL_KEY Signing/verifying email verification tokens
SERVER_URL OAuth callback + magic link URL generation
CLIENT_URL CORS allowed origin + frontend redirects (OAuth/VNPay/guest flow)
ADMIN_CLIENT_URL CORS allowed origin for admin frontend
NODE_ENV Cookie flags (secure, sameSite) in some flows
GOOGLE_CLIENT_ID Google OAuth
GOOGLE_CLIENT_SECRET Google OAuth
MAIL_HOST SMTP host
MAIL_PORT SMTP port
MAIL_USERNAME SMTP username
MAIL_PASSWORD SMTP password
FROM_EMAIL “From” email in outbound mails
ELASTIC_SEARCH_URL Elasticsearch node URL
VNPAY_TMN_CODE VNPay merchant code
VNPAY_HASH_SECRET VNPay hash secret
VNPAY_RETURN_URL VNPay return URL (sent to VNPay)

Setup & Installation

Run backend locally (development)

Prereqs:

  • Node.js (Docker uses Node 22; local dev should use a modern Node version)
  • MongoDB reachable via ATLAS_URI
  • Elasticsearch reachable via ELASTIC_SEARCH_URL (required by the product search + startup indexing)

Steps:

  1. Install dependencies:
    cd backend
    npm install
  2. Create backend env file (example keys):
    PORT=5000
    ATLAS_URI=mongodb+srv://...
    JWT_KEY=...
    EMAIL_KEY=...
    SERVER_URL=http://localhost:5000
    CLIENT_URL=http://localhost:5173
    ADMIN_CLIENT_URL=http://localhost:5174
    
    GOOGLE_CLIENT_ID=...
    GOOGLE_CLIENT_SECRET=...
    
    MAIL_HOST=...
    MAIL_PORT=587
    MAIL_USERNAME=...
    MAIL_PASSWORD=...
    FROM_EMAIL=...
    
    ELASTIC_SEARCH_URL=http://localhost:9200
    
    VNPAY_TMN_CODE=...
    VNPAY_HASH_SECRET=...
    VNPAY_RETURN_URL=http://localhost:5000/api/payments/vnpay/vnpay-return
    
    NODE_ENV=development
  3. Start the server:
    npm run dev

On startup, the backend:

  • Connects to MongoDB
  • Ensures the Elasticsearch products index exists
  • Bulk indexes products from MongoDB into Elasticsearch
  • Starts a MongoDB change stream to keep Elasticsearch in sync

Seed sample catalog data

The seeding script uses ATLAS_URI:

cd backend
npm run seed

Run with Docker Compose (repo root)

The repo contains a compose setup in docker-compose.yml that runs:

  • backend (scaled replicas)
  • nginx (reverse proxy)
  • elasticsearch
  • frontend + frontend_admin
  • ngrok container

Typical start:

docker compose up --build

Note:

  • The compose file references env files (e.g., backend/.env.docker, frontend/.env.docker). Create those files with the variables your environment needs.

Frontend

There are two React/Vite clients:

  • User storefront in the frontend folder (React 19, Vite, Tailwind, Axios)
  • Admin UI in the frontend_admin folder (React 19, Vite, Tailwind, Axios, Recharts)

They communicate with the backend through the /api/* routes, and the backend’s CORS configuration allows origins from CLIENT_URL and ADMIN_CLIENT_URL.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages