A web-based marketplace for trading in-game items and virtual currencies between players. Built to replace the informal Discord and forum-based trading that most gaming communities rely on, with structured listings, real-time messaging, user reputation, and built-in moderation tools.
Live at market.bobbynooby.dev
- Flexible listings - Buy/sell orders with multi-item offer structures. Request a single item or currency, offer multiple of either in exchange
- Real-time messaging - WebSocket-powered direct messages with typing indicators and unread tracking
- Trust system - Upvote/downvote reviews on user profiles with computed trust scores based on review ratio, account age, and trade history
- Search and discovery - Text search, trending listings, popular items, and a personal watchlist
- Admin dashboard - Item catalogue management, bulk JSON import/export, user moderation (bans, warnings), report handling, audit logging, insights, and site branding configuration
- RBAC - Four default roles and thirty-four granular permissions
- Internationalisation - Four languages (English, Spanish, French, Japanese) via compile-time translation with zero runtime overhead
- Image pipeline - Automatic resize to 512px and WebP conversion via Sharp
- Open Graph previews - Rich preview cards for shared listing links, rendered with Satori
- GDPR compliant - Self-service data export and account deletion with cascading removal
- Real-time listing feed - New listings broadcast to connected clients via WebSocket
- Frontend - SvelteKit 2 + Svelte 5 (runes), Tailwind CSS v4, shadcn-svelte
- Backend - Elysia on Bun, better-auth with Discord OAuth
- Database - PostgreSQL 17 with Drizzle ORM
- Monorepo - pnpm workspaces + Turborepo
- Deployment - Docker Compose
- Testing - Playwright E2E + GitHub Actions CI
Three-container deployment: PostgreSQL, Elysia API server (Bun), and SvelteKit frontend (Node). End-to-end type safety flows from Drizzle schemas through Elysia route handlers to Svelte components via Eden Treaty.
packages/
├── frontend/ # SvelteKit application (port 5173 dev / 3001 prod)
├── server/ # Elysia API (port 3000)
└── lib/ # Shared TypeScript types
scripts/ # Docker + deployment helper scripts
# 1. Copy the example environment and fill in your values
cp .env.example .env
# 2. One-command setup: install deps, push schema, seed RBAC
pnpm setup
# 3. Run both frontend and server in watch mode
pnpm devTo also populate a demo catalogue and seed users/listings:
pnpm setup:demo| Script | What it does |
|---|---|
pnpm dev |
Run frontend and server together via Turborepo |
pnpm dev:frontend |
Frontend only (Vite on :5173) |
pnpm dev:server |
Server only (Bun on :3000, with watch mode) |
pnpm check |
Type-check all packages (svelte-check + tsc) |
pnpm build |
Production build of all packages |
| Script | What it does |
|---|---|
pnpm db:push |
Push Drizzle schema to the database (additive only) |
pnpm db:studio |
Open Drizzle Studio GUI for browsing data |
pnpm --filter @openmarket/server db:generate |
Generate SQL migration files |
pnpm --filter @openmarket/server db:seed:rbac |
Seed the 4 roles and 34 permissions |
pnpm --filter @openmarket/server db:seed |
Seed full demo data (users, items, listings, reviews) |
| Script | What it does |
|---|---|
pnpm test |
Run Playwright E2E tests |
pnpm test:ui |
Run Playwright with the interactive UI |
| Script | What it does |
|---|---|
pnpm docker:init |
First-time Docker setup |
pnpm docker:start |
Start the Docker Compose stack |
pnpm docker:stop |
Stop all containers |
pnpm docker:reset |
Reset the stack (wipes volumes) |
pnpm docker:logs |
Tail logs from all services |
pnpm deploy |
Production deployment script |
A simulator script is included for generating realistic activity (useful for demos and load testing). It creates listings, views, reviews, and watchlist changes at a configurable rate.
# From the server package directory
pnpm --filter @openmarket/server simulate -- --tick=2000 --max=300Flags:
--tick=<ms>- Interval between actions (default 2000)--max=<n>- Total actions before stopping
The project is designed for single-command Docker deployment:
docker compose up -dThe server container runs a start.ts script on launch that handles:
- Database schema migration (
drizzle-kit push, additive only) - RBAC seeding (idempotent, uses
onConflictDoNothing) - Starter catalogue population (only if items table is empty)
Subsequent deploys skip seeding since the Postgres volume persists across rebuilds. See .env.example for required environment variables.
For Coolify or similar platforms, set the compose file location to /docker-compose.yml and provide the env vars listed in .env.example.
- Svelte 5 runes -
$state,$derived,$effect,$props,$bindable - shadcn-svelte components preferred over custom implementations
- Drizzle query builder over raw SQL (the
sqltemplate tag is fine for aggregates) - Optimistic UI - Uses local
$statefor status updates rather thaninvalidateAll() - Eden Treaty for server-side API calls in
+page.server.ts - Direct fetch for client-side calls (avoids Eden type gymnastics)
- AlertDialog for destructive confirmations, regular Dialog for everything else
- Paraglide for all user-facing strings (no hardcoded English in templates)
Run from packages/frontend:
npx shadcn-svelte@latest add <component> -yNot npx shadcn - that installs React components.
When running locally, Swagger UI is available at http://localhost:3000/docs listing every endpoint with request/response schemas.