refactor(api): remove the deprecated /api/stats endpoint

The only consumer (the marketing landing) now uses /api/v1/stats, so drop
the legacy /api/stats route and its handler. Delete src/routes/stats.ts and
its test; repoint the index CORS test at /api/v1/stats.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Julien Herr
2026-05-23 23:15:08 +02:00
parent daa93d8093
commit c2a0a68058
7 changed files with 5 additions and 87 deletions
+2 -2
View File
@@ -45,9 +45,9 @@ describe("CORS middleware", () => {
);
});
it("makes /api/stats readable from any origin", async () => {
it("makes /api/v1/stats readable from any origin", async () => {
const res = await worker.fetch(
req("/api/stats", { headers: { Origin: "https://example.com" } }),
req("/api/v1/stats", { headers: { Origin: "https://example.com" } }),
env as unknown as Env,
);
expect(res.status).toBe(200);
+1 -6
View File
@@ -6,7 +6,6 @@ import { handle as handleAtom } from "./routes/atom";
import { handle as handleAdmin } from "./routes/admin";
import { handle as handleEntry } from "./routes/entries";
import { handle as handleFiles } from "./routes/files";
import { handle as handleStats } from "./routes/stats";
import { handle as handleHome } from "./routes/home";
import { handle as handleFavicon, handleFeedFavicon } from "./routes/favicon";
import { hubRouter } from "./routes/hub";
@@ -148,10 +147,6 @@ api.use("/inbound", async (c, next) => {
// API routes (inbound webhook)
api.post("/inbound", handleInbound);
// Public monitoring stats (JSON) — readable from any origin (landing page, embeds)
api.use("/stats", cors({ origin: "*" }));
api.get("/stats", handleStats);
// RSS feed routes (public)
rss.get("/:feedId", handleRSS);
@@ -223,7 +218,7 @@ export default {
logger.info("Feed TTL cleanup", { deleted: expiredIds.length });
}
// Refresh the cached storage-usage snapshot for the status page / /api/stats.
// Refresh the cached storage-usage snapshot for the status page / /api/v1/stats.
try {
const r2 = attachmentBucket
? await scanR2Usage(attachmentBucket)
-66
View File
@@ -1,66 +0,0 @@
import { describe, it, expect } from "vitest";
import worker from "../index";
import { createMockEnv } from "../test/setup";
import { bumpCounters } from "../utils/stats";
import { FEEDS_LIST_KEY } from "../config/constants";
import type { Env, StatsResponse } from "../types";
function req(path: string, init: RequestInit = {}): Request {
return new Request(`https://test.getmynews.app${path}`, init);
}
describe("GET /api/stats", () => {
it("returns zeroed stats for a fresh instance", async () => {
const env = createMockEnv() as unknown as Env;
const res = await worker.fetch(req("/api/stats"), env);
expect(res.status).toBe(200);
const body = (await res.json()) as StatsResponse;
expect(body).toMatchObject({
feeds_created: 0,
feeds_deleted: 0,
emails_received: 0,
emails_rejected: 0,
active_feeds: 0,
websub_subscriptions_active: 0,
});
});
it("reflects persisted counters and live values", async () => {
const env = createMockEnv() as unknown as Env;
await env.EMAIL_STORAGE.put(
FEEDS_LIST_KEY,
JSON.stringify({ feeds: [{ id: "a", title: "A" }] }),
);
await env.EMAIL_STORAGE.put("websub:a:hash", "{}");
await bumpCounters(env.EMAIL_STORAGE, {
emails_received: 3,
emails_rejected: 1,
feeds_created: 1,
});
const res = await worker.fetch(req("/api/stats"), env);
const body = (await res.json()) as StatsResponse;
expect(body.active_feeds).toBe(1);
expect(body.websub_subscriptions_active).toBe(1);
expect(body.emails_received).toBe(3);
expect(body.emails_rejected).toBe(1);
expect(body.feeds_created).toBe(1);
});
});
describe("GET / (public status page)", () => {
it("returns an HTML status page with counters and an admin link", async () => {
const env = createMockEnv() as unknown as Env;
await bumpCounters(env.EMAIL_STORAGE, { emails_received: 7 });
const res = await worker.fetch(req("/"), env);
expect(res.status).toBe(200);
expect(res.headers.get("Content-Type")).toContain("text/html");
const html = await res.text();
expect(html).toContain('href="/admin"');
expect(html).toContain("Active feeds");
expect(html).toContain("Emails received");
expect(html).toContain("7");
});
});
-7
View File
@@ -1,7 +0,0 @@
import { Context } from "hono";
import { Env } from "../types";
import { getStats } from "../utils/stats";
export async function handle(c: Context<{ Bindings: Env }>): Promise<Response> {
return c.json(await getStats(c.env));
}