Commit Graph
10 Commits
Author SHA1 Message Date
nmemmertandClaude Sonnet 5.5 bb83f3e8c5 Completed studies: finishing a study keeps access after leaving; certificates always shown; no reminders for finished studies; v2.11.0
Publish Container / build (push) Successful in 15m31s
- Completion is stored on the user (completedStudySlugs, sticky) when every lesson is done; backfilled at startup
- Lessons, notes, progress, quiz, community and certificate stay open for completed studies after leaving the enrollment
- Dashboard/My Account list completed studies with Review Lessons and certificate access
- Study page shows a completed banner
- Lesson-release reminders, weekly digest and re-engagement emails skip finished studies
- Fix: streak fields (current/longest/last studied) and firstCompletionAt were dropped on every restart when users were loaded
- Tests for completion/access/re-engagement

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 11:42:58 -04:00
nmemmertandClaude Sonnet 5.5 7301982bb1 Fix certificate View link (wrong /study/certificate URL), certificates in My Account, name on certificate; v2.10.10
Publish Container / build (push) Successful in 15m29s
- Certificate URLs in the account overview and the issuance email pointed at /study/certificate/:token,
  which the router treated as a lesson page; now /certificate/:token (old links redirect)
- My Account Certificates card lists every enrolled study: progress, Generate, View, Copy link, Download PNG, Share
- Certificate canvas used a non-existent /api/study-account route and always printed 'Student'; now reads the account name
- Panel no longer collapses after generating

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 11:13:44 -04:00
nmemmertandClaude Sonnet 5.5 8a796964b9 Security & hardening pass: scrypt password hashing, persisted admin sessions, upvote/analytics rate limits, dependency fixes, healthcheck, lint cleanup; v2.10.0
Publish Container / build (push) Successful in 17m3s
- Study passwords: salted scrypt; legacy SHA-256 hashes verified and upgraded on login
- Admin sessions persisted (hashed tokens) so deploys no longer log the admin out
- Upvotes: rate limit + one per client per question, approved questions only; rate limit on analytics play/event
- npm audit: 46 -> 4 vulnerabilities (tiptap bumped to 3.31.4)
- Untrack per-user data (study users/notes/progress/etc.) from git
- Dockerfile HEALTHCHECK; node --test suite for password hashing
- Fix email template cards remounting (lost input focus) on each keystroke
- Lint: 34 errors -> 0

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-09 08:45:39 -04:00
nmemmertandClaude Sonnet 5 841e754e16 Add student account self-service security features and discoverability fixes; v2.1.0
Adds forgot/reset password, signup email verification, sign-out-of-all-devices,
note-usage and certificate display in account settings, and password-changed
security emails. Fixes study sessions being lost on server restart by
persisting them to disk, and fixes sanitizeStudyUsers silently dropping the
new pending-token fields on load. Also surfaces the lesson discussion board
in the main site nav, trims the sign-in page for returning users, and renames
colossiansStudy.tsx/colossiansStudyData.ts to studyPages.tsx/legacyColossiansData.ts
now that the study system covers more than Colossians.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-24 09:21:54 -04:00
nmemmertandClaude Sonnet 4.6 e158e1e53a Codebase audit: remove dead code, fix broken patterns, consolidate duplicates; v2.0.27
Publish Container / docker (push) Failing after 10m40s
- Remove unused prop-types dep, deprecated useOutboundLinkTracking export
- Fix missing linkClicks key in analyticsEvents initial state (B-01)
- Export ADMIN_SESSION_COOKIE from auth.js; remove local copy in admin-auth.js (D-02)
- Remove cookieFlags() wrapper in auth.js and study-helpers.js; use cookieSecureFlag() directly (U-04)
- Remove ADMIN_SESSION_TTL_MS duplicate from config.js (D-01)
- Remove DEFAULT_SEO/DEFAULT_LEGAL/DEFAULT_REDIRECT_RULES/DEFAULT_PODCAST_FEATURED_LINKS/DEFAULT_PUBLISH_STATE
  duplicates from config.js; re-export from helpers.js (D-04, fixes sitemapPaths divergence)
- Fix email.js hardcoded fallback strings to use DEFAULT_RESEND_TO/DEFAULT_RESEND_REPLY_TO (M-02)
- Remove duplicate normalizeIp, isPrivateOrLocalIp, normalizeRedirectPath, sanitizeUrl,
  sanitizeRedirectRules bodies from study-helpers.js; re-export from helpers.js (D-03)
- Fix pruneStatsByDays to import VISITOR_RETENTION_DAYS_DEFAULT from config.js (B-02)
- Fix createTitusDownloadToken to use DOWNLOAD_TOKEN_TTL_MS constant (B-03)
- Remove normalizeMessageType duplicate from study-helpers.js (U-03/D-08)
- Remove dead wantsWelcome variable in contact.js; use shouldSendWelcome (B-04)
- Replace dynamic imports in contact.js with static imports (B-07)
- Remove dead wantsWelcome=true in study-auth.js (B-05)
- Merge duplicate import blocks in analytics.js and public.js (D-05, D-06)
- Replace dynamic readFile re-import in nuggets-host-upload.js (B-06)
- Replace hardcoded SITE_URL fallbacks with getCanonicalBaseUrl() (M-06)
- Replace raw resend.emails.send in study-account.js with sendResendEmailWithRetry (M-01)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-17 16:10:31 -04:00
nmemmertandClaude Sonnet 4.6 038ef2b039 Add study account engagement features; v1.2.4
- Pause reminders: remindersPausedUntil field + scheduler skip + account UI with 7/14/30-day presets
- Email digest mode: reminderFrequency field (immediate/weekly) + weekly digest email + scheduler + account UI toggle
- Admin user list improvements: streak, reengagement, frequency, pause, per-study lesson progress
- Admin manual re-engagement: POST endpoint + tier selector button in admin panel
- Study bookmarks: bookmarkedSectionIds in progress file + POST/DELETE endpoints + toggle button in lesson UI
- Study completion certificate email: auto-sent on first certificate issuance
- Content fields for certificate and weekly digest email templates in admin

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-09-03 15:16:47 -04:00
nmemmertandClaude Sonnet 4.6 1e4fe5f0e3 v1.1.0 — RSS feed, PWA, lesson comments, progress tracking + streaks
RSS Feed
- /feed.xml proxies the Anchor feed under the site's canonical domain
- Rewrites channel <link> and atom:link self-ref to the site URL
- Served with 30-min Cache-Control, reuses the existing episode cache

PWA
- vite-plugin-pwa installed; Workbox service worker auto-generated on build
- manifest.json inlined in vite.config.ts (name, icons, theme, standalone)
- pwa-192.png and pwa-512.png generated from existing book_icon.png
- StaleWhileRevalidate for /api/episodes and /api/questions; CacheFirst for images
- API, feed.xml, and uploads routes excluded from navigate fallback

Lesson Comments
- Import and wire StudySectionComments into ColossiansStudySectionPage
- Replaces the CommunityBoard in the Lesson Discussion section
- All routes and moderation already existed; only the render was missing

Progress Tracking + Streaks
- Mark-complete handler now records lastStudiedDate, currentStreak, longestStreak on the user record
- Streak increments on consecutive calendar days, resets on a gap
- /api/study-account/overview now returns streak fields
- Account page: 4-stat summary row (notes, streak 🔥, longest streak, member since)
- Per-study progress bars showing completedLessons/totalLessons with gold → green fill at 100%

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-22 13:19:25 -04:00
nmemmertandClaude Sonnet 4.6 1d43875e5a Fix 11 bugs: restore crash, draft leak, email failures, memory leaks
Critical fixes:
- sanitizeLoadedHitStats/VisitorStats: restore full state shape so a
  snapshot restore no longer crashes hit-counting middleware (missing
  byPathReal, byPathBot, byDayReal, byDayBot, botReasons, ipHashIndex)
- /questions/share/🆔 read state.questions only, not draft questions
- inbound-email: validate date with Number.isFinite before toISOString
- study-reminders: wrap each send in try/catch so one failure doesn't
  block remaining users; persist sent-markers after each success

Security:
- getClientIp: use req.ip (trust-proxy-resolved) instead of raw
  x-forwarded-for header to prevent IP spoofing
- env-snapshot.env: delete immediately after backup tar stream ends
  so secrets don't linger on disk between exports

Correctness / UX:
- contact form: email failures no longer 500 the user after the
  submission is already saved; log and fall through instead
- study-account profile: cap data URI avatar at 6 MB
- admin enrollment PATCH: validate slug against study catalog
- signup: return 503 at MAX_STUDY_USERS instead of silently dropping
  oldest accounts

Memory leaks:
- contactHits, downloadHits Maps: prune stale entries at 5000 entries
- resendEmailSubmissionIndex: trim to 2000 entries (oldest first)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-16 07:48:54 -04:00
nmemmert 9174331d2d Bug fixes (real breakage):
Newsletter nudge — was calling /api/study-account/profile (wrong endpoint, ignored subscription). Now correctly calls /api/study-account/preferences with PATCH.
Security:

Email regex — replaced the permissive [^\s@]+@[^\s@]+ pattern with a proper RFC-compliant regex in contact.js and downloads.js
Avatar magic bytes — server now checks actual PNG/JPEG/GIF/WEBP header bytes, not just the data URL prefix
Certificate rate limit — public /api/public/certificate/:token now has a 30 req/15min limiter
Session absolute TTL — admin sessions now have a 30-day hard cap; a stolen token can no longer be kept alive indefinitely by passive reads
Account lockout — 5 failed logins locks a study account for 1 hour
CSP headers — Content-Security-Policy, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy headers added globally
Data integrity:

Cascade delete — deleting a study account now also removes their certificates, community posts, comments, and progress file
UX / reliability:

Escape key on modals — all 3 modal groups (study index, notes, account) now close on Escape
Display name min-length — empty spaces-only names rejected; if provided, must be ≥2 chars
Note save rate limit — 30 saves/minute per user max
Analytics fetch timeout — 5s AbortController so a hanging server doesn't block the browser indefinitely
Email validation on signup — frontend catches bad email formats before hitting the server
Cleanup:

Deduplicated download forms — StudyDownloadForm and ResourceDownloadForm now share a single DownloadForm base; both are now thin wrappers
2026-06-18 09:35:44 -04:00
nmemmert 346ecb217c refractor server.js 2026-06-03 15:08:14 -04:00