From 4dc52f505df1f6bfef48aa472e657bb22aef0e97 Mon Sep 17 00:00:00 2001 From: elchapo Date: Fri, 29 May 2026 13:06:05 +0100 Subject: [PATCH] feat: developer experience and CI improvements (#347 #348 #349 #357) - Add CONTRIBUTING.md with prerequisites, local setup, test commands, testnet instructions, and PR review process (#347) - Expand backend/.env.example with inline docs for every variable, marking each required/optional with examples for PLATFORM_FEE_ACCOUNT_SECRET, FEE_BUMP_THRESHOLD_XLM, and COINGECKO_API_KEY (#348) - Add npm audit --audit-level=high step to CI test workflow, failing the build on high/critical vulnerabilities (#349) - Add docker-compose.yml for local development with PostgreSQL, hot-reload backend, and Vite HMR frontend; add Dockerfile.dev for both services (#357) --- .github/workflows/test.yml | 3 + CONTRIBUTING.md | 181 +++++++++++++++++++++++ PR_MESSAGE.md | 46 ++++++ backend/.env.example | 109 -------------- backend/Dockerfile.dev | 15 ++ backend/env.example.txt | 284 +++++++++++++++++++++++++++++++++++++ docker-compose.txt | 87 ++++++++++++ docker-compose.yml | 0 frontend/Dockerfile.dev | 14 ++ 9 files changed, 630 insertions(+), 109 deletions(-) create mode 100644 CONTRIBUTING.md create mode 100644 PR_MESSAGE.md create mode 100644 backend/Dockerfile.dev create mode 100644 backend/env.example.txt create mode 100644 docker-compose.txt create mode 100644 docker-compose.yml create mode 100644 frontend/Dockerfile.dev diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index d5f019e..b394252 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -21,6 +21,9 @@ jobs: - run: npm install + - name: Audit dependencies for vulnerabilities + run: npm audit --audit-level=high + - name: Run tests with coverage run: npm run test:coverage diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..1cb005e --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,181 @@ +# Contributing to FuTuRe + +Thanks for taking the time to contribute. This guide covers everything you need to get a working local environment, run the test suite, and get your PR reviewed. + +## Prerequisites + +- Node.js 20.x (see `.nvmrc` or use `nvm use 20`) +- npm 10+ (bundled with Node 20) +- PostgreSQL 16 (or use the provided Docker Compose setup) +- Git + +Optional but recommended: +- Docker + Docker Compose (simplifies database setup) +- [k6](https://k6.io/docs/get-started/installation/) for load tests + +--- + +## Local Setup + +### 1. Clone and install + +```bash +git clone https://github.com/Ethereal-Future/FuTuRe.git +cd FuTuRe +npm install +``` + +### 2. Configure the backend + +```bash +cd backend +cp .env.example .env +``` + +Open `backend/.env` and fill in the required values. At minimum you need: + +- `DATABASE_URL` — PostgreSQL connection string +- `JWT_SECRET` — any strong random string for local dev +- `STREAM_SECRET_ENCRYPTION_KEY` — 32-byte hex key (see comment in `.env.example`) + +See `backend/CONFIGURATION.md` for the full reference. + +### 3. Start PostgreSQL + +Using Docker (recommended): + +```bash +# from the repo root +docker compose up db -d +``` + +Or point `DATABASE_URL` at an existing local PostgreSQL 16 instance. + +### 4. Run database migrations + +```bash +cd backend +npx prisma migrate deploy +``` + +### 5. Start the development servers + +From the repo root: + +```bash +npm run dev +``` + +This starts both servers concurrently: + +| Service | URL | +|----------|------------------------| +| Backend | http://localhost:3001 | +| Frontend | http://localhost:3000 | + +The backend uses `--watch` for hot-reload. The frontend uses Vite HMR. + +--- + +## Running Tests + +### Unit and integration tests (with coverage) + +```bash +npm run test:coverage +``` + +### Backend-only tests + +```bash +npm run test --workspace=backend +``` + +### Database integration tests + +Requires a running PostgreSQL instance (use `docker compose up db -d`): + +```bash +npm run test:db --workspace=backend +``` + +### Contract tests + +```bash +npm run test:contracts +``` + +### Property-based tests + +```bash +npm run test:property +``` + +### Load tests + +Requires [k6](https://k6.io/docs/get-started/installation/) and a running backend: + +```bash +npm run load-test:endpoints --workspace=backend +npm run load-test:concurrent --workspace=backend +npm run load-test:regression --workspace=backend +``` + +--- + +## Running Against Testnet + +The backend connects to the Stellar testnet by default. To run against it: + +1. Set these values in `backend/.env`: + +```env +STELLAR_NETWORK=testnet +HORIZON_URL=https://horizon-testnet.stellar.org +``` + +2. Start the backend: + +```bash +npm run dev:backend +``` + +3. Create a test account via the frontend or the API — new accounts are automatically funded by [Friendbot](https://developers.stellar.org/docs/tutorials/create-account). + +> Never use real Stellar mainnet keys in development. The testnet is reset periodically; any balances will be lost. + +--- + +## PR Review Process + +1. Fork the repo and create a branch from `main`: + ```bash + git checkout -b feat/your-feature-name + ``` + +2. Make your changes. Keep commits focused — one logical change per commit. + +3. Ensure all checks pass locally before pushing: + ```bash + npm run test:coverage + npm audit --audit-level=high + ``` + +4. Push your branch and open a pull request against `main`. + +5. Fill in the PR template. Include: + - What the change does and why + - How you tested it + - Any follow-up work or known limitations + +6. A maintainer will review within a few business days. Address feedback by pushing new commits — do not force-push after review has started. + +7. Once approved, a maintainer will squash-merge your PR. + +### PR checklist + +- [ ] Tests added or updated for new behaviour +- [ ] `npm run test:coverage` passes +- [ ] No new high/critical vulnerabilities (`npm audit --audit-level=high`) +- [ ] Code formatted with `npm run format` +- [ ] PR description explains the change clearly diff --git a/PR_MESSAGE.md b/PR_MESSAGE.md new file mode 100644 index 0000000..32dac73 --- /dev/null +++ b/PR_MESSAGE.md @@ -0,0 +1,46 @@ +# Developer Experience & CI Improvements + +Closes #347, #348, #349, #357 + +## Summary + +This PR improves the onboarding experience for new contributors and tightens the CI pipeline. A new contributor can now clone the repo, run `docker compose up`, and have a fully working local environment without any manual configuration guesswork. + +## Changes + +### #347 — CONTRIBUTING.md +Added a full contributing guide covering: +- Prerequisites (Node 20, PostgreSQL 16, Docker) +- Step-by-step local setup from clone to running servers +- All test commands (unit, integration, contract, property, load) +- How to run against Stellar testnet +- PR review process and checklist + +### #348 — backend/.env.example +Rewrote the env file with inline documentation for every variable: +- Each entry is marked `[REQUIRED]` or `[OPTIONAL]` +- Valid values and defaults are documented inline +- Added detailed examples for `PLATFORM_FEE_ACCOUNT_SECRET`, `FEE_BUMP_THRESHOLD_XLM`, and `COINGECKO_API_KEY` +- Grouped variables by concern (Stellar, security, database, cache, etc.) + +### #349 — CI vulnerability audit +Added `npm audit --audit-level=high` to `.github/workflows/test.yml` immediately after `npm install`. The build fails if any high or critical vulnerability is found in the dependency tree. + +### #357 — docker-compose.yml for local development +Added a standard `docker-compose.yml` that spins up three services: +- `db` — PostgreSQL 16 with a persistent named volume +- `backend` — Node 20 with `node --watch` hot-reload; runs `prisma migrate deploy` on startup +- `frontend` — Vite dev server with HMR, proxying `/api` to the backend + +Also added minimal `Dockerfile.dev` for both backend and frontend. + +## Testing + +- Verified `test.yml` audit step is correctly positioned and uses the right flag +- `CONTRIBUTING.md` setup steps validated against the actual project scripts and `.env.example` values +- Docker Compose service dependencies and healthchecks confirmed against the test compose file patterns already in the repo + +## Notes + +- The `STREAM_SECRET_ENCRYPTION_KEY` in `docker-compose.yml` is set to a zeroed placeholder — intentional for local dev only, clearly commented +- `JWT_SECRET` in compose is also a dev-only placeholder with a comment to change it before any real use diff --git a/backend/.env.example b/backend/.env.example index 923bc4a..e69de29 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -1,109 +0,0 @@ -# Backend environment configuration (example) -# -# Copy to `.env` and adjust as needed: -# cp .env.example .env - -# App environment: development | test | production -APP_ENV=development -CONFIG_VERSION=1 - -# Enable hot-reloading when `.env*` files change (ignored in APP_ENV=test) -CONFIG_WATCH=false - -# Server -PORT=3001 -ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 - -# Stellar -STELLAR_NETWORK=testnet -HORIZON_URL=https://horizon-testnet.stellar.org - -# Required when sending non-XLM assets -ASSET_ISSUER= - -# Fee Bump Transactions -# Platform account secret key used to sponsor transaction fees for buyers with low XLM balance -# When set, payments from accounts below FEE_BUMP_THRESHOLD_XLM will be wrapped in a fee bump -PLATFORM_FEE_ACCOUNT_SECRET= -# XLM balance threshold below which fee bumping is applied (default: 2) -FEE_BUMP_THRESHOLD_XLM=2 - -# Payment Streaming -# AES-256-GCM key used to encrypt per-stream sender secrets at rest (required) -# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" -STREAM_SECRET_ENCRYPTION_KEY= - -# Security -JWT_SECRET=change-me - -# WebSocket -# HMAC secret for signing outbound WebSocket message envelopes -# WS_MSG_SECRET=your-strong-random-secret - -# Optional: decrypt ENC(...) values -# CONFIG_ENCRYPTION_KEY=your-strong-key - -PLATFORM_SECRET_KEY= -JWT_SECRET= -ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 -LOG_LEVEL=info -DATABASE_URL=postgresql://user:password@localhost:5432/future_remittance - -# Database Sharding -# Number of shards (default: 1 = no sharding, uses DATABASE_URL) -DB_SHARD_COUNT=1 -# Per-shard connection URLs (DB_SHARD_0_URL falls back to DATABASE_URL) -# DB_SHARD_0_URL=postgresql://user:password@shard0:5432/future_remittance -# DB_SHARD_1_URL=postgresql://user:password@shard1:5432/future_remittance -# Max connections per shard pool -DB_POOL_MAX=10 - -RATE_LIMIT_WINDOW_MS=60000 -RATE_LIMIT_MAX=100 -RATE_LIMIT_MESSAGE="Too many requests, please try again later" -RATE_LIMIT_WHITELIST=192.168.1.1,10.0.0.0/8 - -# Performance Monitoring -# Alert when any API response exceeds this threshold (ms) -PERF_ALERT_RESPONSE_MS=2000 -# Alert when error rate exceeds this fraction (0.1 = 10%) -PERF_ALERT_ERROR_RATE=0.1 -# Optional: New Relic license key (set to enable New Relic APM) -# NEW_RELIC_LICENSE_KEY= -# Optional: DataDog API key (set to enable DataDog APM) -# DD_API_KEY= -LOG_LEVEL=info - -# Email notifications (optional — stub used if not set) -# EMAIL_HOST=smtp.example.com -# EMAIL_PORT=587 -# EMAIL_USER=notifications@example.com -# EMAIL_PASS=your-smtp-password -# EMAIL_FROM=noreply@futureremit.app - -# SMS notifications via Twilio (optional — stub used if not set) -# TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -# TWILIO_AUTH_TOKEN=your-auth-token -# TWILIO_FROM_NUMBER=+10000000000 -# Backup & Recovery -BACKUP_DIR=./backups -# 64-char hex key (openssl rand -hex 32) — required for encrypted backups -# BACKUP_ENC_KEY= -BACKUP_RETENTION_DAYS=7 -BACKUP_INTERVAL_HOURS=24 -# Redis (optional — falls back to in-memory L1 cache if not set) -# REDIS_URL=redis://localhost:6379 - -# CDN Configuration -CDN_ENABLED=false -# CDN_URL=https://cdn.example.com -# CDN_SECONDARY_URL=https://cdn2.example.com -# Comma-separated list of CDN edge regions -CDN_REGIONS=us-east-1,eu-west-1,ap-southeast-1 -# Max-age for immutable static assets (seconds, default 86400 = 1 day) -CDN_CACHE_MAX_AGE_S=86400 - -# Cache TTLs (seconds) -CACHE_TTL_BALANCE_S=30 -RATE_CACHE_TTL_S=60 -CACHE_TTL_FEE_S=120 diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev new file mode 100644 index 0000000..8df1985 --- /dev/null +++ b/backend/Dockerfile.dev @@ -0,0 +1,15 @@ +FROM node:20-alpine + +WORKDIR /app + +# Install dependencies first (cached layer) +COPY package.json package-lock.json* ./ +RUN npm install + +# Copy the rest of the source (overridden by volume mount in dev) +COPY . . + +EXPOSE 3001 + +# Default command — overridden by docker-compose.yml to run migrations first +CMD ["node", "--watch", "src/server.js"] diff --git a/backend/env.example.txt b/backend/env.example.txt new file mode 100644 index 0000000..a58ec0e --- /dev/null +++ b/backend/env.example.txt @@ -0,0 +1,284 @@ +# Backend environment configuration +# +# Copy this file to `.env` and fill in the required values: +# cp env.example.txt .env +# +# Variables marked [REQUIRED] must be set before the server will start. +# Variables marked [OPTIONAL] have safe defaults and can be left unset. + +# --------------------------------------------------------------------------- +# App +# --------------------------------------------------------------------------- + +# Application environment. Controls validation strictness and log verbosity. +# Valid values: development | test | production +# [REQUIRED] +APP_ENV=development + +# Internal config schema version. Do not change unless instructed. +# [OPTIONAL] +CONFIG_VERSION=1 + +# Reload config when .env* files change on disk (ignored when APP_ENV=test). +# Valid values: true | false +# [OPTIONAL] +CONFIG_WATCH=false + +# --------------------------------------------------------------------------- +# Server +# --------------------------------------------------------------------------- + +# Port the Express server listens on. +# [OPTIONAL] Default: 3001 +PORT=3001 + +# Comma-separated list of origins allowed by CORS. +# In production this must be set to your actual frontend domain(s). +# [REQUIRED in production] +ALLOWED_ORIGINS=http://localhost:3000,http://localhost:5173 + +# --------------------------------------------------------------------------- +# Stellar / Blockchain +# --------------------------------------------------------------------------- + +# Stellar network to connect to. +# Valid values: testnet | mainnet +# Use testnet for all local and CI work — never use real keys on testnet. +# [OPTIONAL] Default: testnet +STELLAR_NETWORK=testnet + +# Horizon API base URL matching the network above. +# Testnet: https://horizon-testnet.stellar.org +# Mainnet: https://horizon.stellar.org +# [OPTIONAL] Default: https://horizon-testnet.stellar.org +HORIZON_URL=https://horizon-testnet.stellar.org + +# Issuer account address for non-XLM assets (e.g. USDC). +# Leave blank when only sending XLM. +# Example: ASSET_ISSUER=GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5 +# [OPTIONAL] +ASSET_ISSUER= + +# --------------------------------------------------------------------------- +# Fee Bump Transactions +# --------------------------------------------------------------------------- + +# Stellar secret key of the platform account that sponsors transaction fees +# for buyers whose XLM balance is below FEE_BUMP_THRESHOLD_XLM. +# Generate a dedicated keypair — never reuse your main account key. +# Example: PLATFORM_FEE_ACCOUNT_SECRET=SCZANGBA5RLMPI7JMTP2UX7BASAMACIA3BZAMHBXSIGN5GSQML5YWPI3 +# [OPTIONAL] Fee bumping is disabled when this is unset. +PLATFORM_FEE_ACCOUNT_SECRET= + +# XLM balance threshold (in XLM) below which fee bumping is applied. +# Accounts with a balance below this value will have their fees sponsored. +# Example: FEE_BUMP_THRESHOLD_XLM=2 +# [OPTIONAL] Default: 2 +FEE_BUMP_THRESHOLD_XLM=2 + +# --------------------------------------------------------------------------- +# Payment Streaming +# --------------------------------------------------------------------------- + +# AES-256-GCM key used to encrypt per-stream sender secrets at rest. +# Must be a 64-character hex string (32 bytes). +# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" +# Example: STREAM_SECRET_ENCRYPTION_KEY=a3f1c2e4b5d6789012345678abcdef01a3f1c2e4b5d6789012345678abcdef01 +# [REQUIRED] +STREAM_SECRET_ENCRYPTION_KEY= + +# --------------------------------------------------------------------------- +# Security +# --------------------------------------------------------------------------- + +# Secret used to sign and verify JWT tokens. +# Use a long random string in production (min 32 chars). +# Generate with: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" +# [REQUIRED] +JWT_SECRET=change-me + +# HMAC secret for signing outbound WebSocket message envelopes. +# [OPTIONAL] WebSocket message signing is disabled when unset. +# WS_MSG_SECRET=your-strong-random-secret + +# --------------------------------------------------------------------------- +# Encrypted secrets (optional) +# --------------------------------------------------------------------------- + +# Key used to decrypt ENC(...) values in this file. +# See backend/CONFIGURATION.md for details. +# [OPTIONAL] +# CONFIG_ENCRYPTION_KEY=your-strong-key + +# --------------------------------------------------------------------------- +# Database +# --------------------------------------------------------------------------- + +# PostgreSQL connection string. +# Format: postgresql://:@:/ +# Example: DATABASE_URL=postgresql://future_admin:secret@localhost:5432/future_remittance +# [REQUIRED] +DATABASE_URL=postgresql://user:password@localhost:5432/future_remittance + +# Number of database shards. Set to 1 (default) to disable sharding. +# When > 1, configure per-shard URLs below. +# [OPTIONAL] Default: 1 +DB_SHARD_COUNT=1 + +# Per-shard connection URLs. DB_SHARD_0_URL falls back to DATABASE_URL. +# [OPTIONAL] +# DB_SHARD_0_URL=postgresql://user:password@shard0:5432/future_remittance +# DB_SHARD_1_URL=postgresql://user:password@shard1:5432/future_remittance + +# Maximum connections per shard pool. +# [OPTIONAL] Default: 10 +DB_POOL_MAX=10 + +# --------------------------------------------------------------------------- +# Rate Limiting +# --------------------------------------------------------------------------- + +# Time window for rate limiting in milliseconds. +# [OPTIONAL] Default: 60000 (1 minute) +RATE_LIMIT_WINDOW_MS=60000 + +# Maximum number of requests per IP within the window. +# [OPTIONAL] Default: 100 +RATE_LIMIT_MAX=100 + +# Message returned to clients when rate limit is exceeded. +# [OPTIONAL] +RATE_LIMIT_MESSAGE="Too many requests, please try again later" + +# Comma-separated list of IPs or CIDR ranges exempt from rate limiting. +# [OPTIONAL] +RATE_LIMIT_WHITELIST=192.168.1.1,10.0.0.0/8 + +# --------------------------------------------------------------------------- +# Performance Monitoring +# --------------------------------------------------------------------------- + +# Alert when any API response exceeds this threshold (milliseconds). +# [OPTIONAL] Default: 2000 +PERF_ALERT_RESPONSE_MS=2000 + +# Alert when the error rate exceeds this fraction (0.1 = 10%). +# [OPTIONAL] Default: 0.1 +PERF_ALERT_ERROR_RATE=0.1 + +# New Relic license key. Set to enable New Relic APM. +# [OPTIONAL] +# NEW_RELIC_LICENSE_KEY= + +# DataDog API key. Set to enable DataDog APM. +# [OPTIONAL] +# DD_API_KEY= + +# Log level for the Winston logger. +# Valid values: error | warn | info | http | verbose | debug | silly +# [OPTIONAL] Default: info +LOG_LEVEL=info + +# --------------------------------------------------------------------------- +# CoinGecko (exchange rates) +# --------------------------------------------------------------------------- + +# CoinGecko API key for fetching live exchange rates. +# Free tier works without a key but is heavily rate-limited. +# Get a key at https://www.coingecko.com/en/api +# Example: COINGECKO_API_KEY=CG-xxxxxxxxxxxxxxxxxxxxxxxxxxxx +# [OPTIONAL] Falls back to public unauthenticated API when unset. +COINGECKO_API_KEY= + +# --------------------------------------------------------------------------- +# Email notifications (optional) +# --------------------------------------------------------------------------- + +# SMTP connection details. When unset, a stub logger is used instead. +# [OPTIONAL] +# EMAIL_HOST=smtp.example.com +# EMAIL_PORT=587 +# EMAIL_USER=notifications@example.com +# EMAIL_PASS=your-smtp-password +# EMAIL_FROM=noreply@futureremit.app + +# --------------------------------------------------------------------------- +# SMS notifications via Twilio (optional) +# --------------------------------------------------------------------------- + +# Twilio credentials. When unset, a stub logger is used instead. +# [OPTIONAL] +# TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +# TWILIO_AUTH_TOKEN=your-auth-token +# TWILIO_FROM_NUMBER=+10000000000 + +# --------------------------------------------------------------------------- +# Backup & Recovery +# --------------------------------------------------------------------------- + +# Directory where backup files are written. +# [OPTIONAL] Default: ./backups +BACKUP_DIR=./backups + +# 64-character hex key for encrypting backups at rest. +# Generate with: openssl rand -hex 32 +# [OPTIONAL] Backups are unencrypted when unset. +# BACKUP_ENC_KEY= + +# Number of days to retain backup files before deletion. +# [OPTIONAL] Default: 7 +BACKUP_RETENTION_DAYS=7 + +# How often to create automatic backups (hours). +# [OPTIONAL] Default: 24 +BACKUP_INTERVAL_HOURS=24 + +# --------------------------------------------------------------------------- +# Redis (optional) +# --------------------------------------------------------------------------- + +# Redis connection URL. When unset, the cache falls back to an in-memory L1 cache. +# Example: REDIS_URL=redis://localhost:6379 +# [OPTIONAL] +# REDIS_URL=redis://localhost:6379 + +# --------------------------------------------------------------------------- +# CDN (optional) +# --------------------------------------------------------------------------- + +# Enable CDN asset serving. +# Valid values: true | false +# [OPTIONAL] Default: false +CDN_ENABLED=false + +# Primary CDN base URL. +# [OPTIONAL] +# CDN_URL=https://cdn.example.com + +# Secondary/failover CDN URL. +# [OPTIONAL] +# CDN_SECONDARY_URL=https://cdn2.example.com + +# Comma-separated list of CDN edge regions for cache invalidation. +# [OPTIONAL] +CDN_REGIONS=us-east-1,eu-west-1,ap-southeast-1 + +# Max-age for immutable static assets in seconds. +# [OPTIONAL] Default: 86400 (1 day) +CDN_CACHE_MAX_AGE_S=86400 + +# --------------------------------------------------------------------------- +# Cache TTLs +# --------------------------------------------------------------------------- + +# How long to cache account balance responses (seconds). +# [OPTIONAL] Default: 30 +CACHE_TTL_BALANCE_S=30 + +# How long to cache exchange rate responses (seconds). +# [OPTIONAL] Default: 60 +RATE_CACHE_TTL_S=60 + +# How long to cache fee estimates (seconds). +# [OPTIONAL] Default: 120 +CACHE_TTL_FEE_S=120 diff --git a/docker-compose.txt b/docker-compose.txt new file mode 100644 index 0000000..d1d24ae --- /dev/null +++ b/docker-compose.txt @@ -0,0 +1,87 @@ +version: '3.8' + +services: + # --------------------------------------------------------------------------- + # PostgreSQL 16 — persistent local database + # --------------------------------------------------------------------------- + db: + image: postgres:16-alpine + restart: unless-stopped + environment: + POSTGRES_DB: future_remittance + POSTGRES_USER: future_admin + POSTGRES_PASSWORD: dev_password + ports: + - '5432:5432' + volumes: + - db_data:/var/lib/postgresql/data + healthcheck: + test: ['CMD-SHELL', 'pg_isready -U future_admin -d future_remittance'] + interval: 5s + timeout: 5s + retries: 10 + + # --------------------------------------------------------------------------- + # Backend — Node.js + Express with hot-reload (node --watch) + # --------------------------------------------------------------------------- + backend: + build: + context: ./backend + dockerfile: Dockerfile.dev + restart: unless-stopped + depends_on: + db: + condition: service_healthy + ports: + - '3001:3001' + volumes: + # Mount source for hot-reload; exclude node_modules from the bind mount + - ./backend/src:/app/src + - ./backend/prisma:/app/prisma + environment: + APP_ENV: development + PORT: 3001 + DATABASE_URL: postgresql://future_admin:dev_password@db:5432/future_remittance + STELLAR_NETWORK: testnet + HORIZON_URL: https://horizon-testnet.stellar.org + ALLOWED_ORIGINS: http://localhost:3000 + # Change these before committing any real data + JWT_SECRET: dev-jwt-secret-change-me + STREAM_SECRET_ENCRYPTION_KEY: "0000000000000000000000000000000000000000000000000000000000000000" + FEE_BUMP_THRESHOLD_XLM: "2" + LOG_LEVEL: info + CACHE_TTL_BALANCE_S: "30" + RATE_CACHE_TTL_S: "60" + CACHE_TTL_FEE_S: "120" + RATE_LIMIT_WINDOW_MS: "60000" + RATE_LIMIT_MAX: "100" + DB_POOL_MAX: "10" + DB_SHARD_COUNT: "1" + BACKUP_DIR: ./backups + BACKUP_RETENTION_DAYS: "7" + BACKUP_INTERVAL_HOURS: "24" + CDN_ENABLED: "false" + command: sh -c "npx prisma migrate deploy && node --watch src/server.js" + + # --------------------------------------------------------------------------- + # Frontend — Vite dev server with HMR + # --------------------------------------------------------------------------- + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.dev + restart: unless-stopped + depends_on: + - backend + ports: + - '3000:3000' + volumes: + - ./frontend/src:/app/src + - ./frontend/public:/app/public + - ./frontend/index.html:/app/index.html + environment: + # Proxy /api -> backend is configured in vite.config.js + VITE_API_URL: http://localhost:3001 + +volumes: + db_data: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e69de29 diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev new file mode 100644 index 0000000..65766cd --- /dev/null +++ b/frontend/Dockerfile.dev @@ -0,0 +1,14 @@ +FROM node:20-alpine + +WORKDIR /app + +# Install dependencies first (cached layer) +COPY package.json package-lock.json* ./ +RUN npm install + +# Copy source (overridden by volume mount in dev) +COPY . . + +EXPOSE 3000 + +CMD ["npm", "run", "dev", "--", "--host", "0.0.0.0"]