Skip to content

mitekk/lunaland

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

9 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Lunaland Casino logo

A provably-fair social/sweepstakes slot β€” play Β· earn Β· progress Β· redeem

CI License: MIT Node Next.js NestJS PostgreSQL TypeScript

A minimal but production-shaped casino proof of concept: one polished slot machine with verifiable fair outcomes, a dual virtual-currency economy, VIP progression, daily login bonuses, and redemption tracking β€” wired end-to-end across web, API, and database.

⚠️ Not a real-money product. No payments, no purchases, no KYC. Sweeps redemptions are tracked through a status lifecycle, never paid out.



🎰 For players

What it is

Lunaland is a social / sweepstakes casino: you play real casino-style games with virtual currency β€” no purchase, ever. Wins in the promotional currency (Sweeps Coins) can build up to a redemption. This POC proves the whole engagement loop β€” play β†’ earn β†’ progress β†’ redeem β€” around one polished slot game with provably-fair outcomes.

How to play

  1. Register β€” you start with 10,000 Luna Coins πŸŒ™ + 2.00 Sweeps Coins πŸ’Ž at Bronze tier.
  2. Spin β€” pick a currency and a bet, then spin a 3Γ—3 machine with 5 paylines. Wins update your balance and earn XP.
  3. Come back daily β€” claim a daily bonus; consecutive days grow your streak for bigger rewards.
  4. Climb the ranks β€” wagering earns XP and moves you up the VIP tiers, unlocking larger bonuses.
  5. Redeem β€” once you hold β‰₯ 50.00 Sweeps Coins, request a redemption (gift card / bank / PayPal) and track its status.

Features

🎰 Provably-fair slots Classic 3Γ—3 machine, 5 paylines (3 lines + 2 diagonals), 6 symbols + a Luna scatter. Every spin is server-computed and independently verifiable. Target RTP β‰ˆ 94%.
πŸŒ™ πŸ’Ž Dual currency Luna Coins for free play and Sweeps Coins as the redeemable promotional currency (1 SC β‰ˆ $1, redeemable portion only). Exact integer accounting β€” never floats.
πŸ† VIP progression Earn XP by wagering and climb Bronze β†’ Silver β†’ Gold β†’ Platinum β†’ Diamond. Higher tiers grant larger daily bonuses and an XP boost.
🎁 Daily login bonus One claim per day of Luna + Sweeps, scaled by tier and a consecutive-day streak. Idempotent β€” re-claims are cleanly rejected.
πŸ’Έ Redemption tracking At β‰₯ 50 SC redeemable, submit a redemption; Sweeps are held and the request enters the PENDING β†’ APPROVED/REJECTED β†’ PAID lifecycle.

πŸ› οΈ For developers

Quick start

cp .env.example .env        # or: make env
make up                     # build + start db, api, web (with hot reload)
Service URL Notes
Web (Next.js) http://localhost:3000 the app
API (NestJS) http://localhost:4000 health check at /health
Postgres localhost:5432 migrated + seeded on first boot

Requirements: Docker + Docker Compose. make up is the supported path; Node is pinned to 24.16.0 (.nvmrc) for any host-side tooling.

Architecture

The browser only ever calls the same-origin /api/*; the Next.js server proxies those requests to the NestJS API (API_PROXY_TARGET). No CORS, and no per-environment URL baked into the client β€” the same image serves make up and make test.

flowchart LR
  UI["Browser Β· React UI"]
  Next["Next.js web :3000<br/>App Router + /api proxy"]
  Nest["NestJS api :4000<br/>auth Β· wallet Β· slot<br/>vip Β· bonus Β· redemption"]
  PG[("Postgres 16 :5432<br/>Drizzle ORM")]
  Shared[["@casino/shared<br/>BE↔FE type contract"]]
  UI -- "same-origin /api/*" --> Next
  Next -- "server-side proxy" --> Nest
  Nest -- "SQL" --> PG
  Shared -. "types" .-> Next
  Shared -. "types" .-> Nest
Loading

A contract-first monorepo (see ADR-0007): both apps build against the frozen @casino/shared types β€” plus docs/api-contract.md and docs/db-schema.md β€” so the tracks below develop in parallel against disjoint directories.

.
β”œβ”€β”€ backend/             # NestJS API β€” auth Β· wallet Β· slot Β· vip Β· bonus Β· redemption
β”‚   └── src/db/          #   Drizzle schema, migrations, seed
β”œβ”€β”€ frontend/            # Next.js App Router UI (Tailwind v4 "Luna" theme) + /api proxy
β”œβ”€β”€ packages/shared/     # @casino/shared β€” the BE↔FE type contract
β”œβ”€β”€ docs/                # PRD, ADRs (0001–0008), API & DB contracts, CI notes
β”œβ”€β”€ infra/               # Postgres init scripts
β”œβ”€β”€ tests/               # Playwright integration + e2e (run against the containers)
β”œβ”€β”€ docker-compose*.yml  # base Β· override (dev) Β· test Β· ci
└── Makefile             # task runner (see below)

Tech stack

Layer Tech
Frontend Next.js 16 (App Router) Β· React 19 Β· Tailwind CSS v4 Β· TypeScript
Backend NestJS 11 Β· TypeScript
Data PostgreSQL 16 Β· Drizzle ORM
Shared @casino/shared workspace package (the BE↔FE type contract)
Infra Docker Compose Β· Makefile Β· Node 24.16
Tests Jest (unit) Β· Playwright (integration + e2e, run against the containers)

Make targets

Target What it does
make up Build + start db/api/web with hot reload
make down Β· make clean Stop Β· stop and wipe the db volume
make logs Β· make ps Tail logs Β· list containers
make migrate Β· make seed Apply Drizzle migrations Β· seed VIP tiers + demo user
make psql Open a psql shell on the db
make test Β· make test-be Full containerized suite Β· backend unit tests
make lint Β· make typecheck Lint workspaces Β· TypeScript check
make audit Fail on high/critical CVEs
make help List every target

Provably-fair spins

Every spin is server-authoritative and verifiable. A keyed byte stream β€” HMAC_SHA256(serverSeed, "<clientSeed>:<nonce>") β€” drives weighted reel strips via rejection sampling (no modulo bias). Each spin stores its seeds, so the grid can be recomputed and checked after the fact. The engine targets RTP β‰ˆ 94%, asserted by a 200k-spin statistical test. See ADR-0004.

Money & safety

Balances are integer minor units (no floats). Every change runs inside a DB transaction with row locking and appends to an immutable ledger, so balances always reconcile and never go negative. Security basics: bcrypt password hashing, JWT auth, request validation, rate limiting, Helmet headers, a CORS allowlist, parameterized queries, and no secrets in source.

Testing

make test       # boots fresh db/api/web containers and runs the full suite against them
make test-be    # backend unit tests in-process (engine determinism + 200k-spin RTP)

make test brings up an ephemeral stack (docker-compose.test.yml) and runs a Playwright runner against the containers: API integration specs hit the api container, and a browser e2e drives the full journey β€” register β†’ spin β†’ daily bonus β†’ redemption gating β†’ error path β€” on the web container.

Continuous integration

GitHub Actions (.github/workflows/ci.yml) runs on every push and pull request in two stages:

  1. static β€” npm ci, build shared β†’ backend β†’ frontend, then lint, typecheck, the backend Jest suite (engine determinism + RTP), and an npm audit gate.
  2. e2e β€” builds the images and runs the containerized Playwright suite (docker compose … up against db/api/web), uploading the Playwright report as an artifact.

Both npm and Docker layers are cached (GitHub Actions cache) to keep runs fast. Details in docs/ci.md.

Configuration

All configuration is via environment variables β€” copy .env.example to .env. Never commit .env.

Variable Purpose
DATABASE_URL Postgres connection string used by the API
POSTGRES_USER Β· POSTGRES_PASSWORD Β· POSTGRES_DB Database credentials
JWT_SECRET Auth signing key β€” generate with openssl rand -hex 32
JWT_EXPIRES_IN Β· BCRYPT_ROUNDS Token lifetime Β· password-hashing cost
WEB_ORIGIN CORS allowlist (the web origin permitted to call the API)
API_PROXY_TARGET Where Next proxies /api/* server-side (the API address)
THROTTLE_TTL Β· THROTTLE_LIMIT Rate-limit window Β· max requests per window
API_PORT Β· WEB_PORT Β· POSTGRES_PORT Service ports (4000 Β· 3000 Β· 5432)

The backend validates required vars at boot (DATABASE_URL, JWT_SECRET) and fails fast with a clear message if any are missing.

Docs & decisions


Built as a proof of concept Β· MIT licensed Β· not a real-money product

About

πŸŒ™ Lunaland β€” a production-shaped social/sweepstakes casino PoC: provably-fair slots, a dual virtual-currency economy (Luna + Sweeps Coins), VIP tiers & daily bonuses.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors