Imported from karimhammad11307/ahlan-commerce (
AGENTS.md). Install upstream withnpx skills add karimhammad11307/ahlan-commerce. Copyright stays with the author.
For additional context about technologies to be used, project structure, shell commands, and other important information, read the current plan
Ahlan Commerce — Agent Guide
Cargo workspace
apps/api— Axum server (REST + GraphQL). Binaryapi.apps/worker— background import-job processor. Binaryworker.packages/catalog— domain logic. Pure Rust, no I/O.packages/db— persistence via Cornucopia + deadpool-postgres.apps/admin— React/Vite frontend (NOT in Cargo workspace).
Key commands (Makefile)
make run-api/make run-worker/make testmake start— launches all services via mprocsmake db-start+make db-migrate— PostgreSQL + Atlas migrationsmake redis-health— check Redis is alivemake cornucopia-generate— after editingdb/queries/*.sqlmake docs-api— regenerate OpenAPI + GraphQL schema todocs/generated/make docker-build/make docker-run-api— build/run API Docker imagemake prod-up/make prod-down/make prod-logs/make prod-migrate— Docker Compose production- Flatpak sandbox: Makefile auto-detects
host-spawnand delegates docker/pg_isready/redis-cli to the host via theHOSTvariable. .bin/host-execwrapper script used in static config files (mprocs.yaml) to transparently delegate to host-spawn when in sandbox.
Tests
- All tests require live PostgreSQL + Redis. No mocking.
- Integration tests share DB state (no isolation per test). Use
Uuid::now_v7()for unique handles. spawn_test_server()helper is duplicated in every test file underapps/api/tests/.cargo testruns everything — unit + integration. Cache tests also need live Redis.packages/db/tests/do NOT usespawn_test_server()— they calldb::create_pool()directly.
Architecture
- Handlers parse HTTP → call domain → call persistence → return response. No SQL.
- Domain (
packages/catalog) validates and creates domain objects; timestamps set by Rust app clock, not DB. - Persistence (
packages/db) uses Cornucopia-generated code fromdb/queries/*.sql. packages/db/src/cornucopia.rsis generated — never edit manually.- Schema managed by Atlas (
db/schema/products.sql+db/migrations/).
Error responses
All endpoints return {"error":{"code":"...","message":"..."}}.
Codes: validation_failed (400), duplicate_product_handle (409), not_found (404), internal_error (500).
Redis cache
- Cache-aside:
GET /products/{handle}→ check Redis → miss → query DB → render HTML → set cache. - Key:
storefront:product-page:{handle}, TTL = 300s. - Invalidated on product create and publication update (any transport: REST, GraphQL, compat).
- Redis outage → graceful DB fallback, no 500.
Compat adapter (POST /api/compat/products)
- Field mapping:
name→title,slug→handle,body_html→description(empty → None),price→price_cents,qty→inventory_quantity,is_active→published priceis a JSON string (e.g."25.99"), parsed asf64.qtyhas aliasstock,is_activehas aliasis_visible.
GraphQL & docs
- Endpoint:
POST /graphql - Scalar UI (OpenAPI):
GET /docs/scalar - Admin dev server proxies
/graphql→localhost:3000/graphql(seeapps/admin/vite.config.ts)
Admin frontend (apps/admin/)
- Vite + React + TanStack Router + TanStack Query.
- Route tree auto-generated by Vite plugin —
routeTree.gen.tsis gitignored; never edit manually. npm run dev/npm run build/npm run typecheck.