Page:
Architecture
No results
1
Architecture
dlawler489 edited this page 2026-06-20 07:29:33 +10:00
Table of Contents
Architecture
Stack
- Frontend: React + Redux Toolkit + Tailwind, built with Vite, served by nginx
- Backend: Node.js / Express + Mongoose, port 8080
- Database: MongoDB, per-user isolation (
userIdon every document) - Auth: JWT (7-day tokens) in localStorage
- Hosting: Docker on the home server, behind Traefik (see Deployment & CI)
Repo layout
App/
client/ React app (pages/, components/, store/ slices, utils/)
server/ Express API (routes/, models/, services/, middleware/)
Dockerfile multi-stage: client-build, server-build, production (API), nginx-frontend
docker-compose.deploy.yml the file Dockhand deploys
.forgejo/workflows/ CI
Data model (MongoDB)
- Order —
orderNumber(= Etsy receipt id), items[{title, quantity, price, sku, variant, productId, printingCost, costOfGoods}],total,refundTotal,tax, status, customer, shippingAddress, tracking. Costs are snapshotted on the order at sync time so later catalogue edits don't rewrite history. - Product — title, price,
costOfGoods,printingCost, sku, category, tags,aliases[](past order titles that map here),etsyListingId, inventory. - Expense — category, description, amount, date, vendor,
reference, taxDeductible. Etsy fees usereference: etsy-ledger-<entry_id>for idempotency. - Customer — name, email, address, etsyUserId, totalOrders, totalSpent.
- EtsyConnection — OAuth tokens, shopId, lastSyncedAt.
- EtsySettings — per-user API keystring, shared secret, redirect URI.
Matching logic
Order items are matched to catalogue products by, in order: exact title/alias (variant-aware, so …Size: Large beats the bare title) → etsyListingId (size-disambiguated). Matched titles are saved as aliases and listing IDs are learned, so matching gets more deterministic over time. Unmatched items surface in Settings for resolution.