# CaseOrion CS-style case opening site with virtual credits, inventory, multiplayer item betting, shop, leaderboard, and an admin panel for cases / drops / rates. ## Stack - **Server**: Node.js, Express, Prisma, SQLite, express-session, Socket.IO - **Client**: React (Vite), React Router, Socket.IO client ## Setup ```bash npm run install:all npm run db:push npm run db:seed npm run dev ``` - App: http://localhost:5888 - API: http://localhost:3001 - LAN: http://\:5888 (Vite binds to `0.0.0.0`) Create `server/.env` if missing: ``` DATABASE_URL="file:./dev.db" PORT=3001 CLIENT_ORIGIN=http://localhost:5888 SESSION_SECRET=dev-secret STARTING_BALANCE=10000 STARTING_OSU=100 JACKPOT_ROUND_SECONDS=30 JACKPOT_MAX_ITEMS=3 ``` ## Demo accounts | Role | Username | Password | |-------|----------|------------| | Admin | `admin` | `admin123` | | Player| `player` | `player123`| Local seed defaults; override admin via `ADMIN_USERNAME` / `ADMIN_PASSWORD`. In Docker, those env vars in `.env.prod` are required and applied on every start. New registrations receive **100.00** starting credits and **100 osu** (`STARTING_BALANCE` / `STARTING_OSU` in `server/.env`; credits stored in cents). ## Features - Login / register (username + password) - **Inventory**: sort by recent or value, multi-select sell at **instance** value (float/wear), bulk **Sell below** threshold, **favorites** (★ — cannot be sold or auto-sold; can still be bet in battles), **Vault** staking (up to **3** slots by default; +1 per Vault Capacity skill) — shared timer; dividends auto-pay **every 5 min online** (10% value) / **10 min offline** (1%), scaled by float² wear and rarity (cap **500%** value / tick) - **Auto-sell**: optional threshold (shared with bulk sell); on **case opens**, credits are applied after the reel finishes; on **battle wins**, items taken from other players below the threshold are sold immediately - **Wear (float)**: each case drop gets a random float `0–1` and paint seed; grades FN / MW / FT / WW / BS (CS ranges). Catalog `marketValue` is the **Field-Tested** reference; instance `valueCents` scales by float within the wear band (FN up to +50%, MW +15–30%, FT mid ≈ catalog, WW down to −40%, BS lower) - **Feed Drop**: toggle in nav — live left column of latest case unboxes + MultiPlayers Battle winners - Cases page: browse and open crates (CS-style reel, result rolled server-side) - **Case keys** (per crate): **1 key** → **x10**; each **100 opens** of that case earns +1 key up to **x20** (keys 1→11, +1 open per key: x11…x20); from **x20** onward each next key costs **200, 300, 400…** opens (100 × tier) and adds +1 max open (x21, x22…) — no cap - **Battle** hub (`/battle`): - Create / join MultiPlayers pots — live shared pot, up to 3 items, win chance by value, Socket.IO sync - Spectate in-progress battles from the hub - **Shop** (`/shop`): buy **osu** with real money (mock checkout in dev, Stripe-ready), exchange osu for credit packs, or watch an ad every 1 min for 100.00–200.00 cr - Profile (username in nav): avatar, bio, password, stats, logout - **Presence**: online status (Socket.IO), total play time, last connection — shown on own and public profiles - **Achievements** (`/achievements`, also from profile): starter badges unlocked by play (cases, battles, catalog, prestige, shop, playtime); earned badges appear on the profile - **Prestige**: pay **1 000 000 000 000.00** cr to reset; earn **Pr** (1000 base + ~1000 per **250 000 000 000.00** cr excess); spend Pr on a skill tree (Fortune, Yield, Polish, Bulk, PR Farmer, Quality, noTime, Refund, Respin, Vault Capacity, Offline/Online Yield) - **Leaderboard**: richest players (balance + inventory value); click a row to open a public **player profile** (`/player/:username`) with stats and inventory - Admin: overview dashboard, players (credit adjust), cases / items / drops (split pages, search filters) ## Docker (production) ```bash # Build & push to Docker Hub (foufure/casegambling) docker login ./scripts/docker-build-push.sh # optional version tag: ./scripts/docker-build-push.sh v1.0.0 # Run cp .env.prod.example .env.prod # edit secrets / CLIENT_ORIGIN / ADMIN_* docker compose --env-file .env.prod up -d ``` Set **`ADMIN_USERNAME`** and **`ADMIN_PASSWORD`** in `.env.prod` (required). On each container start the admin account is created or updated from these values. App listens on `HOST_PORT` (default **3009** → container 3001). Data: volume `casegambling_data` (SQLite), uploads: `casegambling_uploads`. ### Backup / restore **On the Docker prod server** (export DB to bring home): ```bash # Copy these two scripts onto the server next to docker-compose.yml, then: ./scripts/docker-db-export.sh # → backups/casegambling-… (+ .tar.gz) # Optional: restore a backup into Docker ./scripts/docker-db-import.sh ./backups/casegambling-YYYYMMDD-HHMMSS.tar.gz ``` **On your laptop** (local `server/prisma/dev.db`): ```bash # Import the archive you copied from prod (stop `npm run dev` first) ./scripts/local-db-import.sh ./backups/casegambling-YYYYMMDD-HHMMSS.tar.gz npm run db:push # sync schema if needed npm run dev # Export local DB (e.g. to push back to Docker) ./scripts/local-db-export.sh ./scripts/docker-db-import.sh ./backups/casegambling-….tar.gz ``` Typical **prod → local** flow: `docker-db-export.sh` on the server → `scp` the `.tar.gz` → `local-db-import.sh` on the laptop. ## Notes - Balances and prices are integers in **cents**; **osu** is the premium (real-money) currency — top-ups create `PaymentOrder` rows (`PAYMENTS_PROVIDER=mock|stripe`) - Catalog item price = Field-Tested reference; each inventory row stores `floatValue`, `wear`, `paintSeed`, `valueCents`, `favorite`, optional vault `staked` / `stakedAt` / `lastYieldAt` - Per-user auto-sell settings: `autoSellEnabled`, `autoSellThresholdCents` (default threshold **10.00** cr) - Case key progress stored in `CaseKeyProgress` (per user + case); backfill: `npm run db:backfill-case-keys --prefix server` - Inventory items locked while deposited in a bet; unlocked or transferred on resolve; staked vault items cannot be sold, auto-sold, or bet - Avatars / uploads under `server/uploads/` (volume in Docker) - Real-time via Socket.IO; Docker Hub image name is lowercase: `foufure/casegambling` - Max **2** simultaneous browser windows/tabs per account (Socket.IO); override with `MAX_SOCKETS_PER_USER` - Swap `DATABASE_URL` to PostgreSQL later without changing app logic