session 11: fixed signup flows, unified key store
This commit is contained in:
parent
7bb583745a
commit
8fddc6009e
13 changed files with 401 additions and 143 deletions
26
projects/business/src/pdf-api/dist/index.js
vendored
26
projects/business/src/pdf-api/dist/index.js
vendored
|
|
@ -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...");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
const API_KEYS = new Set((process.env.API_KEYS || "test-key-123").split(",").map((k) => k.trim()));
|
||||
import { isValidKey, getKeyInfo } from "../services/keys.js";
|
||||
export function authMiddleware(req, res, next) {
|
||||
const header = req.headers.authorization;
|
||||
if (!header?.startsWith("Bearer ")) {
|
||||
|
|
@ -6,9 +6,11 @@ export function authMiddleware(req, res, next) {
|
|||
return;
|
||||
}
|
||||
const key = header.slice(7);
|
||||
if (!API_KEYS.has(key)) {
|
||||
if (!isValidKey(key)) {
|
||||
res.status(403).json({ error: "Invalid API key" });
|
||||
return;
|
||||
}
|
||||
// Attach key info to request for downstream use
|
||||
req.apiKeyInfo = getKeyInfo(key);
|
||||
next();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue