You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A modular Express 5 + MongoDB/Mongoose REST API powering a sneaker/shoe e-commerce platform, with JWT auth, Cloudinary image uploads, and VNPAY payments.
This repository is the backend-only service of the Crest Walk platform. It exposes a public storefront API (products, cart, wishlist, checkout, order tracking, reviews) and an admin API (products, categories, brands, inventory, orders, users, vouchers, reviews, banners, revenue & bestseller stats), consumed by a separate React frontend over JWT-authenticated HTTP.
Project Info β related frontend, author, contact, license
π― Overview
Key Features
Storefront API
Public product catalog with listing, search, filtering, and detail lookup.
Cart and wishlist tied to the authenticated user, plus a guest-or-authenticated order-tracking endpoint.
Checkout that snapshots cart items into an order, decrements size-level stock, and (optionally) kicks off a VNPAY payment.
Product reviews with a purchase-eligibility check before a user is allowed to review.
Authentication & Session
JWT access token (short-lived, returned in the response body) + JWT refresh token (long-lived, stored server-side and issued as an httpOnly cookie).
POST /auth/refresh-token mints a new access token from the refresh cookie; POST /auth/logout revokes it.
Password reset flow backed by a hashed, TTL-expiring PasswordResetToken and a pluggable email sender (noop / console / resend).
Role-based access control (user / admin) via authenticate + authorize([...roles]) middleware.
Admin API
Full CRUD for Products, Categories, Brands, Inventory, Orders, Users, Vouchers, Reviews (moderation), and Banners β all gated behind authenticate + authorize(['admin']).
Revenue and bestseller analytics endpoints with date-range/interval query params.
A server-side remote-image fetch proxy (SSRF-guarded) so the admin UI can pull external images without CORS issues.
Media & Payments
Multer (in-memory) + Cloudinary for product/banner image uploads, with 2MB/file limits and automatic overwrite of previously uploaded assets.
VNPAY integration for online payments: signed redirect URL at checkout, HMAC-SHA512-verified return/IPN callback.
Security & Ops
helmet, cors (credentialed, single allowed origin), express-rate-limit (100 req / 15 min / IP), compression, request body limits raised for base64 image payloads.
winston structured logging, graceful shutdown on SIGTERM/SIGINT with MongoDB disconnect.
β οΈKnown gap:/brands, /categories, and /banners are wired directly to the same router files used under /admin (full CRUD, including POST/PUT/DELETE), but are mounted without authenticate/authorize. Only GET is intended to be public β treat write access on these three paths as unauthenticated until this is locked down.
sequenceDiagram
autonumber
participant C as Client
participant R as auth.router.js
participant Svc as auth service
participant DB as MongoDB (User, Token)
C->>R: POST /auth/login { email, password }
R->>Svc: login(email, password)
Svc->>DB: find User, compare bcrypt hash
Svc->>Svc: generateAccessToken(userId)
Svc->>Svc: generateRefreshToken(userId)
Svc->>DB: persist Token { userId, token } (TTL 1w)
Svc-->>R: { user, accessToken, refreshToken }
R-->>C: 200 { user, accessToken } + Set-Cookie refreshToken (httpOnly, sameSite=strict)
Loading
2. Access-token refresh
sequenceDiagram
autonumber
participant C as Client
participant R as auth.router.js
participant DB as MongoDB (Token)
C->>R: POST /auth/refresh-token (Cookie: refreshToken)
R->>DB: find Token by cookie value
R->>R: verifyRefreshToken(token)
R->>R: generateAccessToken(userId)
R-->>C: 200 { accessToken }
Loading
3. Checkout with online payment
sequenceDiagram
autonumber
participant C as Client
participant R as order.router.js
participant Svc as checkout.service.js
participant DB as MongoDB (Cart, Order, Product)
participant VNP as VNPAY
C->>R: POST /orders/checkout (Bearer token)
R->>Svc: checkout(userId, body)
Svc->>DB: read Cart, snapshot items into Order
Svc->>DB: decrement Product size stock
alt payment_method = Online
Svc->>Svc: build signed VNPAY URL (HMAC-SHA512)
Svc-->>R: { order, paymentUrl }
R-->>C: 201 { order, paymentUrl }
C->>VNP: redirect to paymentUrl
VNP-->>R: GET /payment/vnpay_return (signed query params)
R->>R: verify checksum
R->>DB: set order.payment_status = paid | order.status = cancelled
else payment_method = COD
Svc-->>R: { order }
R-->>C: 201 { order }
end
Loading
4. Order history & detail
sequenceDiagram
autonumber
participant C as Client
participant R as order.router.js
participant Svc as history.service.js / detail.service.js
participant DB as MongoDB (Order)
C->>R: GET /orders (Bearer token)
R->>Svc: historyService(userId)
Svc->>DB: Order.find({ user_id }).sort({ createdAt: -1 })
DB-->>Svc: orders[]
Svc-->>R: orders[]
R-->>C: 200 { data: orders[] }
C->>R: GET /orders/:id (Bearer token)
R->>Svc: detailService(userId, orderId)
Svc->>DB: Order.findOne({ _id: orderId, user_id })
alt order found and owned by user
DB-->>Svc: order
Svc-->>R: order
R-->>C: 200 { data: order }
else not found / not owned
Svc-->>R: 404 error
R-->>C: 404 { message }
end
Loading
5. Order tracking (guest or authenticated)
sequenceDiagram
autonumber
participant C as Client
participant OA as optionalAuthenticate
participant Ctrl as trackOrder.controller.js
participant Svc as trackOrder.service.js
participant DB as MongoDB (Order)
C->>OA: GET /orders/track?orderId=...&phone=... (Bearer token optional)
OA->>OA: decode Bearer token if present β req.userId, else continue
OA->>Ctrl: next()
Ctrl->>Svc: trackOrderService(orderId, phone, userId)
Svc->>DB: Order.findById(orderId)
alt order not found
Svc-->>Ctrl: 404 error
Ctrl-->>C: 404 { message }
else req.userId matches order.user_id
Svc-->>Ctrl: order (owner match, phone not required)
Ctrl-->>C: 200 { data: order }
else phone matches order.phone (last 9 digits)
Svc-->>Ctrl: order (guest match by phone)
Ctrl-->>C: 200 { data: order }
else phone missing or mismatched
Svc-->>Ctrl: 400/403 error
Ctrl-->>C: 400/403 { message }
end
Loading
π Getting Started
Requires Node.js >= 18.x, a MongoDB instance (local or Atlas), and a Cloudinary account for image uploads.
Clone the repository
git clone https://github.com/MT-KS-04/crest-walk-api.git
cd crest-walk-api-JS
Install dependencies
npm install
Configure environment variables
Create a .env file in the project root:
# ServerPORT=3000NODE_ENV=developmentLOG_LEVELS=info# DatabaseMONGOOSE_URL=mongodb://localhost:27017/crest-walk-api# JWTJWT_ACCESS_SECRET=change-me-access-secretJWT_REFRESH_SECRET=change-me-refresh-secretACCESS_TOKEN_EXPIRY=15mREFRESH_TOKEN_EXPIRY=7d# CloudinaryCLOUDINARY_CLOUD_NAME=your-cloud-nameCLOUDINARY_API_KEY=your-api-keyCLOUDINARY_API_SECRET=your-api-secret# Password reset email (optional β defaults to a no-op sender)EMAIL_PROVIDER=noop# noop | console | resendEMAIL_FROM=no-reply@example.comEMAIL_API_KEY=FRONTEND_URL=http://localhost:3001PASSWORD_RESET_TOKEN_EXPIRY=15mPASSWORD_RESET_EXPOSE_TOKEN=false
src/server.js currently hardcodes the allowed CORS origin to http://localhost:3001 and VNPAY credentials are hardcoded placeholders in src/service/user/payment/vnpayUtils.js β update both directly in code if your frontend origin or VNPAY merchant details differ.
Run the server in development
npm run dev
The API is available at http://localhost:3000/api/v1/ and auto-reloads on file changes (nodemon.json).
Run the server in production
npm start
Lint
npm run lint # prettier --check .
Run tests
npm test# Jest, ESM mode, coverage report in coverage/
Base path: /admin/*. Every route below requires Authorization: Bearer <accessToken>androle: admin.
Products
Method
Endpoint
Notes
GET
/admin/products
List products
POST
/admin/products
Create; multipart field images (up to 10)
GET
/admin/products/:id
Product detail
PUT
/admin/products/:id
Update; images optional on re-upload
DELETE
/admin/products/:id
Delete
Categories
Method
Endpoint
Notes
GET
/admin/categories
List
POST
/admin/categories
Create
GET
/admin/categories/:id
Detail
PUT
/admin/categories/:id
Update
DELETE
/admin/categories/:id
Delete
Brands
Method
Endpoint
Notes
GET
/admin/brands
List
POST
/admin/brands
Create
GET
/admin/brands/:id
Detail
PUT
/admin/brands/:id
Update
DELETE
/admin/brands/:id
Delete
Orders
Method
Endpoint
Notes
GET
/admin/orders
List all orders
GET
/admin/orders/:id
Order detail
PUT
/admin/orders/:id/status
Update status / payment_status
Users
Method
Endpoint
Notes
GET
/admin/users
List users
GET
/admin/users/:id
User detail
PUT
/admin/users/:id/status
Update status / role
PUT
/admin/users/:id/reset-password
Admin resets a user's password
Inventory
Method
Endpoint
Notes
GET
/admin/inventory
List stock; ?lowStock= filter
PATCH
/admin/inventory/:productId/size/:size
Set/increment stock; body.mode: set|inc
Vouchers
Method
Endpoint
Notes
GET
/admin/vouchers
List
POST
/admin/vouchers
Create
GET
/admin/vouchers/:id
Detail
PUT
/admin/vouchers/:id
Update
DELETE
/admin/vouchers/:id
Delete
Reviews (moderation)
Method
Endpoint
Notes
GET
/admin/reviews
List; filter by status/productId/userId
GET
/admin/reviews/:id
Detail
PUT
/admin/reviews/:id/status
Approve / reject
DELETE
/admin/reviews/:id
Delete
Banners
Method
Endpoint
Notes
GET
/admin/banners
List; filter by position/is_active
POST
/admin/banners
Create; multipart field image_url
GET
/admin/banners/:id
Detail
PUT
/admin/banners/:id
Update; image_url optional
DELETE
/admin/banners/:id
Delete
Stats
Method
Endpoint
Query params
GET
/admin/stats/revenue
interval=day|month|year, startDate, endDate
GET
/admin/stats/bestsellers
limit, startDate, endDate
Media
Method
Endpoint
Notes
POST
/admin/fetch-remote-image
SSRF-guarded proxy that fetches an external image by { url } for the admin UI
All authenticated requests send Authorization: Bearer <accessToken>; the refresh-token cookie is only used by /auth/refresh-token and /auth/logout.
ποΈ Data Model
All 12 schemas live in src/model/. There is no separate "Shoe" model β footwear is represented by Product, with size/stock tracked per entry in its embedded sizes array.
The backend service behind Crest Walk, a sneaker e-commerce platform. It handles everything from the shopping experience to online payments and gives store admins full control over their business.