Files
bikeApp/README.md
T

7.3 KiB

Loam — MTB Build & Tune

A Next.js web app and native Android client 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.

The backend is built with Next.js App Router, Prisma/PostgreSQL, and Ollama for local LLM inference. The Android client is built with Kotlin, Jetpack Compose, Ktor, and Koin against the same JSON API.


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
Android Kotlin, Jetpack Compose, Ktor, Koin

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

npm install

2. Configure environment variables

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

# Create and apply migrations
npm run db:migrate

# Seed product catalog
npm run db:seed

4. Pull an Ollama model (optional)

ollama pull llama3.1:8b

5. Run the dev server

npm run dev

Open 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
android/
  app/src/main/java/com/bikeloam/app/
    data/            # DTOs, Ktor API client, repository
    navigation/      # Compose navigation graph
    ui/              # Compose screens, theme, ViewModels
  app/src/test/      # JVM unit tests for repository + ViewModels

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: verifiedmanufacturer-guidedcommunity-informedestimated

The /api/setup-audit endpoint returns a full breakdown of confidence levels and missing sources.


Android client

The native Android app lives in android/. It uses the same plain JSON API over HTTP with cookie-based auth:

  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

For local emulator development, the debug build points at http://10.0.2.2:3000. Start the web backend with npm run dev, then build or test the app from the Android project:

cd android
./gradlew assembleDebug
./gradlew testDebugUnitTest

The image composition endpoint (/api/images/compose) returns a PNG path; mobile 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

Android commands are run from android/:

Command Purpose
./gradlew assembleDebug Build the debug APK
./gradlew installDebug Install debug build on a connected device or emulator
./gradlew testDebugUnitTest Run JVM unit tests
./gradlew connectedDebugAndroidTest Run instrumented Android tests

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