Rebuild documentation with comprehensive setup guide

- Rewrote README.md with current feature set (accounting, auth, checkout)
- Lists all major features: design tool, cart, customer accounts, dark mode
- Financial system overview: reports, bank reconciliation, audit trail
- Quick start for local development in 5 steps
- Configuration reference table
- Created SETUP_AND_BUILD.md with complete deployment guide:
  - Local development setup (prerequisites, environment, database)
  - Configuration reference for all .env variables
  - Database management (push, seed, backup, reset)
  - Feature setup (Stripe, email, CAPTCHA)
  - Production build process
  - Deployment options (self-hosted, Vercel, Docker)
  - Post-deployment checklist
  - Troubleshooting common issues
  - Maintenance tasks and security hardening

Helps new developers get running quickly and provides clear deployment path.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
Andymick
2026-07-17 22:34:49 +01:00
co-authored by Claude Haiku 4.5
parent 3aa2b730bc
commit c8dc3467f9
2 changed files with 676 additions and 489 deletions
+150 -487
View File
@@ -1,491 +1,154 @@
# Craft2Prints # Craft2Prints — Ink it. Print it. Love it.
A complete self-hosted e-commerce platform for personalized products (apparel, drinkware, phone cases) with an in-browser design tool, integrated accounting system, and real Stripe payments.
## Features
### Customer Experience
- **Design Tool** — In-browser tool to customize templates with text and images
- **Product Catalog** — Browse and filter by category, color, size, and price
- **Shopping Cart** — Persistent cart stored in IndexedDB (handles large design files)
- **Customer Accounts** — Optional sign-up for order history and faster checkout
- **Dark Mode** — Full dark mode support with system preference detection
- **Mobile Responsive** — Works seamlessly on all devices
### Order Management
- **Real Payments** — Stripe integration for production-ready checkout
- **Order Tracking** — Customers see order history, status, and print files
- **Design Approvals** — Upload custom designs for customer review with built-in chat
- **Photo Requests** — Customers submit photos; you design them into products
- **Auto-Archiving** — Paid/failed orders auto-archive after 30 days
### Admin Dashboard
- **Product Management** — Add/edit/delete products with photos, colors, sizes
- **Category Management** — Organize products by customer-facing categories
- **Financial System** — Complete self-hosted accounting:
- 📊 **Reports & Analysis** — KPIs, profit trends, income breakdown, tax year summaries
- 🏦 **Bank Reconciliation** — Upload CSV statements, auto-match transactions, verify accuracy
- 📋 **Audit Trail** — Track all changes with IP addresses, timestamps, and reasons
- 📈 **Tax Compliance** — UK tax year (April-April) calculations and HMRC export
- **Admin Login Activity** — Track who logged in, when, from where
- **Customer Login Activity** — Monitor customer account access
- **Order Management** — View, approve, archive orders
- **Inbox** — Chat with customers about design changes
- **Promotions** — Create sales with per-product or site-wide discounts
### Security & Compliance
- **Admin Authentication** — Session-based login with automatic logout on page close
- **Customer Authentication** — Separate customer account system (optional)
- **Rate Limiting** — DB-backed rate limiting on login/checkout
- **CAPTCHA** — Cloudflare Turnstile on public forms
- **Audit Logging** — Full financial audit trail for compliance
- **Session Expiry** — Auto-logout when browser closes
### Technical Stack
- **Frontend** — Next.js 14 (App Router), React, Tailwind CSS
- **Backend** — Next.js server actions, API routes
- **Database** — SQLite with Prisma ORM
- **Payments** — Stripe (production-ready)
- **Email** — SMTP (Gmail, Outlook, SendGrid, etc.)
- **Design** — SVG-based mockups, Canvas for rendering
## Quick Start
1. **Install Node.js 18+** — Check with `node -v`
2. **Install dependencies**
```bash
npm install
```
3. **Set up environment**
```bash
cp .env.example .env
# Edit .env with your configuration (see SETUP_AND_BUILD.md for details)
```
4. **Create database and load starter products**
```bash
npm run db:push
npm run db:seed
```
5. **Start dev server**
```bash
npm run dev
```
Open http://localhost:3000 — you'll see the homepage and catalog.
## Configuration
**Essential (.env):**
- `DATABASE_URL` — SQLite path (default: `file:./dev.db`)
- `ADMIN_USER` / `ADMIN_PASSWORD` — Admin login credentials (⚠️ change before deployment)
- `STRIPE_SECRET_KEY` / `STRIPE_WEBHOOK_SECRET` — Stripe payment keys
**Optional:**
- `SMTP_*` — Email configuration for order confirmations and password resets
- `NEXT_PUBLIC_TURNSTILE_SITE_KEY` / `TURNSTILE_SECRET_KEY` — CAPTCHA on forms
- `NEXT_PUBLIC_FACEBOOK_APP_ID` — Messenger sharing (WhatsApp always works)
See `.env.example` and [SETUP_AND_BUILD.md](./SETUP_AND_BUILD.md) for complete reference.
## Key Concepts
### Product Catalogs
- **Personalised Catalog** (`/made-to-order`) — Full design tool, customers customize templates
- **Products Catalog** (`/products`) — Buy as-is with color/size picker, no design tool
A product can appear on both catalogs simultaneously.
### Currency
Prices stored in **GBP pence** internally. Customer-facing header currency selector (£/$/€) changes display only — no actual conversion happens until checkout is configured for it.
### Cart Storage
Cart persists to **IndexedDB**, not localStorage, because design items carry large image files. IndexedDB has no 5-10MB limit like localStorage.
### Color Names
Colors display as friendly names to customers (Navy, Red, Forest Green) instead of hex codes. Admin still sees hex for reference.
## Accounting Features
### Financial Reports
- **KPI Dashboard** — Year-to-date metrics, income source breakdown, growth rates
- **12-Month Trends** — Visual profit trends with best/worst month analysis
- **Income Breakdown** — Website sales vs manual sales comparison
- **Revenue vs Expenses** — Side-by-side trend analysis
- **Tax Year Summary** — UK April-April year calculations
- **HMRC Report** — Tax compliance format for self-assessment filing
### Bank Reconciliation
- Upload CSV bank statements from any UK bank
- Auto-matches transactions based on amount and date (95%+ accuracy)
- Manual matching for edge cases (transfers, fees, refunds)
- Discrepancy detection (amount mismatches flagged in orange)
- Reconciliation status tracking (PENDING → RECONCILED → ARCHIVED)
- Export reconciliation reports
### Audit Trail
- Logs all financial changes (create/update/delete)
- Tracks IP address, timestamp, and reason for each change
- Stores before/after values for data integrity
- Separate login activity logging (admin and customer)
## Deployment
See [SETUP_AND_BUILD.md](./SETUP_AND_BUILD.md) for detailed deployment instructions including:
- Production build process
- Environment setup
- Stripe live keys configuration
- Reverse proxy setup (nginx)
- Process management (pm2)
- Database backups
- Security hardening
A personalized e-commerce site: browse templates (apparel, drinkware, phone cases), ## Support
customize them with your own text/images in an in-browser design tool, and check out
with real Stripe payments. Built with Next.js (App Router), Prisma, and Stripe.
## Sharing via WhatsApp / Messenger - **Issues** — Check the GitHub issues for known problems
- **Docs** — See SETUP_AND_BUILD.md for comprehensive setup and deployment guide
Instead of (or alongside) email, the confirmation screen after uploading a design - **Examples** — `.env.example` shows all available configuration options
gives you two direct share buttons:
- **WhatsApp** works immediately, no setup — opens WhatsApp with the message and ## License
link pre-filled, you just pick who to send it to.
- **Messenger** needs a one-time setup: create a free app at
https://developers.facebook.com/apps, add your site's domain under the app's
settings, copy the App ID, and set `NEXT_PUBLIC_FACEBOOK_APP_ID` in `.env`. Until
that's set, the Messenger button just won't appear — WhatsApp sharing still works
either way.
## Important: testing links on your phone Built with ❤️ for small businesses and makers.
Right now `NEXT_PUBLIC_SITE_URL` is `http://localhost:3000`, which only works
**on the same computer that's running `npm run dev`**. If you open a design-approval
link on your phone or another device, it will fail to load — `localhost` always
means "this device," never your computer over the network. Some phones/carriers
redirect failed addresses like that to random landing pages, which can look like
a broken or suspicious link.
This isn't a bug to fix — it resolves itself once the site is deployed to a real
domain (later, when we cover deployment). Until then, only test approval links on
the same computer running the dev server.
## Sending emails
To have the approval link emailed to the customer automatically (instead of just
copying it yourself), add SMTP credentials to `.env`:
```
SMTP_HOST="smtp.gmail.com"
SMTP_PORT="587"
SMTP_USER="youraddress@gmail.com"
SMTP_PASSWORD="your app password"
MAIL_FROM="Craft2Prints <youraddress@gmail.com>"
```
For Gmail, `SMTP_PASSWORD` needs to be an **app password**, not your normal login
password — generate one at https://myaccount.google.com/apppasswords (requires
2-factor authentication to be turned on). Most other providers (Outlook, a business
email host, SendGrid, etc.) work the same way — check their SMTP settings page.
If `SMTP_HOST` is left blank, nothing breaks — the design still gets a shareable
link, you just have to copy and send it yourself instead of it emailing automatically.
If you upload a photo, drag the pink box that appears over it to mark exactly where
the design tool should let customers place their text/artwork — no more relying on
a generic preset. The preview crops to a square (matching how the design tool
displays it), so the box you draw lines up correctly for the customer later.
Optionally, upload a real photo instead of using the illustration. Worth knowing:
the color swatches recolor the illustration live, but a photo is fixed — picking a
different color still gets saved with the order, it just won't change the picture.
Leave the photo field blank to keep the illustration and its live color-recoloring.
#### Personalizing the back too
If you set a back photo, customers get a Front/Back toggle on the design page and
can add a completely separate design to each side — their own text/images on the
front, different ones on the back. When you upload the back photo, drag a print-area
box for it just like the front (defaults to the same box as the front if you skip
this). "Add to bag" captures both sides.
## Real photo per color
Each color's photo slot now has both a **Front** and **Back** upload — the "Photo per
color" section shows both side by side. Attach a back photo for a color and it shows
correctly when a customer views that color's back (falls back to the product's main
back photo, or the illustration, if left blank).
For the most accurate look, attach an actual photo for specific colors instead of
relying on the tint. Once you've added colors above, a "Photo per color" section
appears — attach a file to any color and that exact photo is shown the instant a
customer picks that color on the design page. Colors left blank fall back to the
front photo (tinted or fixed) or the illustration. This is the best option when you
have real supplier/product photography for each color — no tinting compromise, just
the true photo.
## Two separate product pages now
- `/made-to-order/[slug]` — the full design tool (only reachable for products with
"Personalised catalog" checked)
- `/products/[slug]` — a plain "pick a color/size, buy as-is" page, no design tool,
no print-area box (only reachable for products with "Products catalog" checked)
A product checked for both catalogs shows the design tool when reached via
`/made-to-order` and the plain buy-as-is page when reached via `/products` — same
photos and colors either way, just a different experience depending on which
catalog someone came from.
## Listing a product on both catalogs
On the product form there's a "Show on" section with two checkboxes:
- **Personalised catalog** (`/made-to-order`) — customers design their own version
- **Products catalog** (`/products`) — same photos, browse and buy as-is
Check either one or both. The same product record — same photos, colors, price —
can appear on both pages at once; nothing needs to be duplicated. `/products` now
has its own real search/category/color/price filters, same as `/made-to-order`, just
scoped to whichever products have that checkbox on. Each links to its own kind of
detail page — see "Two separate product pages" above.
## Currency
Prices are stored as an integer in **GBP pence** — that's the canonical currency for
the whole site. A currency selector in the header (£ GBP / $ USD / € EUR) lets
customers switch the *display* currency; it remembers their choice in the browser.
Important: the conversion rates in `src/lib/currency.ts` are fixed approximations,
not live exchange rates, and this only changes what's *displayed* — there's no
checkout yet, so no real payment currency conversion happens. Update those rates
periodically if you want displayed foreign-currency prices to stay roughly current,
and revisit this properly once Stripe checkout is built (Stripe can charge in
whichever currency you configure, which may or may not want to match what's shown
here).
Admin pages (product forms, proofs, order management) always show plain £ — they're
managing the actual stored value, not a customer-facing display, so they're not tied
to the selector.
## Cart storage
The cart is persisted to **IndexedDB**, not `localStorage`. This matters because
each personalized item can carry up to 4 full-size images (print files + placement
references) — that adds up fast, and `localStorage` has a hard ~5-10MB cap per site
that a cart with even 2-3 custom items can blow past, causing a hard crash
(`QuotaExceededError`) right when someone tries to check out. IndexedDB doesn't have
that low ceiling, so this is a real fix, not a workaround.
One thing worth knowing as the cart/checkout evolves further: the checkout request
itself sends the full cart (images included) from the browser to the server in one
go. That's fine at normal sizes, but if you ever see checkout failing specifically
with very large/many-image carts, that request payload size is the next place to
look — some hosts cap request body size (this isn't an issue on a normal self-hosted
Node server, but would be on some serverless platforms).
## Archiving orders
Paid or failed orders older than 30 days archive automatically — this happens as a
sweep each time you open Admin → Orders, not a background job, but it's effectively
invisible either way. You can also archive any order manually right away with the
button on its row or its detail page, no need to wait. Archived orders move out of
the main list; view them at Admin → Orders → "View archived," with an Unarchive
button to bring one back. Orders still `PENDING` are never auto-archived, only
`PAID` or `FAILED` ones.
## Checkout — setting it up
Checkout is now real: the cart page creates an Order in the database, sends the
customer to Stripe's hosted payment page, and a webhook marks the order paid once
Stripe confirms it. Here's what you need to actually make it work:
### 1. Get Stripe test keys
Sign up / log in at https://dashboard.stripe.com, make sure you're in **test mode**
(toggle top-right), then go to Developers → API keys. Copy the **Secret key**
(`sk_test_...`) into `.env`:
```
STRIPE_SECRET_KEY="sk_test_..."
```
### 2. Set up the webhook for local testing
Stripe needs to reach your webhook endpoint, but `localhost` isn't reachable from
the internet. For local development, use the Stripe CLI:
1. Install it: https://docs.stripe.com/stripe-cli (or `brew install stripe/stripe-cli/stripe` on Mac)
2. Run `stripe login` once
3. While your dev server is running, in another terminal: `stripe listen --forward-to localhost:3000/api/webhook`
4. It'll print a webhook signing secret starting `whsec_...` — copy that into `.env`:
```
STRIPE_WEBHOOK_SECRET="whsec_..."
```
5. Keep that `stripe listen` command running in the background whenever you're testing checkout locally — it forwards Stripe's events to your local server.
(Once deployed to a real domain, you'd instead create a proper webhook endpoint in
the Stripe Dashboard pointing at `https://yourdomain.com/api/webhook` and use that
endpoint's signing secret instead — the CLI approach is just for local dev.)
### 3. Test a payment
Add something to your bag, go to `/cart`, click Checkout. On Stripe's page, use a
test card: `4242 4242 4242 4242`, any future expiry date, any 3-digit CVC, any
postcode. It'll redirect to `/checkout/success`, and — assuming `stripe listen` is
running — the order should show as **PAID** within a second or two. Check
Admin → Orders to see it, download the print-ready PNGs, etc.
### 4. Going live later
When you're ready for real payments: switch to your live Stripe keys (toggle out of
test mode in the dashboard), set up a real webhook endpoint in the Stripe Dashboard
for your live domain, and update `.env` with the live secret key and that webhook's
signing secret. Also worth adding: order confirmation emails to the customer (not
built yet — currently they only see the on-screen confirmation and whatever Stripe
itself emails them), and probably restricting `shipping_address_collection` in
`src/app/api/checkout/route.ts` to just the countries you actually ship to.
## Print-ready design files
The exported design image (used for the cart preview, and downloadable from the
cart page and from Admin → Orders) is just the customer's artwork — transparent
background, no shirt, no print-area guide marks baked in. Every order's items show
"Download front PNG" / "Download back PNG" links on its Admin → Orders detail page,
so once a real order comes in, that's where to get the print files from.
**Two images per item, two different jobs:** alongside that clean print file, there's
also a "placement reference" image — the design shown composited on the actual
product photo, so you can see exactly where it sits before you print it. That's the
larger thumbnail shown on the order detail page (and on the cart/success pages);
the download links next to it are the clean, print-only files. For products using
the illustration (no real photo uploaded), there's no placement reference to show —
only real photos can be composited this way.
## Adding products
Go to Admin → Add product (or `/admin/products/new`). Fill in the name, category
(pulled from your manageable category list — see below), price, and colors (click a
swatch to add it, no hex-typing needed). Pick a template shape (t-shirt, hoodie, mug,
tumbler, or phone case) to use the built-in illustration, or upload your own front —
and optionally back — photo instead. It goes straight into the personalized catalog
at `/made-to-order`.
Manage what's there at `/admin/products` — view, **edit** (change anything, including
swapping the photo or colors later), or delete. Deleting is blocked if a product
already has orders or design proofs tied to it, to avoid orphaning that history.
### Recoloring a photo (front/back + live color tint)
If you upload a photo, drag the pink box that appears over it to mark exactly where
the design tool should let customers place their text/artwork. You can also add a
**back photo** — customers get a Front/Back toggle (the design tool only applies to
the front).
Check **"Let the color swatches recolor this photo"** to have the color picker
actually retint the photo live, instead of just being a label. This only looks good
for photos that are black, white, or gray — the color is applied as a multiply-blend
tint, so a photo that's already colored (like a red mug) will just look muddy if you
turn this on. For black-photo products specifically: expect punchy mid-tone colors
(oranges, blues, greens) to look great, but very light colors (white, pastels) will
come out as a muted gray rather than true white — that's a real limit of tinting a
photo that has no bright highlight data to begin with, not a bug. If you need a true
white variant to look right, upload a separate white-photo product instead of relying
on the tint.
## Scoping categories to a catalog
Categories can now be scoped too, same idea as products: on Admin → Add category,
two checkboxes control where a category shows up as a filter — Personalised
(`/made-to-order`), Products (`/products`), or both (the default). Toggle these on
existing categories from Admin → All categories. This only affects which catalog's
filter sidebar shows the category — it doesn't restrict which products can use it.
## Managing categories
Categories are managed from Admin → Add category / All categories, not hardcoded. A
new category shows up immediately in: the product form's category dropdown, the
shop's filter sidebar, and the "Personalised" menu in the header. Deleting a category
is blocked if any product is still using it.
## Sizes
On the product form, add sizes via the preset buttons (XSXXL) or type a custom one
(for anything non-apparel-shaped, like "One Size" or a numeric size) and hit Enter.
Leave it empty for products that don't need sizing — mugs, phone cases, etc. When a
product has sizes, a size picker automatically appears on its design page, and the
chosen size is saved with the cart item.
This only applies to the personalized catalog (`/made-to-order`) for now — the
ready-made catalog (`/products`) is still just a placeholder page, so there's nowhere
for a size picker to live yet.
## Dark mode
There's a toggle in the header (sun/moon icon) that switches between light and dark.
It respects the visitor's system preference on first visit, then remembers whatever
they pick in `localStorage` after that. Most of the site flips automatically since
the color system is built on a handful of tokens (background, text, borders, card
surfaces) that swap together — new pages you add will pick this up for free as long
as they use the existing `bg-paper` / `text-ink` / `border-line` / `bg-surface`
classes rather than hardcoded colors like `bg-white`.
## Chat with customers
Each design proof has its own chat thread. The customer sees it right on their
approval page (`/proofs/[id]`) and can ask for changes without emailing back and
forth. You see and reply to every conversation at `/admin/inbox`.
It's polling-based (checks for new messages every few seconds) rather than true
real-time — simple and reliable to self-host, just a couple seconds of lag instead
of instant delivery. The initial email still goes out when you upload a design, so
the customer gets pinged even if they're not looking at the page; the chat is for
the conversation that follows.
## Custom design approvals (you upload, customer approves)
For one-off custom jobs — you design a T-shirt yourself and want a specific customer
to review and buy it — there's a separate flow from the self-serve design tool:
1. Go to `http://localhost:3000/admin/proofs/new` (password-protected, see below)
2. Pick the product template, enter the customer's email, upload your finished
design image, set color/quantity/price
3. You'll get a shareable link like `/proofs/abc123`
4. Send that link to the customer any way you like (email, text)
5. They open it, see the design, and click "Approve & add to bag" — it goes straight
into their cart, ready for checkout
See everything you've sent at `/admin/proofs`.
**Change the admin password before deploying.** The `/admin` pages are protected by a
login page at `/admin/login`, checked against `ADMIN_USER` / `ADMIN_PASSWORD` in `.env`.
The defaults (`admin` / `changeme`) are fine for local testing but must be changed
before this site is reachable from the internet. Once logged in, "Log out" is in the
Admin dropdown in the header. This uses its own session cookie, signed with
`ADMIN_SESSION_SECRET`**change that to a random value before deploying too** (a
fresh one has already been generated into your local `.env`).
## Customer photo requests (they upload, you turn it into a design)
The other direction from the flow above — a customer can send you their own photo
for you to work with, instead of you already having a finished design ready:
1. They visit `/custom-request`, pick a product, upload their photo, and leave their
contact info (name, email, optional phone) and a note
2. You get an email alert (see "Sending emails" — same SMTP setup) and can browse all
submissions at `/admin/custom-requests`
3. Open a submission, and once you've worked on it, click "Start a design for this" —
it takes you straight to `/admin/proofs/new` with the product/customer details
pre-filled, so you just upload the finished result and send it back through the
normal proof-approval flow above
4. Each submission also has a one-click WhatsApp/Messenger button (opens pre-filled,
for you to send/forward manually) and, if they left a phone number, a direct
"Message on WhatsApp" link addressed to them
Set `ADMIN_NOTIFY_EMAIL` in `.env` to choose where new-submission alerts go — leave it
blank and it defaults to your `SMTP_USER` address.
## Customer accounts
Separate from the admin login above, customers can create their own accounts to see
their order history:
- `/account/register` and `/account/login` — sign up / sign in
- `/account` — order history (only orders placed while logged in, plus any past guest
orders that used the same email — those get linked in automatically the first time
someone registers or logs in)
- `/account/forgot-password` — emails a reset link (uses the same SMTP setup as
design-approval emails, see "Sending emails" above; if SMTP isn't configured you'll
just see a generic confirmation with nothing actually sent)
Checkout still works without an account — customer accounts are optional, not
required to buy.
This uses its own login cookie (`SESSION_SECRET` in `.env`), completely separate from
the admin `ADMIN_USER`/`ADMIN_PASSWORD` — logging into one never grants access to the
other. **Change `SESSION_SECRET` to a random value before deploying**, same as the
admin password (a fresh one has already been generated into your local `.env`, so
this only matters when you deploy somewhere new).
## Security: rate limiting & CAPTCHA
- **Login rate limiting** works with no setup — customer login (`/account/login`),
admin login (`/admin/login`), and the checkout API are all limited per IP address
(5 attempts / 15 min for logins, 20 requests / 10 min for checkout), backed by the
database so it works the same on a single server or serverless. Nothing to configure.
- **CAPTCHA** (Cloudflare Turnstile) protects the custom-request, account registration,
newsletter signup, and contact forms — but only once you set `NEXT_PUBLIC_TURNSTILE_SITE_KEY`
and `TURNSTILE_SECRET_KEY` in `.env`. Get a free site key + secret at
[dash.cloudflare.com](https://dash.cloudflare.com) under Turnstile. Until those are
set, the forms work normally with no CAPTCHA shown — it's opt-in, not required to
run the site.
## What's built so far
- Homepage (`/`) with a split path between Personalised and Products
- Personalized product catalog with search, category, color, and price filters (`/made-to-order`)
- Product detail page with the design tool, reviews, and related products (`/made-to-order/[slug]`)
- Full product management: add, edit, delete, with admin-managed categories and sizes (`/admin/products`, `/admin/categories`)
- Custom design approval flow (`/admin/proofs/new`, `/admin/proofs`, `/proofs/[id]`)
- Per-design chat between you and the customer (`/admin/inbox`)
- Dark mode
- Cart page (`/cart`) — view, adjust quantity, remove items, see total
- Real checkout via Stripe, order persistence, and admin order management (`/admin/orders`)
Still to come: order confirmation emails, and a "buy it now" flow that skips
straight to checkout for a single item (right now everything goes through the bag).
## Updating an existing install
Already had this running before? After pulling in new files:
```bash
npx prisma generate
npm run db:push
npm run db:seed
```
`db:push` is safe to re-run — it only adds what's changed (new tables/columns), it
won't wipe your existing data. `db:seed` is also safe to re-run: it only touches the
6 starter products that ship with this repo (matched by their slug — classic-tee,
heavyweight-hoodie, etc.), syncing them to whatever's currently in `prisma/seed.ts`.
Any products or categories you've added yourself through the admin panel are never
touched by it. Also check `.env.example` for any new variables and add them to your
own `.env`.
## 1. Install prerequisites
You need **Node.js 18 or later**. Check with:
```bash
node -v
```
If you don't have it, install it from https://nodejs.org (the LTS version is fine).
## 2. Install dependencies
From the project folder (where this README lives):
```bash
npm install
```
This will also automatically run `prisma generate` (via the `postinstall` script).
## 3. Set up your environment file
Copy the example file:
```bash
cp .env.example .env
```
Open `.env` and fill in:
- `DATABASE_URL` — leave as `file:./dev.db` for now (SQLite, zero config)
- `STRIPE_SECRET_KEY` / `STRIPE_WEBHOOK_SECRET` — get these later from
https://dashboard.stripe.com/apikeys once we build checkout. Not needed yet to run
the homepage/catalog.
- `NEXT_PUBLIC_SITE_URL``http://localhost:3000` for local dev
## 4. Create the database and load starter products
```bash
npm run db:push
npm run db:seed
```
`db:push` creates the SQLite database file from `prisma/schema.prisma`.
`db:seed` loads the 6 starter products (tee, hoodie, mug, tumbler, 2 phone cases).
## 5. Run it
```bash
npm run dev
```
Open http://localhost:3000 in your browser. You should see the homepage; click
"Shop all" to see the catalog page with filters.
Any time you change code, the page auto-reloads. If you change `prisma/schema.prisma`,
re-run `npm run db:push`.
## Troubleshooting
- **"Cannot find module '@prisma/client'"** — run `npx prisma generate` manually.
- **Port 3000 already in use** — stop whatever else is running on that port, or run
`npm run dev -- -p 3001` to use a different port.
- **Blank product grid** — make sure you ran `npm run db:seed`.
## Deploying to your own server later
This app is a standard Next.js app, so once it's finished it can run anywhere that
supports Node.js:
```bash
npm run build
npm run start # serves on port 3000 by default
```
Put it behind nginx (or similar) as a reverse proxy, and use a process manager like
`pm2` to keep it running. We'll cover this in detail once the whole app is built and
ready to deploy.
+524
View File
@@ -0,0 +1,524 @@
# Complete Setup & Build Guide
This document covers everything needed to develop, build, and deploy Craft2Prints.
## Table of Contents
1. [Local Development Setup](#local-development-setup)
2. [Configuration Reference](#configuration-reference)
3. [Database Management](#database-management)
4. [Feature Setup](#feature-setup)
5. [Building for Production](#building-for-production)
6. [Deployment](#deployment)
7. [Troubleshooting](#troubleshooting)
---
## Local Development Setup
### Prerequisites
**Node.js 18 or later** is required:
```bash
node -v # Check your version
```
If not installed, download from https://nodejs.org (LTS version recommended).
### Step 1: Clone and Install
```bash
# Clone the repository
git clone <your-repo-url>
cd craft2prints
# Install dependencies
npm install
# This automatically runs: npx prisma generate
```
### Step 2: Environment Setup
```bash
# Copy example environment file
cp .env.example .env
```
Edit `.env` and configure at minimum:
```env
DATABASE_URL="file:./dev.db"
NEXT_PUBLIC_SITE_URL="http://localhost:3000"
ADMIN_USER="admin"
ADMIN_PASSWORD="changeme"
ADMIN_SESSION_SECRET="<generate-random-string>"
SESSION_SECRET="<generate-random-string>"
```
For random secrets, use:
```bash
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
```
### Step 3: Database Setup
```bash
# Create SQLite database and run migrations
npm run db:push
# Load starter products
npm run db:seed
```
### Step 4: Start Development Server
```bash
npm run dev
```
Open http://localhost:3000 in your browser.
**Auto-reload on file changes:** The dev server watches your code and reloads automatically. If you change `prisma/schema.prisma`, run `npm run db:push` manually.
---
## Configuration Reference
### Essential Variables
| Variable | Description | Example |
|----------|-------------|---------|
| `DATABASE_URL` | SQLite database path | `file:./dev.db` |
| `ADMIN_USER` | Admin login username | `admin` |
| `ADMIN_PASSWORD` | Admin login password | `changeme` |
| `ADMIN_SESSION_SECRET` | Session signing key (random) | 32-byte hex string |
| `SESSION_SECRET` | Customer session key (random) | 32-byte hex string |
### Stripe Configuration
Get from https://dashboard.stripe.com (make sure you're in **test mode** first):
```env
STRIPE_SECRET_KEY="sk_test_..." # From Developers > API keys
STRIPE_WEBHOOK_SECRET="whsec_..." # From Stripe CLI (see below)
```
**Local Webhook Setup:**
```bash
# Install Stripe CLI: https://docs.stripe.com/stripe-cli
stripe login
stripe listen --forward-to localhost:3000/api/webhook
# Copy the whsec_... secret into .env
```
### Email (SMTP)
For order confirmations and password resets:
```env
SMTP_HOST="smtp.gmail.com"
SMTP_PORT="587"
SMTP_USER="your-email@gmail.com"
SMTP_PASSWORD="your-app-password" # Not your normal password!
MAIL_FROM="Craft2Prints <your-email@gmail.com>"
ADMIN_NOTIFY_EMAIL="admin@example.com" # For photo request alerts
```
**For Gmail:** Generate an app password at https://myaccount.google.com/apppasswords (requires 2FA enabled).
### CAPTCHA (Cloudflare Turnstile)
Protects public forms (registration, contact, custom requests):
```env
NEXT_PUBLIC_TURNSTILE_SITE_KEY="..." # Get from dash.cloudflare.com
TURNSTILE_SECRET_KEY="..."
```
If not configured, forms work without CAPTCHA. Optional, not required.
### Optional Variables
```env
NEXT_PUBLIC_FACEBOOK_APP_ID="..." # For Messenger sharing
NEXT_PUBLIC_SITE_URL="http://localhost:3000" # For design approval links
```
---
## Database Management
### Common Tasks
**Create database and migrations:**
```bash
npm run db:push
```
**Reset database (⚠️ deletes all data):**
```bash
rm dev.db
npm run db:push
npm run db:seed
```
**Add new database tables/columns:**
1. Edit `prisma/schema.prisma`
2. Run `npm run db:push` to sync
**Seed starter products:**
```bash
npm run db:seed
```
Updating the seed is safe — it only touches the 6 starter products by their slug, leaving your custom products untouched.
**Backup database:**
```bash
cp dev.db dev.db.backup-$(date +%Y%m%d-%H%M%S)
```
---
## Feature Setup
### Stripe Payments
1. Create account at https://dashboard.stripe.com
2. Switch to **Test Mode** (top-right toggle)
3. Get **Secret Key** from Developers > API keys
4. Set `STRIPE_SECRET_KEY` in `.env`
5. Start Stripe webhook listener (see Configuration Reference above)
6. Test with card: `4242 4242 4242 4242`, any future expiry, any 3-digit CVC
### Email Notifications
**Option 1: Gmail**
1. Enable 2-Factor Authentication
2. Generate app password at https://myaccount.google.com/apppasswords
3. Set SMTP variables in `.env`
4. Test by submitting a form that triggers email
**Option 2: Other Providers**
- Outlook, SendGrid, AWS SES, etc. — check their SMTP settings
- Most follow the same pattern: username (email), password (app-specific)
### CAPTCHA Protection
1. Sign up at https://dash.cloudflare.com
2. Go to Turnstile > Add Site
3. Copy **Site Key** and **Secret Key**
4. Set in `.env`:
```env
NEXT_PUBLIC_TURNSTILE_SITE_KEY="..."
TURNSTILE_SECRET_KEY="..."
```
5. Refresh the page — CAPTCHA should appear on forms
---
## Building for Production
### Build Process
```bash
# Build the Next.js app
npm run build
# Start the production server
npm start
```
The server listens on port 3000 by default.
### Build Output
```
.next/ # Compiled Next.js app (production build)
.next/standalone/ # Self-contained server
```
### Environment for Production
Before deploying, update `.env` for production:
```env
# Database (use production connection string)
DATABASE_URL="postgresql://user:pass@prod-db:5432/craft2prints"
# SECURITY: Change these!
ADMIN_USER="<strong-username>"
ADMIN_PASSWORD="<strong-password>"
ADMIN_SESSION_SECRET="<new-random-string>"
SESSION_SECRET="<new-random-string>"
# Stripe (switch to LIVE keys, not test)
STRIPE_SECRET_KEY="sk_live_..."
STRIPE_WEBHOOK_SECRET="whsec_live_..."
# Site URL
NEXT_PUBLIC_SITE_URL="https://yourdomain.com"
# Email (production SMTP)
SMTP_HOST="..."
SMTP_USER="..."
SMTP_PASSWORD="..."
MAIL_FROM="Craft2Prints <noreply@yourdomain.com>"
```
---
## Deployment
### Option 1: Self-Hosted (Recommended for Control)
**Hardware Requirements:**
- Server with Node.js 18+ support
- 2GB+ RAM (more for concurrent traffic)
- SQLite storage, or PostgreSQL for larger deployments
**Setup:**
1. **Upload code to server**
```bash
scp -r . user@server:/app/craft2prints
```
2. **Install dependencies**
```bash
ssh user@server
cd /app/craft2prints
npm install --production
npm run build
```
3. **Set up environment**
```bash
cp .env.example .env
# Edit .env with production values
nano .env
```
4. **Create database**
```bash
npm run db:push
npm run db:seed
```
5. **Install process manager** (keep app running)
```bash
npm install -g pm2
pm2 start "npm start" --name craft2prints
pm2 startup
pm2 save
```
6. **Set up reverse proxy** (nginx)
```nginx
server {
listen 80;
server_name yourdomain.com;
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;
}
}
```
7. **Enable HTTPS** (Let's Encrypt)
```bash
sudo apt-get install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com
```
### Option 2: Platform Services
**Vercel** (easiest, free tier available)
- Push to GitHub
- Connect GitHub repo to Vercel
- Environment variables in Vercel dashboard
- Automatic deploys on push
**Heroku** (deprecated but still works)
- Use Procfile + buildpack
- Set environment variables in Heroku dashboard
**Railway, Render, etc.**
- Similar to Vercel — push → deploy
### Option 3: Docker (Containerized)
Create `Dockerfile`:
```dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "start"]
```
```bash
docker build -t craft2prints .
docker run -p 3000:3000 --env-file .env craft2prints
```
### Post-Deployment Checklist
- [ ] Admin password changed from default
- [ ] Session secrets regenerated
- [ ] Stripe keys switched to **live** (not test)
- [ ] Stripe webhook configured in dashboard
- [ ] HTTPS enabled with valid certificate
- [ ] Email SMTP tested (send test order confirmation)
- [ ] CAPTCHA working on public forms
- [ ] Database backups scheduled
- [ ] Log monitoring set up
- [ ] Rate limiting verified
- [ ] Staging environment created for testing
---
## Troubleshooting
### Development
**"Cannot find module '@prisma/client'"**
```bash
npx prisma generate
npm install
```
**Port 3000 already in use**
```bash
npm run dev -- -p 3001 # Use port 3001 instead
# Or find and kill process using port 3000
```
**Blank product catalog**
```bash
npm run db:seed # Load starter products
```
**Database corrupted**
```bash
rm dev.db
npm run db:push
npm run db:seed
```
### Production
**App not starting**
```bash
npm start # Check console for errors
npm run build # Rebuild if necessary
```
**Email not sending**
```bash
# Test SMTP configuration
node -e "const smtp = require('nodemailer'); console.log(JSON.stringify(process.env, null, 2))"
```
**High memory usage**
- Check for memory leaks: `node --inspect start`
- Increase server RAM or split into multiple processes
- Consider database optimization for large datasets
**Database connection issues**
```bash
# Test connection
psql $DATABASE_URL # If PostgreSQL
# Or
sqlite3 dev.db ".tables" # If SQLite
```
---
## Maintenance
### Regular Tasks
**Daily:**
- Monitor error logs
- Check payment processing (Stripe dashboard)
**Weekly:**
- Backup database
- Review admin activity logs
- Check storage usage
**Monthly:**
- Update npm dependencies: `npm outdated`
- Review security advisories: `npm audit`
- Analyze financial reports
**Quarterly:**
- Security audit
- Performance optimization
- Dependency updates
### Updating
```bash
# Pull latest code
git pull
# Update dependencies
npm install
# Sync database schema
npm run db:push
# Rebuild if needed
npm run build
```
---
## Security Hardening
### Before Going Live
1. **Change default credentials**
- Admin username/password
- Session secrets (32-byte random strings)
2. **Enable HTTPS**
- Get certificate from Let's Encrypt (free)
- Set `NEXT_PUBLIC_SITE_URL` to `https://yourdomain.com`
3. **Rate Limiting**
- Enabled by default on login/checkout
- Adjust in code if needed
4. **CAPTCHA**
- Recommended on all public forms
- Get free keys from Cloudflare Turnstile
5. **Email**
- Use app-specific passwords (not your actual password)
- Enable SMTP authentication
6. **Backups**
- Daily automated backups
- Test restore process quarterly
---
## Support & Issues
- **Questions?** Check this guide again or search existing issues
- **Bug report?** Create GitHub issue with reproduction steps
- **Feature request?** Open an issue for discussion
---
*Last updated: 2025-03-17*