Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 162 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -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