Skip to content

Repository files navigation

Dropify

Track every release from your favorite Spotify artists — in one place.

A production-ready web app for tracking Spotify artist releases. Multiple users can log in with their Spotify account, track artists, and see all releases in a central dashboard with calendar view, filters, and push/email notifications. All data is stored locally in your own PostgreSQL database — no cloud service required.

Dropify


Table of Contents

  1. Features
  2. Tech Stack
  3. Quick Start (Local)
  4. Spotify Developer App Setup
  5. Database Setup (PostgreSQL + Prisma)
  6. Environment Variables
  7. Production Deployment on Debian 13
  8. Access Control
  9. Architecture
  10. Changelog

Features

FeatureDetails
Spotify OAuth LoginSign in with Spotify via NextAuth.js, profile sync, logout
Access ControlPrivate or public mode, allowlist by Spotify ID or email
Artist SearchDebounced global search, artist cards with image, genres, popularity, followers
WatchlistPer-user tracked artists, no duplicates, instant remove
Release SyncAlbums, Singles, EPs, Compilations, Appears On
DashboardAll releases from tracked artists, filters: artist / type / date / search
CalendarMonthly calendar with per-day release drill-down
Auto SyncDaily cron (08:00 UTC via Vercel or system cron) + manual trigger
Email NotificationsNew release emails via Resend (HTML template)
Push NotificationsBrowser push via VAPID / web-push
SettingsAccount info, notification prefs, watchlist management
SecurityApplication-layer access control, Spotify secrets server-side only
ResponsiveDark theme, works on all screen sizes

Tech Stack

LayerTechnology
FrameworkNext.js 16 (App Router, Turbopack)
LanguageTypeScript 6
StylingTailwind CSS 4
AuthNextAuth.js v4 + Spotify OAuth Provider
DatabasePostgreSQL + Prisma ORM v6
Spotify DataSpotify Client Credentials API
EmailResend v6
Pushweb-push (VAPID)
CronVercel Cron Jobs or system cron
DeploymentVercel (cloud) or standalone Node.js on your own server

Quick Start (Local)

Prerequisites

1. Clone and install

git clone https://github.com/niklask52t/Dropify
cd Dropify
npm install

2. Configure environment

cp .env.example .env.local
# Edit .env.local with your values (see Environment Variables section)

3. Set up the database

# Create the PostgreSQL database
createdb dropify
# Run Prisma migrations (creates all tables)
npm run db:migrate
# Optional: view/edit data in Prisma Studio
npm run db:studio

4. Start development server

npm run dev

Open http://localhost:3000


Spotify Developer App Setup

1. Create a Spotify App

  1. Go to developer.spotify.com/dashboard
  2. Click Create app
  3. Fill in:
    • App name: Dropify (or any name)
    • App description: Spotify release tracker
    • Redirect URIs:
      • http://localhost:3000/api/auth/callback/spotify (local dev)
      • https://yourdomain.com/api/auth/callback/spotify (production)
    • Which API/SDKs are you planning to use?: Web API
  4. Click Save

2. Get your credentials

On the app dashboard:

  • Copy Client IDSPOTIFY_CLIENT_ID
  • Click View client secret → Copy → SPOTIFY_CLIENT_SECRET

3. Required OAuth scopes

Dropify requests these scopes at login (handled automatically by NextAuth.js):

  • user-read-email — to get the user's email address
  • user-read-private — to get the Spotify user ID

All release and artist data is fetched via the Client Credentials flow — no user token is stored or needed.


Database Setup (PostgreSQL + Prisma)

All data is stored 100% locally in your own PostgreSQL database. No Supabase, no external cloud database.

1. Install PostgreSQL (Debian/Ubuntu)

apt install postgresql postgresql-contrib
systemctl enable --now postgresql

2. Create the database and user

sudo -u postgres psql
CREATEUSERdropify WITH PASSWORD 'yourpassword';
CREATEDATABASEdropify OWNER dropify;
\q

3. Set DATABASE_URL in .env.local

DATABASE_URL=postgresql://dropify:yourpassword@localhost:5432/dropify

4. Run migrations

npm run db:migrate

Prisma creates all tables from prisma/schema.prisma:

TablePurpose
User, Account, SessionNextAuth auth tables
ArtistGlobal artist cache (shared across all users)
TrackedArtistPer-user watchlist
ReleaseGlobal release cache
NotificationSettingsPer-user notification prefs
PushSubscriptionVAPID push subscriptions
SyncLogSync history and stats
NotificationSentNotification dedup guard

Environment Variables

Copy .env.example to .env.local and fill in all values:

# ─── Database ────────────────────────────────────────────────────────────────DATABASE_URL=postgresql://dropify:password@localhost:5432/dropify# ─── NextAuth ────────────────────────────────────────────────────────────────# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"NEXTAUTH_SECRET=your_64_char_random_stringNEXTAUTH_URL=https://yourdomain.com# ─── Spotify ─────────────────────────────────────────────────────────────────SPOTIFY_CLIENT_ID=your_spotify_client_idSPOTIFY_CLIENT_SECRET=your_spotify_client_secret# ─── App URL ─────────────────────────────────────────────────────────────────NEXT_PUBLIC_APP_URL=https://yourdomain.com# ─── Access Control ──────────────────────────────────────────────────────────APP_ACCESS_MODE=public# or "private"ALLOWED_SPOTIFY_USER_IDS=abc123,def456# find at open.spotify.com/user/<id>ALLOWED_EMAILS=you@example.com# ─── Resend (Email Notifications) ────────────────────────────────────────────RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxRESEND_FROM_EMAIL=Dropify <notifications@yourdomain.com># ─── Web Push (Browser Notifications) ────────────────────────────────────────# Generate: npx web-push generate-vapid-keysNEXT_PUBLIC_VAPID_PUBLIC_KEY=your_vapid_public_keyVAPID_PRIVATE_KEY=your_vapid_private_keyVAPID_EMAIL=mailto:admin@yourdomain.com# ─── Cron Security ───────────────────────────────────────────────────────────# Generate: node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"CRON_SECRET=your_random_cron_secret

Production Deployment on Debian 13

Complete guide for a fresh Debian 13 (Trixie) server.

System Requirements

ResourceMinimumRecommended
CPU1 vCPU2 vCPU
RAM1 GB2 GB
Disk10 GB20 GB
OSDebian 13Debian 13
Node.js20.924 LTS (Active LTS)
PostgreSQL1517

1. Initial Server Setup

apt update && apt upgrade -y
apt install -y curl wget git build-essential ufw nginx certbot python3-certbot-nginx
ufw allow OpenSSH
ufw allow 'Nginx Full'
ufw enable

2. Install Node.js 24 LTS

curl -fsSL https://deb.nodesource.com/setup_24.x | bash -
apt install -y nodejs
node -v # v24.x.x
npm -v
npm install -g pm2

3. Install PostgreSQL 17

apt install -y postgresql postgresql-contrib
systemctl enable --now postgresql
# Create DB and user
sudo -u postgres psql -c "CREATE USER dropify WITH PASSWORD 'strongpassword';"
sudo -u postgres psql -c "CREATE DATABASE dropify OWNER dropify;"

4. Create a Dedicated App User

useradd -m -s /bin/bash dropify
passwd dropify
su - dropify

5. Clone and Build

cd /home/dropify
git clone https://github.com/niklask52t/Dropify
cd Dropify
npm ci
cp .env.example .env.local
nano .env.local # fill in all values

Run database migrations:

npm run db:migrate

Build the app:

npm run build

6. Start with PM2

pm2 start npm --name "dropify" -- start -- -p 3000
pm2 save
# Generate systemd startup script (run the printed command as root)
pm2 startup systemd -u dropify --hp /home/dropify
pm2 status
pm2 logs dropify

7. Configure Nginx Reverse Proxy

nano /etc/nginx/sites-available/dropify
server{listen80;server_name yourdomain.com www.yourdomain.com;return301 https://$host$request_uri;}server{listen443ssl http2;server_name yourdomain.com www.yourdomain.com;ssl_certificate /etc/letsencrypt/live/yourdomain.com/fullchain.pem;ssl_certificate_key /etc/letsencrypt/live/yourdomain.com/privkey.pem;add_header X-Frame-Options "SAMEORIGIN" always;add_header X-Content-Type-Options "nosniff" always;add_header Referrer-Policy "strict-origin-when-cross-origin" always;location / {proxy_passhttp://127.0.0.1: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;}location /_next/static/ {proxy_passhttp://127.0.0.1:3000;add_header Cache-Control "public, max-age=31536000, immutable";}location /sw.js {proxy_passhttp://127.0.0.1:3000;add_header Cache-Control "no-cache, no-store, must-revalidate";}}
ln -s /etc/nginx/sites-available/dropify /etc/nginx/sites-enabled/
nginx -t
systemctl reload nginx

8. SSL with Let's Encrypt

certbot --nginx -d yourdomain.com -d www.yourdomain.com \
--email your@email.com --agree-tos --non-interactive
certbot renew --dry-run
systemctl status certbot.timer

9. Daily Sync Cron (Alternative to Vercel Cron)

# As the dropify user
crontab -e

Add:

08***curl -s -X GET https://yourdomain.com/api/cron/sync \
-H "Authorization: Bearer YOUR_CRON_SECRET" \
>> /home/dropify/cron.log 2>&1

10. Updates

cd /home/dropify/Dropify
git pull origin main
npm ci
npm run db:migrate # run if schema changed
npm run build
pm2 reload dropify

Deployment Checklist

  • All env vars in .env.local are set
  • NEXTAUTH_URL = https://yourdomain.com
  • NEXT_PUBLIC_APP_URL = https://yourdomain.com
  • Spotify App redirect URI = https://yourdomain.com/api/auth/callback/spotify
  • NEXTAUTH_SECRET is a strong random string (32+ bytes)
  • CRON_SECRET is a strong random string
  • npm run db:migrate completed successfully
  • Firewall: only ports 22, 80, 443 open
  • SSL certificate obtained, auto-renewal tested
  • PM2 startup script installed (survives reboots)
  • npm run build completes without errors

Access Control

Set APP_ACCESS_MODE=private to restrict who can use the app:

APP_ACCESS_MODE=privateALLOWED_SPOTIFY_USER_IDS=abc123,def456ALLOWED_EMAILS=you@example.com,colleague@example.com

How to find your Spotify user ID:


Architecture

src/
app/
(app)/dashboard/ Release feed with filters
(app)/artists/ Search + watchlist management
(app)/calendar/ Monthly calendar view
(app)/changelog/ Version history page
(app)/settings/ Account, notifications, watchlist
api/auth/[...nextauth]/ NextAuth.js route handler
api/artists/search/ GET — Spotify artist search
api/artists/track/ POST/DELETE — track/untrack artist
api/watchlist/ GET — user's tracked artists
api/releases/ GET — filtered releases
api/sync/ POST — manual sync trigger
api/cron/sync/ GET — daily cron endpoint (Bearer auth)
api/notifications/settings/ GET/PATCH — notification preferences
api/notifications/push/subscribe/ POST/DELETE — push subscriptions
api/profile/ GET — current user profile
login/ Login page (Spotify OAuth)
access-denied/ Private mode denial page
lib/
auth.ts NextAuth.js config (Spotify provider, JWT)
db.ts Prisma client singleton
spotify.ts Spotify Client Credentials API helper
sync.ts Full sync + single-artist sync + notifications
email.ts Resend email notifications
push.ts web-push VAPID notifications
access-control.ts Private mode allowlist check
utils.ts Date formatting, type color helpers
proxy.ts Next.js 16 proxy (JWT auth guard + access control)
types/
index.ts Domain types (Artist, Release, etc.)
next-auth.d.ts NextAuth session type extensions
components/
layout/Sidebar.tsx Desktop navigation
layout/TopBar.tsx Mobile nav + sync button + user menu
artists/ Search, watchlist cards
releases/ Feed, card, filters
calendar/CalendarView.tsx Monthly calendar
settings/ Account info, notifications, watchlist manager
providers/AuthProvider.tsx NextAuth SessionProvider wrapper
prisma/
schema.prisma Complete DB schema (User/Auth + Dropify models)
public/
sw.js Service Worker for push notifications
manifest.json PWA manifest
logo-icon.png Icon (no text) — sidebar, favicon
logo-full.png Logo with text — login page
vercel.json Vercel Cron schedule (daily 08:00 UTC)
CHANGELOG.md Version history
.env.example Environment variable template

Data Flow

Login:
User → NextAuth.js → Spotify OAuth → JWT session
(Spotify user ID stored in User.spotifyId via auth event)
Music Data (no user token needed):
App → Spotify Client Credentials API → PostgreSQL cache
Sync Triggers:
On track → immediate syncSingleArtist() fire-and-forget
Manual → POST /api/sync → runFullSync()
Daily cron → GET /api/cron/sync (Bearer CRON_SECRET) → runFullSync()

Deduplication: releases are upserted by spotifyId — no duplicates ever. Notifications: sent for releases created in the past 25 h, tracked in NotificationSent to prevent re-sending.


Changelog

See CHANGELOG.md or the in-app Changelog page.

About

Dropify

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages