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:
co-authored by
Claude Haiku 4.5
parent
3aa2b730bc
commit
c8dc3467f9
@@ -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),
|
||||
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.
|
||||
## Support
|
||||
|
||||
## Sharing via WhatsApp / Messenger
|
||||
|
||||
Instead of (or alongside) email, the confirmation screen after uploading a design
|
||||
gives you two direct share buttons:
|
||||
- **Issues** — Check the GitHub issues for known problems
|
||||
- **Docs** — See SETUP_AND_BUILD.md for comprehensive setup and deployment guide
|
||||
- **Examples** — `.env.example` shows all available configuration options
|
||||
|
||||
- **WhatsApp** works immediately, no setup — opens WhatsApp with the message and
|
||||
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.
|
||||
## License
|
||||
|
||||
## Important: testing links on your phone
|
||||
|
||||
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 (XS–XXL) 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.
|
||||
Built with ❤️ for small businesses and makers.
|
||||
|
||||
@@ -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*
|
||||
Reference in New Issue
Block a user