From d899829abbc87b8048a2f62689e5894db64e0fe9 Mon Sep 17 00:00:00 2001 From: wbaxterh Date: Tue, 14 Jul 2026 16:04:14 -0700 Subject: [PATCH 1/2] docs: rewrite README with architecture, badges, and setup guide - Rename Readme.md -> README.md - Add license/stack badges and mermaid architecture diagram (clients -> Express/Socket.io -> MongoDB + S3/Bunny/Stripe/OAuth) - Document env vars (names only), scripts, project structure, PM2 deploy - Link API_ROUTES.md with staleness caveat Co-Authored-By: Claude Fable 5 --- README.md | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ Readme.md | 88 --------------------------- 2 files changed, 179 insertions(+), 88 deletions(-) create mode 100644 README.md delete mode 100644 Readme.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..9dfd44b --- /dev/null +++ b/README.md @@ -0,0 +1,179 @@ +# TrickBook Backend + +REST API and real-time messaging server for [TrickBook](https://thetrickbook.com) — the skateboarding trick-tracking and social platform. + +![License: Proprietary](https://img.shields.io/badge/License-Proprietary-red) +![Node.js >=20](https://img.shields.io/badge/Node.js-%3E%3D20-339933?logo=node.js&logoColor=white) +![Express 4.x](https://img.shields.io/badge/Express-4.x-000000?logo=express&logoColor=white) +![MongoDB Atlas](https://img.shields.io/badge/MongoDB-Atlas-47A248?logo=mongodb&logoColor=white) +![Socket.io 4.x](https://img.shields.io/badge/Socket.io-4.x-010101?logo=socketdotio&logoColor=white) + +## Overview + +This service is the API layer for both the TrickBook mobile app (React Native/Expo) and the web app (Next.js). Production runs at `api.thetrickbook.com`. + +- **REST API** — 30+ Express route modules covering users, trick lists, the Trickipedia encyclopedia, skate spots, spot lists/reviews, feed, blog, payments, media, analytics, and more. +- **Authentication** — JWT-based auth (email/password with bcrypt), plus Google Sign-In (`google-auth-library`) and Apple Sign-In (`apple-signin-auth`). Rate limiting on login/registration via `express-rate-limit`. +- **Real-time messaging** — Socket.io powers direct messages and live feed updates. +- **Push notifications** — Expo push via `expo-server-sdk`, with background workers for delivery-receipt polling and scheduled practice reminders. +- **Media** — image uploads to AWS S3 (`multer-s3` + `sharp` processing); video upload/streaming through Bunny Stream. +- **Payments** — Stripe subscriptions (freemium premium tier) with webhook handling. +- **Spots** — Google Places integration for skate spot data and photos. +- **Email** — Nodemailer for contact form and account emails. +- **Bot chat** — the "Kaori" assistant (`/api/bot-chat`), backed by an LLM provider and an optional local TTS sidecar (`kith-voice/`). + +### Domain terminology + +- **Trickipedia** (`/api/trickipedia`) — global, admin-curated encyclopedia of tricks (not user-specific). +- **Listings / trick lists** (`/api/listings`) — a user's collections of tricks for tracking personal progress. +- **Listing** (`/api/listing`) — CRUD on individual tricks within a user's trick list. + +## Architecture + +```mermaid +flowchart LR + subgraph Clients + M[Mobile app
React Native / Expo] + W[Web app
Next.js] + end + + subgraph API["Express API — api.thetrickbook.com"] + MW[Middleware
JWT auth · rate limits · subscription] + R[Routes
30+ modules] + S[Services & workers
uploads · notifications · reminders] + IO[Socket.io
DMs · live feed] + end + + DB[(MongoDB Atlas)] + + M -->|HTTPS + WebSocket| MW + W -->|HTTPS| MW + MW --> R --> S + R --> DB + S --> DB + IO --> DB + M <-.-> IO + + MW --> OAUTH[Google & Apple Sign-In] + S --> S3[AWS S3
images] + S --> BUNNY[Bunny Stream
video] + R --> STRIPE[Stripe
subscriptions] + S --> EXPO[Expo Push] + S --> MAIL[Nodemailer
SMTP] +``` + +The entry point (`index.js`) opens a single shared MongoDB connection (`db.js`), mounts every route module with that connection injected, attaches Socket.io to the HTTP server, starts the notification workers, and handles graceful shutdown on `SIGTERM`/`SIGINT`. The app runs behind a proxy (`trust proxy` is set), and CORS is restricted to an allowlist of web origins — mobile clients are unaffected. + +## Tech Stack + +- **Runtime:** Node.js 20 (`.nvmrc`), Express 4 +- **Database:** MongoDB (native driver + Mongoose) +- **Real-time:** Socket.io 4 +- **Auth:** `jsonwebtoken`, `bcrypt`, `google-auth-library`, `apple-signin-auth` +- **Media:** AWS SDK (S3), `multer-s3`, `sharp`, Bunny Stream (via `axios`) +- **Payments:** Stripe +- **Notifications:** `expo-server-sdk`, Nodemailer +- **Hardening:** `helmet`, `compression`, `express-rate-limit`, `joi` validation +- **Tooling:** Biome (lint + format), Husky + lint-staged pre-commit hooks, nodemon +- **Ops:** PM2 (`ecosystem.config.js`) + +## Getting Started + +### Prerequisites + +- Node.js **20+** (see `.nvmrc` — `nvm use`) +- A MongoDB instance (Atlas connection string, or local MongoDB) + +### Setup + +```bash +git clone https://github.com/wbaxterh/TB-Backend.git +cd TB-Backend +npm install +cp .env.example .env # then fill in real values +npm run dev # starts on PORT (default 9000) with nodemon +``` + +## Environment Variables + +All secrets live in `.env` (gitignored). `.env.example` documents placeholders — never commit real values. + +| Variable | Purpose | +|---|---| +| `PORT` | HTTP port (defaults to config value if unset) | +| `ATLAS_URI` | MongoDB connection string | +| `JWT_SECRET` | JWT signing secret | +| `AWS_KEY` / `AWS_SECRET` | AWS credentials for S3 uploads | +| `AWS_REGION` | AWS region for S3 | +| `GOOGLE_CLIENT_ID` | Google OAuth web client ID | +| `GOOGLE_IOS_CLIENT_ID` / `GOOGLE_ANDROID_CLIENT_ID` | Google OAuth mobile client IDs | +| `GOOGLE_PLACES_API_KEY` | Google Places API (skate spot data/photos) | +| `GOOGLE_DRIVE_FOLDER_ID` / `GOOGLE_DRIVE_CREDENTIALS_PATH` | Google Drive integration (service account) | +| `APPLE_CLIENT_ID` | Apple Sign-In app bundle ID | +| `APPLE_WEB_SERVICE_ID` | Apple Sign-In web service ID | +| `STRIPE_SECRET_KEY` | Stripe API key | +| `STRIPE_WEBHOOK_SECRET` | Stripe webhook signature verification | +| `STRIPE_PREMIUM_PRICE_ID` | Stripe price ID for the premium subscription | +| `BUNNY_API_KEY` | Bunny.net account API key | +| `BUNNY_LIBRARY_ID` / `BUNNY_LIBRARY_API_KEY` | Bunny Stream video library credentials | +| `BUNNY_CDN_HOSTNAME` | Bunny CDN hostname for playback URLs | +| `BUNNY_STREAM_TOKEN_KEY` | Bunny signed-URL token key | +| `EMAIL_USER` / `EMAIL_PASSWORD` | Nodemailer SMTP credentials | +| `FRONTEND_URL` | Web app base URL (links in emails/redirects) | +| `OPENROUTER_API_KEY` / `OPENAI_API_KEY` | LLM providers for the Kaori bot chat | +| `BOTCHAT_USE_ELIZA` | Toggle fallback bot-chat mode | +| `KITH_VOICE_URL` | Kaori TTS sidecar service URL | + +## Scripts + +| Script | Command | Description | +|---|---|---| +| `npm start` | `node index.js` | Run the server | +| `npm run dev` | `nodemon index.js` | Run with auto-reload | +| `npm run lint` | `biome check .` | Lint | +| `npm run lint:fix` | `biome check --write .` | Lint and auto-fix | +| `npm run format` | `biome format --write .` | Format | +| `npm run format:check` | `biome format .` | Check formatting | +| `npm run validate` | `biome check .` | CI validation (alias of lint) | + +Husky + lint-staged run Biome on staged files at commit time. + +## Project Structure + +``` +index.js App entry — mounts routes, Socket.io, workers, graceful shutdown +db.js Single shared MongoDB connection +routes/ Express route modules (auth, users, listings, trickipedia, spots, payments, feed, dm, …) +middleware/ JWT auth, admin/account-owner guards, subscription gating, validation +services/ S3 uploads, Bunny Stream, Google Places, push notifications, reminder planning +socket/ Socket.io initialization + DM and feed event handlers +workers/ Background jobs — push receipt poller, reminder sender +store/ Data-access helpers for core collections +mappers/ Response/DTO mapping +config/ node-config environment configs + Stripe client +scripts/ One-off ops scripts (spot seeding, photo backfills, token inspection) +utils/ Shared helpers +kith-voice/ Kaori TTS sidecar service (separate package) +public/, uploads/ Static assets and local upload staging +ecosystem.config.js PM2 process definition (TB-Backend) +``` + +## API Documentation + +- [`API_ROUTES.md`](./API_ROUTES.md) — route reference (partial; the route modules in `routes/` and their mounts in `index.js` are the source of truth). +- Setup/feature guides in this repo: [`STRIPE_SETUP_GUIDE.md`](./STRIPE_SETUP_GUIDE.md), [`FREEMIUM_MODEL_SETUP.md`](./FREEMIUM_MODEL_SETUP.md), [`SPOT_LISTS_GUIDE.md`](./SPOT_LISTS_GUIDE.md), [`MONGODB_COLLECTIONS_SETUP.md`](./MONGODB_COLLECTIONS_SETUP.md). +- Full platform documentation lives in the [TrickBook docs site](https://github.com/wbaxterh/TrickBookDocs). + +## Deployment + +Production runs on AWS EC2 under **PM2** (process name `TB-Backend`, defined in `ecosystem.config.js`) behind `api.thetrickbook.com`. Environment variables live in the server's `.env` — they are never committed. Deployment procedures, server access, and infrastructure details are documented internally (see the docs site); do not add hosts, IPs, or credentials to this repo. + +## Related Repositories + +- [TrickBookFrontend](https://github.com/wbaxterh/TrickBookFrontend) — TrickList mobile app (React Native / Expo) +- [TrickBookWebsite](https://github.com/wbaxterh/TrickBookWebsite) — web app (Next.js) +- [TrickBookDocs](https://github.com/wbaxterh/TrickBookDocs) — documentation site (Docusaurus) + +## License + +Proprietary — Copyright © TrickBook. All rights reserved. diff --git a/Readme.md b/Readme.md deleted file mode 100644 index 526e14b..0000000 --- a/Readme.md +++ /dev/null @@ -1,88 +0,0 @@ -# TrickBook Backend - -This is the backend service for the TrickBook application, a platform for skateboarders to track their trick progress, share listings, and connect with other skaters. - -## Architecture Overview - -The backend is built with Node.js and Express, using MongoDB as the database. It serves as the API layer for both the React Native mobile app and the Next.js web application. - -### Key Components - -- **Authentication**: JWT-based auth with support for both email/password and Google SSO -- **Image Processing**: Handles image uploads and resizing for listings and user profiles -- **Real-time Features**: Push notifications for messages and updates -- **MongoDB Collections**: - - Users - - Listings (User Trick Lists) - - Tricks (User-specific) - - Trickipedia (Global tricks encyclopedia) - - Messages - - Blog Posts - - Categories - -## API Endpoints - -### Authentication - -- `POST /api/auth` - Regular email/password login -- `POST /api/auth/google-auth` - Google SSO authentication - -### User Management - -- `GET /api/user/me` - Get current user profile -- `GET /api/users` - Get users (admin only) -- `PUT /api/user` - Update user profile -- `DELETE /api/users/:id` - Delete user (requires account owner or admin) - -### Listings (User Trick Lists) - -- `GET /api/listings` - Get all trick lists for a user (each list is a collection of tricks) -- `POST /api/listings` - Create new trick list -- `GET /api/listings/countTrickLists` - Get count of trick lists -- `GET /api/listings/all` - Get all trick lists - -### Listing (Tricks in a Trick List) - -- `GET /api/listing` - Get all tricks in a specific trick list (by list_id) -- `PUT /api/listing/:id` - Update a trick in a trick list -- `PUT /api/listing/edit` - Edit a trick in a trick list -- `PUT /api/listing/update` - Update trick completion status -- `POST /api/listing` - Add a new trick to a trick list -- `DELETE /api/listing/:id` - Delete a trick from a trick list -- `GET /api/listing/allData` - Get all tricks (admin/debug) -- `GET /api/listing/allTricks` - Get all tricks for a user (flattened) -- `GET /api/listing/graph` - Get trick completion graph data - -### Trickipedia (Global Tricks Encyclopedia) - -- `GET /api/trickipedia` - Get all tricks (with optional filtering: `?category=...&difficulty=...&search=...`) -- `GET /api/trickipedia/category/:category` - Get tricks by category (with optional filtering) -- `GET /api/trickipedia/:id` - Get a single trick by ID -- `POST /api/trickipedia` - Create a new trick (admin only) -- `PUT /api/trickipedia/:id` - Update a trick (admin only) -- `DELETE /api/trickipedia/:id` - Delete a trick (admin only) - -> **Note:** -> -> - **Trickipedia** is a global, admin-curated encyclopedia of tricks (not user-specific). -> - **Listings/Trick Lists** (`/api/listings`) are user-specific collections of tricks for tracking personal progress. -> - **Listing** (`/api/listing`) is for CRUD operations on individual tricks within a user's trick list. - -### Messages - -- `GET /api/messages` - -### Trick Images (S3 Uploads) - -- `POST /api/trickImage/upload` - Upload a trick image to S3 (requires `trickUrl` query param, returns S3 URL) -- `DELETE /api/trickImage/delete-folder/:slug` - Delete all images for a trick (by URL slug) - -**Example Upload:** - -- POST `/api/trickImage/upload?trickUrl=backside-pop-shove-it` -- Form-data body: `file` (the image file) -- Response: `{ "imageUrl": "https://trickbook.s3.amazonaws.com/backside-pop-shove-it/1687040000000-image.jpg" }` - -**Example Delete:** - -- DELETE `/api/trickImage/delete-folder/backside-pop-shove-it` From 5762f88e3832caf55584907193002e8378c48453 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 14 Jul 2026 23:16:40 +0000 Subject: [PATCH 2/2] fix: format PYTHON_CWD declaration in kith-voice server --- kith-voice/src/server.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/kith-voice/src/server.ts b/kith-voice/src/server.ts index 8e324ff..01acd00 100644 --- a/kith-voice/src/server.ts +++ b/kith-voice/src/server.ts @@ -39,8 +39,7 @@ const ROOT = path.dirname(Bun.fileURLToPath(import.meta.url)); // Env vars still override (e.g. a shared venv on the EC2 box). const PYTHON_VENV = process.env.PIPECAT_PYTHON_PATH || path.resolve(ROOT, '../python-sidecar/.venv/bin/python'); -const PYTHON_CWD = - process.env.PIPECAT_PYTHON_CWD || path.resolve(ROOT, '../python-sidecar'); +const PYTHON_CWD = process.env.PIPECAT_PYTHON_CWD || path.resolve(ROOT, '../python-sidecar'); const apiKey = process.env.ELEVENLABS_API_KEY; const voiceId = process.env.ELEVENLABS_VOICE_ID ?? 'klHOJHbGA89BjwulA7MN';