Automated marketplace listing tracker with real-time Discord notifications
Features β’ Quick Start β’ Configuration β’ API β’ Contributing
Market Monitor is a self-hosted application that automatically tracks listings across online marketplaces and sends instant notifications when new items matching your criteria appear. Built with a modern tech stack, it features a responsive web dashboard for managing watchers and a robust backend for reliable monitoring.
Currently supported marketplaces:
- Blocket (Swedish marketplace)
- Tradera (coming soon)
- Multi-Watcher System β Create independent watchers with custom search queries, price filters, and schedules
- Real-time Notifications β Discord webhook integration with rich embeds, images, and listing details
- Flexible Scheduling β Cron-based scheduling for precise control over check intervals
- Price Filtering β Set min/max price ranges to filter out irrelevant listings
- Modern Dashboard β Intuitive web UI for managing watchers, viewing status, and configuring settings
- Smart Deduplication β In-memory caching prevents duplicate notifications
- First-run Protection β Avoids notification spam when creating new watchers
- Secure Authentication β JWT-based auth with refresh tokens and secure session management
- Docker Ready β Single container deployment with persistent storage
| Component | Technology |
|---|---|
| Backend | Node.js 20, Express, TypeScript, SQLite |
| Frontend | Nuxt 4, Vue 3, Nuxt UI 4, Pinia, Tailwind CSS |
| Authentication | JWT, bcrypt, refresh tokens |
| Real-time | Server-Sent Events (SSE) |
| Deployment | Docker, Supervisor |
- Create directories and environment file:
mkdir -p data logs
# Generate secrets
cat > .env << 'EOF'
JWT_SECRET=your-jwt-secret-minimum-32-characters-here
REFRESH_TOKEN_SECRET=your-refresh-secret-minimum-32-characters
NUXT_SESSION_PASSWORD=your-session-password-minimum-32-chars
EOF
# Or generate random secrets:
# openssl rand -base64 48- Create
docker-compose.yaml:
services:
market-monitor:
image: rutbergphilip/market-monitor:latest
ports:
- '${UI_PORT:-3847}:${UI_PORT:-3847}'
- '${SERVER_PORT:-5847}:${SERVER_PORT:-5847}'
volumes:
- ./data:/app/data
- ./logs:/app/logs
env_file:
- .env
environment:
- NODE_ENV=production
- SERVER_PORT=${SERVER_PORT:-5847}
- UI_PORT=${UI_PORT:-3847}
restart: unless-stopped- Start the application:
docker compose up -dAccess the dashboard at http://localhost:3847 (default login: admin / admin)
docker run -d \
--name market-monitor \
-p 3847:3847 \
-p 5847:5847 \
-v ./data:/app/data \
-v ./logs:/app/logs \
-e JWT_SECRET=your-jwt-secret-minimum-32-characters-here \
-e REFRESH_TOKEN_SECRET=your-refresh-secret-minimum-32-characters \
-e NUXT_SESSION_PASSWORD=your-session-password-minimum-32-chars \
rutbergphilip/market-monitor:latest# Clone the repository
git clone https://github.com/rutbergphilip/market-monitor.git
cd market-monitor
# Install backend dependencies
cd server && npm install
# Install frontend dependencies
cd ../ui && npm install
# Start backend (Terminal 1)
cd server && npm run dev
# Start frontend (Terminal 2)
cd ui && npm run dev| Variable | Description |
|---|---|
JWT_SECRET |
Secret key for JWT signing (min 32 characters) |
REFRESH_TOKEN_SECRET |
Secret key for refresh tokens (min 32 characters) |
NUXT_SESSION_PASSWORD |
Secret for UI sessions (min 32 characters) |
Generate secrets with:
openssl rand -base64 48
| Variable | Default | Description |
|---|---|---|
DB_PATH |
/app/data |
Database directory or file path |
SERVER_PORT |
5847 |
Backend API port |
UI_PORT |
3847 |
Frontend port |
HOST |
0.0.0.0 |
Host binding address |
LOG_LEVEL |
info |
Logging verbosity (debug, info, warn, error) |
NODE_ENV |
production |
Environment mode |
UI_ORIGIN |
http://localhost:3847 |
CORS origin (set to your domain in production) |
The DB_PATH variable supports both directory and file paths:
# Directory (database created as db.sqlite inside)
DB_PATH=/app/data
# Direct file path
DB_PATH=/app/data/my-database.sqlite- Log in to the dashboard at
http://localhost:3847 - Click "New Watcher" to open the creation modal
- Configure your watcher:
- Name: A descriptive name for the watcher
- Schedule: Cron expression (e.g.,
*/15 * * * *for every 15 minutes) - Queries: Add one or more search queries with marketplace selection
- Price Range: Optional min/max price filters
- Notifications: Add Discord webhook URLs
- In Discord, go to Server Settings > Integrations > Webhooks
- Click "New Webhook" and copy the URL
- Add the webhook URL to your watcher's notification targets
Global notification settings can be configured in Settings > Notifications:
- Bot Username: Custom name for Discord messages
- Avatar URL: Custom avatar for the Discord bot
- Batch Size: Number of listings per message
- Retry Settings: Max retries and delay for failed deliveries
The backend exposes a REST API on port 5847 (configurable via SERVER_PORT).
# Login
POST /api/auth/login
Content-Type: application/json
{"username": "user", "password": "pass"}
# Response includes JWT token and refresh token# List all watchers
GET /api/watchers
Authorization: Bearer <token>
# Create watcher
POST /api/watchers
Authorization: Bearer <token>
# Start/stop watcher
POST /api/watchers/:id/start
POST /api/watchers/:id/stop
# Manually trigger watcher
POST /api/watchers/:id/trigger# Get all settings
GET /api/settings
Authorization: Bearer <token>
# Update setting
PATCH /api/settings/:key
Authorization: Bearer <token>βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Web Dashboard β
β (Nuxt 4 / Vue 3) β
βββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββ
β REST API + SSE
βββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββ
β Express API Server β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β Cron Scheduler β SSE Stream β Auth Middleware β
ββββββββββ¬ββββββββββ΄βββββββββββββββ΄ββββββββββββββββββββββββ
β
ββββββββββΌββββββββββ ββββββββββββββββββββ
β Marketplace ββββββΆβ Notification β
β Adapters β β Service β
β (Blocket, etc.) β β (Discord, Email) β
ββββββββββ¬ββββββββββ ββββββββββββββββββββ
β
ββββββββββΌββββββββββ
β SQLite Database β
ββββββββββββββββββββ
apiVersion: apps/v1
kind: Deployment
metadata:
name: market-monitor
spec:
replicas: 1
template:
spec:
containers:
- name: market-monitor
image: rutbergphilip/market-monitor:latest
ports:
- containerPort: 3847
- containerPort: 5847
env:
- name: JWT_SECRET
valueFrom:
secretKeyRef:
name: market-monitor-secrets
key: jwt-secret
- name: REFRESH_TOKEN_SECRET
valueFrom:
secretKeyRef:
name: market-monitor-secrets
key: refresh-token-secret
volumeMounts:
- name: data
mountPath: /app/data
volumes:
- name: data
persistentVolumeClaim:
claimName: market-monitor-pvcThe API exposes a health endpoint with database connectivity status:
curl http://localhost:5847/api/health
# {"status":"healthy","timestamp":"...","database":{"connected":true},"uptime":123}- Mandatory secrets β App will not start without properly configured secrets
- Non-root container β Runs as unprivileged user (UID 1000)
- JWT tokens expire after 24 hours
- Refresh tokens are valid for 30 days with automatic rotation
- Passwords are hashed using bcrypt
- Secure cookie settings (httpOnly, sameSite)
- All API routes (except auth) require authentication
- Health endpoint with database connectivity check
Important: Change the default
admin/admincredentials after first login!
- Tradera marketplace integration
- Telegram notifications
- Email notifications
- Advanced filters (location, category, regex)
- Multi-user support with separate watchers
- Mobile-responsive dashboard improvements
Contributions are welcome! Please feel free to submit a Pull Request.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
This project is licensed under the MIT License. See the LICENSE file for details.
If you find Market Monitor useful, consider giving it a star!