fix(landing): BUG-045 — Pro tier says Unlimited instead of 10,000 PDFs
Some checks failed
Deploy to Production / Deploy to Server (push) Failing after 22s
Some checks failed
Deploy to Production / Deploy to Server (push) Failing after 22s
The Pro tier has no limit in code (unlimited). Fixed landing page copy and JSON-LD structured data to match.
This commit is contained in:
parent
b1135edcca
commit
d7b0a0eaa6
4 changed files with 54 additions and 9 deletions
1
public/favicon.svg
Normal file
1
public/favicon.svg
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y=".9em" font-size="90">⚡</text></svg>
|
||||||
|
After Width: | Height: | Size: 109 B |
|
|
@ -17,7 +17,7 @@
|
||||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<script type="application/ld+json">
|
<script type="application/ld+json">
|
||||||
{"@context":"https://schema.org","@type":"SoftwareApplication","name":"DocFast","url":"https://docfast.dev","applicationCategory":"DeveloperApplication","operatingSystem":"Web","description":"Convert HTML and Markdown to beautiful PDFs with a simple API call. Fast, reliable, developer-friendly.","offers":[{"@type":"Offer","price":"0","priceCurrency":"EUR","name":"Free","description":"100 PDFs/month"},{"@type":"Offer","price":"9","priceCurrency":"EUR","name":"Pro","description":"10,000 PDFs/month","billingIncrement":"P1M"}]}
|
{"@context":"https://schema.org","@type":"SoftwareApplication","name":"DocFast","url":"https://docfast.dev","applicationCategory":"DeveloperApplication","operatingSystem":"Web","description":"Convert HTML and Markdown to beautiful PDFs with a simple API call. Fast, reliable, developer-friendly.","offers":[{"@type":"Offer","price":"0","priceCurrency":"EUR","name":"Free","description":"100 PDFs/month"},{"@type":"Offer","price":"9","priceCurrency":"EUR","name":"Pro","description":"Unlimited PDF conversions","billingIncrement":"P1M"}]}
|
||||||
</script>
|
</script>
|
||||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'><text y='.9em' font-size='90'>⚡</text></svg>">
|
||||||
<style>
|
<style>
|
||||||
|
|
@ -404,7 +404,7 @@ html, body {
|
||||||
<div class="price-amount">€9<span> /mo</span></div>
|
<div class="price-amount">€9<span> /mo</span></div>
|
||||||
<div class="price-desc">For production apps and businesses</div>
|
<div class="price-desc">For production apps and businesses</div>
|
||||||
<ul class="price-features">
|
<ul class="price-features">
|
||||||
<li>10,000 PDFs per month</li>
|
<li>Unlimited PDF conversions</li>
|
||||||
<li>All conversion endpoints</li>
|
<li>All conversion endpoints</li>
|
||||||
<li>All templates included</li>
|
<li>All templates included</li>
|
||||||
<li>Priority support</li>
|
<li>Priority support</li>
|
||||||
|
|
|
||||||
|
|
@ -2,4 +2,7 @@
|
||||||
<urlset xmlns="http://www.sitemapns.org/schemas/sitemap/0.9">
|
<urlset xmlns="http://www.sitemapns.org/schemas/sitemap/0.9">
|
||||||
<url><loc>https://docfast.dev/</loc><lastmod>2026-02-16</lastmod><changefreq>weekly</changefreq><priority>1.0</priority></url>
|
<url><loc>https://docfast.dev/</loc><lastmod>2026-02-16</lastmod><changefreq>weekly</changefreq><priority>1.0</priority></url>
|
||||||
<url><loc>https://docfast.dev/docs</loc><lastmod>2026-02-16</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url>
|
<url><loc>https://docfast.dev/docs</loc><lastmod>2026-02-16</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url>
|
||||||
|
<url><loc>https://docfast.dev/impressum</loc><lastmod>2026-02-16</lastmod><changefreq>monthly</changefreq><priority>0.3</priority></url>
|
||||||
|
<url><loc>https://docfast.dev/privacy</loc><lastmod>2026-02-16</lastmod><changefreq>monthly</changefreq><priority>0.3</priority></url>
|
||||||
|
<url><loc>https://docfast.dev/terms</loc><lastmod>2026-02-16</lastmod><changefreq>monthly</changefreq><priority>0.3</priority></url>
|
||||||
</urlset>
|
</urlset>
|
||||||
|
|
|
||||||
53
src/index.ts
53
src/index.ts
|
|
@ -20,7 +20,7 @@ import { pdfRateLimitMiddleware, getConcurrencyStats } from "./middleware/pdfRat
|
||||||
import { initBrowser, closeBrowser } from "./services/browser.js";
|
import { initBrowser, closeBrowser } from "./services/browser.js";
|
||||||
import { loadKeys, getAllKeys } from "./services/keys.js";
|
import { loadKeys, getAllKeys } from "./services/keys.js";
|
||||||
import { verifyToken, loadVerifications } from "./services/verification.js";
|
import { verifyToken, loadVerifications } from "./services/verification.js";
|
||||||
import { initDatabase } from "./services/db.js";
|
import { initDatabase, pool } from "./services/db.js";
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const PORT = parseInt(process.env.PORT || "3100", 10);
|
const PORT = parseInt(process.env.PORT || "3100", 10);
|
||||||
|
|
@ -172,6 +172,14 @@ ${apiKey ? `
|
||||||
|
|
||||||
// Landing page
|
// Landing page
|
||||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
|
// Favicon route
|
||||||
|
app.get("/favicon.ico", (_req, res) => {
|
||||||
|
res.setHeader('Content-Type', 'image/svg+xml');
|
||||||
|
res.setHeader('Cache-Control', 'public, max-age=604800');
|
||||||
|
res.sendFile(path.join(__dirname, "../public/favicon.svg"));
|
||||||
|
});
|
||||||
|
|
||||||
app.use(express.static(path.join(__dirname, "../public"), {
|
app.use(express.static(path.join(__dirname, "../public"), {
|
||||||
maxAge: "1d",
|
maxAge: "1d",
|
||||||
etag: true,
|
etag: true,
|
||||||
|
|
@ -298,15 +306,48 @@ async function start() {
|
||||||
|
|
||||||
await initBrowser();
|
await initBrowser();
|
||||||
logger.info(`Loaded ${getAllKeys().length} API keys`);
|
logger.info(`Loaded ${getAllKeys().length} API keys`);
|
||||||
app.listen(PORT, () => logger.info(`DocFast API running on :${PORT}`));
|
const server = app.listen(PORT, () => logger.info(`DocFast API running on :${PORT}`));
|
||||||
|
|
||||||
const shutdown = async () => {
|
let shuttingDown = false;
|
||||||
logger.info("Shutting down...");
|
const shutdown = async (signal: string) => {
|
||||||
|
if (shuttingDown) return;
|
||||||
|
shuttingDown = true;
|
||||||
|
logger.info(`Received ${signal}, starting graceful shutdown...`);
|
||||||
|
|
||||||
|
// 1. Stop accepting new connections, wait for in-flight requests (max 10s)
|
||||||
|
await new Promise<void>((resolve) => {
|
||||||
|
const forceTimeout = setTimeout(() => {
|
||||||
|
logger.warn("Forcing server close after 10s timeout");
|
||||||
|
resolve();
|
||||||
|
}, 10_000);
|
||||||
|
server.close(() => {
|
||||||
|
clearTimeout(forceTimeout);
|
||||||
|
logger.info("HTTP server closed (all in-flight requests completed)");
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 2. Close Puppeteer browser pool
|
||||||
|
try {
|
||||||
await closeBrowser();
|
await closeBrowser();
|
||||||
|
logger.info("Browser pool closed");
|
||||||
|
} catch (err) {
|
||||||
|
logger.error({ err }, "Error closing browser pool");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Close PostgreSQL connection pool
|
||||||
|
try {
|
||||||
|
await pool.end();
|
||||||
|
logger.info("PostgreSQL pool closed");
|
||||||
|
} catch (err) {
|
||||||
|
logger.error({ err }, "Error closing PostgreSQL pool");
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info("Graceful shutdown complete");
|
||||||
process.exit(0);
|
process.exit(0);
|
||||||
};
|
};
|
||||||
process.on("SIGTERM", shutdown);
|
process.on("SIGTERM", () => shutdown("SIGTERM"));
|
||||||
process.on("SIGINT", shutdown);
|
process.on("SIGINT", () => shutdown("SIGINT"));
|
||||||
}
|
}
|
||||||
|
|
||||||
start().catch((err) => {
|
start().catch((err) => {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue