From 467a97ae1ce85d088c74f0f825e5468f4bed4e4b Mon Sep 17 00:00:00 2001 From: DocFast Bot Date: Sat, 14 Feb 2026 14:20:05 +0000 Subject: [PATCH] fix: self-service signup, unified key store, persistent data volume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added /v1/signup/free endpoint for instant API key provisioning - Built unified key store (services/keys.ts) with file-based persistence - Refactored auth middleware to use key store (no more hardcoded env keys) - Refactored usage middleware to check key tier from store - Updated billing to use key store for Pro key provisioning - Landing page: replaced mailto: link with signup modal - Landing page: Pro checkout button now properly calls /v1/billing/checkout - Added Docker volume for persistent key storage - Success page now renders HTML instead of raw JSON - Tested: signup → key → PDF generation works end-to-end --- .gitignore | 1 + docker-compose.yml | 5 ++ public/index.html | 133 +++++++++++++++++++++++++++++++++--- src/index.ts | 28 +++++--- src/middleware/auth.ts | 9 ++- src/middleware/usage.ts | 14 ++-- src/routes/billing.ts | 146 +++++++++++++++------------------------- src/routes/signup.ts | 33 +++++++++ src/services/keys.ts | 118 ++++++++++++++++++++++++++++++++ 9 files changed, 361 insertions(+), 126 deletions(-) create mode 100644 src/routes/signup.ts create mode 100644 src/services/keys.ts diff --git a/.gitignore b/.gitignore index aa0926a..caf428f 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules/ dist/ .env *.log +data/ diff --git a/docker-compose.yml b/docker-compose.yml index a1ec12f..9a0458c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,5 +13,10 @@ services: - STRIPE_WEBHOOK_SECRET=${STRIPE_WEBHOOK_SECRET} - BASE_URL=${BASE_URL:-https://docfast.dev} - PRO_KEYS=${PRO_KEYS} + volumes: + - docfast-data:/app/data mem_limit: 512m cpus: 1.0 + +volumes: + docfast-data: diff --git a/public/index.html b/public/index.html index 4d58cd4..ec99b8d 100644 --- a/public/index.html +++ b/public/index.html @@ -11,7 +11,6 @@ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: var(--bg); color: var(--fg); line-height: 1.6; } a { color: var(--accent); text-decoration: none; } a:hover { text-decoration: underline; } - .container { max-width: 960px; margin: 0 auto; padding: 0 24px; } /* Hero */ @@ -20,10 +19,10 @@ a:hover { text-decoration: underline; } .hero h1 span { color: var(--accent); } .hero p { font-size: 1.25rem; color: var(--muted); max-width: 600px; margin: 0 auto 40px; } .hero-actions { display: flex; gap: 16px; justify-content: center; flex-wrap: wrap; } -.btn { display: inline-block; padding: 14px 32px; border-radius: 8px; font-size: 1rem; font-weight: 600; transition: all 0.2s; } +.btn { display: inline-block; padding: 14px 32px; border-radius: 8px; font-size: 1rem; font-weight: 600; transition: all 0.2s; border: none; cursor: pointer; } .btn-primary { background: var(--accent); color: #000; } .btn-primary:hover { background: #6fb; text-decoration: none; } -.btn-secondary { border: 1px solid var(--border); color: var(--fg); } +.btn-secondary { border: 1px solid var(--border); color: var(--fg); background: transparent; } .btn-secondary:hover { border-color: var(--muted); text-decoration: none; } /* Code block */ @@ -65,6 +64,24 @@ a:hover { text-decoration: underline; } /* Footer */ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0.85rem; border-top: 1px solid var(--border); } + +/* Modal */ +.modal-overlay { display: none; position: fixed; inset: 0; background: rgba(0,0,0,0.7); z-index: 100; align-items: center; justify-content: center; } +.modal-overlay.active { display: flex; } +.modal { background: var(--card); border: 1px solid var(--border); border-radius: 16px; padding: 40px; max-width: 440px; width: 90%; } +.modal h2 { margin-bottom: 8px; font-size: 1.5rem; } +.modal p { color: var(--muted); margin-bottom: 24px; font-size: 0.95rem; } +.modal input { width: 100%; padding: 14px 16px; border-radius: 8px; border: 1px solid var(--border); background: var(--bg); color: var(--fg); font-size: 1rem; margin-bottom: 16px; outline: none; } +.modal input:focus { border-color: var(--accent); } +.modal .btn { width: 100%; text-align: center; } +.modal .error { color: #f66; font-size: 0.85rem; margin-bottom: 12px; display: none; } +.modal .close { position: absolute; top: 16px; right: 20px; color: var(--muted); font-size: 1.5rem; cursor: pointer; background: none; border: none; } + +/* Key result */ +.key-result { display: none; } +.key-result .key-box { background: var(--bg); border: 1px solid var(--accent); border-radius: 8px; padding: 16px; font-family: monospace; font-size: 0.85rem; word-break: break-all; margin: 16px 0; cursor: pointer; transition: background 0.2s; } +.key-result .key-box:hover { background: #111; } +.key-result .copy-hint { color: var(--muted); font-size: 0.8rem; text-align: center; } @@ -74,12 +91,12 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0.

HTML & Markdown to PDF

One API call. Beautiful PDFs. Built-in invoice templates. No headless browser setup, no dependencies, no hassle.

- Get API Key + View Docs
// Convert markdown to PDF in one call
- curl -X POST https://api.docfast.dev/v1/convert/markdown \
+ curl -X POST https://docfast.dev/v1/convert/markdown \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"markdown": "# Invoice\\n\\nAmount: $500"}' \
@@ -139,6 +156,11 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0. /v1/convert/markdown
Convert Markdown to styled PDF with syntax highlighting.
+
+ POST + /v1/convert/url +
Navigate to a URL and convert the page to PDF.
+
GET /v1/templates @@ -171,7 +193,7 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0.
  • All templates
  • Community support
  • - Get Free Key +
    @@ -195,9 +217,102 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0. + + +