Wocart
Wocart runs the whole cash-on-delivery business for Algerian stores: an Arabic-first checkout, phone verification, Yalidine, ZR Express, NOEST, Guepex and 82 EcoTrack couriers, your own drivers, and M
翻訳待ち
No updates detected yet. GitHub releases will appear here.
Full-stack auction marketplace: proxy bidding serialized per lot, worker settlement, order state machine, outbox notifications, SSE. React, Express, PostgreSQL.
翻訳待ち
Loading the full README.
Read the full README ↗An online auction site for collectibles: proxy bidding, auctions that close on their own, orders, live prices and notifications. Built with React, Express and PostgreSQL.
Live demo: https://online-auction-1-a3t4.onrender.com (free Render tier, the first load after idle takes about a minute)
API reference: https://online-auction-1-a3t4.onrender.com/api-docs.html (OpenAPI 3.1, try requests against the live API)
Log in as demo_buyer1, demo_buyer2 or demo_seller with password Passw0rd!demo. Open the same lot as two
buyers in two browsers to see proxy bidding and live price updates.
Payments are a manual receipt upload, a mock gateway, or card checkout through Stripe in test mode (card 4242 4242 4242 4242). No real money is involved.
node:test against a real PostgreSQL, Vitest, Playwrightflowchart LR
B[React SPA] -- REST --> A[Express API]
B -- SSE per lot --> A
A -- one transaction per command --> DB[(PostgreSQL)]
W[Worker] -- FOR UPDATE SKIP LOCKED --> DB
DB -- outbox --> W
W --> E[Email and in-app notifications]
PostgreSQL holds everything: state, idempotency keys, the outbox and the audit log. There is no Redis or message broker.
The parts that took the most care:
SELECT ... FOR UPDATE) and checks the deadline against the
database clock after taking the lock. A test fires 50 concurrent bids at one lot and checks the result equals a
serial replay.Idempotency-Key that is stored in the same transaction, so a retried request returns
the original result instead of bidding twice.FOR UPDATE SKIP LOCKED, so several workers can run at
once, and a unique constraint guarantees one order per auction. Tests kill the worker mid-settlement.numeric(15,0) and handled as BigInt, never as floats.On a laptop, 50 bidders on a single lot got p95 549 ms with no errors; spread over 20 lots it reached 146 req/s
(backend/scripts/loadtest.js).
More detail, including the design decisions and the alternatives I rejected: docs/architecture.md. API reference: docs/api.md. Security notes: docs/security.md.
With Docker only:
docker compose up
Open http://localhost:5173 once the api container is healthy. The seed also creates demo_admin (same password)
locally.
Or with Node 22.18+ and Docker for the database:
docker compose up -d db
cd backend
cp .env.example .env
npm ci
npm run db:migrate && npm run db:seed
npm run dev # API on :3000
npm run worker # second terminal
cd ../frontend
cp .env.example .env
npm ci
npm run dev # http://localhost:5173
cd backend && npm run lint && npm run test:unit && npm run test:integration # needs the db container
cd frontend && npm run lint && npm test && npm run build
cd frontend && npx playwright test # needs API, worker and Vite running
Integration tests only run against a database whose name ends in _test.