Skip to content

KronigDev/Dropify

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

Feature Details
Spotify OAuth Login Sign in with Spotify via NextAuth.js, profile sync, logout
Access Control Private or public mode, allowlist by Spotify ID or email
Artist Search Debounced global search, artist cards with image, genres, popularity, followers
Watchlist Per-user tracked artists, no duplicates, instant remove
Release Sync Albums, Singles, EPs, Compilations, Appears On
Dashboard All releases from tracked artists, filters: artist / type / date / search
Calendar Monthly calendar with per-day release drill-down
Auto Sync Daily cron (08:00 UTC via Vercel or system cron) + manual trigger
Email Notifications New release emails via Resend (HTML template)
Push Notifications Browser push via VAPID / web-push
Settings Account info, notification prefs, watchlist management
Security Application-layer access control, Spotify secrets server-side only
Responsive Dark theme, works on all screen sizes

Tech Stack

Layer Technology
Framework Next.js 16 (App Router, Turbopack)
Language TypeScript 6
Styling Tailwind CSS 4
Auth NextAuth.js v4 + Spotify OAuth Provider
Database PostgreSQL + Prisma ORM v6
Spotify Data Spotify Client Credentials API
Email Resend v6
Push web-push (VAPID)
Cron Vercel Cron Jobs or system cron
Deployment Vercel (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
CREATE USER dropify WITH PASSWORD 'yourpassword';
CREATE DATABASE dropify 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:

Table Purpose
User, Account, Session NextAuth auth tables
Artist Global artist cache (shared across all users)
TrackedArtist Per-user watchlist
Release Global release cache
NotificationSettings Per-user notification prefs
PushSubscription VAPID push subscriptions
SyncLog Sync history and stats
NotificationSent Notification 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_string
NEXTAUTH_URL=https://yourdomain.com

# ─── Spotify ─────────────────────────────────────────────────────────────────
SPOTIFY_CLIENT_ID=your_spotify_client_id
SPOTIFY_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_xxxxxxxxxxxxxxxxxxxx
RESEND_FROM_EMAIL=Dropify <notifications@yourdomain.com>

# ─── Web Push (Browser Notifications) ────────────────────────────────────────
# Generate: npx web-push generate-vapid-keys
NEXT_PUBLIC_VAPID_PUBLIC_KEY=your_vapid_public_key
VAPID_PRIVATE_KEY=your_vapid_private_key
VAPID_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

Resource Minimum Recommended
CPU 1 vCPU 2 vCPU
RAM 1 GB 2 GB
Disk 10 GB 20 GB
OS Debian 13 Debian 13
Node.js 20.9 24 LTS (Active LTS)
PostgreSQL 15 17

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 {
    listen 80;
    server_name yourdomain.com www.yourdomain.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl 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_pass         http://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_pass http://127.0.0.1:3000;
        add_header Cache-Control "public, max-age=31536000, immutable";
    }

    location /sw.js {
        proxy_pass http://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:

0 8 * * * 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=private
ALLOWED_SPOTIFY_USER_IDS=abc123,def456
ALLOWED_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

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors