Create README.md
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
# Loam — MTB Build & Tune
|
||||
|
||||
A Next.js web app for mountain bike riders to configure builds, get manufacturer-verified suspension and tire setup starting points, save post-ride reviews, and receive AI-powered tuning recommendations based on personal preference history.
|
||||
|
||||
Built with Next.js App Router, Prisma/PostgreSQL, and Ollama for local LLM inference. The API layer is designed for reuse in a future React Native/Android client.
|
||||
|
||||
---
|
||||
|
||||
## Tech stack
|
||||
|
||||
| Layer | Tech |
|
||||
|---|---|
|
||||
| Framework | Next.js 16 (App Router) |
|
||||
| Language | TypeScript |
|
||||
| Database | PostgreSQL |
|
||||
| ORM | Prisma |
|
||||
| Auth | bcryptjs + jose (JWT in HTTP-only cookies) |
|
||||
| Images | sharp (layer compositing) |
|
||||
| AI | Ollama (local LLM) |
|
||||
| Validation | Zod |
|
||||
|
||||
---
|
||||
|
||||
## Features
|
||||
|
||||
- **Catalog** — Curated frames, forks, shocks, and tires with real-world setup ranges
|
||||
- **Setup verification** — Every product carries a research audit trail (manufacturer docs, community sources, confidence levels)
|
||||
- **Build composer** — Layered bike image generation from selected components
|
||||
- **Per-user learning** — Builds, reviews, and learned preferences are scoped to authenticated users
|
||||
- **AI recommendations** — Ollama-powered tuning advice that incorporates the rider's own review history
|
||||
- **Post-ride reviews** — Rate suspension feel, grip, rolling speed, and report issues (bottom-outs, harsh hits, wallow, small-bump harshness)
|
||||
|
||||
---
|
||||
|
||||
## Getting started
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js 20+
|
||||
- PostgreSQL running locally (or a remote instance)
|
||||
- Ollama installed and running (optional, for AI recommendations)
|
||||
|
||||
### 1. Install dependencies
|
||||
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
### 2. Configure environment variables
|
||||
|
||||
```bash
|
||||
cp .env.example .env.local
|
||||
```
|
||||
|
||||
Edit `.env.local`:
|
||||
|
||||
```
|
||||
DATABASE_URL=postgresql://user:password@localhost:5432/loam
|
||||
JWT_SECRET=change-this-to-a-long-random-string-in-production
|
||||
|
||||
# Optional — defaults shown
|
||||
OLLAMA_URL=http://localhost:11434
|
||||
OLLAMA_MODEL=llama3.1:8b
|
||||
```
|
||||
|
||||
### 3. Set up the database
|
||||
|
||||
```bash
|
||||
# Create and apply migrations
|
||||
npm run db:migrate
|
||||
|
||||
# Seed product catalog
|
||||
npm run db:seed
|
||||
```
|
||||
|
||||
### 4. Pull an Ollama model (optional)
|
||||
|
||||
```bash
|
||||
ollama pull llama3.1:8b
|
||||
```
|
||||
|
||||
### 5. Run the dev server
|
||||
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
Open [http://localhost:3000](http://localhost:3000).
|
||||
|
||||
---
|
||||
|
||||
## Project structure
|
||||
|
||||
```
|
||||
prisma/
|
||||
schema.prisma # User, BikeBuild, RideReview, RiderPreference, Product
|
||||
seed.ts # Seeds catalog into Product table
|
||||
src/
|
||||
app/
|
||||
api/
|
||||
auth/ # register, login, me, logout
|
||||
builds/ # list / create builds (scoped to user)
|
||||
reviews/ # create reviews (scoped to user + build ownership)
|
||||
ai/recommend # Ollama recommendation prompt
|
||||
images/compose # sharp layer compositing
|
||||
catalog/ # public catalog + setupVerification
|
||||
setup-audit/ # research audit endpoint
|
||||
components/
|
||||
BikeBuilderApp.tsx
|
||||
lib/
|
||||
auth.ts # bcrypt, JWT cookie session, requireAuth
|
||||
db.ts # per-user Prisma queries
|
||||
catalog.ts # static product data with citations
|
||||
recommendations.ts # setup math by rider profile
|
||||
setupVerification.ts # research audit metadata
|
||||
types.ts # shared TypeScript types
|
||||
public/
|
||||
images/ # product assets + user-generated builds
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API routes
|
||||
|
||||
| Route | Method | Auth | Description |
|
||||
|---|---|---|---|
|
||||
| `/api/auth/register` | POST | No | Create account, set session cookie |
|
||||
| `/api/auth/login` | POST | No | Log in, set session cookie |
|
||||
| `/api/auth/me` | GET | No | Return current session user |
|
||||
| `/api/auth/logout` | POST | No | Clear session cookie |
|
||||
| `/api/builds` | GET | Yes | List current user's builds |
|
||||
| `/api/builds` | POST | Yes | Save a new build |
|
||||
| `/api/reviews` | POST | Yes | Review a build (must own it) |
|
||||
| `/api/ai/recommend` | POST | Yes | AI tuning advice with user preferences |
|
||||
| `/api/images/compose` | POST | No | Compose build image from layers |
|
||||
| `/api/catalog` | GET | No | Full catalog + setupVerification |
|
||||
| `/api/setup-audit` | GET | No | Research audit report |
|
||||
|
||||
---
|
||||
|
||||
## Auth system
|
||||
|
||||
- Passwords hashed with bcrypt (12 rounds)
|
||||
- Sessions are JWTs stored in HTTP-only `loam-session` cookies (7-day expiry)
|
||||
- `requireAuth()` guards data-modifying routes
|
||||
- All builds, reviews, and preferences are scoped to the authenticated `userId`
|
||||
|
||||
---
|
||||
|
||||
## Data model overview
|
||||
|
||||
- **User** — email (unique), passwordHash, name
|
||||
- **BikeBuild** — belongs to User; stores component IDs, rider profile, setup JSON, image path
|
||||
- **RideReview** — belongs to User + BikeBuild; ratings and issue flags
|
||||
- **RiderPreference** — one per User; aggregated from review history (harsh/soft tendency, grip preference)
|
||||
- **Product** — seeded catalog of frames, forks, shocks, tires with specs and sources JSON
|
||||
|
||||
---
|
||||
|
||||
## Research & citations
|
||||
|
||||
Every product entry includes a `sources` array and a `SetupVerification` record:
|
||||
|
||||
- `manufacturer` — official setup guides, owner manuals, product pages
|
||||
- `community` — Pinkbike reviews, Reddit discussions, Blister/MTBR test notes
|
||||
- Status levels: `verified` → `manufacturer-guided` → `community-informed` → `estimated`
|
||||
|
||||
The `/api/setup-audit` endpoint returns a full breakdown of confidence levels and missing sources.
|
||||
|
||||
---
|
||||
|
||||
## Notes on Android reuse
|
||||
|
||||
The API surface is plain JSON over HTTP with cookie-based auth. A React Native or Android client can reuse the same endpoints by:
|
||||
|
||||
1. Implementing cookie jar storage for the `loam-session` cookie
|
||||
2. Calling `/api/auth/login` or `/api/auth/register` to obtain a session
|
||||
3. Reusing `/api/builds`, `/api/reviews`, `/api/ai/recommend`, and `/api/catalog` as-is
|
||||
|
||||
The image composition endpoint (`/api/images/compose`) returns a PNG path; clients can either request server-side composition or replicate the layer-stacking logic locally.
|
||||
|
||||
---
|
||||
|
||||
## Scripts
|
||||
|
||||
| Command | Purpose |
|
||||
|---|---|
|
||||
| `npm run dev` | Start Next.js dev server |
|
||||
| `npm run build` | Production build |
|
||||
| `npm run db:migrate` | Run Prisma migrations |
|
||||
| `npm run db:seed` | Seed product catalog |
|
||||
| `npm run db:studio` | Open Prisma Studio |
|
||||
| `npm run lint` | Run ESLint |
|
||||
|
||||
---
|
||||
|
||||
## Environment variables
|
||||
|
||||
| Variable | Required | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `DATABASE_URL` | Yes | — | PostgreSQL connection string |
|
||||
| `JWT_SECRET` | Yes | — | Secret for signing session JWTs |
|
||||
| `OLLAMA_URL` | No | `http://localhost:11434` | Ollama server base URL |
|
||||
| `OLLAMA_MODEL` | No | `llama3.1:8b` | Model name for recommendations |
|
||||
Reference in New Issue
Block a user