84 lines
3.7 KiB
Markdown
84 lines
3.7 KiB
Markdown
# ToolRate
|
|
|
|
Community platform for listing and rating developer/productivity tools by usefulness and usability.
|
|
|
|
## Stack
|
|
|
|
- pnpm workspaces, Node.js 24, TypeScript 5.9
|
|
- API: Express 5 + express-session + connect-pg-simple
|
|
- DB: PostgreSQL + Drizzle ORM
|
|
- Validation: Zod (`zod/v4`), `drizzle-zod`
|
|
- API codegen: Orval (from OpenAPI spec)
|
|
- Build: esbuild (CJS bundle)
|
|
- Frontend: React 19 + Vite + TanStack Query + wouter + shadcn/ui + recharts
|
|
|
|
## Packages
|
|
|
|
- `lib/api-spec` — OpenAPI contract (source of truth)
|
|
- `lib/api-zod` — generated Zod schemas from the spec
|
|
- `lib/api-client-react` — generated React Query hooks
|
|
- `lib/db` — Drizzle schema + DB access
|
|
- `artifacts/api-server` — Express backend
|
|
- `artifacts/toolrate` — React frontend
|
|
- `artifacts/mockup-sandbox` — component preview
|
|
|
|
## Run & develop
|
|
|
|
- `pnpm --filter @workspace/api-server run dev` — run the API server
|
|
- `pnpm --filter @workspace/toolrate run dev` — run the frontend
|
|
- `pnpm run typecheck` — full typecheck across all packages
|
|
- `pnpm run build` — typecheck + build all packages
|
|
- `pnpm --filter @workspace/api-spec run codegen` — regenerate API hooks and Zod schemas from the OpenAPI spec
|
|
- `pnpm --filter @workspace/db run push` — push DB schema changes (dev only)
|
|
|
|
## Required env vars
|
|
|
|
- `DATABASE_URL` — Postgres connection string
|
|
- `SESSION_SECRET` — session signing secret
|
|
- `VOTER_SECRET` — secret for voter submissions
|
|
- `LOCAL_ADMIN_USERNAME` / `LOCAL_ADMIN_PASSWORD` — local admin login
|
|
|
|
## Auth
|
|
|
|
- Session-based auth (not JWT), sessions stored in Postgres via connect-pg-simple.
|
|
- Write operations (create/update/delete tools, submit ratings) require auth; reads are public.
|
|
- Login via local admin user (`LOCAL_ADMIN_*`). Keycloak/OIDC support exists as an optional legacy path.
|
|
|
|
## Where things live
|
|
|
|
- `lib/api-spec/openapi.yaml` — source of truth for the API contract
|
|
- `lib/db/src/schema/` — Drizzle table definitions (`tools.ts`, `ratings.ts`)
|
|
- `lib/api-client-react/src/generated/` — generated React Query hooks (do not edit)
|
|
- `lib/api-zod/src/generated/` — generated Zod schemas (do not edit)
|
|
- `artifacts/api-server/src/routes/` — Express route handlers
|
|
- `artifacts/api-server/src/middleware/auth.ts` — `requireAuth` middleware
|
|
- `artifacts/api-server/src/routes/auth.ts` — login/logout/me
|
|
- `artifacts/toolrate/src/pages/` — frontend pages
|
|
- `artifacts/toolrate/src/components/` — shared components (layout, tool-card, category-combobox, feature-input)
|
|
- `artifacts/toolrate/src/hooks/use-auth.ts` — auth state hook
|
|
|
|
## Architecture decisions
|
|
|
|
- Contract-first: OpenAPI spec → codegen → typed hooks + Zod schemas. Never hand-write fetch calls.
|
|
- Session-based auth (not JWT) — sessions stored in Postgres via connect-pg-simple.
|
|
- Write operations (create/update/delete tools, submit votes) require auth. Reads are public.
|
|
- Category and feature autocomplete are client-side filtered against live API data (no separate index).
|
|
- Grafana can consume `/api/analytics/*` endpoints directly via the JSON datasource plugin.
|
|
|
|
## Product
|
|
|
|
- Browse and search tools by category, with ratings (usefulness 1-5 + usability 1-5)
|
|
- Submit new tools with features and tags
|
|
- Rate tools with comment and reviewer name
|
|
- Analytics dashboard: top tools chart, category breakdown, score distribution histograms
|
|
- Grafana integration: all `/api/analytics/*` endpoints return clean JSON
|
|
|
|
## Deployment
|
|
|
|
- Container image: `git.kubebase.de/admin/tool-evaluator:<tag>` (pushed by CI)
|
|
- Deployed to k3s via the `apps` repo (kustomize), served at `https://toolr.kubebase.de`
|
|
|
|
## Gotchas
|
|
|
|
- Orval clears the output folder during codegen — transient HMR errors in the dev server are normal and auto-recover.
|
|
- Auth uses PKCE — the `code_verifier` is stored in the session, not in-memory state. |