session 11: fixed signup flows, unified key store

This commit is contained in:
Hoid 2026-02-14 14:20:32 +00:00
parent 7bb583745a
commit 8fddc6009e
13 changed files with 401 additions and 143 deletions

View file

@ -6,19 +6,23 @@ import rateLimit from "express-rate-limit";
import { convertRouter } from "./routes/convert.js";
import { templatesRouter } from "./routes/templates.js";
import { healthRouter } from "./routes/health.js";
import { signupRouter } from "./routes/signup.js";
import { billingRouter } from "./routes/billing.js";
import { authMiddleware } from "./middleware/auth.js";
import { usageMiddleware } from "./middleware/usage.js";
import { getUsageStats } from "./middleware/usage.js";
import { initBrowser, closeBrowser } from "./services/browser.js";
import { billingRouter } from "./routes/billing.js";
import { loadKeys, getAllKeys } from "./services/keys.js";
const app = express();
const PORT = parseInt(process.env.PORT || "3100", 10);
// Load API keys from persistent store
loadKeys();
app.use(helmet());
// Raw body for Stripe webhook signature verification
app.use("/v1/billing/webhook", express.raw({ type: "application/json" }));
app.use(express.json({ limit: "2mb" }));
app.use(express.text({ limit: "2mb", type: "text/*" }));
// Rate limiting: 100 req/min for free tier
// Rate limiting
const limiter = rateLimit({
windowMs: 60_000,
max: 100,
@ -26,37 +30,39 @@ const limiter = rateLimit({
legacyHeaders: false,
});
app.use(limiter);
// Public
// Public routes
app.use("/health", healthRouter);
// Authenticated
app.use("/v1/signup", signupRouter);
app.use("/v1/billing", billingRouter);
// Authenticated routes
app.use("/v1/convert", authMiddleware, usageMiddleware, convertRouter);
app.use("/v1/templates", authMiddleware, usageMiddleware, templatesRouter);
// Billing (public — Stripe handles auth)
app.use("/v1/billing", billingRouter);
// Admin: usage stats (protected by auth)
// Admin: usage stats
app.get("/v1/usage", authMiddleware, (_req, res) => {
res.json(getUsageStats());
});
// Landing page
const __dirname = path.dirname(fileURLToPath(import.meta.url));
app.use(express.static(path.join(__dirname, "../public")));
// API root (for programmatic discovery)
// API root
app.get("/api", (_req, res) => {
res.json({
name: "DocFast API",
version: "0.1.0",
docs: "/health",
version: "0.2.0",
endpoints: [
"POST /v1/signup/free — Get a free API key",
"POST /v1/convert/html",
"POST /v1/convert/markdown",
"POST /v1/convert/url",
"POST /v1/templates/:id/render",
"GET /v1/templates",
"POST /v1/billing/checkout — Start Pro subscription",
],
});
});
async function start() {
await initBrowser();
console.log(`Loaded ${getAllKeys().length} API keys`);
app.listen(PORT, () => console.log(`DocFast API running on :${PORT}`));
const shutdown = async () => {
console.log("Shutting down...");