Changelog
Every release that shipped to production, in reverse chronological order. Sourced from CHANGELOG.md in the repo — same file the team writes against.
Unreleased
In progressDevX
- husky pre-commit runs
lint-staged(eslint --fix on staged code) +tsc --noEmit; commit-msg hook nudges Conventional Commits, blocks one-word vague subjects. - GitHub issue templates (bug, feature, deal-data correction) + PR template + Dependabot config (weekly npm, monthly Actions/Docker, grouped major-bump bans).
- CONTRIBUTING.md and CHANGELOG.md (this file).
- husky pre-commit runs
2026-04-29
perf: caching + N+1 + fonts + edge SWR
Performance
listDealscollapsed N+1 — single LATERALjsonb_agground-trip.- In-process
cached(key, ttl, loader)helper. Applied tocountActiveDeals(5 min),/api/health(10 s), HotStrip top-6 query (30 s). /vouchers,/free,/discussionsnowrevalidate = 60for ISR.- Caddy
@publicHtml+@feeds+@manifestmatchers add edge cache + SWR;@gokeepsno-storefor click counter. - Geist sans + mono get
display: 'swap'+ explicit fallback stack;adjustFontFallback: trueon sans for CLS.
2026-04-29
feat(seo/growth): FAQ + Person + Review LD, RSS, x-default, newsletter
Added
- FAQPage JSON-LD on
/howwith 7-question accordion. - Person JSON-LD for deal posters; Review JSON-LD for top-5 upvoted comments per deal.
- RSS feeds:
/feed.xml(latest 50) and/feed/nutrient/[slug]per-nutrient. - Newsletter signup with double opt-in:
newsletter_subscriptiontable,subscribeNewsletterAction,/api/newsletter/{confirm,unsubscribe}. hreflang="x-default"added to sitemap and root layout alternates.
- FAQPage JSON-LD on
2026-04-29
feat(observability): Sentry, request IDs, worker DLQ, health alerter
Added
- Sentry SDK (server, edge, client) with optional init via
SENTRY_DSN.error.tsxandglobal-error.tsxcapture exceptions. - Worker (
@sentry/node) reports failed jobs. proxy.tsattachesx-request-idheader on every request;/api/healthechoes it;loggerForRequest()helper for action-level correlation.- Worker dead-letter queue (
price-tracker-dlq) parks jobs that exhaust 3 attempts with full payload + error. scripts/health-alert.shcron-friendly Telegram/Slack alerter with state file dedup; systemd.service+.timer(every minute).
- Sentry SDK (server, edge, client) with optional init via
2026-04-29
feat(a11y/ux): PWA + skip-link + focus-trap + breadcrumbs + locale + plurals
Added
- PWA manifest with theme-color, 3 shortcuts; SVG icon set + ImageResponse-rendered apple-icon.
- Skip-to-content link in
[locale]/layout. prefers-reduced-motionquery disablesthermo-pulseand shrinks all animations to 0.01ms.useFocusTraphook applied to mobile drawer + search dialog; restores focus on close.Breadcrumbprimitive on nutrient/brand/deal pages.- next-intl ICU plural messages (
counts.deals,counts.comments, etc.). - SearchBar discriminated
SearchResult:ok | rate_limited | unavailable.
Fixed
- Locale-aware
formatPriceeverywhere — removed'en-GB'hardcodes from StackBuilder + ResolvedStack + SearchBar.
2026-04-29
feat: wire half-built features — uploads, stacks, profile, GDPR, README
Added
- Submit form image upload via MinIO presigned URL with progress bar.
/stacks/[id](public/private) and/stacks/exploregallery.- Profile edit UI (
/profile/settings): name + email preferences toggles. - GDPR JSON data export + permanent account deletion (anonymises comments).
- One-click unsave on /profile saved cards.
- Full README rewrite (project intro, stack, scripts, layout, deploy).
2026-04-29
chore(quality): Vitest + Playwright e2e, move mock-data, audit any types
Added
- Vitest setup with happy-dom, 41 unit tests across 6 files (cost-per-dose, SSRF, affiliate, filters, blur, reputation tiers).
- Playwright e2e (11 specs × chromium + iPhone 14): hero, locale switcher, search, JSON-LD assertion, auth-redirects, 404.
- CI extended with vitest + e2e jobs (Postgres + Redis + Meili services, chromium + webkit).
Changed
mock-data.tsmoved fromsrc/lib/toscripts/seed-data.ts— out of the app bundle.
2026-04-29
feat: UX polish — sitemap split, blur, verify banner, notifications, compare, rep badges, countdown, optimistic comments
Added
- Sitemap split into static / nutrients / brands / deal-paged segments scaling to 100k+ deals;
/sitemap-index.xmlindex. - Image blur placeholder (leaf→cream shimmer SVG) on deal cards + hot strip.
- Email verification banner in
[locale]/layout. /notificationsaggregated feed (comments, replies, price drops, vote milestones).- Category chip filter on homepage (vitamin/mineral/omega/…).
/compare?ids=…view with Best-value highlight.- 5-tier
ReputationBadge(Newcomer → Legend). - Live
ExpiresCountdown(1s/30s/60s tick depending on remaining time). - Optimistic comment posting via
useOptimistic.
- Sitemap split into static / nutrients / brands / deal-paged segments scaling to 100k+ deals;
2026-04-29
feat: production-readiness — pagination, skeletons, streaming, deploy, CI
Added
- "Load more" pagination via
?show=NURL param (24..240). loading.tsxroute segment fallbacks +Skeletonprimitives.- Suspense around HotStrip and Comments — shell streams while DB queries finish.
/api/healthendpoint (postgres + redis + meili pings).- pino structured logger with redact list; worker + scheduler converted from
console.log. scripts/backup-postgres.sh(pg_dump + gzip + S3 SigV4 upload + retention).- VPS deploy: Dockerfile + Dockerfile.worker + Caddyfile + systemd units + PM2 ecosystem + deploy README.
- GitHub Actions CI: typecheck + lint + tests + build with Postgres/Redis/Meili services.
- DeepL Pro translation refresh script.
- "Load more" pagination via
2026-04-29
feat: wire half-broken UI — saves, votes, stacks, profile, mobile, footer, errors
Added
toggleSavedDealAction+ SaveButton client (optimistic).voteOnCommentAction+ CommentVoteRow with three-state click.saveStackAction+ Stack builder save UI;?nutrients=…URL preload for shareable stacks./profile(auth-gated): saved deals, my stacks, recent comments.- MobileNav drawer + dedicated mobile search sheet.
- Footer pages: /about, /how, /guidelines, /privacy, /terms, /affiliate.
not-found.tsxper locale,error.tsxper segment,global-error.tsxroot fallback.
2026-04-29
security: SSRF, open redirect, rate limit, CSP, image host whitelist
Added
assertSafeUrl()blocks file://, javascript:, credentials, localhost, RFC1918, AWS metadata 169.254.169.254, IPv6 loopback, link-local; DNS lookup rejects hosts that resolve to private IPs./go/[dealId]validates target host against active store domains (cached 5 min).- Redis token-bucket rate limiting on vote/comment/report/submit/extract/search/upload.
- CSP + HSTS + X-Frame-Options + Referrer-Policy + Permissions-Policy + COOP headers.
next/image.remotePatternsswitched from wildcard**to explicit list.
2026-04-29
feat: Level 3 — SEO, admin, uploads, email, queues, analytics, telegram
Added
- robots.txt, sitemap.xml with hreflang × 7 locales, per-page metadata + canonical.
- Organization, WebSite, Product+Offer, BreadcrumbList JSON-LD.
- Dynamic OG images (next/og ImageResponse) for home + per-deal.
- Admin moderation: report + audit_log tables, role-gated
/admin. - MinIO S3-compat container + presigned-URL upload action.
- Resend email scaffolding (deal alert + weekly digest).
- BullMQ price-tracker queue + worker + scheduler + weekly digest scripts.
- Plausible/Umami self-hosted analytics integration.
- Telegram bot webhook with
/start,/link,/subscribe,/unsubscribe.
2026-04-29
feat: Level 2 — affiliate, search, stack builder, real stub pages
Added
- Affiliate URL rewriting per store (Amazon, iHerb, Awin, CJ);
/go/[dealId]click-tracking redirect. - MeiliSearch v1.13 container + sync script + 3 indexes; instant search dropdown with keyboard nav.
- Stack builder: pick nutrients → cheapest deal per nutrient → daily/monthly cost.
- Real /vouchers, /free, /discussions pages replacing stubs.
- Affiliate URL rewriting per store (Amazon, iHerb, Awin, CJ);
2026-04-29
feat: Level 1 functionality — DB, auth, voting, comments, filters, submit
Added
- Drizzle ORM + Postgres 16 (15 tables), docker-compose, seed script.
- Auth.js v5 with DrizzleAdapter, Google OAuth + Resend magic link.
- Real voting (votes table, transactional temperature recompute).
- Threaded comments with Zod-validated server action.
- Filters wired to URL via searchParams; server-side filtering + pagination.
- Submit deal form with generic OG/JSON-LD URL meta extractor.
2026-04-29
feat: scaffold VitaminDB community deals platform
Added
- Next.js 16 App Router with Turbopack,
output: 'standalone'. - next-intl v3 routing across 7 locales (EN/DE/ES/FR/PT/IT/NL).
- Tailwind v4 design system (leaf/citrus/temperature palette).
- Deal card grid + row layouts, thermometer pill, sticky header, sidebar filters, hot strip.
- Mock data: 10 deals, 16 nutrients, 10 brands, 8 stores.
- Next.js 16 App Router with Turbopack,
2026-04-28
Initial commit