diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8d36800 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,162 @@ +# Contributing to trood-leader + +Thanks for helping out. This guide gets you from a fresh checkout to a running +local API with a working database and embedding server in a few minutes. + +## Prerequisites + +- **Node.js 20+** (the Dockerfile targets `node:20`) +- **Docker + Docker Compose** (for the Postgres + API containers) +- **npm** (ships with Node) + +## 1. Clone and install dependencies + +```bash +git clone https://github.com/TroodInc/trood-leader +cd trood-leader +npm install +``` + +`npm install` runs in the repo root. The embedding server is a separate npm +package under `embedding-server/` — install its dependencies too if you plan +to run it locally (see step 4): + +```bash +(cd embedding-server && npm install) +``` + +## 2. Configure environment + +Copy the template and fill the four service values: + +```bash +cp .env.example .env +``` + +`DATABASE_URL` in `.env.example` points at the Postgres service declared in +`docker-compose.yml`, which is correct inside the Docker Compose network. For +host-side commands such as `npm run dev`, `npm run prisma:migrate`, or +`npm run prisma:push`, use `localhost` instead: + +```bash +DATABASE_URL=postgresql://trood:trood@localhost:5432/trood_leader +``` + +Values you must set before `/import` or `/export` endpoints work: + +| Variable | Purpose | +|-------------------------|----------------------------------------------------------| +| `EMBEDDING_API_URL` | URL of your embedding service (e.g. `http://host.docker.internal:3001` when the API runs in Docker) | +| `EMBEDDING_VERSION` | Version tag stored alongside each embedding | +| `EMAILOCTOPUS_API_KEY` | API key for EmailOctopus exports | +| `EMAILOCTOPUS_LIST_ID` | Target list ID for EmailOctopus exports | + +## 3. Run the database and API + +The easiest path is Docker Compose, which brings up Postgres and the API +together: + +```bash +docker compose up --build +``` + +The API listens on `http://localhost:3000`. Postgres listens on +`localhost:5432` with user/password/database `trood/trood/trood_leader` +(matches `docker-compose.yml`). + +Docker Compose does not apply Prisma migrations automatically. On a fresh +database, run step 4 once before using API endpoints. + +If you want to run the API outside Docker while keeping Postgres in Docker: + +```bash +docker compose up -d postgres +npm run dev # ts-node-dev, reloads on save +``` + +Before running `npm run dev` on your host, make sure `DATABASE_URL` uses +`localhost` as shown in step 2, not the Docker-only `postgres` hostname. + +## 4. Run migrations + +Prisma generates the client and applies migrations. These commands run on your +host, so make sure `DATABASE_URL` uses `localhost` as shown in step 2: + +```bash +npm run prisma:generate # prisma generate +npm run prisma:migrate # prisma migrate dev (creates tables) +``` + +For quick iteration against an empty dev database you can use +`npm run prisma:push` instead of `prisma:migrate` — it syncs the schema +without creating a migration file. + +## 5. Start the embedding server (local dev) + +`embedding-server/` is a small Express service that speaks the `/embed` +contract the API expects. It's useful for local dev so you don't need to +point at a real embedding provider: + +```bash +cd embedding-server +npm start +``` + +It listens on `http://localhost:3001`. If the API runs on your host, set +`EMBEDDING_API_URL` in `.env` to that URL. If the API runs in Docker Compose, +use `http://host.docker.internal:3001` so the container can reach the host +service. + +The default implementation returns a deterministic 384-dimension mock vector +per text input, which is fine for end-to-end tests but obviously not a real +embedding. + +## 6. Build for production + +```bash +npm run build # tsc -> dist/ +npm start # node dist/index.js +``` + +## Troubleshooting + +**`ECONNREFUSED` on the embedding URL.** The API expects +`EMBEDDING_API_URL` to point at a reachable embedding service. In Docker, +`localhost` from inside the API container is the container itself, not your +Mac — use `host.docker.internal:3001` or add the embedding server to +`docker-compose.yml` so they share the network. + +**Prisma: `database "trood_leader" does not exist`.** The Postgres container +creates the database from `POSTGRES_DB` on first start. If you ran Postgres +without it, either `docker compose down -v` to reset the volume or +`createdb -U trood trood_leader` manually. + +**`npm run prisma:migrate` fails with `getaddrinfo ENOTFOUND postgres`.** You +are running Prisma on your host while `DATABASE_URL` still points at the +Docker-only `postgres` hostname. Use +`postgresql://trood:trood@localhost:5432/trood_leader`, start Postgres with +`docker compose up -d postgres`, then re-run the migrate command. + +**Port 3000 or 3001 already in use.** The API uses `PORT` from `.env` +(default 3000); the embedding server hard-codes 3001. Free the port or +change the API's `PORT` (the embedding server would need a small code edit +at `embedding-server/index.js` to honour a different port). + +**Schema drift after a merge.** Run `npm run prisma:generate` after pulling +— the generated client lives under `node_modules/.prisma` and is not +checked in, so rebasing or switching branches often leaves it stale. + +## Where to put changes + +- API routes: `src/` (Express + TypeScript) +- Prisma schema + migrations: `prisma/` +- Embedding mock: `embedding-server/` +- Dev/CI config: root (`tsconfig.json`, `docker-compose.yml`, `Dockerfile`) + +Before opening a PR, make sure: + +1. `npm run build` succeeds (TypeScript compiles cleanly) +2. Prisma migrations you added apply cleanly from an empty database +3. Your change has a one-line note in the PR description about which + endpoints / tables it touches, so reviewers can spot reply-tracking or + classification regressions early