A full stack web application that fetches bee observation data and creates specimen labels for the Oregon Bee Project
Orchestrated with Docker Compose (docker-compose.yml; dev overrides in docker-compose.override.yml): MongoDB, RabbitMQ, an Express API (server/), an R-based worker (worker/), an nginx reverse proxy, and a Vite/React client (client/).
- Copy
.env.exampleto.envand fill it in (generate the secrets withopenssl rand -hex 32). The server validates required variables at startup viashared/lib/config/environment.js. - Start the backend:
docker compose up --build mongo rabbitmq server nginx. (The fulldocker compose upadditionally builds the R worker image — slow on the first run — and a Vite dev server at http://localhost:5173.) - Build the client so nginx can serve it:
cd client && npm run build, then open http://localhost/. - Log in as an Administrator with the
ADMIN_USERNAME/ADMIN_PASSWORDfrom your.env(seeded on startup).
Run npm install at the repo root, then:
npm test(ornpm run test:watch) — Vitest tests, colocated with the code as*.test.ts. They run on your machine, not in Docker, and never touch a real database or.env(seetest/setupEnv.ts).npm run typecheck— type-checks withtsc. The codebase is migrating to TypeScript incrementally;.jsand.tsfiles coexist (seetsconfig.base.json).
Two structural notes:
shared/libhas nopackage.jsonof its own; in Docker its imports resolve against each container'snode_modules. So a dependency used byshared/libmust be declared in three places:server/package.json,worker/package.json, and the rootpackage.json(which makes it resolvable for host-side tests).server/sharedandworker/sharedare committed symlinks toshared/, mirroring the layout Docker Compose assembles with bind mounts, so that../shared/lib/...imports also resolve outside Docker. They're excluded from image builds via each package's.dockerignore.
Everything under shared/data/ is gitignored (see .gitignore), so a fresh clone has to recreate it. An Administrator login at melittologist.org can supply populated copies of the CSVs; empty files are enough to boot.
- Create the directories the subtasks write their output into:
cd shared/data && mkdir -p addresses backups duplicates elevation emails flags labels mismatches observations occurrences pivots pulls reports taxonomy uploads - Create the seed and lookup files:
touch workingOccurrences.csv backupOccurrences.csv plantList.csv cp usernames.example.csv usernames.csvworkingOccurrences.csvseeds the occurrences collection when the database is empty on startup (server/src/index.js).usernames.csvmaps each occurrence'suserLoginto a volunteer's contact info (shared/lib/services/UsernamesService.js). Without it, every record from the Observations subtask gets a name error flag and never enters the occurrences collection.usernames.example.csvis a two-row sample showing the format; replace it with a populated copy from an Administrator login at melittologist.org.plantList.csvis the Oregon Bee Project plant list served by the plant-list endpoints. (The related iNaturalist taxonomy cache,plantTaxa.json, is created automatically.)
- Optional — elevation data. To fill the
elevationfield on pulled records, download 1-arc-second GeoTIFF tiles from the USGS Earth Explorer and unzip them intoshared/data/elevation/(shared/lib/services/ElevationService.js). Skip this if you don't need elevation.
- Port 443 is mapped for production TLS but unused in dev. If it collides on your machine, add a gitignored
docker-compose.local.ymloverriding the nginx ports and pass all three files to compose:# docker-compose.local.yml services: nginx: ports: !override - '80:80'
docker compose -f docker-compose.yml -f docker-compose.override.yml -f docker-compose.local.yml up