UNPKG

@limitrate/cli

Version:

CLI dashboard for LimitRate rate limiting inspection

150 lines (146 loc) 5.84 kB
#!/usr/bin/env node import { inspect } from "./chunk-PKQ457TL.mjs"; import "./chunk-ZWXM33YD.mjs"; // src/cli.ts import { Command } from "commander"; // src/commands/inspect-endpoints.ts import Table from "cli-table3"; import { getGlobalEndpointTracker } from "@limitrate/core"; function inspectEndpoints(options = {}) { const tracker = getGlobalEndpointTracker(); const endpoints = tracker.getEndpoints(); const stats = tracker.getStats(); if (options.json) { console.log( JSON.stringify( { summary: stats, endpoints: endpoints.map((e) => ({ method: e.method, path: e.path, hasRateLimit: e.hasRateLimit, requestCount: e.requestCount, rateLimitedCount: e.rateLimitedCount, policy: e.policy, limit: e.limit, firstSeen: e.firstSeen, lastSeen: e.lastSeen })) }, null, 2 ) ); if (options.failOnUnprotected && stats.unprotectedEndpoints > 0) { process.exit(1); } return; } console.log("\n\u{1F50D} Endpoint Auto-Discovery (Last 24 Hours)\n"); console.log("=" + "=".repeat(80)); console.log(""); console.log(`\u{1F4CA} Summary:`); console.log(` Total Endpoints: ${stats.totalEndpoints}`); console.log(` Protected: ${stats.protectedEndpoints} \u2705`); console.log(` Unprotected: ${stats.unprotectedEndpoints} ${stats.unprotectedEndpoints > 0 ? "\u26A0\uFE0F" : "\u2705"}`); console.log(` Total Requests: ${stats.totalRequests.toLocaleString()}`); console.log(` Rate Limited: ${stats.totalRateLimited.toLocaleString()}`); console.log(""); if (endpoints.length === 0) { console.log("\u{1F4ED} No endpoints discovered yet. Start making requests!\n"); console.log("=" + "=".repeat(80)); console.log(""); return; } const protectedEndpoints = endpoints.filter((e) => e.hasRateLimit); if (protectedEndpoints.length > 0) { console.log("\u2705 Protected Endpoints:\n"); const protectedTable = new Table({ head: ["Status", "Method", "Path", "Requests", "Limited", "Policy", "Limit"], colWidths: [8, 8, 30, 12, 10, 12, 10] }); for (const endpoint of protectedEndpoints) { protectedTable.push([ "\u2713", endpoint.method, endpoint.path.length > 28 ? endpoint.path.substring(0, 25) + "..." : endpoint.path, endpoint.requestCount.toLocaleString(), endpoint.rateLimitedCount.toLocaleString(), endpoint.policy || "-", endpoint.limit ? `${endpoint.limit}/min` : "-" ]); } console.log(protectedTable.toString()); console.log(""); } const unprotectedEndpoints = endpoints.filter((e) => !e.hasRateLimit); if (unprotectedEndpoints.length > 0) { console.log("\u26A0\uFE0F UNPROTECTED Endpoints (NO RATE LIMITS):\n"); const unprotectedTable = new Table({ head: ["Status", "Method", "Path", "Requests", "Severity"], colWidths: [8, 8, 30, 12, 20], style: { head: ["yellow"] } }); for (const endpoint of unprotectedEndpoints) { const severity = getSeverity(endpoint.requestCount); unprotectedTable.push([ "\u26A0", endpoint.method, endpoint.path.length > 28 ? endpoint.path.substring(0, 25) + "..." : endpoint.path, endpoint.requestCount.toLocaleString(), severity ]); } console.log(unprotectedTable.toString()); console.log(""); console.log("\u{1F6A8} WARNING: These endpoints have NO rate limiting!\n"); console.log("Suggestions:"); console.log(" - Add limitrate middleware before these routes"); console.log(" - Or add them to your policy configuration"); console.log(" - Or use skip() to intentionally bypass rate limiting"); console.log(""); const highTraffic = unprotectedEndpoints.filter((e) => e.requestCount >= 50); if (highTraffic.length > 0) { console.log("\u{1F525} High Priority (high traffic, no protection):"); for (const endpoint of highTraffic) { console.log(` ${endpoint.method} ${endpoint.path} - ${endpoint.requestCount} requests`); } console.log(""); } } console.log("=" + "=".repeat(80)); console.log(""); if (stats.unprotectedEndpoints > 0) { console.log("\u{1F4A1} Tip: Add rate limits to unprotected endpoints to prevent abuse"); console.log("\u{1F4D6} Docs: https://github.com/limitrate/limitrate\n"); if (options.failOnUnprotected) { console.log("\u274C CI/CD Check FAILED: Unprotected endpoints detected\n"); process.exit(1); } } else { console.log("\u2705 All discovered endpoints are protected by rate limiting!"); console.log("\u{1F4D6} Docs: https://github.com/limitrate/limitrate\n"); } } function getSeverity(requestCount) { if (requestCount >= 500) return "\u{1F534} CRITICAL"; if (requestCount >= 100) return "\u{1F7E0} HIGH"; if (requestCount >= 50) return "\u{1F7E1} MEDIUM"; return "\u{1F7E2} LOW"; } // src/cli.ts var program = new Command(); program.name("limitrate").description("LimitRate CLI - Inspect rate limiting and cost control events").version("0.1.0"); program.command("inspect").description("Show dashboard with endpoint stats, top offenders, and recent events").action(() => { inspect(); }); program.command("inspect-endpoints").description("Show endpoint auto-discovery (v1.4.0) - which endpoints have rate limits").option("--json", "Output as JSON for CI/CD").option("--fail-on-unprotected", "Exit with code 1 if unprotected endpoints found (for CI/CD)").action((options) => { inspectEndpoints({ json: options.json, failOnUnprotected: options.failOnUnprotected }); }); if (process.argv.length === 2) { inspect(); } else { program.parse(); }