See CONTRIBUTING.md for contribution guidelines.
The web app is a React + Vite frontend backed by a Spring Boot REST API and a PostgreSQL + Neo4j data layer. Everything below assumes you are at the repo root.
Make sure you have Docker and Docker Compose installed.
cp .env.example .env
docker compose --profile demo up --build -dThat single command does three things:
- Builds the backend, frontend, and custom Neo4j-GDS images on first run.
- Starts PostgreSQL, Neo4j, the backend, and the frontend.
- Once the backend reports healthy, runs the one-shot
seedservice — it loads the hand-crafted demo dataset (78 users, 288 posts, ~50 mentor ratings, 6 active + 48 past mentorships, profile photos, post images, task-attached PDFs) into Postgres and into the shared uploads volume.
The seed step is opt-in: omit --profile demo if you want a clean DB.
Once everything settles you have four long-running services plus a one-shot seed container that exits cleanly:
| Service | URL | Description |
|---|---|---|
| Frontend | http://localhost:8000 | React + Vite dev server |
| Backend | http://localhost:8080 | Spring Boot REST API |
| Swagger | http://localhost:8080/swagger-ui.html | API docs |
| Neo4j | http://localhost:7474 | Follow-graph mirror (GDS plugin) |
| Database | localhost:5433 |
PostgreSQL (managed via Flyway migrations) |
Defaults in .env.example are good enough for local development — no editing
required. The seed-friendly safety flags (APP_RATELIMIT_ENABLED=false,
APP_EMAIL_ENABLED=false, APP_SPAM_ENABLED=false) and the four advanced
ranker toggles (MENTOR_ADVANCED_RANKER, MENTOR_EXPLANATION_ENABLED,
FEED_ADVANCED_RANKER, FOLLOW_RANKER=advanced) are all pre-set so the
demo dataset surfaces the full UI. The only value worth editing for a
real local-with-LLM run is OPENAI_API_KEY — see the env reference
below.
Stop the stack:
docker compose --profile demo downInclude --profile demo here too — without it the seed container is left
orphaned with a stale network reference and the next up will fail. Add
-v to also drop the Postgres and Neo4j volumes for a fully clean reset.
Re-seed after manual mutation (stack already running):
docker compose --profile demo run --rm seedrun --rm runs the loader as a one-shot and removes the container on exit,
so it never goes stale.
Two seeders are available depending on what you need.
The hand-crafted demo dataset (78 users, 288 posts with images,
6 active + 48 past mentorships with tasks/meetings/messages, 45 ratings,
100 mentor-mentee + mentor-pair messages, AI-generated profile photos,
Unsplash-fetched post images, task-attached PDFs) ships pre-built as a
snapshot under scripts/seed_snapshot_data/ and loads automatically
through the demo compose profile shown in the Quick start
above:
docker compose --profile demo up --build -d # first run on a fresh checkout
docker compose --profile demo down # tear down (don't drop the profile flag)
docker compose --profile demo run --rm seed # re-seed against a running stackEach load is idempotent (DELETE-then-COPY against seed-owned rows; the
admin@group7.com bootstrap is never touched).
The same snapshot is reachable from the host without involving the compose service, which is useful when iterating on the snapshot itself:
./scripts/seed.sh load # uses scripts/seed_snapshot_data/ by default
./scripts/seed.sh dump # re-captures current DB state into the same dir
./scripts/seed.sh status # prints seed-owned counts
./scripts/seed.sh helpThe first call bootstraps .venv/ and installs scripts/requirements-seed.txt;
subsequent calls reuse it.
If you only need a clean list of accounts (1 admin + 40 mentors + 60
mentees, no posts/mentorships/photos), use the older direct-Postgres
seeder. It's destructive-but-scoped — it wipes every prior
@seed.local user first and re-inserts the roster.
pip install -r scripts/requirements-seed.txt
python3 scripts/seed_local.pyThe script prints a credentials block to stdout and writes the full
roster to scripts/seed_local_credentials.txt.
Sample accounts from the demo dataset (--profile demo, @seed.test,
all passwords Seed1234!):
| Role | Notes | |
|---|---|---|
| Demo mentee | defne.korkmaz.74@seed.test |
CS senior, 3 past mentorships, no active mentor — ready for AI-match flow |
| Demo mentor | yildiz.demir.78@seed.test |
Active mentorship with Rıza Yavuz; full task + meeting timeline |
| Senior mentor | melis.sezen.2@seed.test |
Cloud Architecture, active mentee Tolga |
| Research mentor | dilara.oz.5@seed.test |
Completed engagement with Defne, 5-star review attached |
The full 78-user roster (20 mentors + 57 mentees + 1 admin) is in
scripts/seed_snapshot_data/data/users.csv. Every user has the same
password Seed1234!.
If you ran scripts/seed_local.py instead, accounts use the
@seed.local domain with role-specific passwords (Admin1234!,
Mentor1234!, Mentee1234!); the full list is written to
scripts/seed_local_credentials.txt.
All knobs below live in .env.example with full inline comments. The
table here is a quick map of what each one controls and what state the
demo expects.
| Variable | Demo (.env.example) |
Prod | What it does |
|---|---|---|---|
APP_RATELIMIT_ENABLED |
false |
true |
Per-IP/per-user login throttling. 78-account demo needs it off for E2E. |
APP_EMAIL_ENABLED |
false |
true |
If on, registration sends through Resend; placeholder API key returns 401. |
APP_SPAM_ENABLED |
false |
true |
Form-token + honeypot defences on the register form. |
| Variable | Demo | Default in compose | What it controls |
|---|---|---|---|
MENTOR_ADVANCED_RANKER |
true |
false |
Swaps the rule-based mentor matcher for the weighted-signal pipeline. |
MENTOR_EXPLANATION_ENABLED |
true |
false |
LLM-authored "why recommended" sentence on each mentor card. |
MENTOR_EXPLANATION_TIMEOUT_MS |
15000 |
3000 |
Upper bound on the gpt-4o-mini call. 3000 is too tight under load. |
FEED_ADVANCED_RANKER |
true |
false |
Advanced For-You pipeline (candidate generation + scoring + MMR). |
FEED_BANDIT_ENABLED |
false |
false |
Contextual bandit — gated behind impression tracking; stays off. |
FOLLOW_RANKER |
advanced |
legacy |
Multi-signal follow ranker (PPR + second-hop + …). Needs Neo4j. |
FOLLOW_GRAPH_SYNC_ENABLED |
true |
false |
Mirrors every PG follow/unfollow into Neo4j after-commit. |
| Variable | When to set |
|---|---|
OPENAI_API_KEY |
If you want LLM mentor explanations and the semantic-affinity follow signal. Empty = graceful fallback. |
RESEND_API_KEY |
Only when APP_EMAIL_ENABLED=true. The demo stack doesn't need it. |
JWT_SECRET |
Already set to a base64 dev secret; rotate in .env.production for any deploy. |
NEO4J_PASSWORD |
group7pass locally; must be rotated in .env.production. |
The OpenAI key flows into both embeddings (text-embedding-3-small) and
chat (gpt-4o-mini) by default. Override with OPENAI_CHAT_MODEL /
OPENAI_EMBEDDING_MODEL if needed.
Prerequisites:
- Java 21
- Maven
- Node.js 18+
- PostgreSQL running on port 5433 with database
group7db, usergroup7, passwordgroup7pass(matching.env.example) - Neo4j running on
bolt://localhost:7687with userneo4j/group7pass(only required whenFOLLOW_GRAPH_SYNC_ENABLED=true, the default)
Backend:
cd backend
mvn spring-boot:runFrontend:
cd frontend
npm install
npm run devFrontend will be available at http://localhost:8000 and proxies /api
requests to the backend at port 8080.
End-to-end tests, including how to enable the /api/test/** fixture endpoints
used by Playwright, are documented in frontend/README.md.
The live demo at mymentornet.org is deployed by
.github/workflows/deploy.yml on every
push to main. The workflow SSHes into the DigitalOcean droplet and runs:
git pull origin main
docker compose --profile demo up --build -dThe droplet uses the same docker-compose.yml as local dev (bundled
Postgres + Neo4j) with its own .env carrying real secrets (JWT_SECRET,
OPENAI_API_KEY, RESEND_API_KEY, NEO4J_PASSWORD, …). The --profile demo
flag activates the one-shot seed service so every deploy re-syncs the
hand-crafted demo dataset; the seed is scoped to %@seed.test users only
and never touches real accounts registered through the UI.
For the demo profile to surface the full UI, the droplet's .env should
include the same advanced flags shipped in .env.example:
MENTOR_ADVANCED_RANKER=true
MENTOR_EXPLANATION_ENABLED=true
MENTOR_EXPLANATION_TIMEOUT_MS=15000
FEED_ADVANCED_RANKER=true
FOLLOW_RANKER=advanced
FOLLOW_GRAPH_SYNC_ENABLED=trueAPP_RATELIMIT_ENABLED, APP_EMAIL_ENABLED, and APP_SPAM_ENABLED can
stay at their prod defaults (true) — the seed itself uses direct
Postgres connections, so rate limits and spam defences don't affect it.
For a hardened install that doesn't bundle Postgres in-stack (e.g. against
managed PG), use the separate docker-compose.prod.yml:
cp .env.production.example .env.production
# edit .env.production: set NEO4J_PASSWORD, JWT_SECRET, SPRING_DATASOURCE_URL,
# SPRING_DATASOURCE_USERNAME, SPRING_DATASOURCE_PASSWORD, RESEND_API_KEY,
# APP_BASE_URL, APP_FRONTEND_URL, APP_CORS_ALLOWED_ORIGINS
docker compose --env-file .env.production -f docker-compose.prod.yml up --build -dThat compose targets a managed PostgreSQL via SPRING_DATASOURCE_URL,
runs Neo4j and the backend in-stack, and binds the frontend to host
port 80 by default (override with FRONTEND_PORT). The seed service
isn't wired into the prod compose by design — production datasets
shouldn't be reset on every boot.
See PRODUCTION_CHECKLIST.md for the broader deployment checklist (TLS, backups, secret rotation).
See mobile-app/README.md for full setup instructions.
cd mobile-app
npm install
cp .env.example .env # set EXPO_PUBLIC_API_URL
npx expo startThe app communicates with the backend via EXPO_PUBLIC_API_URL in mobile-app/.env.
| Device | EXPO_PUBLIC_API_URL value |
|---|---|
| Android emulator (AVD) | http://10.0.2.2:8080 |
| Physical device (same Wi-Fi) | http://<your-machine-ip>:8080 |
| Production | http://<server-ip-or-domain>:8080 |
Do not use localhost — it resolves to the device itself, not the host machine.
| Role | Password | |
|---|---|---|
| Admin | admin@group7.com | Admin1234! |
| Mentor | mentor@group7.com | Mentor1234! |
| Mentee | mentee@group7.com | Mentee1234! |