Skip to content

Du7chManiac/PakTrack

Repository files navigation

PakTrack - Self-Hosted Package Tracking Dashboard

Version License

PakTrack is a self-hosted, single-user package tracking dashboard for monitoring parcels from Belgian and Benelux-region carriers. Paste a tracking code, and PakTrack auto-detects the carrier, polls for status updates, and sends push notifications via Pushover when the status changes.

Features

Core Features

  • Multi-Carrier Support: Track parcels from Bpost, PostNL, GLS, and Colis Prive
  • Real Carrier APIs: All carriers use live tracking APIs (no API keys required)
    • Bpost: Public track.bpost.cloud JSON API
    • PostNL: Public jouw.postnl.nl tracking API (auto-derives country from postal code)
    • GLS: Public api.gls-group.eu REST API
    • Colis Prive: HTML scraping with cheerio
  • Auto-Detection: Automatically detects carrier from tracking code format
  • Manual Override: Select any carrier manually when auto-detection doesn't match
  • Background Polling: Configurable interval polling with random drift (anti-bot)
  • Push Notifications: Get notified via Pushover on status changes
  • Smart Polling:
    • Polls active parcels only
    • Continues polling delivered parcels for 24 hours
    • 15-minute cooldown between notifications per parcel
    • Carrier circuit breaker (backs off after 10 consecutive failures)
    • Per-parcel jitter (0-60s) to space out requests
    • Per-cycle drift (+-10 minutes) to avoid fixed intervals
  • Timeline View: Visual timeline of all tracking events
  • Archive Page: Move parcels out of the active dashboard without deleting their history
  • Auto-Archive Rules: Automatically archive delivered parcels after 7, 14, or 30 days
  • Delivery Calendar: Group active and archived deliveries by estimated arrival date or delivered date
  • Carrier Diagnostics: Inspect poller health, carrier backoff state, and recent carrier errors from Settings
  • Notification Rules: Globally choose which update types are worth sending to Pushover
  • Analytics & History: Review lifecycle totals, carrier mix, delivery timing, and archived parcel history from one page

UI Features

  • PWA Support: Install on iOS/Android home screen with offline support
  • Dark Mode: System-aware dark mode with manual override
  • Per-Parcel Notifications: Toggle notifications per parcel
  • Copy Tracking: One-click copy tracking code to clipboard
  • Labels: Add custom labels to identify packages
  • Archive Management: Restore archived parcels back to the active dashboard at any time
  • Calendar View: Browse estimated arrivals, deliveries, and parcels without a current ETA in one timeline-focused page
  • Diagnostics Panel: See whether background polling is healthy for each carrier without leaving the app
  • Notification Categories: Enable or suppress routine transit, delivery-day, delivered, or problem alerts globally
  • History Dashboard: See current delivery throughput, archived totals, and recent lifecycle activity without leaving the app
  • Dedicated Archive View: Keep using /archive as a focused restore view even though History is now the main lifecycle dashboard

Tech Stack

Layer Technology
Frontend React 19 + Vite + Tailwind CSS
Backend Node.js 22 + Express
Database SQLite (better-sqlite3)
ORM Drizzle ORM
Notifications Pushover API
HTML Scraping cheerio
PWA vite-plugin-pwa
Container Docker

Quick Start

Using Docker (Recommended)

# Clone the repository
git clone https://github.com/Du7chManiac/PakTrack.git
cd paktrack

# Copy environment file and configure
cp env.example .env
# Edit .env with your Pushover credentials (optional)

# Build and start
docker compose up -d --build

Note: The included docker-compose.yml is designed for use behind a reverse proxy (Traefik, Nginx Proxy Manager, etc.) and does not expose ports. For local or standalone use, add a port mapping:

services:
  paktrack:
    ports:
      - '3000:3000'

Manual Setup

Prerequisites

  • Node.js 22+
  • npm

Installation

# Install all dependencies (root + workspaces)
npm install

Development

Start the development servers:

npm run dev

This will start:

Production Build

# Build frontend
npm run build

# Run production server
cd backend && node src/index.js

Configuration

Environment Variables

Variable Default Description
PORT 3000 Server listen port
DATABASE_PATH ./data/paktrack.db locally, /data/paktrack.db in production containers when /data exists Path to SQLite database
PUSHOVER_USER_KEY - Pushover user key (optional)
PUSHOVER_API_TOKEN - Pushover API token (optional)
PUSHOVER_DEVICE - Target device name for notifications (optional, sends to all devices if omitted)
POLL_INTERVAL_MINUTES 30 Polling interval (15, 30, 60, 120, 240)
LOG_LEVEL info Logging level (debug, info, warn, error)
NODE_ENV development Environment mode

See env.example for a ready-to-use template.

Pushover Setup

  1. Create a Pushover account at https://pushover.net
  2. Note your User Key from the dashboard
  3. Create a new Application to get an API Token
  4. Enter both in Settings > Pushover Notifications
  5. Optionally, enter a Device Name to send notifications only to a specific device (e.g. iphone, myandroid). Leave blank to send to all your registered devices.
  6. Click "Test Notification" to verify

You can also configure credentials and the target device via environment variables:

PUSHOVER_USER_KEY=your_user_key
PUSHOVER_API_TOKEN=your_api_token
PUSHOVER_DEVICE=iphone   # optional — omit to send to all devices

Note: Environment variables take priority over values stored in the Settings UI.

Carrier Support

Auto-Detection

PakTrack automatically detects carriers based on tracking code format:

Carrier Format Example
Bpost (International) 2 letters + 9 digits + BE CE123456789BE
Bpost (Domestic) 24-26 digits starting with 32/30 323298761234987648753589
Bpost (JJBEA) Starts with JJBEA JJBEA1234567891012345
PostNL (3S) Starts with 3S or 2S 3SABCD1234567
PostNL (International) 2 letters + 9 digits + NL RR123456789NL
GLS (Short) 8 alphanumeric ZWKV4CV6
GLS (Numeric) 11-14 digits 47150051801147
Colis Prive Fallback / Manual Any 10-15 characters

Manual Carrier Selection

When adding a parcel, all 4 carriers are shown. Auto-detected carriers appear first with confidence badges, and remaining carriers are available below for manual override. This is useful for cross-border parcels (e.g., a PostNL-format code tracked by Bpost).

Carrier API Details

All carrier integrations use public endpoints — no API keys or business accounts required.

Carrier Endpoint Auth Notes
Bpost track.bpost.cloud/track/items None Postal code improves results
PostNL jouw.postnl.nl/track-and-trace/api/trackAndTrace None Auto-derives country (BE/NL) from postal code
GLS api.gls-group.eu/.../rstt001 None Public REST API
Colis Prive colisprive.com/.../detailColis.aspx None HTML scraping, most fragile

API Reference

Parcels

Method Endpoint Description
GET /api/parcels List parcels. No query returns all parcels, ?archived=false returns active parcels, ?archived=true returns archived parcels, and ?calendar=true&from=<iso>&to=<iso> returns dated calendar entries within a range
POST /api/parcels Add new parcel. Body: { trackingCode, postalCode, label?, carrier? }
GET /api/parcels/:id Get parcel with events
PATCH /api/parcels/:id Update label, notify, isArchived, archiveReason, or archiveSource
DELETE /api/parcels/:id Remove parcel and events
POST /api/parcels/:id/refresh Force immediate poll

Carrier Detection

Method Endpoint Description
POST /api/detect Detect carrier. Body: { trackingCode }

Settings

Method Endpoint Description
GET /api/settings Get all settings (masks Pushover token)
PATCH /api/settings Update settings including Pushover credentials, poll interval, auto-archive rules, and notification_rules
POST /api/settings/test-notification Send test notification

Archive-related parcel responses now include isArchived, archivedAt, archiveReason, archiveSource, deliveredAt, and lastStatusChangeAt so the frontend can show archive lifecycle details.

The delivery calendar uses estimatedDelivery for parcels that are still moving, and deliveredAt for parcels that have already been delivered. Parcels without a usable date are omitted from the dated buckets and shown in the calendar's No ETA section.

Analytics and history use parcel lifecycle timestamps already stored by PakTrack: createdAt, deliveredAt, archivedAt, and lastStatusChangeAt. Archived parcels are included in history metrics by default so delivery history stays visible even after parcels leave the active dashboard.

Health

Method Endpoint Description
GET /api/health Health check with version and uptime
GET /api/health/poller Poller diagnostics including carrier failures, carrier backoff, and recent poll timing

Analytics

Method Endpoint Description
GET /api/analytics/history?days=30 Aggregate lifecycle history for the requested window, including current snapshots, carrier rollups, and daily created/delivered/archived counts

Carrier diagnostics live in Settings and expose PakTrack's current poller state: active parcel counts, the configured poll interval, carrier degradation or backoff, and the last successful or failed poll timestamps. Diagnostics are kept in memory and reset when the app restarts.

notification_rules is a global settings object with four booleans: in_transit_updates, delivery_day_updates, delivered_updates, and problem_updates. These rules apply only to parcels with per-parcel notifications enabled, and the existing cooldown still suppresses rapid repeat notifications.

The main navigation now points archived workflows to History, which combines lifecycle analytics with restore controls for archived parcels. The existing /archive route still remains available as a dedicated archived-only view.

Deployment

Docker

The included docker-compose.yml works with reverse proxy setups (Traefik, Nginx Proxy Manager, Coolify, etc.) where the proxy handles port mapping and SSL.

For standalone Docker deployment with direct port access:

docker run -d \
  --name paktrack \
  --restart unless-stopped \
  -p 3000:3000 \
  -v /path/to/data:/data \
  -e DATABASE_PATH=/data/paktrack.db \
  -e NODE_ENV=production \
  paktrack

Platform Deployment (Coolify / Dokploy / Portainer)

PakTrack works with Docker-based deployment platforms:

  • Point the platform at this repo or use the included docker-compose.yml
  • Configure a bind mount for /data to persist the SQLite database
  • No port mappings needed if using a reverse proxy (Traefik, etc.)
  • SSL via your platform's certificate management

Reverse Proxy (Nginx)

server {
    listen 80;
    server_name paktrack.yourdomain.com;

    location / {
        proxy_pass http://localhost:3000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_cache_bypass $http_upgrade;
    }
}

SSL with Let's Encrypt

sudo certbot --nginx -d paktrack.yourdomain.com

Backups

The SQLite database is stored at the path specified by DATABASE_PATH (default: ./data/paktrack.db locally, /data/paktrack.db in production containers). To backup:

# Copy the database file
cp /path/to/data/paktrack.db /backup/paktrack-$(date +%Y%m%d).db

Testing

# Run all tests
npm test

# Run backend tests only
npm run test:backend

# Run frontend tests only
npm run test:frontend

Project Structure

paktrack/
├── frontend/                 # React frontend
│   ├── public/              # Static assets & PWA icons
│   ├── src/
│   │   ├── components/     # React components
│   │   ├── pages/          # Page components
│   │   ├── hooks/          # Custom hooks
│   │   └── lib/            # Utilities
│   └── test/               # Frontend tests
├── backend/                 # Node.js backend
│   ├── src/
│   │   ├── carriers/       # Carrier adapters & detection
│   │   ├── db/             # Database schema & setup
│   │   ├── routes/         # API routes
│   │   ├── services/       # Poller & Pushover
│   │   └── utils/          # Logger, settings helper
│   └── test/               # Backend tests
├── Dockerfile              # Multi-stage Docker build
├── docker-compose.yml      # Docker Compose config
├── env.example             # Environment variable template
└── package.json            # Root monorepo config

Troubleshooting

Notifications not working

  • Verify Pushover credentials in Settings
  • Check that parcel has notifications enabled
  • Verify the Device Name in Settings matches an actual registered device (or leave blank for all devices)
  • Test notification from Settings page
  • Check server logs for errors
  • Verify the matching notification rule is enabled in Settings > Pushover Notifications
  • Remember that fast back-to-back updates can still be skipped by the cooldown window

Parcels disappeared from the dashboard

  • Check History first, or open /archive for the dedicated archived-only view
  • Delivered parcels can be auto-archived from Settings after 7, 14, or 30 days
  • Unarchive a parcel to return it to the active dashboard and resume active polling

Delivery calendar looks empty

  • Many carriers do not provide an estimated delivery date until a parcel is close to arrival
  • Parcels without an ETA are grouped under No ETA instead of a calendar day
  • Delivered parcels remain visible through their delivered date, even after they are archived

Carrier diagnostics show backoff or errors

  • Backoff pauses automatic polling for that carrier after repeated failures and usually points to a carrier API or scraping issue
  • Diagnostics reset on restart, so a freshly restarted app may show healthy counters even if a carrier was failing earlier
  • Manual refresh can still be useful while a carrier is marked degraded, but repeated failures usually mean the carrier endpoint changed

History numbers look different from the dashboard

  • History includes archived parcels by default so past deliveries remain visible after they leave active tracking
  • Delivery timing is calculated from when a parcel was added to PakTrack until it reached a delivered state
  • Unarchiving a parcel removes it from the current archived snapshot because PakTrack does not keep a separate archive event log yet

Carrier tracking not working

  • Carrier websites/APIs may have changed (check logs)
  • Circuit breaker may be active (waits 1 hour after 10 failures)
  • Try manual refresh from parcel detail page
  • Colis Prive is most fragile (HTML scraping) — may break if page structure changes

Database locked errors

  • Ensure only one instance is running
  • Check file permissions on data directory
  • Restart the container/service

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT License - see LICENSE file for details.

Acknowledgments

  • Originally built as a personal project
  • "Pak" is Dutch for package

About

PakTrack is a self-hosted, single-user package tracking dashboard for monitoring parcels from Belgian and Benelux-region carriers. Paste a tracking code, and PakTrack auto-detects the carrier, polls for status updates, and sends push notifications via Pushover when the status changes.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages