Skip to content

Predictify-org/predictify-backend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

218 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

predictify-backend

CI

Backend API for Predictify — a Stellar/Soroban prediction-markets dApp.

This service indexes on-chain market state from the Predictify Soroban contract, exposes a REST API for the frontend, handles wallet-based authentication, and ships notifications + leaderboards.

Stack

  • Node.js 20 + TypeScript
  • Express for HTTP
  • Drizzle ORM + PostgreSQL for persistence
  • BullMQ + Redis for async job queues (webhook delivery, backup verification, reconciliation, market resolution)
  • zod for env + request validation
  • pino for structured logging
  • JWT (jsonwebtoken) for wallet-based session auth
  • Stellar SDK for Soroban RPC + Horizon
  • Jest + supertest for tests

Quick start

cp .env.example .env        # copy the template
# Edit .env — set JWT_SECRET, DATABASE_URL, and PREDICTIFY_CONTRACT_ID
# (all other keys have working testnet defaults)

npm install
npm run check-env           # validate .env before touching the DB
npm run db:migrate
npm run dev                  # predev hook re-runs check-env automatically

Once running:

Request body size limits

  • Default JSON request body limit: 256kb.
  • Route-level overrides are applied in middleware using createBodyLimitMiddleware(...).
  • Webhook routes may opt into a larger limit of 1mb.
  • Requests exceeding the configured limit return HTTP 413 with the standard error envelope, including correlation and request IDs.

Indexer gap scan

The gap-scan worker detects missing ledger ranges in indexer_events between the durable cursor and chain tip, emits indexer_gap_detected_total{from,to}, and self-heals via backfillRange:

npm run indexer:gap-scan

Configure via INDEXER_GAP_SCAN_INTERVAL_MS, INDEXER_REWIND_LEDGERS, and INDEXER_BACKFILL_CHUNK_SIZE in .env.

Job Queue (BullMQ + Redis)

Slow and IO-bound jobs (webhook delivery, backup verification, market resolution, reconciliation) run in BullMQ workers backed by Redis. See docs/job-queue.md for the full design, enqueue patterns, environment variables, and testing guide.

Quick start:

docker run -p 6379:6379 redis:7-alpine   # local Redis
# Set REDIS_URL=redis://localhost:6379 in .env
npm run dev

Layout

src/
  config/      env + logger
  routes/      health, markets (more to come)
  services/    domain services
  workers/     indexer gap scan worker
  metrics/     in-process counters (indexer_gap_detected_total)
  middleware/  errorHandler, auth (planned)
  workers/     long-running processes (Soroban indexer)
  db/          drizzle schema, client, repositories
tests/         jest tests
drizzle/       generated migrations + meta
scripts/       dev helpers (check-drizzle-drift.ts)
.github/
  workflows/   CI pipeline (lint, test, drift check, migrate)

Roadmap

This starter is intentionally minimal. The full backlog is tracked in GitHub Issues under the OFFICIAL CAMPAIGN label. Major themes:

  • Wallet-based auth (Stellar address challenge/signature → JWT)
  • Market CRUD + caching layer
  • Soroban-RPC indexer with reorg/gap handling
  • Predictions + claims endpoints
  • Leaderboards & user profiles
  • Webhook delivery + DLQ
  • Observability (metrics, tracing, /readyz with deep checks)
  • OpenAPI spec + contract tests

Auth Refresh Flow

  • POST /api/auth/refresh accepts { "refreshToken": "<opaque token>" }, revokes the presented refresh token, and returns a fresh accessToken plus a rotated refreshToken.
  • Refresh tokens are stored only as SHA-256 hashes in the refresh_tokens table. The raw bearer token is generated once and is never persisted.
  • If a revoked refresh token is presented again, the service treats it as suspected theft and revokes every still-active token in the same familyId.
  • POST /api/auth/logout accepts the same body and revokes the remaining active tokens in that refresh-token family.

Testing

Unit Tests

Run the unit test suite:

npm test              # Run all tests
npm run test:unit     # Run unit tests only (excludes E2E)
npm run test:coverage # Run with coverage report

E2E Tests

End-to-end tests validate the complete prediction lifecycle on Stellar testnet:

npm run test:e2e           # Run E2E tests
npm run test:e2e:coverage  # Run E2E tests with coverage

E2E tests validate:

  • User authentication with wallet signatures
  • Market creation on testnet
  • Placing predictions
  • Market resolution
  • Claiming winnings
  • Data consistency across the lifecycle

Setup required:

  • Funded Stellar testnet account
  • Deployed Predictify contract on testnet
  • Test database (separate from dev/prod)

See tests/e2e/README.md and docs/e2e-testing.md for detailed setup and usage.

CI/CD:

  • E2E tests run nightly at 2 AM UTC
  • Manual trigger via GitHub Actions
  • Automatic issue creation on failure

Refresh Token Tests

npm test -- tests/refreshToken.test.ts

The refresh-token test suite covers rotation, expiry handling, reuse detection, logout family revocation, and hash-only storage.

Social graph

Follow graph mutations are exposed at:

  • POST /api/users/:addr/follow
  • DELETE /api/users/:addr/follow

These endpoints require authentication, enforce users.is_private, update cached followers_count and following_count values transactionally, and write structured audit entries with the request correlation ID.

Run with Docker

You can spin up the entire Predictify stack (API, Indexer, and PostgreSQL) using Docker Compose.

Prerequisites

  • Docker installed and running.
  • A local .env file generated from .env.example. Ensure DATABASE_URL is set to postgres://postgres:postgres@db:5432/predictify for Docker compatibility.

Commands

  1. Start the stack:

    docker compose up --build
  2. Verify the services: Once booted, the API will be available at http://localhost:3001. Check the health endpoint:

    curl localhost:3001/health
    # Expected response: 200 OK

Notes

  • The migrate service runs automatically on startup to ensure the database schema is up-to-date before the API and Indexer start.

  • The indexer service runs as a persistent container; check the logs with docker compose logs -f indexer if you encounter sync issues.

Implementation Notes for Review

  • Performance: Multi-stage builds reduce the final image size by excluding source code and dev dependencies.
  • Security: By using USER node and slim base images, we reduce the attack surface.
  • Resilience: The depends_on condition using service_healthy or service_completed_successfully ensures the database is ready and migrations are applied before application services boot, preventing race conditions.
  • Supply-Chain: The base image is pinned by a specific digest. Important: When you run this, verify the digest matches your local build requirements, or update it to the latest node:20-bookworm-slim digest if you prefer the absolute latest patch version.

License

MIT

About

No description, website, or topics provided.

Resources

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages