Developer Documentation

NexaByte Electronics Store

A full-stack e-commerce platform built with Next.js 15, Express.js, Prisma, MySQL, and AWS S3. This guide covers everything from first clone to production deployment.

Next.js 15 App Router Express.js API Prisma ORM MySQL NextAuth.js AWS S3 / MinIO Tailwind CSS TypeScript
01

Project Overview

NexaByte is a production-ready electronics e-commerce platform with a fully-featured admin dashboard, customer storefront, authentication, order management, and dynamic site settings.

🛍️

Storefront

Product listings, categories, search, cart, checkout and place order for customers.

📊

Admin Dashboard

Full CRUD for products, categories, orders, users, FAQs, merchants, and settings.

🔐

Auth & Roles

NextAuth.js sessions, email verification, password reset, and role-based access control.

☁️

Cloud Storage

Image uploads to S3-compatible storage (MinIO ready) with automatic orphan cleanup.

📧

Email

Transactional emails via Gmail OAuth2 — verification and password reset.

Architecture

The project runs two separate processes:

ProcessPortTechnologyRole
Next.js (frontend + API routes) 3000 Next.js 15 App Router SSR pages, client UI, NextAuth, internal API routes
Express.js (data API) 3001 Node.js / Express All CRUD operations, image uploads, email, Prisma queries
ℹ️
The Next.js frontend runs on port 3000 and the Express API server runs on port 3001. No URL prefixes are required — both are accessible at their root paths.

Directory Structure

Project Root
nexabyte/
├── app/                    Next.js App Router pages
│   ├── _zustand/           Global Zustand store (cart, wishlist)
│   ├── (dashboard)/        Admin dashboard (role-gated)
│   ├── about-us/
│   ├── actions/            Next.js server actions
│   ├── api/                Internal Next.js API routes (NextAuth etc.)
│   ├── cart/
│   ├── checkout/
│   ├── contact-us/
│   ├── faq/
│   ├── feedback/
│   ├── forgot-password/
│   ├── login/
│   ├── notifications/
│   ├── privacy-policy/
│   ├── product/            Single product page [slug]
│   ├── register/
│   ├── reset-password/
│   ├── search/
│   ├── shop/               Product listing / category [[...slug]]
│   ├── terms-of-service/
│   ├── terms-of-use/
│   ├── verify-email/
│   ├── error.tsx
│   ├── globals.css
│   ├── layout.tsx          Root layout (metadata, providers)
│   ├── not-found.tsx
│   └── page.tsx            Homepage
├── components/             Shared React components
├── helpers/                Utility helpers (is-browser, is-small-screen)
├── hooks/                  Custom React hooks
├── lib/                    Auth options, API client, sanitize, utils
├── prisma/                 Root-level Prisma schema (Next.js generate target)
├── public/                 Static assets
├── server/                 Express.js API server
│   ├── controllers/        Business logic for every resource
│   ├── logs/               Request / error log files
│   ├── middleware/         Rate limiting, request logging
│   ├── prisma/             Server-side Prisma schema + migrations
│   ├── routes/             Express route definitions
│   ├── scripts/            Utility / seed scripts
│   ├── services/           Email service (nodemailer / OAuth2)
│   ├── tests/              API test files
│   ├── utils/              Server-side utility helpers
│   ├── app.js              Entry point (port 3001)
│   ├── createAdminUser.js  CLI — create a new admin user
│   ├── makeUserAdmin.js    CLI — promote existing user to admin
│   ├── listUsers.js        CLI — list all registered users
│   └── package.json
├── types/                  Shared TypeScript type definitions
├── utils/                  Frontend utility functions
├── middleware.ts            Next.js route protection middleware
├── next.config.mjs
├── tailwind.config.ts
└── package.json
02

Prerequisites

ToolMinimum VersionNotes
Node.js18.x or 20.xLTS recommended. Check with node -v
npm9+Bundled with Node.js
MySQL8.0+Or a compatible host (PlanetScale, Railway, AWS RDS)
S3-compatible storage—MinIO (self-hosted), AWS S3, or any compatible service
GitanyFor cloning the repository
ℹ️
A Gmail account with 2 factor authentication enabled (for not marking emails as spams) and OAuth2 credentials is required for transactional emails (password reset, email verification). See the Server .env section for setup details.
03

Quick Start

1

Extract the project

You will receive the project as a .zip file. When extracted, it will contain two folders:

  • nexabyte/ — the main application (Next.js + Express server)
  • documentation/ — this documentation file and assets

Navigate into the nexabyte folder to begin setup.

TerminalBASH
unzip nexabyte.zip
cd nexabyte
2

Install dependencies

TerminalBASH
# Install Next.js / frontend dependencies
npm install

# Install Express API server dependencies
cd server && npm install && cd ..
3

Create environment files

Create .env in the project root and server/.env in the server folder. See the Root .env and Server .env sections for all variables.

4

Set up the database

TerminalBASH
# Push the Prisma schema to your MySQL database
npm run db:push

# Generate Prisma client types
npm run db:generate
5

Create your first admin user

TerminalBASH
node server/createAdminUser.js [email protected] yourpassword
6

Start both servers

TerminalBASH
# Option A — built-in concurrent script
npm run start:all

# Option B — two separate terminals
node server/app.js          # terminal 1 → port 3001
npm run dev                 # terminal 2 → port 3000
7

Open in browser

Storefront → http://localhost:3000 Admin → http://localhost:3000/admin

Available npm Scripts

ScriptDescription
npm run devStart Next.js in development mode (port 3000)
npm run serverStart Express API server (port 3001)
npm run start:allStart both servers concurrently
npm run buildGenerate Prisma client + Next.js production build
npm startStart Next.js in production mode (after build)
npm run db:pushSync Prisma schema to the database
npm run db:generateRe-generate Prisma Client types
npm run db:studioOpen Prisma Studio (visual DB browser)
npm run lintRun ESLint on the project
04

Root Environment Variables

Create a file named .env in the project root (same folder as package.json). These variables are loaded by Next.js.

⚠️
Never commit your .env files. The .gitignore already excludes them. Keep production secrets in your hosting provider's secret manager.
.env (project root)ENV
# ── Next.js & Auth ───────────────────────────────────────
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-random-secret-string-min-32-chars

# ── API Base URL (Express server) ────────────────────────
NEXT_PUBLIC_API_BASE_URL=http://localhost:3001

# ── S3 / MinIO (public URL used in <img> src) ────────────
NEXT_PUBLIC_S3BUCKET=https://your-s3-endpoint

# ── Database ─────────────────────────────────────────────
DATABASE_URL=mysql://user:password@host:3306/nexabyte
VariableRequiredExampleNotes
NEXTAUTH_URL Required http://localhost:3000 Full URL to the Next.js app
NEXTAUTH_SECRET Required — Generate with: openssl rand -base64 32
NEXT_PUBLIC_API_BASE_URL Required http://localhost:3001 Express API base URL — used by frontend fetches
NEXT_PUBLIC_S3BUCKET Required https://ds.example.com Public endpoint for S3/MinIO (for image src URLs)
DATABASE_URL Required mysql://user:pass@host:3306/db MySQL connection string (used by root Prisma client for Next.js)
05

Server Environment Variables

Create a file named .env inside the server/ folder. Express loads this file first, then falls back to the root .env.

server/.envENV
# ── Database ─────────────────────────────────────────────
DATABASE_URL=mysql://user:password@host:3306/nexabyte

# ── NextAuth ─────────────────────────────────────────────
NEXTAUTH_URL=http://localhost:3000

# ── CORS & base paths ────────────────────────────────────
FRONTEND_URL=https://your-production-domain.com

# ── Gmail OAuth2 (for transactional emails) ───────────────
GMAIL_USER=[email protected]
OAUTH_CLIENT_ID=xxxx.apps.googleusercontent.com
OAUTH_CLIENT_SECRET=GOCSPX-xxxxx
OAUTH_REFRESH_TOKEN=1//xxxxx

# ── S3 / MinIO ───────────────────────────────────────────
NEXT_PUBLIC_S3BUCKET=https://your-s3-endpoint
AWS_BUCKET=your-bucket-name
AWS_ACCESS_KEY_ID=your-access-key-id
AWS_SECRET_ACCESS_KEY=your-secret-access-key
AWS_REGION=ap-southeast-1
VariableRequiredNotes
DATABASE_URL Required MySQL connection string for Prisma (server-side)
NEXTAUTH_URL Required Used to build email callback links
FRONTEND_URL Optional Production domain — added to CORS whitelist
GMAIL_USER Required for email Gmail address used as the "from" address
OAUTH_CLIENT_ID Required for email Google Cloud OAuth2 Client ID
OAUTH_CLIENT_SECRET Required for email Google Cloud OAuth2 Client Secret
OAUTH_REFRESH_TOKEN Required for email Long-lived OAuth2 refresh token from Google
NEXT_PUBLIC_S3BUCKET, AWS_* Required Same S3 credentials as root .env — needed by Express for uploads & deletions

Setting up Gmail OAuth2 for email

1

Create a Google Cloud project

Go to console.cloud.google.com → Create project → Enable the Gmail API.

2

Create OAuth2 credentials

APIs & Services → Credentials → Create Credentials → OAuth Client ID → Application type: Web application. Configure the following:

  • Authorized JavaScript origins: http://localhost:3000
  • Authorized redirect URIs: https://developers.google.com/oauthplayground
3

Get a refresh token

Visit OAuth Playground, use your Client ID/Secret, authorize Gmail API scope https://mail.google.com/, and exchange for a refresh token.

06

Database Setup

NexaByte uses Prisma ORM with MySQL. There are two Prisma configurations — one at the project root (used by Next.js) and one inside server/prisma/ (used by Express).

Creating the MySQL database

MySQLSQL
CREATE DATABASE nexabyte CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'nexabyte_user'@'%' IDENTIFIED BY 'your_strong_password';
GRANT ALL PRIVILEGES ON nexabyte.* TO 'nexabyte_user'@'%';
FLUSH PRIVILEGES;

Applying the schema

TerminalBASH
# Push schema to DB (creates/alters tables)
npm run db:push

# Or use migrations for production
npx prisma migrate deploy --schema server/prisma/schema.prisma

Database Models

ModelKey FieldsDescription
Productid, slug, title, sku, mainImage, price, rating, inStock, categoryId, merchantIdStore products with category and merchant relations
ImageimageID, productID, imageProduct gallery images (multiple per product)
Categoryid, name, iconProduct categories with optional icon name
Merchantid, name, email, phone, statusSuppliers / merchants linked to products
Userid, email, password, role, emailVerifiedCustomers and admins. role: "user" | "admin"
Customer_orderid, name, email, status, total, dateTimeCustomer orders with address and line items
customer_order_productid, customerOrderId, productId, quantityOrder line items (join table)
Notificationid, userId, title, message, type, isRead, priorityIn-app notifications per user
Settingid, key, value, type, group, labelDynamic site configuration (seeded on startup)
Feedbackid, name, email, feedback, rating, isReadCustomer feedback / reviews submitted via site
ContactMessageid, email, message, isReadMessages submitted via Contact Us page
PasswordResetTokenemail, code, expiresAt, usedTime-limited password reset codes
EmailVerificationTokenemail, token, expiresAt, usedEmail verification tokens
FAQid, question, answer, category, order, isActiveFrequently asked questions displayed on the storefront

Prisma Studio (visual DB browser)

TerminalBASH
npm run db:studio   # opens at http://localhost:5555
07

Admin CLI Tools

Three Node.js scripts in the server/ directory help you manage users directly from the terminal — no UI needed.

ℹ️
All three scripts must be run from the project root (not inside server/) so the path server/createAdminUser.js resolves correctly and Prisma can find the server/.env.

1 — Create a brand-new admin user

TerminalBASH
node server/createAdminUser.js <email> <password>

# Example:
node server/createAdminUser.js [email protected] MyS3cure!Pass

This script will:

  • Check if the email is already registered — exits if it is
  • Hash the password with bcrypt (10 rounds)
  • Create the user with role: "admin" and a UUID
  • Print the credentials to the console

2 — Promote an existing user to admin

TerminalBASH
node server/makeUserAdmin.js <email>

# Example:
node server/makeUserAdmin.js [email protected]

Updates the existing user's role field from "user" to "admin". Safe to run if the user is already an admin — it will just report the current status.

3 — List all registered users

TerminalBASH
node server/listUsers.js

Prints a table of all users with their ID, email, role, and email verification status. Useful for finding a user's email before calling makeUserAdmin.js.

08

Storefront Pages

All public-facing pages accessible to visitors and registered customers.

🏠 Homepage /

Hero banner, introducing section, category menu, featured products (animated), testimonials, and member CTA for guests.

🛒 Shop /shop

Full product catalog with filters (category, price range), sort controls, and pagination. URL-driven state — shareable links.

📦 Product Detail /product/[slug]

Product images, price, stock status, description tabs, urgency text, add-to-cart, buy now, and social share buttons.

🛍️ Cart /cart

Cart managed by Zustand (client state). Quantity controls, item removal, total calculation, and proceed to checkout.

✅ Checkout /checkout

Billing & shipping form with order review. Requires session. Creates order via Express API on submit.

🔍 Search /search

Full-text product search with instant results.

🔔 Notifications /notifications

Per-user notification center — mark read, delete, bulk actions. Requires login.

👤 Auth Pages

/login · /register · /forgot-password · /reset-password · /verify-email

ℹ️ Static Pages

/about-us · /contact-us · /faq · /privacy-policy · /terms-of-service · /terms-of-use

💬 Feedback /feedback

Logged-in customers submit star-rated feedback. Stored and reviewable by admin.

Authentication Flow

Authentication uses NextAuth.js v4 with a custom Credentials provider.

1

Registration

User registers → account created → verification email sent → user clicks link → emailVerified set to true.

2

Login

Credentials submitted to NextAuth → user fetched from DB via Express API → bcrypt compare → session JWT created with id, email, role.

3

Admin access

Middleware checks token.role === "admin" on every /admin/* route. Redirects to homepage if not admin.

09

Admin Dashboard

Accessible at /admin. Requires role: "admin". Protected by Next.js middleware — unauthenticated users are redirected to login.

🔒
All admin routes are protected at the middleware level. Even a direct URL visit will redirect non-admin users. You cannot bypass this without a valid admin JWT session.

Dashboard Overview /admin

The main dashboard shows:

  • Stats cards — Total revenue, orders, products, and users with trend indicators
  • Chart — Revenue/orders trend chart (weekly / monthly / yearly toggle) powered by ApexCharts
  • Quick-action cards — One-click links to Products, Orders, Users, Categories, Settings, FAQs, Merchants, and Static Pages

Products /admin/products

Product List

Sortable, searchable table with product image, title, price, stock, category, and merchant. Inline edit and delete.

Create Product /admin/products/new

Full form: title, slug (auto-generated), SKU, price, stock, category, merchant, description (rich text), main image upload, gallery images.

Edit Product /admin/products/[id]

Same form pre-populated. Replacing the main image automatically deletes the old one from S3.

Delete Product

Deletes the product, all gallery images from DB, and all associated S3 objects automatically.

Categories /admin/categories

Create, edit, and delete product categories. Each category has a name and an optional icon name (used by the CategoryIcon component to render SVG icons on the storefront).

Orders /admin/orders

Order List

Paginated table with customer name, email, total, status badge, and date. Filter by status.

Order Detail /admin/orders/[id]

Full order info — items, quantities, billing address, and status. Update status (Pending → Processing → Shipped → Delivered).

Users /admin/users

View all registered customers. See role, email verification status, and join date. Create users, edit their details, or delete accounts. Promote a user to admin from within the UI.

Merchants /admin/merchant

Manage suppliers and merchants that are linked to products. Each merchant has a name, optional email, phone, address, description, and a status (ACTIVE / INACTIVE). Products can be filtered by merchant on the product list.

FAQs /admin/faq

Add, edit, order, and toggle visibility of FAQ items. Each item has a question, answer, category tag, and an order number for display sequence. FAQs appear on the public /faq page.

Feedback /admin/feedbacks

View all customer feedback submissions. Displays name, email, star rating, message, read/unread status, and date. Mark as read or delete. The notification bell shows unread count.

Messages /admin/messages

Inbox for all contact form submissions from the /contact-us page. View full message in a side panel, mark read, or delete. Unread count shown in the notification bell.

Static Pages /admin/static-pages

Edit the rich-text content for:

  • Privacy Policy — displayed at /privacy-policy
  • Terms of Service — displayed at /terms-of-service
  • Terms of Use — displayed at /terms-of-use
  • About Us — structured editor for the hero, story, values, stats, and CTA sections at /about-us

All page content is stored in the Setting table with keys prefixed page_*.

Settings /admin/settings

See the Settings Reference section for a complete breakdown of all configurable options.

Notifications Bell

The top-right bell icon in the admin header shows a combined unread count from both Feedback and Contact Messages. Clicking it opens a dropdown with the latest unread items and links to their respective pages.

10

Settings Reference

Settings are stored in the Setting database table and automatically seeded on server startup. They are grouped into tabs in the admin UI.

ℹ️
Settings are seeded automatically when the Express server starts. If a key doesn't exist in the DB, it's created from the defaults. You never need to manually insert settings rows.

General Group

KeyTypeDefaultDescription
site_namestringNexaByteStore name — shown in the browser title bar dynamically
site_descriptionstringYour premium…Meta description for SEO
contact_emailstring[email protected]Displayed in the footer and contact page
contact_phonestring+1 (555) 000-0000Displayed in the footer and contact page
currencystringUSDCurrency code used in labels
currency_symbolstring$Symbol prepended to prices throughout the store
faviconimage""Site favicon — uploaded via admin. ICO, PNG, and SVG supported. Replaces default /favicon.ico

Billing Group

KeyTypeDefaultDescription
tax_ratenumber0Tax percentage applied at checkout
shipping_feenumber0Default shipping fee added to orders
free_shipping_thresholdnumber0Order total above which shipping is free (0 = disabled)

Inventory Group

KeyTypeDefaultDescription
low_stock_thresholdnumber5Products with stock ≤ this number show a "Low Stock" warning

Social Group

KeyTypeDefaultDescription
social_facebookstring""Full Facebook page URL
social_instagramstring""Full Instagram profile URL
social_twitterstring""Full Twitter / X profile URL

Image-type Settings

The following settings accept image uploads directly in the admin UI. Old images are automatically deleted from S3 when replaced.

KeyRecommended SizeSupported Formats
site_logo220 × 60 pxPNG, SVG (transparent background recommended)
footer_logo220 × 60 pxPNG, SVG (transparent background recommended)
hero_image512 × 520 pxPNG (transparent background recommended)
favicon64 × 64 pxICO, PNG, SVG

Static Page Keys

These keys hold rich-text HTML content and are edited via the Static Pages editor in the admin dashboard.

page_privacy_policy page_terms_of_service page_terms_of_use page_about_hero_title page_about_story page_about_mission page_about_value_* page_about_stat_* page_about_why_* page_about_cta_*
11

API Reference

All endpoints are served by the Express server at http://localhost:3001.

ℹ️
In the tables below, the base URL http://localhost:3001 is omitted. The paths shown are the full paths after the host.

Products

MethodPathDescription
GET/api/productsList all products (supports query params: category, search, page, limit, sort)
POST/api/productsCreate a product
GET/api/products/:idGet a single product by ID or slug
PUT/api/products/:idUpdate product (cleans old S3 image if mainImage changes)
DELETE/api/products/:idDelete product + all S3 images

Product Gallery Images

MethodPathDescription
GET/api/images/:idGet all gallery images for a product (id = productID)
POST/api/imagesAdd a gallery image to a product
PUT/api/images/:idUpdate a gallery image record (id = imageID)
DELETE/api/images/:idDelete all gallery images for a product (id = productID) + S3 cleanup

Image Upload

MethodPathDescription
POST/api/main-image?folder=productsUpload an image to S3. folder must be products or general. Default: products.

The request must use multipart/form-data with the file field named uploadedFile. Returns { fileUrl }.

Categories

MethodPathDescription
GET/api/categoriesList all categories
POST/api/categoriesCreate a category
GET/api/categories/:idGet a single category
PUT/api/categories/:idUpdate a category
DELETE/api/categories/:idDelete a category (cascades to products)

Users

MethodPathDescription
GET/api/usersList all users
POST/api/usersCreate a user (registration)
GET/api/users/:idGet user by ID
PUT/api/users/:idUpdate user (role, email, etc.)
DELETE/api/users/:idDelete a user
GET/api/users/email/:emailLook up user by email (used by NextAuth)

Orders

MethodPathDescription
GET/api/ordersList all orders
POST/api/ordersCreate an order (checkout)
GET/api/orders/pending/countGet count of pending orders
GET/api/orders/:idGet single order with line items
PUT/api/orders/:idUpdate order (status change)
DELETE/api/orders/:idDelete an order

Settings

MethodPathDescription
GET/api/settingsGet all settings (triggers auto-seed on first call)
PUT/api/settings/bulkBulk update multiple settings at once. Old S3 images are cleaned up automatically.
GET/api/settings/:keyGet a single setting by key
PUT/api/settings/:keyUpdate a single setting
POST/api/settingsCreate a new custom setting
DELETE/api/settings/:keyDelete a setting by key

Stats (Dashboard)

MethodPathDescription
GET/api/stats/dashboardReturns total revenue, orders, products, users, and trend percentages
GET/api/stats/chartsReturns time-series data for the revenue/orders chart. Query param: range=weekly|monthly|yearly

Authentication (email flows)

MethodPathDescription
POST/api/auth/forgot-passwordSend a 6-digit reset code to the given email
POST/api/auth/verify-reset-codeVerify the 6-digit code. Returns a one-use token.
POST/api/auth/reset-passwordSet a new password using the verified token
POST/api/auth/send-verification-emailSend email verification link to a new user
POST/api/auth/verify-emailVerify the email token and mark user as verified

Other Endpoints

ResourceBase PathOperations
Search/api/searchGET — full-text product search
Merchants/api/merchantsGET, POST, GET/:id, PUT/:id, DELETE/:id
FAQs/api/faqGET, POST, GET/:id, PUT/:id, DELETE/:id
Feedback/api/feedbackPOST (submit), GET (admin), GET/:id, PATCH/:id/read, DELETE/:id, GET /unread/count
Contact/api/contactPOST (submit), GET (admin), PATCH/:id/read, DELETE/:id, GET /unread/count
Notifications/api/notificationsGET/:userId, POST, PUT/:id, DELETE/:id, POST /mark-read, DELETE /bulk, GET /:userId/unread-count
Slugs/api/slugs/:slugGET — fetch a product by its slug (used by the storefront product page)
Order Products/api/order-productGET, POST, GET/:id, PUT/:id, DELETE/:id
Health Check/healthGET — server liveness check

Rate Limiting

The Express server applies layered rate limiting to protect against abuse:

LimiterApplied toPurpose
GeneralAll routesBase protection for all requests
Auth/api/users/emailStrict limit on login attempts
Upload/api/images, /api/main-imagePrevent storage abuse
Search/api/searchPrevent scraping
Order/api/orders, /api/order-productPrevent order flooding
User Management/api/usersProtect account operations
12

Image Uploads & S3 Storage

Storage Structure

All images are stored in S3/MinIO under the bucket defined by AWS_BUCKET. The path structure is:

S3 Object Key Pattern
nexabyte/{folder}/{nanoid}-{timestamp}.ext

# Product images (main + gallery):
nexabyte/products/abc123-1716545600000.jpg

# Site images (logos, hero, favicon):
nexabyte/general/xyz789-1716545600000.png

The public URL for any image is:

Public Image URL Pattern
{NEXT_PUBLIC_S3BUCKET}/{AWS_BUCKET}/nexabyte/{folder}/filename.ext

Allowed File Types

image/jpeg image/jpg image/png image/webp image/gif image/svg+xml image/x-icon image/vnd.microsoft.icon

File Size Limit

Maximum file size: 5 MB per upload.

Automatic S3 Cleanup

Old images are automatically deleted from S3 in these scenarios:

TriggerWhat gets deleted
Product mainImage replacedPrevious mainImage S3 object
Product deletedmainImage + all gallery Image records from S3
Gallery images deletedAll gallery S3 objects for that product
Image-type Setting replacedPrevious image S3 object (logo, favicon, etc.)
🛡️
Deletion is safe by design. The delete helper validates that the URL belongs to your configured bucket before issuing any command. Foreign URLs, empty values, and misconfigured environments all result in a silent no-op. Failures are logged but never crash the request.

Using MinIO (self-hosted S3)

Set NEXT_PUBLIC_S3BUCKET to your MinIO endpoint (e.g., http://192.168.1.10:9000). The S3 client is already configured with forcePathStyle: true, which is required for MinIO.

⚠️
If you use MinIO, ensure the bucket policy allows public read access, or configure a proxy/CDN in front of MinIO for production use. The application constructs image URLs directly from the endpoint + bucket name.

Configuring next.config.mjs for images

This is a critical step. Next.js blocks all external images by default. Any domain you serve images from (your S3 endpoint, MinIO host, CDN, etc.) must be explicitly whitelisted in next.config.mjs or <Image> components will throw a runtime error.

⚠️
Do this before anything else. If you see "hostname is not configured under images.remotePatterns" in the browser, it means your S3/MinIO hostname is missing from this config.
next.config.mjsJS
const nextConfig = {
  basePath: '', // no base path prefix — app is served at the root

  images: {
    // Set to false if you want Next.js built-in image optimization (recommended for production).
    // Keep true only if you are serving pre-optimized images or using an external CDN.
    unoptimized: true,

    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'your-s3-or-minio-hostname.com', // ← replace with your actual S3/MinIO domain
        port: '',
      },
      {
        protocol: 'https',
        hostname: 'placehold.co', // placeholder images — keep this
        port: '',
      },
      // Add more patterns here for any other external image host you use
    ],
  },

  env: {
    NEXT_PUBLIC_API_BASE_URL: process.env.NEXT_PUBLIC_API_BASE_URL,
  },

  // Security headers — already configured, do not remove
  async headers() {
    return [
      {
        source: '/(.*)',
        headers: [
          { key: 'X-Frame-Options',       value: 'DENY' },
          { key: 'X-Content-Type-Options', value: 'nosniff' },
          { key: 'X-XSS-Protection',      value: '1; mode=block' },
        ],
      },
    ];
  },
};

How to find your hostname

Your hostname is the domain part of your NEXT_PUBLIC_S3BUCKET environment variable — everything between https:// and the first /.

Example
# If your NEXT_PUBLIC_S3BUCKET is:
NEXT_PUBLIC_S3BUCKET=https://storage.mycompany.com

# Then your remotePatterns entry is:
{ protocol: 'https', hostname: 'storage.mycompany.com', port: '' }

Using unoptimized: true vs built-in optimization

SettingWhen to useTrade-off
unoptimized: true Images are already optimized at source, or you use an external CDN/image service No server-side processing. Faster builds, but no automatic WebP/AVIF conversion.
unoptimized: false (default) You want Next.js to auto-convert to WebP/AVIF and resize on-demand Requires a Node.js server (not static export). Adds processing overhead on first request.
13

Customization Guide

Brand Colors

The primary color palette is Tailwind's indigo with a custom yellow accent. To change the primary color, update these files:

tailwind.config.tsTS
theme: {
  extend: {
    colors: {
      // Change this to your brand accent color
      'custom-yellow': '#FED700',
    }
  },
},

For the primary indigo color, replace Tailwind's indigo-* classes in components with your chosen color. The main files to update are:

  • components/Hero.tsx — Hero section background and CTA button
  • components/DashboardSidebar.tsx — Sidebar active states
  • app/globals.css — Range slider color (#3B82F6)
  • components/Header.tsx — Cart badge and navigation

Site Name, Logo, and Favicon

These are all configurable via the Admin → Settings → General tab without touching any code:

  • Site Name — Updates the browser title dynamically via generateMetadata()
  • Site Logo / Footer Logo — Upload via the image upload button next to the setting
  • Favicon — Upload an ICO, PNG, or SVG and it becomes the browser tab icon immediately

Homepage Sections

The homepage renders sections as independent components. To hide a section, remove it from app/page.tsx:

app/page.tsxTSX
// Remove any line below to hide that section:
<Hero />
<IntroducingSection />
<CategoryMenu />
<ProductsSection />
<Testimonial />
{!session && <MemberCTA />}

Adding a New Product Category Icon

Category icons are rendered by the CategoryIcon component. To add a new icon:

1

Open the component

components/CategoryIcon.tsx

2

Add a new case to the icon map

The component maps icon name strings to SVG elements. Add your category's icon name as a new key with an SVG path.

3

Use the icon name in the admin

When creating or editing a category in the admin, enter the exact icon name string you added in step 2.

Changing the Base Path

ℹ️
No action needed for standard setup. Out of the box the app runs with no URL prefix — frontend at http://localhost:3000 and API at http://localhost:3001. Only follow this section if you need to host the app under a sub-path (e.g., yourdomain.com/shop).

If you do need a sub-path, all four places below must be updated in sync:

FileKey to changeExample value
next.config.mjsbasePath'/shop'
Root .envNEXTAUTH_URLhttp://localhost:3000/shop
Root .envNEXT_PUBLIC_API_BASE_URLhttp://localhost:3001/api-prefix
Root .envNEXT_PUBLIC_BASE_PATH/shop
server/.envSERVER_BASE_PATH/api-prefix
server/.envNEXTAUTH_URLhttp://localhost:3000/shop

Currency

Change the currency symbol across the entire site from Admin → Settings → General. Set currency (e.g., EUR) and currency_symbol (e.g., €). All price displays use the useSiteSettings() hook which reads from the settings API.

Testimonials

Testimonials are dynamic — they come from the database, not hardcoded text. Logged-in users can submit feedback with a star rating via the site. The storefront homepage automatically fetches and displays the top 5 highest-rated feedbacks as testimonials using components/Testimonial.tsx.

To manage feedback, go to Admin → Feedback where you can view, read, and delete submitted entries.

Footer Links and Content

Edit components/Footer.tsx to change footer columns, links, and social icons. Social media URLs are pulled from settings (social_facebook, social_instagram, social_twitter).

14

Deployment

Production Build

TerminalBASH
# 1. Generate Prisma client and build Next.js
npm run build

# 2. Start Next.js in production mode
npm start   # port 3000

# 3. Start Express server
node server/app.js   # port 3001

Production Environment Variables

Update the following for production:

VariableProduction value example
NEXTAUTH_URLhttps://yourdomain.com
NEXT_PUBLIC_API_BASE_URLhttps://api.yourdomain.com
FRONTEND_URLhttps://yourdomain.com
NODE_ENVproduction
⚠️
In production, set NODE_ENV=production. CORS is restricted to the FRONTEND_URL and NEXTAUTH_URL when not in development mode.

Using a Process Manager (PM2)

TerminalBASH
npm install -g pm2

# Start Express API
pm2 start server/app.js --name nexabyte-api

# Start Next.js
pm2 start npm --name nexabyte-web -- start

# Save process list (survives reboots)
pm2 save
pm2 startup

Nginx Reverse Proxy Example

nginx.conf (partial)NGINX
server {
  listen 80;
  server_name yourdomain.com;

  # Express API — must come before / to avoid swallowing API traffic
  location /api/ {
    proxy_pass         http://localhost:3001;
    proxy_http_version 1.1;
    proxy_set_header   Host $host;
    proxy_set_header   X-Real-IP $remote_addr;
    proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  # Next.js (frontend) — catch-all
  location / {
    proxy_pass         http://localhost:3000;
    proxy_http_version 1.1;
    proxy_set_header   Upgrade $http_upgrade;
    proxy_set_header   Connection 'upgrade';
    proxy_set_header   Host $host;
    proxy_cache_bypass $http_upgrade;
  }
}

Pre-deployment Checklist

  • ✅ Both .env files configured with production values
  • ✅ NEXTAUTH_SECRET is a long, random, unpredictable string
  • ✅ MySQL database created and schema applied (npm run db:push)
  • ✅ S3 bucket created with correct permissions
  • ✅ Admin user created via CLI
  • ✅ Gmail OAuth2 credentials configured and tested
  • ✅ npm run build completes without errors
  • ✅ Nginx (or other reverse proxy) configured for both ports
  • ✅ HTTPS / SSL certificate provisioned (Let's Encrypt recommended)
  • ✅ S3 image hostname added to next.config.mjs remotePatterns