Run the full stack (client build, server, Postgres, Redis, pgAdmin) with a single command:
cp env.prod.example .env
# Add deployment type to .env (single source of truth)
echo "DEPLOYMENT=production" >> .env
# Generate deployment assets
npm run deploy:gen
# Bring up full stack (Docker)
npm run deploy:compose:upPrerequisites: build and push images lensfinder-client:latest and lensfinder-server:latest to a registry accessible by your cluster, or use imagePullPolicy: Never with a local cluster like kind/minikube and load images manually.
# Optional: set custom host in ingress (default lensfinder.local). Edit infra/k8s/ingress.yaml or /etc/hosts.
# Regenerate deployment assets from env (single source of truth)
npm run deploy:gen
# Create namespace and deploy all
kubectl apply -k infra/k8s/
# Verify
kubectl -n lensfinder get pods,svc,ingress
# Port-forward if no ingress
kubectl -n lensfinder port-forward svc/client 8080:3000
kubectl -n lensfinder port-forward svc/server 8081:3001Services:
- server: Node API (serves built client), exposes
${PORT}(3001 default) - client: Vite preview served by Nginx in container (built)
- postgres: Postgres 16 with persistent volume
- redis: Redis 7 for caching
- pgadmin: optional admin UI on port 5050
Stop and remove volumes:
# Entire stack down + remove volumes
npm run deploy:compose:downEnvironment (centralized):
- Use a single
.envfor all environments. SetDEPLOYMENT=development|productionin.envto control behavior. DATABASE_URLmust be set (no hardcoded fallbacks in generation scripts). In Docker, point topostgres:postgres://lens:lens@postgres:5432/lensfinder.REDIS_URLshould point toredis://redis:6379for Compose.- Client uses same-origin by default. Set
VITE_API_BASE_URLin env to override.
A detailed explanation of the two-stage filter (brand/capabilities first, then fine selection with sliders) and the full list of filter features is available at docs/filter-workflow.md.
Reusable layout shell, stage components, and design tokens are documented in docs/layout-architecture.md and exported via client/src/layout for consumption in other apps.
Lean tool to explore camera and lens options and generate reports. Frontend is Vite + React + TypeScript; backend is Express. Database is Postgres in all environments.
- Node.js 18+ (recommended 20+)
- npm 9+
client/: Vite + React appserver/: Express API with pluggable DBdb/: database assets (seed JSON indb/data/, Docker Compose for Postgres)
npm install- Seed JSON lives in
db/data/. - Postgres (via Docker)
- Bring up/down and view logs:
npm run db:upnpm run db:logsnpm run db:down
- Migrate/seed from JSON:
npm run db:migrate(readsDATABASE_URLfrom env).
- Bring up/down and view logs:
Two options:
- Node dev servers (HMR): API first, then client; ports auto-cleared.
npm run devExplicit startup:
npm run dev:server:pg
npm run dev:clientDev servers:
- API:
http://localhost:${PORT}(default 3001) - Client:
http://localhost:${CLIENT_PORT}(default 3000)
- Dockerized dev stack (single
.env):
npm run stack:up:docker # generate manifests, build as needed, bring up stack
npm run stack:logs:docker # follow logs
npm run stack:down:docker # stop containers (preserve volumes)
npm run stack:clean:docker # stop and remove volumes- Full-screen glass overlay blocks interactions when services are unavailable; background shines through
- Subtle top banner surfaces partial/degraded availability; includes Retry, Pause/Resume retries, and Copy diagnostics
- Preview outage overlay without breaking services by setting
VITE_FORCE_OUTAGE=1for the client build/dev - Glass tokens live in
client/src/components/ui/styles.ts(GLASS_PANEL,AURA_ACCENT,GLASS_CARD_SM)
- Regenerate manifests from
.env:npm run deploy:gen - Docker Compose stack:
npm run deploy:compose:up - Kubernetes namespace:
kubectl apply -k infra/k8s/ - On code changes:
kubectl rollout restart deploy/server && kubectl rollout restart deploy/client
npm run buildStart production servers (serves built client from server):
npm run start:orderedQuick local pre-commit check:
npm run test:precommitFull verification (build + unit + e2e; e2e skipped when Docker not running):
npm run ci:verifyRun only the e2e smoke locally:
npm run test:e2e:smokeFrom the server/ workspace:
npm run dev– start API with tsx (Postgres)
- The API preserves the original JSON response shapes:
GET /api/cameras– list of camerasGET /api/lenses– list of lensesPOST /api/report– derives a report from ranked lens items
- Data sources are defined in
db/data/*.json; the migrate script is idempotent.
-
REST + OpenAPI:
-
The server auto-generates an OpenAPI spec from the current Postgres schema before tests via
server/scripts/generate-openapi.ts. -
Spec output:
server/openapi.json. -
Generate manually:
cd server && npm run openapi
-
Suggested client usage (optional):
- Generate TS types/client from
openapi.json(e.g.,openapi-typescript,orval,swagger-typescript-api). - Validate responses at runtime using
openapi-zod-clientif stricter guarantees are needed.
- Generate TS types/client from
-
View docs:
GET /docs(Swagger UI and Redoc) orGET /openapi.json.
-
-
GraphQL:
-
GraphQL is available alongside REST at
POST /graphql. -
In development, GraphiQL is available at
GET /graphiql. -
Example query:
curl -s http://localhost:3001/graphql \ -H 'Content-Type: application/json' \ -d '{"query":"{ cameras { name brand mount sensor { name width_mm height_mm } } }"}'
-
Example mutation:
curl -s http://localhost:3001/graphql \ -H 'Content-Type: application/json' \ -d '{"query":"mutation($cam:String!,$goal:String!,$top:[ReportItemInput!]!){ report(cameraName:$cam, goal:$goal, top:$top){ cameraName goal items { name rank } }}","variables": {"cam":"A","goal":"B","top":[{"name":"L1","total":90,"weight_g":1000,"price_chf":2000,"type":"zoom"}]}}'
-
Suggested client usage (optional):
- Use
@graphql-codegen/*to generate typed operations and hooks from a.graphqldocuments set.
- Use
-
- Server tests cover REST and GraphQL endpoints.
- DB-backed tests create a timestamped schema per run (e.g.,
lf_test_1699999999999), seed minimalcameras/lenses, setsearch_pathfor the server pool, and drop/prune old schemas (keeps the 10 most recent). - Mock-based tests are kept for fast unit runs (no DB dependency) and run in parallel with DB tests.
- REST endpoints are also accessible under
/v1/*as a stable alias. Deprecations will follow a minor version deprecation notice before removal.