Live card price dashboard for 10 TCG games (Pokémon, MTG, Yu-Gi-Oh, One Piece, Lorcana, Digimon, Dragon Ball, Gundam, Star Wars Unlimited, Riftbound). Browse prices from PriceCharting, manage collections, and purchase cards via Stripe checkout.
- Live card prices — Ungraded / Grade 9 / PSA 10
- Smart filtering — Sort by price, name, or grading; real-time search
- Visual highlighting — Gold $100+, orange PSA 10 $500+
- User accounts — Email/password authentication
- Collections — Create multiple named wishlists (persistent, server-backed)
- Shopping cart — Add any card to cart, manage quantities
- Checkout — Stripe payment processing with webhook confirmation
- Admin inventory — Dashboard to manage store stock and pricing
- Dark/light mode — Theme toggle
- Responsive design — Horizontal game bar, mobile-friendly
- Node.js v14+
- PostgreSQL v12+
- npm (bundled with Node.js)
git clone <repository>
cd tcg-price-tracker
npm installCopy .env.example to .env and update with your values:
cp .env.example .envThen edit .env:
# Database (PostgreSQL)
DATABASE_URL=postgresql://user:password@localhost:5432/tcg_tracker
# JWT secret for auth tokens (generate a random string)
JWT_SECRET=your_random_jwt_secret_key_here
# Stripe test keys from https://dashboard.stripe.com/test/apikeys
STRIPE_SECRET_KEY=sk_test_...
# Admin API key (for managing inventory via CLI/API)
ADMIN_API_KEY=your_random_admin_api_key
# Server port
PORT=3847
NODE_ENV=developmentCreate PostgreSQL database:
createdb tcg_trackerRun migrations:
node db/migrate.jsThis will:
- Create all tables (users, collections, inventory, cart_items, orders)
- Add
is_admincolumn to users - Mark the first user as admin
Optional: Load SQL directly:
psql -U postgres -d tcg_tracker -f db/schema.sqlnode server/server.jsServer runs on http://localhost:3847
The TCG Price Tracker can also run as a standalone desktop application on Windows and macOS.
- Rust — Install from rustup.rs
- On Windows: Install MSVC toolchain (default) and Visual Studio 2022 Build Tools with "C++ build tools" workload
- Node.js v14+ (same as web version)
- PostgreSQL v12+ (running locally — required for the desktop app)
# Install Tauri and caxa
npm install --save-dev @tauri-apps/cli@^2 caxa
# Build for Windows
npm run tauri:build
# Or build and run in dev mode
npm run tauri:devBuild outputs:
- Windows:
src-tauri/target/release/bundle/nsis/TCG Price Tracker_1.0.0_x64-setup.exe - macOS:
src-tauri/target/release/bundle/dmg/TCG Price Tracker_1.0.0_arm64.dmg(Apple Silicon) andx86_64.dmg(Intel)
-
First run: The desktop app will:
- Start the Node.js server (sidecar) automatically
- Load your
.envfile from the project root - Wait for the server to be ready
- Display the app window when ready
-
Closing the app: When you close the window, all server processes are automatically terminated
-
Configuration: The app uses the
.envfile from your project directory. Make sure it contains:DATABASE_URL=postgresql://user:password@localhost:5432/tcg_tracker JWT_SECRET=your_jwt_secret STRIPE_SECRET_KEY=sk_test_... ADMIN_API_KEY=your_admin_key PORT=3847
- Windows 10/11: Full support via NSIS installer
- macOS 10.13+: Full support via DMG installer (both Intel and Apple Silicon)
- Note: First install may require clicking "Open" in Security preferences
App won't start / "Start the proxy server" error
- Ensure PostgreSQL is running
- Check
.envfile has correctDATABASE_URL - Check port 3847 is available (not in use)
Process still running after close
- Desktop app automatically kills all server processes on close
- If a process persists, manually kill:
taskkill /F /IM server-sidecar.exe
Reinstaller fails with "Error opening file for writing"
- Uninstall the previous version first
- Or run installer again — it will automatically kill any running instances
- Select a game — Click game tabs at the top
- Pick a set — Sidebar shows all sets for that game
- Search cards — Use search bar to find by name or number
- View prices — See Ungraded, Grade 9, and PSA 10 prices
- Click a card — Expand row to see full details
- Click Log In in the top toolbar
- Enter email & password
- Click Register (creates new account) or Login
- You're authenticated! Token stored in browser
- Click Your Collection in sidebar
- Add cards:
- Click + next to any card
- Pick grading (Ungraded / 9 / 10)
- Set quantity
- Create new collection:
- Click New Collection button
- Name it and add cards
- View collection:
- Collections saved in your account
- Switch between them in sidebar
- Remove cards:
- Click − next to a card in your collection
- Click On Sale button to see items with prices
- Click + Add to Cart on any card
- Click Cart icon (top right) to view
- Adjust quantities or remove items
- Click Checkout to go to Stripe payment form
- Complete payment — order confirmed via webhook
Only admins can manage store inventory (first user is marked admin by default).
- Click Inventory button (sidebar) — admin-only view
- See all inventory items with columns:
- Game, Set, Card, Grading
- Current qty, Price, Available stock
- Add/update stock:
- Click Edit button (pencil)
- Popover appears with qty/price fields
- Update and save
- Set pricing:
- Same edit popover
- Enter price in cents (e.g.,
9999= $99.99)
- Delete items:
- Click Edit button
- Click Delete Item at bottom
- Confirms and removes completely
- On Sale view:
- Only shows items with price > $0 AND qty > 0
- Zero-stock items hidden from public view
Add inventory items without UI (useful for bulk operations):
curl -X POST http://localhost:3847/api/admin/inventory/add \
-H "Authorization: your_admin_api_key" \
-H "Content-Type: application/json" \
-d '{
"game": "pokemon",
"set_slug": "pokemon-base-set",
"card_number": "1",
"grading": 0,
"quantity_available": 5,
"price_cents": 1999,
"card_name": "Bulbasaur"
}'List all inventory:
curl -H "Authorization: your_admin_api_key" \
http://localhost:3847/api/admin/inventorytcg-price-tracker/
├── site/ # Frontend (Single Page App)
│ ├── index.html # Main HTML
│ ├── app.js # All frontend logic
│ └── style.css # Styling
├── server/
│ ├── server.js # Express API server
│ └── sidecar-entry.js # Entry point for Tauri sidecar
├── src-tauri/ # Tauri desktop app (Windows/macOS)
│ ├── src/
│ │ └── main.rs # Tauri app setup & sidecar spawning
│ ├── Cargo.toml # Rust dependencies
│ ├── tauri.conf.json # Tauri configuration
│ ├── capabilities/ # Permission definitions
│ ├── binaries/ # Compiled sidecar binary
│ └── nsis/ # Windows installer scripts
├── db/
│ ├── schema.sql # PostgreSQL schema
│ ├── migrate.js # Migration runner
│ └── add_is_admin_column.sql
├── package.json
├── .env.example
└── README.md
| File | Purpose |
|---|---|
site/app.js |
Frontend state, UI, cart, collections, auth token management |
server/server.js |
Express routes: auth, cart, checkout, admin inventory, PriceCharting proxy |
server/sidecar-entry.js |
Entry point for Tauri bundled sidecar (loads .env and starts server) |
src-tauri/src/main.rs |
Tauri app setup: spawns sidecar, polls for readiness, handles process cleanup |
src-tauri/tauri.conf.json |
Desktop app configuration: window settings, sidecar bundling |
db/schema.sql |
PostgreSQL tables & indexes |
db/migrate.js |
One-time setup (creates columns, marks first user as admin) |
POST /api/auth/register { email, password } → { token, userId, isAdmin }
POST /api/auth/login { email, password } → { token, userId, isAdmin }
GET /api/collections (requires auth token)
POST /api/collections { name }
DELETE /api/collections/:id
GET /api/collections/:id/items
POST /api/collections/:id/items { game, set_slug, card_number, grading, quantity }
DELETE /api/collections/:id/items/:itemId
GET /api/cart (requires auth token)
POST /api/cart/add { game, set_slug, card_number, grading, quantity }
PUT /api/cart/update { game, set_slug, card_number, grading, quantity }
DELETE /api/cart/remove { game, set_slug, card_number, grading }
POST /api/checkout/session (requires auth token)
POST /api/checkout/webhook (Stripe signature validation)
GET /api/admin/inventory -H "Authorization: your_admin_api_key"
POST /api/admin/inventory/add { game, set_slug, card_number, grading, quantity_available, price_cents, card_name }
DELETE /api/admin/inventory/remove { game, set_slug, card_number, grading }
Edit GAME_DATA in server/server.js:
pokemon: {
label: 'Pokémon',
sets: [
{ name: 'Base Set', slug: 'pokemon-base-set', released: '1999-01-09' },
{ name: 'Jungle', slug: 'pokemon-jungle', released: '1999-06-16' }
]
}- Add to
GAME_DATAinserver/server.js - Add color accent to
ACCENTSinsite/app.js:
ACCENTS.newgame = {
accent: '#hexcolor',
dim: 'rgba(r, g, b, 0.10)',
rgb: 'r,g,b'
};Edit .env:
PORT=3847- Check PostgreSQL is running:
psql -U postgres - Verify
DATABASE_URLin.envmatches your setup - Run migrations:
node db/migrate.js
- Hard refresh browser:
Ctrl+Shift+R(Windows) /Cmd+Shift+R(Mac) - Check JWT token is valid: Browser DevTools → Application → Storage
- Verify user is admin: Login with email/password
- Use Stripe test keys from https://dashboard.stripe.com/test/apikeys
- Set
STRIPE_SECRET_KEYin.env - Restart server:
node server/server.js
- Ensure auth token is valid
- Check browser console for API errors
- Verify database is running and migrations completed
npm testEnable verbose logging:
DEBUG=* node server/server.jsMIT