A simple local-first web app to track vouchers / gift cards with expiry dates, categories, usage status, and private mode.
π« No cloud
π« No login
π« No database
π³ Runs fully on your computer using Docker
- β Create, βοΈ update, β delete vouchers
- β³ Track voucher expiry dates
- π Automatically calculate days left
- π§ Email notifications for expiring vouchers
- β° Notification runs:
- π Immediately when server starts
- π Every day at 9:00 AM IST
- π·οΈ Mark vouchers as Used / Active / Expired
- π Auto sort by expiry (expiring first)
- π° Show total voucher value based on filters
- π Search & filter vouchers
- π¦ Fully offline (data stored locally)
- π³ Dockerized (no Node.js, no Javascript required)
- π§ Implemented using node-cron
- π’ Runs only while Docker containers are running
- π§ Sends HTML email containing:
- π’ Company name
- ποΈ Voucher code
- π Voucher PIN
- π Expiry date
- β³ Days left
- π« Skips already used vouchers
- π Looks ahead 10 days for upcoming expiries
This project runs entirely using Docker.
Download Docker Desktop: https://www.docker.com/products/docker-desktop/
Supported platforms:
- πͺ Windows
- π macOS
- π§ Linux
Git is required to clone the repository and manage source code.
Download Git: https://git-scm.com/downloads
Supported platforms:
- πͺ Windows
- π macOS
- π§ Linux
Before running any command below, Docker Desktop must be running.
- πͺ Windows: Start Menu β Docker Desktop
- π macOS: Applications β Docker Desktop
Docker is ready ONLY when:
- π³ Docker whale icon is visible
- π’ Status shows βRunningβ
- π’ Green dot / "Engine runningβ is visible on the bottom left corner
Email notifications require a Gmail App Password.
- π€ Google account
- π 2-Step Verification enabled
- Google Account β Security
- Enable 2-Step Verification
- Open App passwords
- Select:
- App: Mail
- Device: Other (Custom)
- Name: Voucher Tracker SMTP
- Generate password
- Copy the 16-character password
- β Do NOT use your normal Gmail password
- β Use ONLY the App Password in environment file
- Paste the password without any space
No Javascript. No Node.js. Everything runs via Docker.
β οΈ Important β Read Before You Start
- β Ensure Email to Send, App Password, and Receiver email(s) are ready and configured before proceeding with this step
- π§ For multiple recipients, separate email addresses using a semicolon (
;)Example:
john@example.com;jane@example.com;team@example.com
Open PowerShell and run (Copy, Paste and Press Enter if script not started automatically) and provide details needed to send voucher expiring emails
{
$folder = "voucher-tracker"
$repo = "https://github.com/sohampshetty/voucher-tracker.git"
$branch = "main"
$currentDir = (Get-Location).Path
Write-Host "`nπ Voucher Tracker β Auto Setup Started" -ForegroundColor Cyan
# ------------------------------------------------------------
# 1οΈβ£ Clone or update repo
# ------------------------------------------------------------
if (Test-Path $folder) {
Write-Host "π₯ Updating existing repository..." -ForegroundColor Yellow
Set-Location $folder
git fetch origin
git reset --hard
git clean -fd
git checkout $branch
git pull origin $branch
Set-Location $currentDir
} else {
Write-Host "π¦ Cloning repository..." -ForegroundColor Green
git clone -b $branch $repo
}
Set-Location "$currentDir\$folder"
# ------------------------------------------------------------
# 2οΈβ£ Ask for ports
# ------------------------------------------------------------
Write-Host "`nβοΈ Port Configuration" -ForegroundColor Cyan
Write-Host "π Press Enter to use default ports" -ForegroundColor DarkGray
$frontendPort = Read-Host "Frontend (UI) port [9000]"
if ([string]::IsNullOrWhiteSpace($frontendPort)) { $frontendPort = "9000" }
$backendPort = Read-Host "Backend (API) port [9100]"
if ([string]::IsNullOrWhiteSpace($backendPort)) { $backendPort = "9100" }
Write-Host "`nπ§ Using ports β UI: $frontendPort | API: $backendPort" -ForegroundColor Green
$env:FRONTEND_PORT = $frontendPort
$env:PORT = $backendPort
# ------------------------------------------------------------
# 3οΈβ£ Email config (ask ONLY if missing)
# ------------------------------------------------------------
Write-Host "`nπ§ Email Configuration" -ForegroundColor Cyan
Write-Host "βΉοΈ Email is used only for sending voucher expiry notifications. Enter information requested (will be asked only when missing) and press Enter" -ForegroundColor DarkGray
$envFile = "server\.env"
$requiredVars = @("EMAIL_USER", "EMAIL_PASS", "NOTIFY_EMAIL")
$envMap = @{}
$allPresent = $true
if (Test-Path $envFile) {
Get-Content $envFile | ForEach-Object {
if ($_ -match '^\s*([^#=]+)\s*=\s*(.*)\s*$') {
$envMap[$matches[1]] = $matches[2]
}
}
}
foreach ($key in $requiredVars) {
if (-not ($envMap.ContainsKey($key) -and -not [string]::IsNullOrWhiteSpace($envMap[$key]))) {
switch ($key) {
"EMAIL_USER" {
$envMap[$key] = Read-Host "β‘οΈ Enter sender email address (used for SMTP sending)"
}
"EMAIL_PASS" {
$envMap[$key] = Read-Host "β‘οΈ Enter email app password (SMTP app password, NOT your email login password)"
}
"NOTIFY_EMAIL" {
$envMap[$key] = Read-Host "β‘οΈ Enter receiver email address(es) (use ; to separate multiple emails)"
}
}
$allPresent = $false
}
}
if ($allPresent) {
Write-Host "π¨ Email configuration validated successfully!" -ForegroundColor Green
} else {
($requiredVars | ForEach-Object { "$_=$($envMap[$_])" }) |
Set-Content $envFile -Encoding UTF8
}
# ------------------------------------------------------------
# 4οΈβ£ Rebuild everything clean
# ------------------------------------------------------------
Write-Host "`nπ³ Building Docker containers... (this may take a while)" -ForegroundColor Cyan
$env:BUILD_TIME = (Get-Date).ToString("yyyyMMddHHmmss")
docker-compose down --volumes --remove-orphans
docker-compose build --no-cache
docker-compose up -d
# ------------------------------------------------------------
# 5οΈβ£ Open UI
# ------------------------------------------------------------
$url = "http://localhost:$frontendPort"
Write-Host "`nπ Opening Voucher Tracker β $url" -ForegroundColor Green
Start-Process $url
Write-Host "`nβ
Setup complete!" -ForegroundColor Green
Start-Sleep -Seconds 1
Stop-Process -Id $PID
} | & { process { $_.Invoke() } }
Open Terminal and run (Copy, Paste and Press Enter if script not started automatically) and provide details needed to send voucher expiring emails
FOLDER="voucher-tracker"
REPO="https://github.com/sohampshetty/voucher-tracker.git"
BRANCH="main"
CURRENT_DIR="$(pwd)"
echo
echo "Voucher Tracker - Auto Setup Started"
echo
# ------------------------------------------------------------
# 1. Clone or update repo
# ------------------------------------------------------------
if [ -d "$FOLDER" ]; then
echo "Updating existing repository..."
cd "$FOLDER" || exit 1
git fetch origin
git reset --hard
git clean -fd
git checkout "$BRANCH"
git pull origin "$BRANCH"
cd "$CURRENT_DIR" || exit 1
else
echo "Cloning repository..."
git clone -b "$BRANCH" "$REPO"
fi
cd "$CURRENT_DIR/$FOLDER" || exit 1
# ------------------------------------------------------------
# 2. Port configuration
# ------------------------------------------------------------
echo
echo "Port Configuration"
echo "Press Enter to use default ports"
printf "\nFrontend (UI) port [9000]: "
read FRONTEND_PORT
FRONTEND_PORT=${FRONTEND_PORT:-9000}
printf "\nBackend (API) port [9100]: "
read BACKEND_PORT
BACKEND_PORT=${BACKEND_PORT:-9100}
echo
echo "Using ports -> UI: $FRONTEND_PORT | API: $BACKEND_PORT"
export FRONTEND_PORT
export PORT="$BACKEND_PORT"
# ------------------------------------------------------------
# 3. Email configuration
# ------------------------------------------------------------
echo
echo "Email Configuration"
echo "Email is used only for sending voucher expiry notifications. Enter information requested (will be asked only when missing) and press Enter."
ENV_FILE="server/.env"
EMAIL_USER=""
EMAIL_PASS=""
NOTIFY_EMAIL=""
if [ -f "$ENV_FILE" ]; then
while IFS='=' read -r key value; do
case "$key" in
EMAIL_USER) EMAIL_USER="$value" ;;
EMAIL_PASS) EMAIL_PASS="$value" ;;
NOTIFY_EMAIL) NOTIFY_EMAIL="$value" ;;
esac
done < "$ENV_FILE"
fi
if [ -z "$EMAIL_USER" ]; then
printf "\nEnter sender email address (used for SMTP sending): "
read EMAIL_USER
fi
if [ -z "$EMAIL_PASS" ]; then
printf "Enter email app password (SMTP app password, NOT your email login password): "
read EMAIL_PASS
fi
if [ -z "$NOTIFY_EMAIL" ]; then
printf "Enter receiver email address(es) (use ; to separate multiple emails):"
read NOTIFY_EMAIL
fi
echo "EMAIL_USER=$EMAIL_USER" > "$ENV_FILE"
echo "EMAIL_PASS=$EMAIL_PASS" >> "$ENV_FILE"
echo "NOTIFY_EMAIL=$NOTIFY_EMAIL" >> "$ENV_FILE"
echo
echo "Email configuration ready"
# ------------------------------------------------------------
# 4. Docker build
# ------------------------------------------------------------
echo
echo "Building Docker containers (this may take a while)"
export BUILD_TIME="$(date +%Y%m%d%H%M%S)"
docker-compose down --volumes --remove-orphans > /dev/null 2>&1
docker-compose build --no-cache --quiet
docker-compose up -d > /dev/null 2>&1
# ------------------------------------------------------------
# 5. Open UI
# ------------------------------------------------------------
URL="http://localhost:$FRONTEND_PORT"
echo
echo "Opening Voucher Tracker -> $URL"
open "$URL"
echo
echo "Setup complete"
The application will open automatically in default browser, if not use below url (replace port if not used default) in Chrome.
You can manage the app containers from Docker Desktop UI.
- Open Docker Desktop
- Go to the Containers tab
- You will see containers like:
- voucher-tracker (Manages both the below)
- voucher-backend
- voucher-frontend
From here you can:
βΆοΈ Start containers- βΈοΈ Stop containers
- π Restart containers
- π View logs
- π Browse container files (Files tab)
- ποΈ Remove containers safely
This is the easiest way if you are not comfortable with terminal commands.
- π’ Containers must be running for email notifications to work
All voucher data is stored locally at:
server/data/vouchers.json
π Always back up this file β all your vouchers live here.
- π« Never commit .env files
- π« Never commit server/data/vouchers.json
- π Always use Gmail App Password
β οΈ Avoid running multiple Docker instances (prevents duplicate emails)
If your system is restarted or shut down, Docker containers do NOT start automatically unless Docker Desktop is running.
Follow these steps to bring the app back online.
- πͺ Windows: Open Docker Desktop from Start Menu
- π macOS: Open Docker Desktop from Applications
Wait until:
- π³ Docker whale icon is visible
- π’ Status shows Running
- Open Docker Desktop
- Go to Containers
- Locate the project
- Click Start
βΆοΈ
- Open browser: http://localhost:9000
- If backend is running, email notifications will resume automatically
- β° Scheduled emails run only while containers are running
- π If Docker Desktop is closed, emails will NOT be sent
- π No rebuild is required after restart
- π
.envand voucher data remain safe
You can enable βStart Docker Desktop on loginβ in Docker settings so the app comes back automatically after reboot.
π¦ X (Twitter): https://x.com/aree_dinosaur
π§ Email: aree.dinosaur@gmail.com
π Thank you for using Voucher Tracker.
Built with care β€οΈ
Hope it saves you money before vouchers expire π
Happy hacking π