fix: OpenAPI spec accuracy — hide internal endpoints, mark signup/verify deprecated
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 13m9s
All checks were successful
Build & Deploy to Staging / Build & Deploy to Staging (push) Successful in 13m9s
- Remove @openapi annotations from /v1/billing/webhook (Stripe-internal) - Remove @openapi annotations from /v1/billing/success (browser redirect) - Mark /v1/signup/verify as deprecated (returns 410) - Add 3 TDD tests in openapi-spec.test.ts - Update 2 existing tests in app-routes.test.ts - 530 tests passing (was 527)
This commit is contained in:
parent
1d5d9adf08
commit
6b1b3d584e
15 changed files with 399 additions and 290 deletions
73
dist/index.js
vendored
73
dist/index.js
vendored
|
|
@ -14,13 +14,14 @@ import { templatesRouter } from "./routes/templates.js";
|
|||
import { healthRouter } from "./routes/health.js";
|
||||
import { demoRouter } from "./routes/demo.js";
|
||||
import { recoverRouter } from "./routes/recover.js";
|
||||
import { emailChangeRouter } from "./routes/email-change.js";
|
||||
import { billingRouter } from "./routes/billing.js";
|
||||
import { authMiddleware } from "./middleware/auth.js";
|
||||
import { usageMiddleware, loadUsageData } from "./middleware/usage.js";
|
||||
import { getUsageStats } from "./middleware/usage.js";
|
||||
import { usageMiddleware, loadUsageData, flushDirtyEntries } from "./middleware/usage.js";
|
||||
import { getUsageStats, getUsageForKey } from "./middleware/usage.js";
|
||||
import { pdfRateLimitMiddleware, getConcurrencyStats } from "./middleware/pdfRateLimit.js";
|
||||
import { initBrowser, closeBrowser } from "./services/browser.js";
|
||||
import { loadKeys, getAllKeys } from "./services/keys.js";
|
||||
import { loadKeys, getAllKeys, isProKey } from "./services/keys.js";
|
||||
import { verifyToken, loadVerifications } from "./services/verification.js";
|
||||
import { initDatabase, pool, cleanupStaleData } from "./services/db.js";
|
||||
import { swaggerSpec } from "./swagger.js";
|
||||
|
|
@ -53,7 +54,8 @@ app.use((req, res, next) => {
|
|||
const isAuthBillingRoute = req.path.startsWith('/v1/signup') ||
|
||||
req.path.startsWith('/v1/recover') ||
|
||||
req.path.startsWith('/v1/billing') ||
|
||||
req.path.startsWith('/v1/demo');
|
||||
req.path.startsWith('/v1/demo') ||
|
||||
req.path.startsWith('/v1/email-change');
|
||||
if (isAuthBillingRoute) {
|
||||
res.setHeader("Access-Control-Allow-Origin", "https://docfast.dev");
|
||||
}
|
||||
|
|
@ -71,7 +73,8 @@ app.use((req, res, next) => {
|
|||
});
|
||||
// Raw body for Stripe webhook signature verification
|
||||
app.use("/v1/billing/webhook", express.raw({ type: "application/json" }));
|
||||
app.use(express.json({ limit: "2mb" }));
|
||||
// NOTE: No global express.json() here — route-specific parsers are applied
|
||||
// per-route below to enforce correct body size limits (BUG-101 fix).
|
||||
app.use(express.text({ limit: "2mb", type: "text/*" }));
|
||||
// Trust nginx proxy
|
||||
app.set("trust proxy", 1);
|
||||
|
|
@ -116,12 +119,58 @@ app.use("/v1/signup", (_req, res) => {
|
|||
pro_url: "https://docfast.dev/#pricing"
|
||||
});
|
||||
});
|
||||
app.use("/v1/recover", recoverRouter);
|
||||
app.use("/v1/billing", billingRouter);
|
||||
// Default 2MB JSON parser for standard routes
|
||||
const defaultJsonParser = express.json({ limit: "2mb" });
|
||||
app.use("/v1/recover", defaultJsonParser, recoverRouter);
|
||||
app.use("/v1/email-change", defaultJsonParser, emailChangeRouter);
|
||||
app.use("/v1/billing", defaultJsonParser, billingRouter);
|
||||
// Authenticated routes — conversion routes get tighter body limits (500KB)
|
||||
const convertBodyLimit = express.json({ limit: "500kb" });
|
||||
app.use("/v1/convert", convertBodyLimit, authMiddleware, usageMiddleware, pdfRateLimitMiddleware, convertRouter);
|
||||
app.use("/v1/templates", authMiddleware, usageMiddleware, templatesRouter);
|
||||
app.use("/v1/templates", defaultJsonParser, authMiddleware, usageMiddleware, templatesRouter);
|
||||
/**
|
||||
* @openapi
|
||||
* /v1/usage/me:
|
||||
* get:
|
||||
* summary: Get your current month's usage
|
||||
* description: Returns the authenticated user's PDF generation usage for the current billing month.
|
||||
* security:
|
||||
* - ApiKeyAuth: []
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Current usage statistics
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* used:
|
||||
* type: integer
|
||||
* description: Number of PDFs generated this month
|
||||
* limit:
|
||||
* type: integer
|
||||
* description: Monthly PDF limit for your plan
|
||||
* plan:
|
||||
* type: string
|
||||
* enum: [pro, demo]
|
||||
* description: Your current plan
|
||||
* month:
|
||||
* type: string
|
||||
* description: Current billing month (YYYY-MM)
|
||||
* 401:
|
||||
* description: Missing or invalid API key
|
||||
*/
|
||||
app.get("/v1/usage/me", authMiddleware, (req, res) => {
|
||||
const key = req.apiKeyInfo.key;
|
||||
const { count, monthKey } = getUsageForKey(key);
|
||||
const pro = isProKey(key);
|
||||
res.json({
|
||||
used: count,
|
||||
limit: pro ? 5000 : 100,
|
||||
plan: pro ? "pro" : "demo",
|
||||
month: monthKey,
|
||||
});
|
||||
});
|
||||
// Admin: usage stats (admin key required)
|
||||
const adminAuth = (req, res, next) => {
|
||||
const adminKey = process.env.ADMIN_API_KEY;
|
||||
|
|
@ -362,6 +411,14 @@ async function start() {
|
|||
resolve();
|
||||
});
|
||||
});
|
||||
// 1.5. Flush dirty usage entries while DB pool is still alive
|
||||
try {
|
||||
await flushDirtyEntries();
|
||||
logger.info("Usage data flushed");
|
||||
}
|
||||
catch (err) {
|
||||
logger.error({ err }, "Error flushing usage data during shutdown");
|
||||
}
|
||||
// 2. Close Puppeteer browser pool
|
||||
try {
|
||||
await closeBrowser();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue