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.
Transactional emails via Gmail OAuth2 — verification and password reset.
Architecture
The project runs two separate processes:
| Process | Port | Technology | Role |
|---|---|---|---|
| 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 |
3000 and the Express API server runs on port 3001. No URL prefixes are required — both are accessible at their root paths.Directory Structure
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
Prerequisites
| Tool | Minimum Version | Notes |
|---|---|---|
| Node.js | 18.x or 20.x | LTS recommended. Check with node -v |
| npm | 9+ | Bundled with Node.js |
| MySQL | 8.0+ | Or a compatible host (PlanetScale, Railway, AWS RDS) |
| S3-compatible storage | — | MinIO (self-hosted), AWS S3, or any compatible service |
| Git | any | For cloning the repository |
Quick Start
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.
unzip nexabyte.zip cd nexabyte
Install dependencies
# Install Next.js / frontend dependencies npm install # Install Express API server dependencies cd server && npm install && cd ..
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.
Set up the database
# Push the Prisma schema to your MySQL database npm run db:push # Generate Prisma client types npm run db:generate
Create your first admin user
node server/createAdminUser.js [email protected] yourpassword
Start both servers
# 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
Open in browser
Available npm Scripts
| Script | Description |
|---|---|
npm run dev | Start Next.js in development mode (port 3000) |
npm run server | Start Express API server (port 3001) |
npm run start:all | Start both servers concurrently |
npm run build | Generate Prisma client + Next.js production build |
npm start | Start Next.js in production mode (after build) |
npm run db:push | Sync Prisma schema to the database |
npm run db:generate | Re-generate Prisma Client types |
npm run db:studio | Open Prisma Studio (visual DB browser) |
npm run lint | Run ESLint on the project |
Root Environment Variables
Create a file named .env in the project root (same folder as package.json). These variables are loaded by Next.js.
.env files. The .gitignore already excludes them. Keep production secrets in your hosting provider's secret manager.# ── 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
| Variable | Required | Example | Notes |
|---|---|---|---|
| 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) |
Server Environment Variables
Create a file named .env inside the server/ folder. Express loads this file first, then falls back to the root .env.
# ── 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
| Variable | Required | Notes |
|---|---|---|
| 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
Create a Google Cloud project
Go to console.cloud.google.com → Create project → Enable the Gmail API.
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
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.
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
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
# 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
| Model | Key Fields | Description |
|---|---|---|
| Product | id, slug, title, sku, mainImage, price, rating, inStock, categoryId, merchantId | Store products with category and merchant relations |
| Image | imageID, productID, image | Product gallery images (multiple per product) |
| Category | id, name, icon | Product categories with optional icon name |
| Merchant | id, name, email, phone, status | Suppliers / merchants linked to products |
| User | id, email, password, role, emailVerified | Customers and admins. role: "user" | "admin" |
| Customer_order | id, name, email, status, total, dateTime | Customer orders with address and line items |
| customer_order_product | id, customerOrderId, productId, quantity | Order line items (join table) |
| Notification | id, userId, title, message, type, isRead, priority | In-app notifications per user |
| Setting | id, key, value, type, group, label | Dynamic site configuration (seeded on startup) |
| Feedback | id, name, email, feedback, rating, isRead | Customer feedback / reviews submitted via site |
| ContactMessage | id, email, message, isRead | Messages submitted via Contact Us page |
| PasswordResetToken | email, code, expiresAt, used | Time-limited password reset codes |
| EmailVerificationToken | email, token, expiresAt, used | Email verification tokens |
| FAQ | id, question, answer, category, order, isActive | Frequently asked questions displayed on the storefront |
Prisma Studio (visual DB browser)
npm run db:studio # opens at http://localhost:5555
Admin CLI Tools
Three Node.js scripts in the server/ directory help you manage users directly from the terminal — no UI needed.
server/) so the path server/createAdminUser.js resolves correctly and Prisma can find the server/.env.1 — Create a brand-new admin user
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
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
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.
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.
Registration
User registers → account created → verification email sent → user clicks link → emailVerified set to true.
Login
Credentials submitted to NextAuth → user fetched from DB via Express API → bcrypt compare → session JWT created with id, email, role.
Admin access
Middleware checks token.role === "admin" on every /admin/* route. Redirects to homepage if not admin.
Admin Dashboard
Accessible at /admin. Requires role: "admin". Protected by Next.js middleware — unauthenticated users are redirected to login.
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.
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.
General Group
| Key | Type | Default | Description |
|---|---|---|---|
site_name | string | NexaByte | Store name — shown in the browser title bar dynamically |
site_description | string | Your premium… | Meta description for SEO |
contact_email | string | [email protected] | Displayed in the footer and contact page |
contact_phone | string | +1 (555) 000-0000 | Displayed in the footer and contact page |
currency | string | USD | Currency code used in labels |
currency_symbol | string | $ | Symbol prepended to prices throughout the store |
favicon | image | "" | Site favicon — uploaded via admin. ICO, PNG, and SVG supported. Replaces default /favicon.ico |
Billing Group
| Key | Type | Default | Description |
|---|---|---|---|
tax_rate | number | 0 | Tax percentage applied at checkout |
shipping_fee | number | 0 | Default shipping fee added to orders |
free_shipping_threshold | number | 0 | Order total above which shipping is free (0 = disabled) |
Inventory Group
| Key | Type | Default | Description |
|---|---|---|---|
low_stock_threshold | number | 5 | Products with stock ≤ this number show a "Low Stock" warning |
Social Group
| Key | Type | Default | Description |
|---|---|---|---|
social_facebook | string | "" | Full Facebook page URL |
social_instagram | string | "" | Full Instagram profile URL |
social_twitter | string | "" | 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.
| Key | Recommended Size | Supported Formats |
|---|---|---|
site_logo | 220 × 60 px | PNG, SVG (transparent background recommended) |
footer_logo | 220 × 60 px | PNG, SVG (transparent background recommended) |
hero_image | 512 × 520 px | PNG (transparent background recommended) |
favicon | 64 × 64 px | ICO, PNG, SVG |
Static Page Keys
These keys hold rich-text HTML content and are edited via the Static Pages editor in the admin dashboard.
API Reference
All endpoints are served by the Express server at http://localhost:3001.
http://localhost:3001 is omitted. The paths shown are the full paths after the host.Products
| Method | Path | Description |
|---|---|---|
| GET | /api/products | List all products (supports query params: category, search, page, limit, sort) |
| POST | /api/products | Create a product |
| GET | /api/products/:id | Get a single product by ID or slug |
| PUT | /api/products/:id | Update product (cleans old S3 image if mainImage changes) |
| DELETE | /api/products/:id | Delete product + all S3 images |
Product Gallery Images
| Method | Path | Description |
|---|---|---|
| GET | /api/images/:id | Get all gallery images for a product (id = productID) |
| POST | /api/images | Add a gallery image to a product |
| PUT | /api/images/:id | Update a gallery image record (id = imageID) |
| DELETE | /api/images/:id | Delete all gallery images for a product (id = productID) + S3 cleanup |
Image Upload
| Method | Path | Description |
|---|---|---|
| POST | /api/main-image?folder=products | Upload 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
| Method | Path | Description |
|---|---|---|
| GET | /api/categories | List all categories |
| POST | /api/categories | Create a category |
| GET | /api/categories/:id | Get a single category |
| PUT | /api/categories/:id | Update a category |
| DELETE | /api/categories/:id | Delete a category (cascades to products) |
Users
| Method | Path | Description |
|---|---|---|
| GET | /api/users | List all users |
| POST | /api/users | Create a user (registration) |
| GET | /api/users/:id | Get user by ID |
| PUT | /api/users/:id | Update user (role, email, etc.) |
| DELETE | /api/users/:id | Delete a user |
| GET | /api/users/email/:email | Look up user by email (used by NextAuth) |
Orders
| Method | Path | Description |
|---|---|---|
| GET | /api/orders | List all orders |
| POST | /api/orders | Create an order (checkout) |
| GET | /api/orders/pending/count | Get count of pending orders |
| GET | /api/orders/:id | Get single order with line items |
| PUT | /api/orders/:id | Update order (status change) |
| DELETE | /api/orders/:id | Delete an order |
Settings
| Method | Path | Description |
|---|---|---|
| GET | /api/settings | Get all settings (triggers auto-seed on first call) |
| PUT | /api/settings/bulk | Bulk update multiple settings at once. Old S3 images are cleaned up automatically. |
| GET | /api/settings/:key | Get a single setting by key |
| PUT | /api/settings/:key | Update a single setting |
| POST | /api/settings | Create a new custom setting |
| DELETE | /api/settings/:key | Delete a setting by key |
Stats (Dashboard)
| Method | Path | Description |
|---|---|---|
| GET | /api/stats/dashboard | Returns total revenue, orders, products, users, and trend percentages |
| GET | /api/stats/charts | Returns time-series data for the revenue/orders chart. Query param: range=weekly|monthly|yearly |
Authentication (email flows)
| Method | Path | Description |
|---|---|---|
| POST | /api/auth/forgot-password | Send a 6-digit reset code to the given email |
| POST | /api/auth/verify-reset-code | Verify the 6-digit code. Returns a one-use token. |
| POST | /api/auth/reset-password | Set a new password using the verified token |
| POST | /api/auth/send-verification-email | Send email verification link to a new user |
| POST | /api/auth/verify-email | Verify the email token and mark user as verified |
Other Endpoints
| Resource | Base Path | Operations |
|---|---|---|
| Search | /api/search | GET — full-text product search |
| Merchants | /api/merchants | GET, POST, GET/:id, PUT/:id, DELETE/:id |
| FAQs | /api/faq | GET, POST, GET/:id, PUT/:id, DELETE/:id |
| Feedback | /api/feedback | POST (submit), GET (admin), GET/:id, PATCH/:id/read, DELETE/:id, GET /unread/count |
| Contact | /api/contact | POST (submit), GET (admin), PATCH/:id/read, DELETE/:id, GET /unread/count |
| Notifications | /api/notifications | GET/:userId, POST, PUT/:id, DELETE/:id, POST /mark-read, DELETE /bulk, GET /:userId/unread-count |
| Slugs | /api/slugs/:slug | GET — fetch a product by its slug (used by the storefront product page) |
| Order Products | /api/order-product | GET, POST, GET/:id, PUT/:id, DELETE/:id |
| Health Check | /health | GET — server liveness check |
Rate Limiting
The Express server applies layered rate limiting to protect against abuse:
| Limiter | Applied to | Purpose |
|---|---|---|
| General | All routes | Base protection for all requests |
| Auth | /api/users/email | Strict limit on login attempts |
| Upload | /api/images, /api/main-image | Prevent storage abuse |
| Search | /api/search | Prevent scraping |
| Order | /api/orders, /api/order-product | Prevent order flooding |
| User Management | /api/users | Protect account operations |
Image Uploads & S3 Storage
Storage Structure
All images are stored in S3/MinIO under the bucket defined by AWS_BUCKET. The path structure is:
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:
{NEXT_PUBLIC_S3BUCKET}/{AWS_BUCKET}/nexabyte/{folder}/filename.ext
Allowed File Types
File Size Limit
Maximum file size: 5 MB per upload.
Automatic S3 Cleanup
Old images are automatically deleted from S3 in these scenarios:
| Trigger | What gets deleted |
|---|---|
| Product mainImage replaced | Previous mainImage S3 object |
| Product deleted | mainImage + all gallery Image records from S3 |
| Gallery images deleted | All gallery S3 objects for that product |
| Image-type Setting replaced | Previous image S3 object (logo, favicon, etc.) |
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.
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.
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 /.
# 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
| Setting | When to use | Trade-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. |
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:
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 buttoncomponents/DashboardSidebar.tsx— Sidebar active statesapp/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:
// 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:
Open the component
components/CategoryIcon.tsx
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.
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
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:
| File | Key to change | Example value |
|---|---|---|
next.config.mjs | basePath | '/shop' |
Root .env | NEXTAUTH_URL | http://localhost:3000/shop |
Root .env | NEXT_PUBLIC_API_BASE_URL | http://localhost:3001/api-prefix |
Root .env | NEXT_PUBLIC_BASE_PATH | /shop |
server/.env | SERVER_BASE_PATH | /api-prefix |
server/.env | NEXTAUTH_URL | http://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).
Deployment
Production Build
# 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:
| Variable | Production value example |
|---|---|
NEXTAUTH_URL | https://yourdomain.com |
NEXT_PUBLIC_API_BASE_URL | https://api.yourdomain.com |
FRONTEND_URL | https://yourdomain.com |
NODE_ENV | production |
NODE_ENV=production. CORS is restricted to the FRONTEND_URL and NEXTAUTH_URL when not in development mode.Using a Process Manager (PM2)
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
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
.envfiles configured with production values - ✅
NEXTAUTH_SECRETis 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 buildcompletes 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.mjsremotePatterns