- Add /change-email as a proper standalone page (public/src/change-email.html)
with API key input, new email input, verification code flow, and success state
- Update footer partial: change "/#change-email" link to "/change-email" on all pages
- Remove email change modal HTML and hash-handler JS from index page source
- Add /change-email to sitemap.xml
- Rebuild all HTML files via build-html.cjs
- Add updateEmailByCustomer() to src/services/keys.ts
- Add customer.updated webhook handler in src/routes/billing.ts
to sync email changes made via Stripe dashboard back to DocFast
- Replace revokeByCustomer with downgradeByCustomer in keys.ts
- Sets tier='free' in cache and DB (UPDATE, not DELETE)
- Add isDocFastSubscription() product filter helper in billing.ts
- Filters all subscription events by prod_TygeG8tQPtEAdE
- Handle customer.subscription.updated event
- Downgrades on status=canceled/past_due/unpaid or cancel_at_period_end=true
- Handle customer.subscription.deleted with product filter
- Downgrades to free (was incorrectly deleting the key)
Fixes revenue integrity bug: cancelled Pro subscribers kept Pro access.
- BUG-056: Fix sitemap namespace sitemapns.org -> sitemaps.org
- BUG-062: Extend <main> to wrap all page content (hero+features+pricing+EU section)
- BUG-064: Add sr-only <label> elements to all modal form inputs (signup, recovery, change-email)
- BUG-051/052: Remove duplicate X-Content-Type-Options headers from nginx (let helmet handle)
- BUG-057: Fix JSON-LD and pricing card: Pro plan is 2,500 PDFs/month not 5,000
- BUG-059: Add meta description, canonical URL, og: tags to /docs page
- BUG-063: Change eu-hosting h3 to h2 (correct heading hierarchy)
- BUG-065/066: Add aria-modal=true, role=dialog to modals; aria-label=Close to close buttons
- BUG-068: Add hash-based modal open for #change-email on page load
- Add .sr-only CSS utility class to base and index styles
- Created build-time templating system using existing build-html.cjs
- Extracted index.html into source template with partials:
_styles_index.html, _nav_index.html, _modals.html
- All 4 templated pages (index, impressum, privacy, terms) use partials
- docs.html excluded (Swagger UI, completely different structure)
- Added HTML build step to Dockerfile
- Built output is byte-identical to original files
- Set Pro tier limit to 2,500 PDFs/month (was unlimited/5000)
- Added Pro limit enforcement in usage middleware
- Updated landing page, JSON-LD, and Stripe product description
- Created build-time HTML templating (partials for nav/footer/styles)
- Source files in public/src/, partials in public/partials/
- Build script: node scripts/build-html.cjs
- Deleted stale backup file
- Fixed index.html nav logo to use <a> tag for consistency
BUG-046 (CRITICAL): getUsageStats() now accepts apiKey param and returns
only that key usage instead of all users. Route passes req.apiKeyInfo.key.
BUG-047: Added visible Copy button to Pro key success page in billing.ts.
BUG-048: Added class="open-email-change" to Change Email links in all
HTML pages so the JS modal opener can find them.
- Add footer links for Impressum, Privacy Policy, Terms of Service
- Create legal pages: /impressum, /privacy, /terms (Austrian/EU compliant)
- Add EU hosting badge highlighting GDPR compliance and EU data residency
- Add Express routes for legal pages with caching headers
- All pages use consistent dark theme design matching landing page
- Docker healthcheck: Use Node.js instead of curl (not installed in slim image)
- Pricing: Change from USD ($) to EUR (€) in frontend and backend Stripe integration
- Static assets: Add Cache-Control headers (1 day) for /public and /docs files
- Remove duplicate text/html from nginx gzip_types (fixes nginx -t warning)
- Update hardcoded API version from 0.2.0 to 0.2.1 to match package.json
- Update logrotate config: daily rotation, 7 days retention, improved postrotate
- Add Docker logging configuration with size/file limits
- Add fe80::/10 (IPv6 link-local) detection to isPrivateIP()
- Update error message to match specification: 'URL resolves to a private/internal IP address'
- Existing protections already covered all required IPv4 ranges and IPv6 localhost
- Update sitemap.xml to include /v1/health endpoint
- Add proper 404 handling (JSON for API paths, HTML for browser paths)
- Create optimized nginx config with gzip, cache headers, specific locations
- Add logrotate configuration for DocFast logs
- Add security headers and static asset caching
- Full backup of PostgreSQL, Docker volumes, nginx config, SSL certs, crontabs, OpenDKIM
- Daily backups at 03:00 UTC with 7d/4w/3m retention
- Local storage at /opt/borg-backups/docfast
- Restore testing verified
- Documentation for disaster recovery procedures
- Add .forgejo/workflows/deploy.yml for automated deployment
- Include rollback mechanism with image tagging
- Add health check verification (http://127.0.0.1:3100/health)
- Create manual rollback script for emergency use
- Add deployment documentation and setup instructions
- Supports auto-rollback on deployment failure