diff --git a/README.md b/README.md index 0a1b259..30a2fa8 100644 --- a/README.md +++ b/README.md @@ -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 + +## Support + +- **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 -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. +## License -## Sharing via WhatsApp / Messenger - -Instead of (or alongside) email, the confirmation screen after uploading a design -gives you two direct share buttons: - -- **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. - -## 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 " -``` - -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. diff --git a/SETUP_AND_BUILD.md b/SETUP_AND_BUILD.md new file mode 100644 index 0000000..33138a0 --- /dev/null +++ b/SETUP_AND_BUILD.md @@ -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 +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="" +SESSION_SECRET="" +``` + +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 " +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="" +ADMIN_PASSWORD="" +ADMIN_SESSION_SECRET="" +SESSION_SECRET="" + +# 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 " +``` + +--- + +## 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*