Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

47 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ’ธ SubTrack

Subscription and recurring expense tracker โ€” never lose track of your monthly costs again.

Live demo License: MIT

SubTrack lets you log subscriptions (Netflix, Spotify, gym memberships, cloud storageโ€ฆ) and see what they cost you monthly and yearly, with multi-currency conversion, smart reminders, sharing, and analytics that actually answer questions you'd ask yourself.

Live app โ†’ https://subtrack-psi-nine.vercel.app

๐ŸŽฎ No sign-up required โ€” click "Explore the demo" on the login page to drop straight into a fully-seeded account (subscriptions, budgets, analytics and spend history).


๐Ÿ“ธ Screenshots

Sign-in โ€” 3D subscription-logo hero Dashboard
Analytics Budgets
Subscription detail Settings

โœจ Features

Core

  • ๐Ÿ“Š Dashboard with monthly/yearly totals, upcoming payments, recent activity, and a quick "review these" widget
  • ๐Ÿ’ธ Per-subscription cost breakdown (per cycle, monthly equivalent, yearly projection, optional yearly-vs-monthly savings hint)
  • ๐Ÿช™ Multi-currency display with live FX (Frankfurter) โ€” default picked from your browser locale
  • ๐Ÿ” Powerful list view: search, filter, sort, bulk actions, command palette, keyboard shortcuts (? for the cheat sheet)
  • ๐ŸŒ English + Turkish UI, <html lang> kept in sync for screen readers
  • ๐ŸŽจ Glassmorphism dark theme with light option, installable as a PWA, works offline for cached views
  • ๐Ÿช 3D auth & onboarding hero with real subscription-platform logos (react-three-fiber) โ€” lightweight mobile variant + reduced-motion / WebGL fallbacks
  • ๐ŸŽฎ One-click demo โ€” "Explore the demo" provisions a throwaway, fully-seeded account (subscriptions, budgets, price history, spend snapshots) that auto-expires after 48h, so anyone can try the app without registering

Insights

  • ๐Ÿ“ˆ Price history per subscription with stepped chart + total/last-change percent
  • ๐Ÿ“… Payment heatmap (year-long GitHub-style grid showing days you'll pay)
  • ๐Ÿ”ฎ Linear-regression forecast of next 6 months based on monthly snapshots
  • ๐Ÿ†š Period comparison ("now vs then" totals + per-category delta + added/cancelled list)
  • ๐Ÿ’ฐ Budgets with TOTAL or per-CATEGORY scope, MONTHLY/YEARLY periods, and 80%/100% threshold alerts (email + in-app + push)
  • ๐Ÿง  Smart category suggestions as you type a name; bulk auto-categorize for uncategorized rows
  • ๐Ÿšจ Suggestions widget flags expensive subs, price hikes, expired trials, unused-recently rows, and duplicate categories โ€” with 30-day dismiss

Notifications

  • ๐Ÿ“ง Renewal & trial-ending email reminders via SMTP (Nodemailer)
  • ๐Ÿ”” In-app notification center (bell icon, unread badge, accept/decline invitations inline)
  • ๐Ÿ“ฒ Web Push via VAPID with installable service worker
  • ๐Ÿ—“๏ธ Calendar feed (webcal:// + .ics per subscription, Google/Outlook quick-add)

Sharing & multi-user

  • ๐Ÿ‘ฅ Per-subscription sharing with VIEWER / EDITOR roles
  • โœ‰๏ธ Invitation by email with single-use 7-day tokens; in-app pending invitations on the bell
  • ๐Ÿ›ก๏ธ Email-bound acceptance: only the invited email can take the share

Security & privacy

  • ๐Ÿ” 2FA (TOTP) via authenticator apps with single-use backup codes
  • ๐ŸŒ OAuth login (Google + GitHub) with link-by-email account merge
  • ๐Ÿ”„ Server-side JWT revocation via tokenVersion โ€” logout, password change, and 2FA toggles invalidate every live token
  • ๐Ÿ“ค Full account data export (one JSON file of every row you own, password-hashโ€“free)
  • ๐Ÿ—‘๏ธ Account deletion behind password + 2FA + typed-DELETE confirmation

Observability

  • ๐Ÿ› Sentry on both server (Express handler) and client (React ErrorBoundary), user context auto-attached
  • ๐Ÿ“œ Pino structured logs on the server; HTTP request log middleware
  • ๐Ÿ“Š Activity log page with type chips, search, date range filtering

๐Ÿ› ๏ธ Tech Stack

Frontend โ€” React 19 ยท TypeScript ยท Vite ยท TailwindCSS ยท shadcn/ui ยท TanStack Query ยท Zustand ยท Recharts ยท Framer Motion ยท react-three-fiber / three.js ยท simple-icons ยท i18next ยท Storybook ยท Sentry React.

Backend โ€” Node.js 22 ยท Express 5 ยท TypeScript ยท PostgreSQL ยท Prisma ยท JWT (with server-side revocation) ยท Pino ยท Nodemailer ยท web-push ยท otplib ยท Sentry Node.

DevOps โ€” Docker ยท docker-compose (Postgres + Adminer + Mailhog) ยท GitHub Actions (type-check ยท lint ยท tests ยท build) ยท ESLint ยท Vitest (53 server unit tests + client tests).


๐Ÿ—๏ธ Architecture

flowchart LR
    User[๐Ÿ‘ค User browser] -- "HTTPS" --> Vercel
    User -- "Web Push (VAPID)" --> SW[Service Worker]

    subgraph Vercel["Vercel ยท Edge"]
        Client[Vite static SPA<br/>React + Tailwind]
    end

    subgraph Render["Render ยท Docker container"]
        API[Express + Prisma<br/>JWT ยท cookie-parser ยท helmet]
        Cron[/Cron triggers<br/>/jobs/notify-renewals<br/>/jobs/snapshot-monthly/]
    end

    subgraph Neon["Neon ยท Postgres 16"]
        DB[(subtrack_db<br/>users ยท subscriptions<br/>budgets ยท price_history<br/>events ยท shares ...)]
    end

    Mail[(SMTP / Mailhog)]
    Sentry[(Sentry)]
    Frankfurter[(Frankfurter FX API)]

    Client -- "/api/* (Vercel rewrite)" --> API
    API -- "Prisma" --> DB
    API -- "Renewal & invitation emails" --> Mail
    API -- "Web push payloads" --> SW
    API -- "FX rates" --> Frankfurter
    API -. "errors / traces" .-> Sentry
    Client -. "errors / traces" .-> Sentry
    Cron -- "X-Job-Key" --> API
Loading

The same SPA is served from Vercel's CDN; every /api/* request is rewritten to the Render API, so the browser always sees same-origin requests (no CORS preflight headaches). Push delivery goes from the API straight to the browser's service worker via VAPID, bypassing Vercel.


๐Ÿงญ Quick start (local)

# 1. Bring up Postgres + Mailhog + Adminer
npm run docker:up

# 2. Install deps
npm install

# 3. Server: set env, migrate, run
cd apps/server
cp .env.example .env  # fill JWT_SECRET and INTERNAL_JOB_KEY
npx prisma migrate dev
npm run dev

# 4. Client (separate terminal)
cd apps/client
npm run dev

Visit http://localhost:5173 for the app, http://localhost:8025 for the Mailhog inbox, and http://localhost:8080 for Adminer (Postgres GUI).

Useful scripts

# Workspace
npm run type-check          # tsc across all workspaces
npm run lint                # eslint across client + server
npm run docker:up | down    # toggle local Postgres / Mailhog / Adminer

# Server (cd apps/server)
npm test                    # vitest, 53 unit tests
npm run test:coverage       # v8 coverage report
npx prisma studio           # browse the DB

# Client (cd apps/client)
npm run dev | build         # vite
npm run storybook           # component catalog on :6006
npm run check:locales       # sanity-check TR/EN parity

๐Ÿš€ Deploy

The recommended free stack is Neon (Postgres) + Render (API) + Vercel (client). All three have permanent free tiers and no credit card required to start.

1. Database โ€” Neon

  1. Sign up at https://neon.tech with GitHub.
  2. Create a new project (region close to Render is ideal โ€” Frankfurt for this repo's render.yaml).
  3. Copy the pooled connection string (it contains -pooler). You'll paste it into Render below as DATABASE_URL.

2. API โ€” Render

  1. Sign up at https://render.com with GitHub.

  2. New + โ†’ Blueprint โ†’ connect this repo. Render reads render.yaml and provisions a web service named subtrack-api from apps/server/Dockerfile.

  3. After the service is created, set the env vars that were left blank:

    Key Value
    DATABASE_URL The Neon pooled connection string
    APP_URL The Vercel URL from step 3
    CORS_ORIGIN Same as APP_URL
    OAUTH_CALLBACK_BASE_URL The Render URL (e.g. https://subtrack-api.onrender.com)
    SMTP / VAPID / OAuth / Sentry Optional โ€” leave blank to disable that feature
  4. Render redeploys. On boot the container runs prisma migrate deploy automatically, so the schema is created on first launch.

Heads up โ€” Render's free Web Service spins down after 15 minutes of inactivity and takes ~30s to wake. A scheduled GitHub Actions workflow (.github/workflows/keep-alive.yml) pings /health every ~10 min to keep it warm; for guaranteed always-on hosting move to a paid Render plan or Fly.io, or use an external pinger like UptimeRobot.

3. Client โ€” Vercel

  1. Sign up at https://vercel.com with GitHub.
  2. New Project โ†’ import the repo โ†’ set Root Directory to apps/client. Vercel detects Vite automatically.
  3. Open apps/client/vercel.json and replace the CHANGE-ME placeholder with your actual Render URL, then commit. Vercel auto-redeploys.
  4. (Optional) Add a VITE_SENTRY_DSN env var for browser error reporting.

The Vercel build outputs dist/; the rewrites block in vercel.json proxies /api/* to your Render API and rewrites every other path to index.html so client-side routing works.

4. Post-deploy checklist

  • https://<render-url>/health returns {"status":"ok"}.
  • Open https://<vercel-url> and either register or click "Explore the demo".
  • Confirm the user lands at /dashboard and the bell, sidebar, topbar render.
  • If using OAuth: update Google/GitHub OAuth app callbacks to https://<render-url>/api/auth/oauth/<provider>/callback.
  • If using SMTP: send a test renewal job with curl -X POST https://<render-url>/api/jobs/notify-renewals -H "X-Job-Key: <INTERNAL_JOB_KEY>".

๐Ÿ”Œ Optional integrations

Everything in this section is wired up but disabled by default. Add the env vars below to Render to switch each one on.

Feature What you need Render env vars
SMTP email A Resend, Postmark, or any SMTP provider account SMTP_HOST, SMTP_PORT, SMTP_USER, SMTP_PASS, SMTP_SECURE, EMAIL_FROM, APP_URL
Web push A VAPID key pair (node -e "console.log(require('web-push').generateVAPIDKeys())") VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT
Google OAuth A Google Cloud OAuth client with the Render callback URL whitelisted GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, OAUTH_CALLBACK_BASE_URL
GitHub OAuth A GitHub OAuth app GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, OAUTH_CALLBACK_BASE_URL
Sentry server A Sentry project DSN SENTRY_DSN, SENTRY_TRACES_SAMPLE_RATE
Sentry client Same DSN, set on Vercel instead VITE_SENTRY_DSN (on Vercel)
Renewal cron Any cron service (Render Cron, cron-job.org, GitHub Actions) Hit POST /api/jobs/notify-renewals daily with X-Job-Key: <INTERNAL_JOB_KEY>
Monthly snapshot cron Same Hit POST /api/jobs/snapshot-monthly on the 1st of each month
Budget-alert cron Same Hit POST /api/jobs/check-budgets on your preferred cadence
Demo cleanup cron Same (optional โ€” demos also self-prune on each new demo) Hit POST /api/jobs/cleanup-demo daily to remove demo accounts >48h old

๐Ÿ“ Project layout

subtrack/
โ”œโ”€โ”€ apps/
โ”‚   โ”œโ”€โ”€ client/                  React + Vite SPA
โ”‚   โ”‚   โ”œโ”€โ”€ src/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ features/        feature-scoped folders (auth, budgets, shares, โ€ฆ)
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ components/      shared UI primitives
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ pages/           top-level routes
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ store/           Zustand stores
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ lib/             api client, i18n, sentry, formatters
โ”‚   โ”‚   โ”œโ”€โ”€ public/              static assets + sw.js + manifest.webmanifest
โ”‚   โ”‚   โ””โ”€โ”€ .storybook/          component catalog config
โ”‚   โ””โ”€โ”€ server/                  Express + Prisma API
โ”‚       โ”œโ”€โ”€ src/
โ”‚       โ”‚   โ”œโ”€โ”€ controllers/     route handlers
โ”‚       โ”‚   โ”œโ”€โ”€ services/        business logic (forecast, comparison, โ€ฆ)
โ”‚       โ”‚   โ”œโ”€โ”€ lib/             primitives (jwt, ics, totp, fx, sentry, โ€ฆ)
โ”‚       โ”‚   โ”œโ”€โ”€ middlewares/     auth, rate-limit, error handler
โ”‚       โ”‚   โ””โ”€โ”€ routes/          Express routers
โ”‚       โ””โ”€โ”€ prisma/              schema + migrations
โ”œโ”€โ”€ packages/shared/             cross-app TypeScript types
โ”œโ”€โ”€ docs/screenshots/            README image gallery
โ”œโ”€โ”€ docker-compose.yml           local Postgres + Mailhog + Adminer
โ”œโ”€โ”€ render.yaml                  one-click Render blueprint
โ””โ”€โ”€ apps/client/vercel.json      Vercel rewrites + headers

๐Ÿ“„ License

MIT โ€” see LICENSE.

About

Subscription tracker

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages